suneditor 3.0.0-alpha.9 → 3.0.0-beta.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 (315) hide show
  1. package/CONTRIBUTING.md +170 -22
  2. package/{LICENSE.txt → LICENSE} +9 -9
  3. package/README.md +168 -30
  4. package/dist/suneditor.min.css +1 -1
  5. package/dist/suneditor.min.js +1 -1
  6. package/package.json +47 -21
  7. package/src/assets/design/color.css +121 -0
  8. package/src/assets/design/index.css +3 -0
  9. package/src/assets/design/size.css +35 -0
  10. package/src/assets/design/typography.css +37 -0
  11. package/src/assets/icons/defaultIcons.js +232 -0
  12. package/src/assets/suneditor-contents.css +181 -46
  13. package/src/assets/suneditor.css +1403 -650
  14. package/src/core/base/eventHandlers/handler_toolbar.js +35 -14
  15. package/src/core/base/eventHandlers/handler_ww_clipboard.js +23 -4
  16. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +49 -10
  17. package/src/core/base/eventHandlers/handler_ww_key_input.js +422 -224
  18. package/src/core/base/eventHandlers/handler_ww_mouse.js +83 -36
  19. package/src/core/base/eventManager.js +520 -179
  20. package/src/core/base/history.js +95 -41
  21. package/src/core/class/char.js +26 -11
  22. package/src/core/class/component.js +345 -137
  23. package/src/core/class/format.js +683 -519
  24. package/src/core/class/html.js +485 -305
  25. package/src/core/class/menu.js +133 -47
  26. package/src/core/class/nodeTransform.js +90 -71
  27. package/src/core/class/offset.js +408 -92
  28. package/src/core/class/selection.js +216 -106
  29. package/src/core/class/shortcuts.js +68 -8
  30. package/src/core/class/toolbar.js +106 -116
  31. package/src/core/class/ui.js +422 -0
  32. package/src/core/class/viewer.js +178 -74
  33. package/src/core/editor.js +496 -389
  34. package/src/core/section/actives.js +123 -27
  35. package/src/core/section/constructor.js +615 -206
  36. package/src/core/section/context.js +28 -23
  37. package/src/core/section/documentType.js +561 -0
  38. package/src/editorInjector/_classes.js +19 -5
  39. package/src/editorInjector/_core.js +71 -7
  40. package/src/editorInjector/index.js +63 -1
  41. package/src/events.js +622 -0
  42. package/src/helper/clipboard.js +59 -0
  43. package/src/helper/converter.js +202 -26
  44. package/src/helper/dom/domCheck.js +304 -0
  45. package/src/helper/dom/domQuery.js +669 -0
  46. package/src/helper/dom/domUtils.js +557 -0
  47. package/src/helper/dom/index.js +12 -0
  48. package/src/helper/env.js +46 -56
  49. package/src/helper/index.js +10 -4
  50. package/src/helper/keyCodeMap.js +183 -0
  51. package/src/helper/numbers.js +12 -8
  52. package/src/helper/unicode.js +9 -5
  53. package/src/langs/ckb.js +74 -4
  54. package/src/langs/cs.js +72 -2
  55. package/src/langs/da.js +73 -3
  56. package/src/langs/de.js +73 -4
  57. package/src/langs/en.js +23 -3
  58. package/src/langs/es.js +73 -4
  59. package/src/langs/fa.js +75 -3
  60. package/src/langs/fr.js +73 -3
  61. package/src/langs/he.js +73 -4
  62. package/src/langs/hu.js +230 -0
  63. package/src/langs/index.js +7 -3
  64. package/src/langs/it.js +70 -1
  65. package/src/langs/ja.js +72 -4
  66. package/src/langs/km.js +230 -0
  67. package/src/langs/ko.js +22 -2
  68. package/src/langs/lv.js +74 -5
  69. package/src/langs/nl.js +73 -4
  70. package/src/langs/pl.js +73 -4
  71. package/src/langs/pt_br.js +70 -1
  72. package/src/langs/ro.js +74 -5
  73. package/src/langs/ru.js +73 -4
  74. package/src/langs/se.js +73 -4
  75. package/src/langs/tr.js +73 -1
  76. package/src/langs/{ua.js → uk.js} +75 -6
  77. package/src/langs/ur.js +77 -8
  78. package/src/langs/zh_cn.js +74 -5
  79. package/src/modules/ApiManager.js +77 -54
  80. package/src/modules/Browser.js +667 -0
  81. package/src/modules/ColorPicker.js +162 -102
  82. package/src/modules/Controller.js +273 -142
  83. package/src/modules/Figure.js +925 -484
  84. package/src/modules/FileManager.js +121 -69
  85. package/src/modules/HueSlider.js +113 -61
  86. package/src/modules/Modal.js +291 -122
  87. package/src/modules/ModalAnchorEditor.js +383 -234
  88. package/src/modules/SelectMenu.js +270 -168
  89. package/src/modules/_DragHandle.js +2 -1
  90. package/src/modules/index.js +3 -3
  91. package/src/plugins/browser/audioGallery.js +83 -0
  92. package/src/plugins/browser/fileBrowser.js +103 -0
  93. package/src/plugins/browser/fileGallery.js +83 -0
  94. package/src/plugins/browser/imageGallery.js +81 -0
  95. package/src/plugins/browser/videoGallery.js +103 -0
  96. package/src/plugins/command/blockquote.js +40 -27
  97. package/src/plugins/command/exportPDF.js +134 -0
  98. package/src/plugins/command/fileUpload.js +229 -162
  99. package/src/plugins/command/list_bulleted.js +83 -47
  100. package/src/plugins/command/list_numbered.js +83 -47
  101. package/src/plugins/dropdown/align.js +66 -54
  102. package/src/plugins/dropdown/backgroundColor.js +63 -49
  103. package/src/plugins/dropdown/font.js +71 -47
  104. package/src/plugins/dropdown/fontColor.js +63 -48
  105. package/src/plugins/dropdown/formatBlock.js +70 -33
  106. package/src/plugins/dropdown/hr.js +92 -51
  107. package/src/plugins/dropdown/layout.js +37 -26
  108. package/src/plugins/dropdown/lineHeight.js +54 -38
  109. package/src/plugins/dropdown/list.js +60 -45
  110. package/src/plugins/dropdown/paragraphStyle.js +51 -30
  111. package/src/plugins/dropdown/table.js +2003 -813
  112. package/src/plugins/dropdown/template.js +38 -26
  113. package/src/plugins/dropdown/textStyle.js +43 -31
  114. package/src/plugins/field/mention.js +147 -86
  115. package/src/plugins/index.js +32 -6
  116. package/src/plugins/input/fontSize.js +161 -108
  117. package/src/plugins/input/pageNavigator.js +70 -0
  118. package/src/plugins/modal/audio.js +358 -173
  119. package/src/plugins/modal/drawing.js +531 -0
  120. package/src/plugins/modal/embed.js +886 -0
  121. package/src/plugins/modal/image.js +674 -362
  122. package/src/plugins/modal/link.js +100 -71
  123. package/src/plugins/modal/math.js +367 -167
  124. package/src/plugins/modal/video.js +691 -335
  125. package/src/plugins/popup/anchor.js +222 -0
  126. package/src/suneditor.js +50 -13
  127. package/src/themes/dark.css +122 -0
  128. package/src/typedef.js +130 -0
  129. package/types/assets/icons/defaultIcons.d.ts +153 -0
  130. package/types/core/base/eventHandlers/handler_toolbar.d.ts +41 -0
  131. package/types/core/base/eventHandlers/handler_ww_clipboard.d.ts +40 -0
  132. package/types/core/base/eventHandlers/handler_ww_dragDrop.d.ts +35 -0
  133. package/types/core/base/eventHandlers/handler_ww_key_input.d.ts +45 -0
  134. package/types/core/base/eventHandlers/handler_ww_mouse.d.ts +39 -0
  135. package/types/core/base/eventManager.d.ts +385 -0
  136. package/types/core/base/history.d.ts +81 -0
  137. package/types/core/class/char.d.ts +60 -0
  138. package/types/core/class/component.d.ts +212 -0
  139. package/types/core/class/format.d.ts +616 -0
  140. package/types/core/class/html.d.ts +422 -0
  141. package/types/core/class/menu.d.ts +126 -0
  142. package/types/core/class/nodeTransform.d.ts +93 -0
  143. package/types/core/class/offset.d.ts +522 -0
  144. package/types/core/class/selection.d.ts +188 -0
  145. package/types/core/class/shortcuts.d.ts +142 -0
  146. package/types/core/class/toolbar.d.ts +189 -0
  147. package/types/core/class/ui.d.ts +164 -0
  148. package/types/core/class/viewer.d.ts +140 -0
  149. package/types/core/editor.d.ts +610 -0
  150. package/types/core/section/actives.d.ts +46 -0
  151. package/types/core/section/constructor.d.ts +777 -0
  152. package/types/core/section/context.d.ts +45 -0
  153. package/types/core/section/documentType.d.ts +178 -0
  154. package/types/editorInjector/_classes.d.ts +41 -0
  155. package/types/editorInjector/_core.d.ts +92 -0
  156. package/types/editorInjector/index.d.ts +71 -0
  157. package/types/events.d.ts +273 -0
  158. package/types/helper/clipboard.d.ts +12 -0
  159. package/types/helper/converter.d.ts +197 -0
  160. package/types/helper/dom/domCheck.d.ts +189 -0
  161. package/types/helper/dom/domQuery.d.ts +223 -0
  162. package/types/helper/dom/domUtils.d.ts +226 -0
  163. package/types/helper/dom/index.d.ts +9 -0
  164. package/types/helper/env.d.ts +132 -0
  165. package/types/helper/index.d.ts +174 -0
  166. package/types/helper/keyCodeMap.d.ts +110 -0
  167. package/types/helper/numbers.d.ts +46 -0
  168. package/types/helper/unicode.d.ts +28 -0
  169. package/types/index.d.ts +120 -0
  170. package/{typings/Lang.d.ts → types/langs/_Lang.d.ts} +173 -103
  171. package/types/langs/ckb.d.ts +3 -0
  172. package/types/langs/cs.d.ts +3 -0
  173. package/types/langs/da.d.ts +3 -0
  174. package/types/langs/de.d.ts +3 -0
  175. package/types/langs/en.d.ts +3 -0
  176. package/types/langs/es.d.ts +3 -0
  177. package/types/langs/fa.d.ts +3 -0
  178. package/types/langs/fr.d.ts +3 -0
  179. package/types/langs/he.d.ts +3 -0
  180. package/types/langs/hu.d.ts +3 -0
  181. package/types/langs/index.d.ts +54 -0
  182. package/types/langs/it.d.ts +3 -0
  183. package/types/langs/ja.d.ts +3 -0
  184. package/types/langs/km.d.ts +3 -0
  185. package/types/langs/ko.d.ts +3 -0
  186. package/types/langs/lv.d.ts +3 -0
  187. package/types/langs/nl.d.ts +3 -0
  188. package/types/langs/pl.d.ts +3 -0
  189. package/types/langs/pt_br.d.ts +3 -0
  190. package/types/langs/ro.d.ts +3 -0
  191. package/types/langs/ru.d.ts +3 -0
  192. package/types/langs/se.d.ts +3 -0
  193. package/types/langs/tr.d.ts +3 -0
  194. package/types/langs/uk.d.ts +3 -0
  195. package/types/langs/ur.d.ts +3 -0
  196. package/types/langs/zh_cn.d.ts +3 -0
  197. package/types/modules/ApiManager.d.ts +125 -0
  198. package/types/modules/Browser.d.ts +326 -0
  199. package/types/modules/ColorPicker.d.ts +131 -0
  200. package/types/modules/Controller.d.ts +251 -0
  201. package/types/modules/Figure.d.ts +517 -0
  202. package/types/modules/FileManager.d.ts +202 -0
  203. package/types/modules/HueSlider.d.ts +136 -0
  204. package/types/modules/Modal.d.ts +111 -0
  205. package/types/modules/ModalAnchorEditor.d.ts +236 -0
  206. package/types/modules/SelectMenu.d.ts +194 -0
  207. package/types/modules/_DragHandle.d.ts +7 -0
  208. package/types/modules/index.d.ts +26 -0
  209. package/types/plugins/browser/audioGallery.d.ts +55 -0
  210. package/types/plugins/browser/fileBrowser.d.ts +64 -0
  211. package/types/plugins/browser/fileGallery.d.ts +55 -0
  212. package/types/plugins/browser/imageGallery.d.ts +51 -0
  213. package/types/plugins/browser/videoGallery.d.ts +57 -0
  214. package/types/plugins/command/blockquote.d.ts +28 -0
  215. package/types/plugins/command/exportPDF.d.ts +46 -0
  216. package/types/plugins/command/fileUpload.d.ts +156 -0
  217. package/types/plugins/command/list_bulleted.d.ts +46 -0
  218. package/types/plugins/command/list_numbered.d.ts +46 -0
  219. package/types/plugins/dropdown/align.d.ts +60 -0
  220. package/types/plugins/dropdown/backgroundColor.d.ts +63 -0
  221. package/types/plugins/dropdown/font.d.ts +54 -0
  222. package/types/plugins/dropdown/fontColor.d.ts +63 -0
  223. package/types/plugins/dropdown/formatBlock.d.ts +54 -0
  224. package/types/plugins/dropdown/hr.d.ts +71 -0
  225. package/types/plugins/dropdown/layout.d.ts +40 -0
  226. package/types/plugins/dropdown/lineHeight.d.ts +50 -0
  227. package/types/plugins/dropdown/list.d.ts +39 -0
  228. package/types/plugins/dropdown/paragraphStyle.d.ts +54 -0
  229. package/types/plugins/dropdown/table.d.ts +627 -0
  230. package/types/plugins/dropdown/template.d.ts +40 -0
  231. package/types/plugins/dropdown/textStyle.d.ts +41 -0
  232. package/types/plugins/field/mention.d.ts +102 -0
  233. package/types/plugins/index.d.ts +107 -0
  234. package/types/plugins/input/fontSize.d.ts +170 -0
  235. package/types/plugins/input/pageNavigator.d.ts +28 -0
  236. package/types/plugins/modal/audio.d.ts +269 -0
  237. package/types/plugins/modal/drawing.d.ts +246 -0
  238. package/types/plugins/modal/embed.d.ts +387 -0
  239. package/types/plugins/modal/image.d.ts +451 -0
  240. package/types/plugins/modal/link.d.ts +128 -0
  241. package/types/plugins/modal/math.d.ts +193 -0
  242. package/types/plugins/modal/video.d.ts +485 -0
  243. package/types/plugins/popup/anchor.d.ts +56 -0
  244. package/types/suneditor.d.ts +51 -0
  245. package/types/typedef.d.ts +233 -0
  246. package/.eslintignore +0 -7
  247. package/.eslintrc.json +0 -64
  248. package/src/assets/icons/_default.js +0 -194
  249. package/src/core/base/events.js +0 -320
  250. package/src/core/class/notice.js +0 -42
  251. package/src/helper/domUtils.js +0 -1177
  252. package/src/modules/FileBrowser.js +0 -271
  253. package/src/plugins/command/exportPdf.js +0 -168
  254. package/src/plugins/fileBrowser/imageGallery.js +0 -81
  255. package/src/themes/test.css +0 -61
  256. package/typings/CommandPlugin.d.ts +0 -8
  257. package/typings/DialogPlugin.d.ts +0 -20
  258. package/typings/FileBrowserPlugin.d.ts +0 -30
  259. package/typings/Module.d.ts +0 -15
  260. package/typings/Plugin.d.ts +0 -42
  261. package/typings/SubmenuPlugin.d.ts +0 -8
  262. package/typings/_classes.d.ts +0 -17
  263. package/typings/_colorPicker.d.ts +0 -60
  264. package/typings/_core.d.ts +0 -55
  265. package/typings/align.d.ts +0 -5
  266. package/typings/audio.d.ts +0 -5
  267. package/typings/backgroundColor.d.ts +0 -5
  268. package/typings/blockquote.d.ts +0 -5
  269. package/typings/char.d.ts +0 -39
  270. package/typings/component.d.ts +0 -38
  271. package/typings/context.d.ts +0 -39
  272. package/typings/converter.d.ts +0 -33
  273. package/typings/dialog.d.ts +0 -28
  274. package/typings/domUtils.d.ts +0 -361
  275. package/typings/editor.d.ts +0 -7
  276. package/typings/editor.ts +0 -542
  277. package/typings/env.d.ts +0 -70
  278. package/typings/eventManager.d.ts +0 -37
  279. package/typings/events.d.ts +0 -262
  280. package/typings/fileBrowser.d.ts +0 -42
  281. package/typings/fileManager.d.ts +0 -67
  282. package/typings/font.d.ts +0 -5
  283. package/typings/fontColor.d.ts +0 -5
  284. package/typings/fontSize.d.ts +0 -5
  285. package/typings/format.d.ts +0 -191
  286. package/typings/formatBlock.d.ts +0 -5
  287. package/typings/history.d.ts +0 -48
  288. package/typings/horizontalRule.d.ts +0 -5
  289. package/typings/image.d.ts +0 -5
  290. package/typings/imageGallery.d.ts +0 -5
  291. package/typings/index.d.ts +0 -21
  292. package/typings/index.modules.d.ts +0 -11
  293. package/typings/index.plugins.d.ts +0 -58
  294. package/typings/lineHeight.d.ts +0 -5
  295. package/typings/link.d.ts +0 -5
  296. package/typings/list.d.ts +0 -5
  297. package/typings/math.d.ts +0 -5
  298. package/typings/mediaContainer.d.ts +0 -25
  299. package/typings/mention.d.ts +0 -5
  300. package/typings/node.d.ts +0 -57
  301. package/typings/notice.d.ts +0 -16
  302. package/typings/numbers.d.ts +0 -29
  303. package/typings/offset.d.ts +0 -24
  304. package/typings/options.d.ts +0 -589
  305. package/typings/paragraphStyle.d.ts +0 -5
  306. package/typings/resizing.d.ts +0 -141
  307. package/typings/selection.d.ts +0 -94
  308. package/typings/shortcuts.d.ts +0 -13
  309. package/typings/suneditor.d.ts +0 -9
  310. package/typings/table.d.ts +0 -5
  311. package/typings/template.d.ts +0 -5
  312. package/typings/textStyle.d.ts +0 -5
  313. package/typings/toolbar.d.ts +0 -32
  314. package/typings/unicode.d.ts +0 -25
  315. package/typings/video.d.ts +0 -5
@@ -1,17 +1,35 @@
1
- import _icons from '../../assets/icons/_default';
1
+ import _icons from '../../assets/icons/defaultIcons';
2
2
  import _defaultLang from '../../langs/en';
3
3
  import { CreateContext, CreateFrameContext } from './context';
4
- import { domUtils, numbers, converter, env } from '../../helper';
4
+ import { dom, numbers, converter, env } from '../../helper';
5
5
 
6
6
  const _d = env._d;
7
- const DEFAULT_BUTTON_LIST = [['undo', 'redo'], ['bold', 'underline', 'italic', 'strike', 'subscript', 'superscript'], ['removeFormat'], ['outdent', 'indent'], ['fullScreen', 'showBlocks', 'codeView'], ['preview', 'print']];
7
+ const DEFAULT_BUTTON_LIST = [
8
+ ['undo', 'redo'],
9
+ '|',
10
+ ['bold', 'underline', 'italic', 'strike', '|', 'subscript', 'superscript'],
11
+ '|',
12
+ ['removeFormat'],
13
+ '|',
14
+ ['outdent', 'indent'],
15
+ '|',
16
+ ['fullScreen', 'showBlocks', 'codeView'],
17
+ '|',
18
+ ['preview', 'print']
19
+ ];
8
20
 
9
21
  const REQUIRED_FORMAT_LINE = 'div';
10
22
  const REQUIRED_ELEMENT_WHITELIST = 'br|div';
11
23
  const DEFAULT_ELEMENT_WHITELIST =
12
24
  'p|pre|blockquote|h1|h2|h3|h4|h5|h6|ol|ul|li|hr|figure|figcaption|img|iframe|audio|video|source|table|thead|tbody|tr|th|td|caption|a|b|strong|var|i|em|u|ins|s|span|strike|del|sub|sup|code|svg|path|details|summary';
13
25
  const DEFAULT_TEXT_STYLE_TAGS = 'strong|span|font|b|var|i|em|u|ins|s|strike|del|sub|sup|mark|a|label|code|summary';
14
- const DEFAULT_ATTRIBUTE_WHITELIST = 'contenteditable|target|href|title|download|rel|src|alt|class|type|controls|colspan|rowspan';
26
+
27
+ /* scopeSelectionTags */
28
+ const DEFAULT_SCOPE_SELECTION_TAGS = 'td|table|li|ol|ul|pre|figcaption|blockquote|dl|dt|dd';
29
+
30
+ const _video_audio_attr = '|controls|autoplay|loop|muted|poster|preload|playsinline|volume|crossorigin|disableRemotePlayback|controlsList';
31
+ const _iframe_attr = '|allowfullscreen|sandbox|loading|allow|referrerpolicy|frameborder|scrolling';
32
+ const DEFAULT_ATTRIBUTE_WHITELIST = 'contenteditable|target|href|title|download|rel|src|alt|class|type|colspan|rowspan' + _video_audio_attr + _iframe_attr;
15
33
 
16
34
  const DEFAULT_FORMAT_LINE = 'P|H[1-6]|LI|TH|TD|DETAILS';
17
35
  const DEFAULT_FORMAT_BR_LINE = 'PRE';
@@ -19,9 +37,12 @@ const DEFAULT_FORMAT_CLOSURE_BR_LINE = '';
19
37
  const DEFAULT_FORMAT_BLOCK = 'BLOCKQUOTE|OL|UL|FIGCAPTION|TABLE|THEAD|TBODY|TR|CAPTION|DETAILS';
20
38
  const DEFAULT_FORMAT_CLOSURE_BLOCK = 'TH|TD';
21
39
 
40
+ const DEFAULT_ALLOWED_EMPTY_NODE_LIST = '.se-component, pre, blockquote, hr, li, table, img, iframe, video, audio, canvas, details';
41
+
22
42
  const DEFAULT_SIZE_UNITS = ['px', 'pt', 'em', 'rem'];
23
43
 
24
- const DEFAULT_CLASS_NAME = '^__se__|^se-|^katex';
44
+ const DEFAULT_CLASS_NAME = '^__se__|^se-|^katex|^MathJax';
45
+ const DEFAULT_CLASS_MJX = 'mjx-container|mjx-math|mjx-mrow|mjx-mi|mjx-mo|mjx-mn|mjx-msup|mjx-mfrac|mjx-munderover';
25
46
  const DEFAULT_EXTRA_TAG_MAP = { script: false, style: false, meta: false, link: false, '[a-z]+:[a-z]+': false };
26
47
 
27
48
  const DEFAULT_TAG_STYLES = {
@@ -42,6 +63,7 @@ const DEFAULT_CONTENT_STYLES =
42
63
  'margin|margin-block-end|margin-block-start|margin-bottom|margin-inline-end|margin-inline-start|margin-left|margin-right|margin-top|max-width|min-width|' +
43
64
  'outline|overflow|' +
44
65
  'position|padding|padding-bottom|padding-inline-start|padding-left|padding-right|padding-top|' +
66
+ 'page-break-before|page-break-after|page-break-inside|' +
45
67
  'rotate|rotateX|rotateY|' +
46
68
  'table-layout|text-align|text-decoration|text-shadow|text-transform|top|' +
47
69
  'text-indent|text-rendering|' +
@@ -50,49 +72,270 @@ const DEFAULT_CONTENT_STYLES =
50
72
 
51
73
  const RETAIN_STYLE_MODE = ['repeat', 'always', 'none'];
52
74
 
53
- export const RO_UNAVAILABD = [
54
- 'mode',
55
- 'externalLibs',
56
- 'keepStyleOnDelete',
57
- 'iframe',
58
- 'convertTextTags',
59
- 'textStyleTags',
60
- 'fontSizeUnits',
61
- 'spanStyles',
62
- 'lineStyles',
63
- 'tagStyles',
64
- 'reverseCommands',
65
- 'shortcutsDisable',
66
- 'shortcuts',
67
- 'buttonList',
68
- 'subToolbar',
69
- 'toolbar_container',
70
- 'statusbar_container',
71
- 'elementWhitelist',
72
- 'elementBlacklist',
73
- 'attributeWhitelist',
74
- 'attributeBlacklist',
75
- 'defaultLine',
76
- 'formatClosureBrLine',
77
- 'formatBrLine',
78
- 'formatLine',
79
- 'formatClosureBlock',
80
- 'formatBlock',
81
- '__defaultElementWhitelist',
82
- '__defaultAttributeWhitelist',
83
- '__listCommonStyle',
84
- 'icons',
85
- 'lang',
86
- 'codeMirror'
87
- ];
75
+ /**
76
+ * @typedef {Object} EditorFrameOptions
77
+ * @property {string} [value=""] - Initial value for the editor.
78
+ * @property {string} [placeholder=""] - Placeholder text.
79
+ * @property {Object<string, string>} [editableFrameAttributes={}] - Attributes for the editable frame[.sun-editor-editable]. (e.g. [key]: value)
80
+ * @property {string} [width="100%"] - Width for the editor.
81
+ * @property {string} [minWidth=""] - Min width for the editor.
82
+ * @property {string} [maxWidth=""] - Max width for the editor.
83
+ * @property {string} [height="auto"] - Height for the editor.
84
+ * @property {string} [minHeight=""] - Min height for the editor.
85
+ * @property {string} [maxHeight=""] - Max height for the editor.
86
+ * @property {string} [editorStyle=""] - Style string of the top frame of the editor. (e.g. "border: 1px solid #ccc;").
87
+ * @property {boolean} [iframe=false] - Content will be placed in an iframe and isolated from the rest of the page.
88
+ * @property {boolean} [iframe_fullPage=false] - Allows the usage of HTML, HEAD, BODY tags and DOCTYPE declaration on the "iframe".
89
+ * @property {Object<string, string>} [iframe_attributes={}] - Attributes of the "iframe". (e.g. {'scrolling': 'no'})
90
+ * @property {string} [iframe_cssFileName="suneditor"] - Name or Array of the CSS file to apply inside the iframe.
91
+ * - You can also use regular expressions.
92
+ * - Applied by searching by filename in the link tag of document,
93
+ * - or put the URL value (".css" can be omitted).
94
+ * @property {boolean} [statusbar=true] - Enables the status bar.
95
+ * @property {boolean} [statusbar_showPathLabel=true] - Displays the current node structure to status bar.
96
+ * @property {boolean} [statusbar_resizeEnable=true] - Enables resize function of bottom status bar
97
+ * @property {boolean} [charCounter=false] - Shows the number of characters in the editor.
98
+ * - If the maxCharCount option has a value, it becomes true.
99
+ * @property {number} [charCounter_max] - The maximum number of characters allowed to be inserted into the editor.
100
+ * @property {string} [charCounter_label] - Text to be displayed in the "charCounter" area of the bottom bar. (e.g. "Characters : 20/200")
101
+ * @property {"char"|"byte"|"byte-html"} [charCounter_type="char"] - Defines the calculation method of the "charCounter" option.
102
+ * - 'char': Characters length.
103
+ * - 'byte': Binary data size of characters.
104
+ * - 'byte-html': Binary data size of the full HTML string.
105
+ */
106
+
107
+ /**
108
+ * @typedef {Object} EditorBaseOptions
109
+ * @property {Object<string, *>|Array<Object<string, *>>} [plugins] - Plugin configuration.
110
+ * @property {Array<string>} [excludedPlugins] - Plugin configuration.
111
+ * @property {Array<string[]|string>} [buttonList] - List of toolbar buttons, grouped by sub-arrays.
112
+ * @property {boolean} [v2Migration=false] - Enables migration mode for SunEditor v2.
113
+ * @property {boolean|{tagFilter: boolean, formatFilter: boolean, classFilter: boolean, styleNodeFilter: boolean, attrFilter: boolean, styleFilter: boolean}} [strictMode=true] - Enables strict filtering of tags, attributes, and styles.
114
+ * @property {"classic"|"inline"|"balloon"|"balloon-always"} [mode="classic"] - Toolbar mode: "classic", "inline", "balloon", "balloon-always".
115
+ * @property {string} [type=""] - Editor type: "document:header,page".
116
+ * @property {string} [theme=""] - Editor theme.
117
+ * @property {Object<string, string>} [lang] - Language configuration.
118
+ * @property {Array<string>} [fontSizeUnits=["px", "pt", "em", "rem"]] - Allowed font size units.
119
+ * @property {string} [allowedClassName] - Allowed class names.
120
+ * @property {boolean} [closeModalOutsideClick=false] - Closes modals when clicking outside.
121
+ * @property {boolean} [copyFormatKeepOn=false] - Keeps the format of the copied content.
122
+ * @property {boolean} [syncTabIndent=true] - Synchronizes tab indent with spaces.
123
+ * @property {boolean} [tabDisable=false] - Disables tab key input.
124
+ * @property {boolean} [autoLinkify] - Automatically converts URLs into hyperlinks. ("Link" plugin required)
125
+ * @property {Array<string>} [autoStyleify=["bold", "underline", "italic", "strike"]] - Styles applied automatically on text input.
126
+ * @property {Object<string, string|number>} [scrollToOptions={behavior: "auto", block: "nearest"}] - Configuration for scroll behavior when navigating editor content.
127
+ * @property {Object<string, string|number>} [componentScrollToOptions={behavior: "smooth", block: "center"}] - Configuration for scroll behavior when navigating components.
128
+ * @property {"repeat"|"always"|"none"} [retainStyleMode="repeat"] - This option determines how inline elements (such as <span>, <strong>, etc.) are handled when deleting text.
129
+ * - "repeat": Inline styles are retained unless the backspace key is repeatedly pressed. If the user continuously presses backspace, the styles will eventually be removed.
130
+ * - "none": Inline styles are not retained at all. When deleting text, the associated inline elements are immediately removed along with it.
131
+ * - "always": Inline styles persist indefinitely unless explicitly removed. Even if all text inside an inline element is deleted, the element itself remains until manually removed.
132
+ * @property {Object<string, boolean>} [allowedExtraTags={script: false, style: false, meta: false, link: false, "[a-z]+:[a-z]+": false}] - Specifies extra allowed or disallowed tags.
133
+ * @property {Object<string, (...args: *) => *>} [events={}] - Custom event handlers.
134
+ * @property {string} [__textStyleTags="strong|span|font|b|var|i|em|u|ins|s|strike|del|sub|sup|mark|a|label|code|summary"] - The basic tags that serves as the base for "textStyleTags"
135
+ * @property {string} [textStyleTags="strong|span|font|b|var|i|em|u|ins|s|strike|del|sub|sup|mark|a|label|code|summary"] - Additional text style tags.
136
+ * @property {Object<string, string>} [convertTextTags={bold: "strong", underline: "u", italic: "em", strike: "del", subscript: "sub", superscript: "sup"}] - Maps text styles to specific HTML tags.
137
+ * @property {Object<string, string>} [__tagStyles={'table|th|td': 'border|border-[a-z]+|background-color|text-align|float|font-weight|text-decoration|font-style', 'ol|ul': 'list-style-type'}] - The basic tags that serves as the base for "tagStyles"
138
+ * @property {Object<string, string>} [tagStyles={}] - Specifies allowed styles for HTML tags.
139
+ * @property {string} [spanStyles="font-family|font-size|color|background-color"] - Specifies allowed styles for the "span" tag.
140
+ * @property {string} [lineStyles="text-align|margin-left|margin-right|line-height"] - Specifies allowed styles for the "line" element (p..).
141
+ * @property {string} [textDirection="ltr"] - Text direction: "ltr" or "rtl".
142
+ * @property {Array<string>} [reverseButtons=['indent-outdent']] - An array of command pairs whose shortcut icons should be opposite each other, depending on the "textDirection" mode.
143
+ * @property {number} [historyStackDelayTime=400] - Delay time for history stack updates (ms).
144
+ * @property {string} [lineAttrReset=""] - Line properties that should be reset when changing lines (e.g. "id|name").
145
+ * @property {string} [printClass=""] - Class name for printing.
146
+ * @property {string} [defaultLine="p"] - Default line element when inserting new lines.
147
+ * @property {"line"|"br"} [defaultLineBreakFormat="line"] - Specifies the default line break format.
148
+ * - [Recommended] "line" : is a line break that is divided into general tags.
149
+ * - [Not recommended] "br" : Line breaks are treated as <br> on the same line. (like shift+enter)
150
+ * - Line breaks are handled as <br> within "line".
151
+ * - You can create a new "line" by entering a line break twice in a row.
152
+ * - Formats that include "line", such as "Quote", still operate on a "line" basis.
153
+ * - ● suneditor processes work in "line" units.
154
+ * - ● When set to "br", performance may decrease when editing a lot of data.
155
+ * @property {Array<string>} [scopeSelectionTags=["td", "table", "li", "ol", "ul", "pre", "figcaption", "blockquote", "dl", "dt", "dd"]] - Tags treated as whole units when selecting all content.
156
+ * @property {string} [__defaultElementWhitelist="br|div"] - Default allowed HTML elements. The default values are maintained.
157
+ * @property {string} [elementWhitelist=""] - Allowed HTML elements. Delimiter: "|" (e.g. "p|div", "*").
158
+ * @property {string} [elementBlacklist=""] - Disallowed HTML elements. Delimiter: "|" (e.g. "script|style").
159
+ * @property {string} [__defaultAttributeWhitelist] - Allowed attributes. Delimiter: "|" (e.g. "href|target").
160
+ * @property {Object<string, string>} [attributeWhitelist=""] - Allowed attributes. (e.g. {a: "href|target", img: "src|alt"}).
161
+ * @property {Object<string, string>} [attributeBlacklist=""] - Disallowed attributes. (e.g. {a: "href|target", img: "src|alt"}).
162
+ * @property {string} [__defaultFormatLine="P|DIV|H[1-6]|LI|TH|TD|DETAILS"] - Overrides the editor's default "line" element.
163
+ * @property {string} [formatLine="P|DIV|H[1-6]|LI|TH|TD|DETAILS"] - Specifies the editor's "line" elements.
164
+ * - (P, DIV, H[1-6], PRE, LI | class="__se__format__line_xxx")
165
+ * - "line" element also contain "brLine" element
166
+ * @property {string} [__defaultFormatBrLine="PRE"] - Overrides the editor's default "brLine" element.
167
+ * @property {string} [formatBrLine="PRE"] - Specifies the editor's "brLine" elements. (e.g. "PRE").
168
+ * - (PRE | class="__se__format__br_line_xxx")
169
+ * - "brLine" elements is included in the "line" element.
170
+ * - "brLine" elements's line break is "BR" tag.
171
+ * ※ Entering the Enter key in the space on the last line ends "brLine" and appends "line".
172
+ * @property {string} [__defaultFormatClosureBrLine=""] - Overrides the editor's default "closureBrLine" element.
173
+ * @property {string} [formatClosureBrLine=""] - Specifies the editor's "closureBrLine" elements.
174
+ * - (class="__se__format__br_line__closure_xxx")
175
+ * - "closureBrLine" elements is included in the "brLine".
176
+ * - "closureBrLine" elements's line break is "BR" tag.
177
+ * - ※ You cannot exit this format with the Enter key or Backspace key.
178
+ * - ※ Use it only in special cases. ([ex] format of table cells)
179
+ * @property {string} [__defaultFormatBlock="BLOCKQUOTE|OL|UL|FIGCAPTION|TABLE|THEAD|TBODY|TR|CAPTION|DETAILS"] - Overrides the editor's default "block" element.
180
+ * @property {string} [formatBlock="BLOCKQUOTE|OL|UL|FIGCAPTION|TABLE|THEAD|TBODY|TR|CAPTION|DETAILS"] - Specifies the editor's "block" elements.
181
+ * - (BLOCKQUOTE, OL, UL, FIGCAPTION, TABLE, THEAD, TBODY, TR, TH, TD | class="__se__format__block_xxx")
182
+ * - "block" is wrap the "line" and "component"
183
+ * @property {string} [__defaultFormatClosureBlock="TH|TD"] - Overrides the editor's default "closureBlock" element.
184
+ * @property {string} [formatClosureBlock="TH|TD"] - Specifies the editor's "closureBlock" elements.
185
+ * - (TH, TD | class="__se__format__block_closure_xxx")
186
+ * - "closureBlock" elements is included in the "block".
187
+ * - "closureBlock" element is wrap the "line" and "component"
188
+ * - ※ You cannot exit this format with the Enter key or Backspace key.
189
+ * - ※ Use it only in special cases. ([ex] format of table cells)
190
+ * @property {string} [allowedEmptyTags=".se-component, pre, blockquote, hr, li, table, img, iframe, video, audio, canvas, details"] - Allowed empty tags.
191
+ * @property {number|string} [toolbar_width="auto"] - Toolbar width.
192
+ * @property {Element|string} [toolbar_container] - Container element for the toolbar.
193
+ * @property {number} [toolbar_sticky=0] - Enables sticky toolbar with optional offset.
194
+ * @property {boolean} [toolbar_hide=false] - Hides toolbar initially.
195
+ * @property {Object} [subToolbar] - Sub-toolbar configuration.
196
+ * @property {Array<Array<string>>} [subToolbar.buttonList] - List of Sub-toolbar buttons, grouped by sub-arrays.
197
+ * @property {"balloon"|"balloon-always"} [subToolbar.mode="balloon"] - Sub-toolbar mode: "balloon", "balloon-always".
198
+ * @property {number|string} [subToolbar.width="auto"] - Sub-toolbar width.
199
+ * @property {Element|string} [statusbar_container] - Container element for the status bar.
200
+ * @property {boolean} [shortcutsHint=true] - Displays shortcut hints in tooltips.
201
+ * @property {boolean} [shortcutsDisable=false] - Disables keyboard shortcuts.
202
+ * @property {Object<string, Array<string>>} [shortcuts] - Custom keyboard shortcuts.
203
+ * @property {number} [fullScreenOffset=0] - Offset applied when entering fullscreen mode.
204
+ * @property {string} [previewTemplate] - Custom template for preview mode.
205
+ * @property {string} [printTemplate] - Custom template for print mode.
206
+ * @property {boolean} [componentAutoSelect=false] - Enables automatic selection of inserted components.
207
+ * @property {string} [defaultUrlProtocol] - Default URL protocol for links.
208
+ * @property {string} [allUsedStyles] - Specifies additional styles to the list of allowed styles. Delimiter: "|" (e.g. "color|background-color").
209
+ * @property {Object<"copy", number>} [toastMessageTime] - {"copy": 1500} - Duration for displaying toast messages.
210
+ * @property {Object<string, string>} [icons] - Overrides the default icons.
211
+ * @property {string} [freeCodeViewMode=false] - Enables free code view mode.
212
+ * @property {boolean} [__lineFormatFilter=true] - Line format filter configuration.
213
+ * @property {boolean} [__pluginRetainFilter=true] - Plugin retain filter configuration.
214
+ * @property {Array<string>} [__listCommonStyle=["fontSize", "color", "fontFamily", "fontWeight", "fontStyle"]] - Defines the list of styles that are applied directly to the `<li>` element
215
+ * - when a text style is applied to the entire list item.
216
+ * - For example, when changing the font size or color of a list item (`<li>`),
217
+ * - these styles will be applied to the `<li>` tag instead of wrapping the content inside additional tags.
218
+ * @property {Object<string, *>} [externalLibs] - External libraries like CodeMirror or MathJax.
219
+ *
220
+ * @property {Object<string, *>} [Dynamic_pluginOptions] - Dynamic plugin options, where the key is the plugin name and the value is its configuration.
221
+ */
222
+
223
+ /**
224
+ * @typedef {EditorBaseOptions & EditorFrameOptions} EditorInitOptions
225
+ */
226
+
227
+ /**
228
+ * @description For all EditorInitOptions keys, only boolean | null values are allowed.
229
+ * - 'fixed' → Immutable / null → Resettable.
230
+ * @type {Partial<Record<keyof EditorInitOptions, "fixed" | true>>}
231
+ */
232
+ export const OPTION_FIXED_FLAG = {
233
+ value: 'fixed',
234
+ placeholder: 'fixed',
235
+ editableFrameAttributes: null,
236
+ width: null,
237
+ minWidth: null,
238
+ maxWidth: null,
239
+ height: null,
240
+ minHeight: null,
241
+ maxHeight: null,
242
+ editorStyle: null,
243
+ iframe: 'fixed',
244
+ iframe_fullPage: null,
245
+ iframe_attributes: null,
246
+ iframe_cssFileName: null,
247
+ statusbar: null,
248
+ statusbar_showPathLabel: null,
249
+ statusbar_resizeEnable: null,
250
+ charCounter: null,
251
+ charCounter_max: null,
252
+ charCounter_label: null,
253
+ charCounter_type: null,
254
+ plugins: null,
255
+ excludedPlugins: null,
256
+ buttonList: 'fixed',
257
+ v2Migration: null,
258
+ strictMode: null,
259
+ mode: 'fixed',
260
+ type: 'fixed',
261
+ theme: null,
262
+ lang: 'fixed',
263
+ fontSizeUnits: 'fixed',
264
+ allowedClassName: null,
265
+ closeModalOutsideClick: null,
266
+ copyFormatKeepOn: null,
267
+ syncTabIndent: null,
268
+ tabDisable: null,
269
+ autoLinkify: null,
270
+ autoStyleify: null,
271
+ scrollToOptions: null,
272
+ componentScrollToOptions: null,
273
+ retainStyleMode: null,
274
+ allowedExtraTags: null,
275
+ events: null,
276
+ __textStyleTags: 'fixed',
277
+ textStyleTags: 'fixed',
278
+ convertTextTags: 'fixed',
279
+ __tagStyles: null,
280
+ tagStyles: 'fixed',
281
+ spanStyles: 'fixed',
282
+ lineStyles: 'fixed',
283
+ textDirection: null,
284
+ reverseButtons: null,
285
+ historyStackDelayTime: null,
286
+ lineAttrReset: null,
287
+ printClass: null,
288
+ defaultLine: 'fixed',
289
+ defaultLineBreakFormat: null,
290
+ scopeSelectionTags: null,
291
+ __defaultElementWhitelist: 'fixed',
292
+ elementWhitelist: 'fixed',
293
+ elementBlacklist: 'fixed',
294
+ __defaultAttributeWhitelist: 'fixed',
295
+ attributeWhitelist: 'fixed',
296
+ attributeBlacklist: 'fixed',
297
+ __defaultFormatLine: null,
298
+ formatLine: 'fixed',
299
+ __defaultFormatBrLine: null,
300
+ formatBrLine: 'fixed',
301
+ __defaultFormatClosureBrLine: 'fixed',
302
+ formatClosureBrLine: 'fixed',
303
+ __defaultFormatBlock: null,
304
+ formatBlock: 'fixed',
305
+ __defaultFormatClosureBlock: null,
306
+ formatClosureBlock: 'fixed',
307
+ allowedEmptyTags: null,
308
+ toolbar_width: null,
309
+ toolbar_container: 'fixed',
310
+ toolbar_sticky: null,
311
+ toolbar_hide: null,
312
+ subToolbar: 'fixed',
313
+ statusbar_container: 'fixed',
314
+ shortcutsHint: null,
315
+ shortcutsDisable: 'fixed',
316
+ shortcuts: 'fixed',
317
+ fullScreenOffset: null,
318
+ previewTemplate: null,
319
+ printTemplate: null,
320
+ componentAutoSelect: null,
321
+ defaultUrlProtocol: null,
322
+ allUsedStyles: null,
323
+ toastMessageTime: null,
324
+ icons: 'fixed',
325
+ freeCodeViewMode: null,
326
+ __lineFormatFilter: null,
327
+ __pluginRetainFilter: null,
328
+ __listCommonStyle: 'fixed',
329
+ externalLibs: 'fixed'
330
+ };
88
331
 
89
332
  /**
90
- * @description document create
91
- * @param {Object} options Options
92
- * @param {Element|Array.<Element>} editorTargets Target textarea
93
- * @returns {Object}
333
+ * @description Creates a new SunEditor instance with specified options.
334
+ * @param {Array<{target: Element, key: *, options: EditorFrameOptions}>} editorTargets - Target element or multi-root object.
335
+ * @param {EditorInitOptions} options - Configuration options for the editor.
336
+ * @returns {Object<string, *>} - SunEditor instance with context, options, and DOM elements.
94
337
  */
95
- const Constructor = function (editorTargets, options) {
338
+ function Constructor(editorTargets, options) {
96
339
  if (typeof options !== 'object') options = {};
97
340
 
98
341
  /** --- Plugins ------------------------------------------------------------------------------------------ */
@@ -113,35 +356,45 @@ const Constructor = function (editorTargets, options) {
113
356
  const o = optionMap.o;
114
357
  const icons = optionMap.i;
115
358
  const lang = optionMap.l;
116
- const loadingBox = domUtils.createElement('DIV', { class: 'se-loading-box sun-editor-common' }, '<div class="se-loading-effect"></div>');
359
+ const loadingBox = dom.utils.createElement('DIV', { class: 'se-loading-box sun-editor-common' }, '<div class="se-loading-effect"></div>');
117
360
 
118
361
  /** --- carrier wrapper --------------------------------------------------------------- */
119
- const editor_carrier_wrapper = domUtils.createElement('DIV', { class: 'sun-editor sun-editor-carrier-wrapper sun-editor-common' + (o.get('_rtl') ? ' se-rtl' : '') });
362
+ const editor_carrier_wrapper = dom.utils.createElement('DIV', { class: 'sun-editor sun-editor-carrier-wrapper sun-editor-common' + o.get('_themeClass') + (o.get('_rtl') ? ' se-rtl' : '') });
120
363
  // menuTray
121
- const menuTray = domUtils.createElement('DIV', { class: 'se-menu-tray' });
364
+ const menuTray = dom.utils.createElement('DIV', { class: 'se-menu-tray' });
122
365
  editor_carrier_wrapper.appendChild(menuTray);
123
366
  // focus temp element
124
- const focusTemp = domUtils.createElement('INPUT', {
125
- class: '__se__focus__temp__',
126
- style: 'position: absolute !important; top: -10000px !important; display: block !important; width: 0 !important; height: 0 !important; margin: 0 !important; padding: 0 !important;'
127
- });
367
+ const focusTemp = /** @type {HTMLInputElement} */ (
368
+ dom.utils.createElement('INPUT', {
369
+ class: '__se__focus__temp__',
370
+ style: 'position: fixed !important; top: -10000px !important; left: -10000px !important; display: block !important; width: 0 !important; height: 0 !important; margin: 0 !important; padding: 0 !important;'
371
+ })
372
+ );
128
373
  focusTemp.tabIndex = 0;
129
374
  editor_carrier_wrapper.appendChild(focusTemp);
130
375
 
131
376
  // modal
132
- const modal = domUtils.createElement('DIV', { class: 'se-modal sun-editor-common' });
133
- const modal_back = domUtils.createElement('DIV', { class: 'se-modal-back', style: 'display: none;' });
134
- const modal_inner = domUtils.createElement('DIV', { class: 'se-modal-inner', style: 'display: none;' });
377
+ const modal = dom.utils.createElement('DIV', { class: 'se-modal se-modal-area sun-editor-common' });
378
+ const modal_back = dom.utils.createElement('DIV', { class: 'se-modal-back' });
379
+ const modal_inner = dom.utils.createElement('DIV', { class: 'se-modal-inner' });
135
380
  modal.appendChild(modal_back);
136
381
  modal.appendChild(modal_inner);
137
382
  editor_carrier_wrapper.appendChild(modal);
138
383
 
384
+ // alert
385
+ const alert = dom.utils.createElement('DIV', { class: 'se-alert se-modal-area sun-editor-common', style: 'display: none;' });
386
+ const alert_back = dom.utils.createElement('DIV', { class: 'se-modal-back' });
387
+ const alert_inner = dom.utils.createElement('DIV', { class: 'se-modal-inner' });
388
+ alert.appendChild(alert_back);
389
+ alert.appendChild(alert_inner);
390
+ editor_carrier_wrapper.appendChild(alert);
391
+
139
392
  // loding box, resizing back
140
- editor_carrier_wrapper.appendChild(domUtils.createElement('DIV', { class: 'se-back-wrapper' }));
393
+ editor_carrier_wrapper.appendChild(dom.utils.createElement('DIV', { class: 'se-back-wrapper' }));
141
394
  editor_carrier_wrapper.appendChild(loadingBox.cloneNode(true));
142
395
 
143
396
  // drag cursor
144
- const dragCursor = domUtils.createElement('DIV', { class: 'se-drag-cursor' });
397
+ const dragCursor = dom.utils.createElement('DIV', { class: 'se-drag-cursor' });
145
398
  editor_carrier_wrapper.appendChild(dragCursor);
146
399
 
147
400
  // set carrier wrapper
@@ -160,7 +413,7 @@ const Constructor = function (editorTargets, options) {
160
413
  } else if (/balloon/i.test(o.get('mode'))) {
161
414
  toolbar.className += ' se-toolbar-balloon';
162
415
  toolbar.style.width = o.get('toolbar_width');
163
- toolbar.appendChild(domUtils.createElement('DIV', { class: 'se-arrow' }));
416
+ toolbar.appendChild(dom.utils.createElement('DIV', { class: 'se-arrow' }));
164
417
  }
165
418
 
166
419
  /** --- subToolbar --------------------------------------------------------------- */
@@ -171,7 +424,7 @@ const Constructor = function (editorTargets, options) {
171
424
  // subbar mode must be balloon-*
172
425
  subbar.className += ' se-toolbar-balloon se-toolbar-sub';
173
426
  subbar.style.width = o.get('toolbar.sub_width');
174
- subbar.appendChild(domUtils.createElement('DIV', { class: 'se-arrow' }));
427
+ subbar.appendChild(dom.utils.createElement('DIV', { class: 'se-arrow' }));
175
428
  }
176
429
 
177
430
  /** frame - root set - start -------------------------------------------------------------- */
@@ -182,12 +435,12 @@ const Constructor = function (editorTargets, options) {
182
435
  let default_status_bar = null;
183
436
  for (let i = 0, len = editorTargets.length; i < len; i++) {
184
437
  const editTarget = editorTargets[i];
185
- const to = editTarget.options;
186
- const top_div = domUtils.createElement('DIV', { class: 'sun-editor' + (to.get('_rtl') ? ' se-rtl' : '') });
187
- const container = domUtils.createElement('DIV', { class: 'se-container' });
188
- const editor_div = domUtils.createElement('DIV', { class: 'se-wrapper' });
438
+ const to = optionMap.frameMap.get(editTarget.key);
439
+ const top_div = dom.utils.createElement('DIV', { class: 'sun-editor' + o.get('_themeClass') + (o.get('_rtl') ? ' se-rtl' : '') });
440
+ const container = dom.utils.createElement('DIV', { class: 'se-container' });
441
+ const editor_div = dom.utils.createElement('DIV', { class: 'se-wrapper' + (o.get('type') === 'document' ? ' se-type-document' : '') });
189
442
 
190
- container.appendChild(domUtils.createElement('DIV', { class: 'se-toolbar-shadow' }));
443
+ container.appendChild(dom.utils.createElement('DIV', { class: 'se-toolbar-shadow' }));
191
444
 
192
445
  // init element
193
446
  const initElements = _initTargetElements(editTarget.key, o, top_div, to);
@@ -198,22 +451,22 @@ const Constructor = function (editorTargets, options) {
198
451
  let textarea = initElements.codeView;
199
452
 
200
453
  // line breaker
201
- const line_breaker_t = domUtils.createElement('DIV', { class: 'se-line-breaker-component se-line-breaker-component-t', title: lang.insertLine }, icons.line_break);
202
- const line_breaker_b = domUtils.createElement('DIV', { class: 'se-line-breaker-component se-line-breaker-component-b', title: lang.insertLine }, icons.line_break);
454
+ const line_breaker_t = dom.utils.createElement('DIV', { class: 'se-line-breaker-component se-line-breaker-component-t', title: lang.insertLine }, icons.line_break);
455
+ const line_breaker_b = dom.utils.createElement('DIV', { class: 'se-line-breaker-component se-line-breaker-component-b', title: lang.insertLine }, icons.line_break);
203
456
 
204
457
  editor_div.appendChild(line_breaker_t);
205
458
  editor_div.appendChild(line_breaker_b);
206
459
 
207
460
  // append container
208
461
  if (placeholder_span) editor_div.appendChild(placeholder_span);
209
- container.appendChild(domUtils.createElement('DIV', { class: 'se-toolbar-sticky-dummy' }));
462
+ container.appendChild(dom.utils.createElement('DIV', { class: 'se-toolbar-sticky-dummy' }));
210
463
  container.appendChild(editor_div);
211
464
 
212
465
  // statusbar
213
466
  if (statusbar) {
214
467
  if (statusbarContainer) {
215
468
  if (!default_status_bar) {
216
- statusbarContainer.appendChild(domUtils.createElement('DIV', { class: 'sun-editor' }, statusbar));
469
+ statusbarContainer.appendChild(dom.utils.createElement('DIV', { class: 'sun-editor' + o.get('_themeClass') }, statusbar));
217
470
  default_status_bar = statusbar;
218
471
  }
219
472
  } else {
@@ -228,7 +481,7 @@ const Constructor = function (editorTargets, options) {
228
481
  const key = editTarget.key || null;
229
482
 
230
483
  // code view - wrapper
231
- const codeWrapper = domUtils.createElement('DIV', { class: 'se-code-wrapper' }, textarea);
484
+ const codeWrapper = dom.utils.createElement('DIV', { class: 'se-code-wrapper' }, textarea);
232
485
  codeWrapper.style.setProperty('display', 'none', 'important');
233
486
  editor_div.appendChild(codeWrapper);
234
487
 
@@ -237,29 +490,46 @@ const Constructor = function (editorTargets, options) {
237
490
  // not used code mirror
238
491
  if (textarea === codeMirrorEl) {
239
492
  // add line nubers
240
- const codeNumbers = domUtils.createElement('TEXTAREA', { class: 'se-code-view-line', readonly: 'true' }, null);
493
+ const codeNumbers = dom.utils.createElement('TEXTAREA', { class: 'se-code-view-line', readonly: 'true' }, null);
241
494
  codeWrapper.insertBefore(codeNumbers, textarea);
242
495
  } else {
243
496
  textarea = codeMirrorEl;
244
497
  }
245
498
 
499
+ // document type
500
+ const documentTypeInner = { inner: null, page: null, pageMirror: null };
501
+ if (o.get('_type_options').includes('header')) {
502
+ documentTypeInner.inner = dom.utils.createElement('DIV', { class: 'se-document-lines', style: `height: ${to.get('height')};` }, '<div class="se-document-lines-inner"></div>');
503
+ }
504
+ if (o.get('_type_options').includes('page')) {
505
+ documentTypeInner.page = dom.utils.createElement('DIV', { class: 'se-document-page' }, null);
506
+ documentTypeInner.pageMirror = dom.utils.createElement(
507
+ 'DIV',
508
+ {
509
+ class: 'sun-editor-editable se-document-page-mirror-a4',
510
+ style: `position: absolute; width: 21cm; columns: 21cm; border: 0; overflow: hidden; height: auto; top: -10000px; left: -10000px;`
511
+ },
512
+ null
513
+ );
514
+ }
515
+
246
516
  // set container
247
517
  top_div.appendChild(container);
248
518
  rootKeys.push(key);
249
- frameRoots.set(key, CreateFrameContext(editTarget, top_div, wysiwyg_div, codeWrapper, textarea, default_status_bar || statusbar, key));
519
+ frameRoots.set(key, CreateFrameContext({ target: editTarget.target, key: editTarget.key, options: to }, top_div, wysiwyg_div, codeWrapper, textarea, default_status_bar || statusbar, documentTypeInner, key));
250
520
  }
251
521
  /** frame - root set - end -------------------------------------------------------------- */
252
522
 
253
523
  // toolbar container
254
524
  const toolbar_container = o.get('toolbar_container');
255
525
  if (toolbar_container) {
256
- const top_div = domUtils.createElement('DIV', { class: 'sun-editor' + (o.get('_rtl') ? ' se-rtl' : '') });
257
- const container = domUtils.createElement('DIV', { class: 'se-container' });
526
+ const top_div = dom.utils.createElement('DIV', { class: 'sun-editor' + o.get('_themeClass') + (o.get('_rtl') ? ' se-rtl' : '') });
527
+ const container = dom.utils.createElement('DIV', { class: 'se-container' });
258
528
  container.appendChild(toolbar);
259
529
  if (subbar) container.appendChild(subbar);
260
530
  top_div.appendChild(container);
261
531
  toolbar_container.appendChild(top_div);
262
- toolbar_container.appendChild(domUtils.createElement('DIV', { class: 'se-toolbar-sticky-dummy' }));
532
+ toolbar_container.appendChild(dom.utils.createElement('DIV', { class: 'se-toolbar-sticky-dummy' }));
263
533
  } else {
264
534
  const rootContainer = frameRoots.get(rootId).get('container');
265
535
  rootContainer.insertBefore(toolbar, rootContainer.firstElementChild);
@@ -282,30 +552,74 @@ const Constructor = function (editorTargets, options) {
282
552
  pluginCallButtons_sub: sub_main ? sub_main.pluginCallButtons : [],
283
553
  responsiveButtons_sub: sub_main ? sub_main.responsiveButtons : []
284
554
  };
285
- };
555
+ }
286
556
 
287
557
  /**
288
558
  * @description Create shortcuts desc span.
289
559
  * @param {string} command Command string
290
- * @param {Array.<string>} values options.shortcuts[command]
291
- * @param {Element} button Command button element
292
- * @param {Map} keyMap Map to store shortcut key info
560
+ * @param {Array<string>} values options.shortcuts[command]
561
+ * @param {Element|null} button Command button element
562
+ * @param {Map<string, *>} keyMap Map to store shortcut key info
293
563
  * @param {Array} rc "_reverseCommandArray" option
294
564
  * @param {Array} reverseKeys Reverse key array
295
565
  */
296
566
  export function CreateShortcuts(command, button, values, keyMap, rc, reverseKeys) {
297
567
  if (!values || values.length < 2) return;
298
- const tooptip = button.querySelector('.se-tooltip-text');
568
+ const tooptip = button?.querySelector('.se-tooltip-text');
569
+
570
+ for (let i = 0, a, v, c, s, edge, space, enter, textTrigger, plugin, method, t, k, r, _i; i < values.length; i += 2 + _i) {
571
+ _i = 0;
572
+ a = values[i].split('+');
573
+
574
+ plugin = null;
575
+ method = a[a.length - 1].trim?.();
576
+ if (method.startsWith('~')) {
577
+ plugin = command;
578
+ method = a.pop().trim().substring(1);
579
+ } else if (method.startsWith('p~')) {
580
+ const a_ = a.pop().trim().substring(2).split('.');
581
+ plugin = a_[0];
582
+ method = a_[1];
583
+ } else if (method.startsWith('$')) {
584
+ _i = 1;
585
+ method = values[i + 2];
586
+ } else {
587
+ method = '';
588
+ }
299
589
 
300
- for (let i = 0, v, s, t, k, r; i < values.length; i += 2) {
301
- v = values[i];
302
- s = /^s/i.test(v);
303
- k = numbers.get(v) + (s ? 1000 : 0);
590
+ c = s = edge = space = enter = textTrigger = v = null;
591
+ for (const a_ of a) {
592
+ switch (a_.trim()) {
593
+ case 'c':
594
+ c = true;
595
+ break;
596
+ case '!':
597
+ edge = true;
598
+ break;
599
+ case 's':
600
+ s = true;
601
+ break;
602
+ case '_':
603
+ space = true;
604
+ break;
605
+ case '=':
606
+ textTrigger = true;
607
+ break;
608
+ case '/':
609
+ enter = true;
610
+ break;
611
+ default:
612
+ v = a_;
613
+ }
614
+ }
615
+
616
+ k = c ? v + (s ? '1000' : '') : v;
304
617
  if (!keyMap.has(k)) {
305
618
  r = rc.indexOf(command);
306
619
  r = r === -1 ? '' : numbers.isOdd(r) ? rc[r + 1] : rc[r - 1];
307
620
  if (r) reverseKeys.push(k);
308
- keyMap.set(k, { c: command, r: r, t: button.getAttribute('data-type'), e: button });
621
+
622
+ keyMap.set(k, { c, s, edge, space, enter, textTrigger, plugin, command, method, r, type: button?.getAttribute('data-type'), button, key: k });
309
623
  }
310
624
 
311
625
  if (!(t = values[i + 1])) continue;
@@ -314,13 +628,15 @@ export function CreateShortcuts(command, button, values, keyMap, rc, reverseKeys
314
628
  }
315
629
 
316
630
  function _addTooltip(tooptipBtn, shift, shortcut) {
317
- tooptipBtn.appendChild(domUtils.createElement('SPAN', { class: 'se-shortcut' }, env.cmdIcon + (shift ? env.shiftIcon : '') + '+<span class="se-shortcut-key">' + shortcut + '</span>'));
631
+ tooptipBtn.appendChild(dom.utils.createElement('SPAN', { class: 'se-shortcut' }, env.cmdIcon + (shift ? env.shiftIcon : '') + '+<span class="se-shortcut-key">' + shortcut + '</span>'));
318
632
  }
319
633
 
320
634
  /**
635
+ * @private
321
636
  * @description Returns a new object with merge "a" and "b"
322
- * @param {Object} obj object
323
- * @returns {Object}
637
+ * @param {Object<*, *>} a object
638
+ * @param {Object<*, *>} b object
639
+ * @returns {Object<*, *>} new object
324
640
  */
325
641
  function _mergeObject(a, b) {
326
642
  return [a, b].reduce((_default, _new) => {
@@ -333,10 +649,18 @@ function _mergeObject(a, b) {
333
649
 
334
650
  /**
335
651
  * @description Initialize options
336
- * @param {Object} options Options object
337
- * @param {Array.<Element>} editorTargets Target textarea
338
- * @param {Object} plugins Plugins object
339
- * @returns {o:Map, p:Map} {{o: options map, p: plugins map}}
652
+ * @param {EditorInitOptions} options Configuration options for the editor.
653
+ * @param {Array<{target: Element, key: *, options: EditorFrameOptions}>} editorTargets Target textarea
654
+ * @param {Object<string, *>} plugins Plugins object
655
+ * @returns {{o: Map<string, *>, i: Object<string, string>, l: Object<string, string>, v: string, buttons: Array<string[]|string>, subButtons: Array<string[]|string>, statusbarContainer: Element|null, frameMap: Map<*, *>}}
656
+ * - o: options
657
+ * - i: icons
658
+ * - l: lang
659
+ * - v: value
660
+ * - buttons: Toolbar button list
661
+ * - subButtons: Sub-Toolbar button list
662
+ * - statusbarContainer: statusbar container
663
+ * - frameMap: converted options map
340
664
  */
341
665
  export function InitOptions(options, editorTargets, plugins) {
342
666
  const buttonList = options.buttonList || DEFAULT_BUTTON_LIST;
@@ -347,7 +671,11 @@ export function InitOptions(options, editorTargets, plugins) {
347
671
  if (!options.toolbar_container && !/inline|balloon/i.test(options.mode)) throw Error('[SUNEDITOR.create.fail] In multi root, The "mode" option cannot be "classic" without using the "toolbar_container" option.');
348
672
  }
349
673
 
674
+ // migration data-.+
675
+ o.set('v2Migration', !!options.v2Migration);
676
+
350
677
  /** Base */
678
+ o.set('buttons', new Set(buttonList.toString().split(',')));
351
679
  const modeValue = options.strictMode !== false;
352
680
  o.set('strictMode', {
353
681
  tagFilter: modeValue,
@@ -356,18 +684,24 @@ export function InitOptions(options, editorTargets, plugins) {
356
684
  styleNodeFilter: modeValue,
357
685
  attrFilter: modeValue,
358
686
  styleFilter: modeValue,
359
- ...options.strictMode
687
+ ...(typeof options.strictMode === 'boolean' ? {} : options.strictMode)
360
688
  });
689
+ o.set('freeCodeViewMode', !!options.freeCodeViewMode);
361
690
  o.set('__lineFormatFilter', options.__lineFormatFilter ?? true);
362
691
  o.set('__pluginRetainFilter', options.__pluginRetainFilter ?? true);
363
692
  o.set('mode', options.mode || 'classic'); // classic, inline, balloon, balloon-always
693
+ o.set('type', options.type?.split(':')[0] || ''); // document:header,page
694
+ o.set('theme', options.theme || '');
695
+ o.set('_themeClass', options.theme ? ` se-theme-${options.theme}` : '');
696
+ o.set('_type_options', options.type?.split(':')[1] || '');
364
697
  o.set('externalLibs', options.externalLibs || {});
365
- o.set('keepStyleOnDelete', !!options.keepStyleOnDelete);
366
698
  o.set('fontSizeUnits', Array.isArray(options.fontSizeUnits) && options.fontSizeUnits.length > 0 ? options.fontSizeUnits.map((v) => v.toLowerCase()) : DEFAULT_SIZE_UNITS);
367
699
  o.set('allowedClassName', new RegExp(`${options.allowedClassName && typeof options.allowedClassName === 'string' ? options.allowedClassName + '|' : ''}${DEFAULT_CLASS_NAME}`));
700
+ o.set('closeModalOutsideClick', !!options.closeModalOutsideClick);
368
701
 
369
702
  // format
370
703
  o.set('copyFormatKeepOn', !!options.copyFormatKeepOn);
704
+ o.set('syncTabIndent', options.syncTabIndent ?? true);
371
705
 
372
706
  // auto convert on paste
373
707
  o.set('autoLinkify', options.autoLinkify ?? !!plugins.link);
@@ -377,9 +711,6 @@ export function InitOptions(options, editorTargets, plugins) {
377
711
  o.set('scrollToOptions', { behavior: 'auto', block: 'nearest', ...options.scrollToOptions });
378
712
  o.set('componentScrollToOptions', { behavior: 'smooth', block: 'center', ...options.componentScrollToOptions });
379
713
 
380
- // migration data-.+
381
- o.set('v2Migration', !!options.v2Migration);
382
-
383
714
  let retainStyleMode = options.retainStyleMode;
384
715
  if (typeof retainStyleMode === 'string' && !RETAIN_STYLE_MODE.includes(retainStyleMode)) {
385
716
  console.error(`Invalid retainStyleMode: ${retainStyleMode}. Valid options are ${RETAIN_STYLE_MODE.join(', ')}. Using default 'once'.`);
@@ -462,6 +793,7 @@ export function InitOptions(options, editorTargets, plugins) {
462
793
  // text direction
463
794
  o.set('textDirection', typeof options.textDirection !== 'string' ? 'ltr' : options.textDirection);
464
795
  o.set('_rtl', o.get('textDirection') === 'rtl');
796
+ // An array of key codes generated with the reverseButtons option, used to reverse the action for a specific key combination.
465
797
  o.set('reverseCommands', ['indent-outdent'].concat(options.reverseButtons || []));
466
798
  o.set('_reverseCommandArray', ('-' + o.get('reverseCommands').join('-')).split('-'));
467
799
  if (numbers.isEven(o.get('_reverseCommandArray').length)) {
@@ -470,16 +802,19 @@ export function InitOptions(options, editorTargets, plugins) {
470
802
 
471
803
  // etc
472
804
  o.set('historyStackDelayTime', typeof options.historyStackDelayTime === 'number' ? options.historyStackDelayTime : 400);
473
- o.set('_editableClass', 'sun-editor-editable' + (o.get('_rtl') ? ' se-rtl' : ''));
805
+ o.set('_editableClass', 'sun-editor-editable' + o.get('_themeClass') + (o.get('_rtl') ? ' se-rtl' : '') + (o.get('type') === 'document' ? ' se-type-document-editable' : ''));
474
806
  o.set('lineAttrReset', ['id'].concat(options.lineAttrReset && typeof options.lineAttrReset === 'string' ? options.lineAttrReset.toLowerCase().split('|') : []));
475
- o.set('printClass', typeof options.printClass === 'string' ? options.printClass : null);
807
+ o.set('printClass', typeof options.printClass === 'string' ? options.printClass + ' ' + o.get('_editableClass') : null);
476
808
 
477
809
  /** whitelist, blacklist */
478
810
  // default line
479
811
  o.set('defaultLine', typeof options.defaultLine === 'string' && options.defaultLine.length > 0 ? options.defaultLine : 'p');
812
+ o.set('_defaultBrLineBreak', options.defaultLineBreakFormat === 'br');
813
+ o.set('scopeSelectionTags', options.scopeSelectionTags || DEFAULT_SCOPE_SELECTION_TAGS.split('|'));
480
814
  // element
481
815
  const elw = (typeof options.elementWhitelist === 'string' ? options.elementWhitelist : '').toLowerCase();
482
- o.set('elementWhitelist', elw + (elw ? '|' : '') + o.get('_allowedExtraTag'));
816
+ const mjxEls = o.get('externalLibs').mathjax ? DEFAULT_CLASS_MJX + '|' : '';
817
+ o.set('elementWhitelist', elw + (elw ? '|' : '') + mjxEls + o.get('_allowedExtraTag'));
483
818
  const elb = _createBlacklist((typeof options.elementBlacklist === 'string' ? options.elementBlacklist : '').toLowerCase(), o.get('defaultLine'));
484
819
  o.set('elementBlacklist', elb + (elb ? '|' : '') + o.get('_disallowedExtraTag'));
485
820
  // attribute
@@ -533,6 +868,8 @@ export function InitOptions(options, editorTargets, plugins) {
533
868
  )
534
869
  );
535
870
 
871
+ o.set('allowedEmptyTags', DEFAULT_ALLOWED_EMPTY_NODE_LIST + (options.allowedEmptyTags ? ', ' + options.allowedEmptyTags : ''));
872
+
536
873
  /** __defaults */
537
874
  o.set('__defaultElementWhitelist', REQUIRED_ELEMENT_WHITELIST + '|' + (typeof options.__defaultElementWhitelist === 'string' ? options.__defaultElementWhitelist : DEFAULT_ELEMENT_WHITELIST).toLowerCase());
538
875
  o.set('__defaultAttributeWhitelist', (typeof options.__defaultAttributeWhitelist === 'string' ? options.__defaultAttributeWhitelist : DEFAULT_ATTRIBUTE_WHITELIST).toLowerCase());
@@ -542,7 +879,7 @@ export function InitOptions(options, editorTargets, plugins) {
542
879
  /** Toolbar */
543
880
  o.set('toolbar_width', options.toolbar_width ? (numbers.is(options.toolbar_width) ? options.toolbar_width + 'px' : options.toolbar_width) : 'auto');
544
881
  o.set('toolbar_container', options.toolbar_container && !/inline/i.test(o.get('mode')) ? (typeof options.toolbar_container === 'string' ? _d.querySelector(options.toolbar_container) : options.toolbar_container) : null);
545
- o.set('toolbar_sticky', /balloon/i.test(o.get('mode')) ? -1 : options.toolbar_sticky === undefined ? 0 : /^\d+/.test(options.toolbar_sticky) ? numbers.get(options.toolbar_sticky, 0) : -1);
882
+ o.set('toolbar_sticky', /balloon/i.test(o.get('mode')) ? -1 : options.toolbar_sticky === undefined ? 0 : numbers.is(options.toolbar_sticky) ? numbers.get(options.toolbar_sticky, 0) : -1);
546
883
  o.set('toolbar_hide', !!options.toolbar_hide);
547
884
 
548
885
  /** subToolbar */
@@ -555,12 +892,14 @@ export function InitOptions(options, editorTargets, plugins) {
555
892
  o.set('_subMode', subbar.mode || 'balloon');
556
893
  o.set('toolbar.sub_width', subbar.width ? (numbers.is(subbar.width) ? subbar.width + 'px' : subbar.width) : 'auto');
557
894
  subButtons = o.get('_rtl') ? subbar.buttonList.reverse() : subbar.buttonList;
895
+ o.set('buttons_sub', new Set(subButtons.toString().split(',')));
558
896
  }
559
897
  }
560
898
 
561
899
  /** root options */
900
+ const frameMap = new Map();
562
901
  for (let i = 0, len = editorTargets.length; i < len; i++) {
563
- InitFrameOptions(editorTargets[i].options || {}, options, (editorTargets[i].options = new Map()));
902
+ frameMap.set(editorTargets[i].key, InitFrameOptions(editorTargets[i].options || {}, options));
564
903
  }
565
904
 
566
905
  /** Key actions */
@@ -571,20 +910,25 @@ export function InitOptions(options, editorTargets, plugins) {
571
910
  : [
572
911
  {
573
912
  // default command
574
- selectAll: ['65', 'A'],
575
- bold: ['66', 'B'],
576
- strike: ['s83', 'S'],
577
- underline: ['85', 'U'],
578
- italic: ['73', 'I'],
579
- redo: ['89', 'Y', 's90', 'Z'],
580
- undo: ['90', 'Z'],
581
- indent: ['221', ']'],
582
- outdent: ['219', '['],
583
- sup: ['187', '='],
584
- sub: ['s187', '='],
585
- save: ['83', 'S'],
913
+ selectAll: ['c+KeyA', 'A'],
914
+ bold: ['c+KeyB', 'B'],
915
+ strike: ['c+s+KeyS', 'S'],
916
+ underline: ['c+KeyU', 'U'],
917
+ italic: ['c+KeyI', 'I'],
918
+ redo: ['c+KeyY', 'Y', 'c+s+KeyZ', 'Z'],
919
+ undo: ['c+KeyZ', 'Z'],
920
+ indent: ['c+BracketRight', ']'],
921
+ outdent: ['c+BracketLeft', '['],
922
+ save: ['c+KeyS', 'S'],
586
923
  // plugins
587
- link: ['75', 'K']
924
+ link: ['c+KeyK', 'K'],
925
+ hr: ['!+---+=+~shortcut', ''],
926
+ list_numbered: ['!+1.+_+~shortcut', ''],
927
+ list_bulleted: ['!+*.+_+~shortcut', ''],
928
+ // custom
929
+ _h1: ['c+s+Digit1+p~formatBlock.createHeader', ''],
930
+ _h2: ['c+s+Digit2+p~formatBlock.createHeader', ''],
931
+ _h3: ['c+s+Digit3+p~formatBlock.createHeader', '']
588
932
  },
589
933
  options.shortcuts || {}
590
934
  ].reduce((_default, _new) => {
@@ -596,7 +940,7 @@ export function InitOptions(options, editorTargets, plugins) {
596
940
  o.set('shortcuts', shortcuts);
597
941
 
598
942
  /** View */
599
- o.set('fullScreenOffset', options.fullScreenOffset === undefined ? 0 : /^\d+/.test(options.fullScreenOffset) ? numbers.get(options.fullScreenOffset, 0) : 0);
943
+ o.set('fullScreenOffset', options.fullScreenOffset === undefined ? 0 : numbers.is(options.fullScreenOffset) ? numbers.get(options.fullScreenOffset, 0) : 0);
600
944
  o.set('previewTemplate', typeof options.previewTemplate === 'string' ? options.previewTemplate : null);
601
945
  o.set('printTemplate', typeof options.printTemplate === 'string' ? options.printTemplate : null);
602
946
 
@@ -661,46 +1005,54 @@ export function InitOptions(options, editorTargets, plugins) {
661
1005
  allUsedStyles.add(_aus[i]);
662
1006
  }
663
1007
  o.set('allUsedStyles', allUsedStyles);
1008
+ o.set('toastMessageTime', { copy: 1500, ...options.toastMessageTime });
664
1009
 
665
1010
  return {
666
1011
  o: o,
667
1012
  i: icons,
668
- l: options.lang || _defaultLang,
1013
+ l: /** @type {Object<string, string>} */ (options.lang || _defaultLang),
669
1014
  v: (options.value = typeof options.value === 'string' ? options.value : null),
670
1015
  buttons: o.get('_rtl') ? buttonList.reverse() : buttonList,
671
1016
  subButtons: subButtons,
672
- statusbarContainer: typeof options.statusbar_container === 'string' ? _d.querySelector(options.statusbar_container) : options.statusbar_container
1017
+ statusbarContainer: typeof options.statusbar_container === 'string' ? _d.querySelector(options.statusbar_container) : options.statusbar_container,
1018
+ frameMap: frameMap
673
1019
  };
674
1020
  }
675
1021
 
1022
+ /**
1023
+ * @description Create a context object for the editor frame.
1024
+ * @param {Map<string, *>} targetOptions - editor.frameOptions
1025
+ * @param {HTMLElement} statusbar - statusbar element
1026
+ * @returns {{statusbar: HTMLElement, navigation: HTMLElement, charWrapper: HTMLElement, charCounter: HTMLElement}}
1027
+ */
676
1028
  export function CreateStatusbar(targetOptions, statusbar) {
677
1029
  let navigation = null;
678
1030
  let charWrapper = null;
679
1031
  let charCounter = null;
680
1032
 
681
1033
  if (targetOptions.get('statusbar')) {
682
- statusbar = statusbar || domUtils.createElement('DIV', { class: 'se-status-bar sun-editor-common' });
1034
+ statusbar = statusbar || dom.utils.createElement('DIV', { class: 'se-status-bar sun-editor-common' });
683
1035
 
684
1036
  /** navigation */
685
- navigation = statusbar.querySelector('.se-navigation') || domUtils.createElement('DIV', { class: 'se-navigation sun-editor-common' });
1037
+ navigation = statusbar.querySelector('.se-navigation') || dom.utils.createElement('DIV', { class: 'se-navigation sun-editor-common' });
686
1038
  statusbar.appendChild(navigation);
687
1039
 
688
1040
  /** char counter */
689
1041
  if (targetOptions.get('charCounter')) {
690
- charWrapper = statusbar.querySelector('.se-char-counter-wrapper') || domUtils.createElement('DIV', { class: 'se-char-counter-wrapper' });
1042
+ charWrapper = statusbar.querySelector('.se-char-counter-wrapper') || dom.utils.createElement('DIV', { class: 'se-char-counter-wrapper' });
691
1043
 
692
1044
  if (targetOptions.get('charCounter_label')) {
693
- const charLabel = charWrapper.querySelector('.se-char-label') || domUtils.createElement('SPAN', { class: 'se-char-label' });
1045
+ const charLabel = charWrapper.querySelector('.se-char-label') || dom.utils.createElement('SPAN', { class: 'se-char-label' });
694
1046
  charLabel.textContent = targetOptions.get('charCounter_label');
695
1047
  charWrapper.appendChild(charLabel);
696
1048
  }
697
1049
 
698
- charCounter = charWrapper.querySelector('.se-char-counter') || domUtils.createElement('SPAN', { class: 'se-char-counter' });
1050
+ charCounter = charWrapper.querySelector('.se-char-counter') || dom.utils.createElement('SPAN', { class: 'se-char-counter' });
699
1051
  charCounter.textContent = '0';
700
1052
  charWrapper.appendChild(charCounter);
701
1053
 
702
1054
  if (targetOptions.get('charCounter_max') > 0) {
703
- const char_max = charWrapper.querySelector('.se-char-max') || domUtils.createElement('SPAN', { class: 'se-char-max' });
1055
+ const char_max = charWrapper.querySelector('.se-char-max') || dom.utils.createElement('SPAN', { class: 'se-char-max' });
704
1056
  char_max.textContent = ' / ' + targetOptions.get('charCounter_max');
705
1057
  charWrapper.appendChild(char_max);
706
1058
  }
@@ -711,13 +1063,21 @@ export function CreateStatusbar(targetOptions, statusbar) {
711
1063
 
712
1064
  return {
713
1065
  statusbar: statusbar,
714
- navigation: navigation,
715
- charWrapper: charWrapper,
716
- charCounter: charCounter
1066
+ navigation: /** @type {HTMLElement} */ (navigation),
1067
+ charWrapper: /** @type {HTMLElement} */ (charWrapper),
1068
+ charCounter: /** @type {HTMLElement} */ (charCounter)
717
1069
  };
718
1070
  }
719
1071
 
720
- function InitFrameOptions(o, origin, fo) {
1072
+ /**
1073
+ * @description Initialize options.
1074
+ * @param {EditorFrameOptions} o - Target options
1075
+ * @param {EditorInitOptions} origin - Full options
1076
+ * @returns {Map<string, *>}
1077
+ */
1078
+ function InitFrameOptions(o, origin) {
1079
+ const fo = new Map();
1080
+
721
1081
  fo.set('_origin', o);
722
1082
  const barContainer = origin.statusbar_container;
723
1083
 
@@ -771,14 +1131,18 @@ function InitFrameOptions(o, origin, fo) {
771
1131
  fo.set('charCounter_max', numbers.is(charCounter_max) && charCounter_max > -1 ? charCounter_max * 1 : null);
772
1132
  fo.set('charCounter_label', typeof charCounter_label === 'string' ? charCounter_label.trim() : null);
773
1133
  fo.set('charCounter_type', typeof charCounter_type === 'string' ? charCounter_type : 'char');
1134
+
1135
+ return fo;
774
1136
  }
775
1137
 
776
1138
  /**
1139
+ * @private
777
1140
  * @description Initialize property of suneditor elements
778
- * @param {string} key Key
779
- * @param {Object} options Options
780
- * @param {Element} topDiv Suneditor top div
781
- * @returns {Object} Bottom bar elements (statusbar, navigation, charWrapper, charCounter)
1141
+ * @param {string} key - The key of the editor frame
1142
+ * @param {Map<string, *>} options - options
1143
+ * @param {HTMLElement} topDiv - top div
1144
+ * @param {Map<string, *>} targetOptions - editor.frameOptions
1145
+ * @returns {{bottomBar: ReturnType<CreateStatusbar>, wysiwygFrame: HTMLElement, codeView: HTMLElement, placeholder: HTMLElement}}
782
1146
  */
783
1147
  function _initTargetElements(key, options, topDiv, targetOptions) {
784
1148
  const editorStyles = targetOptions.get('_defaultStyles');
@@ -787,13 +1151,13 @@ function _initTargetElements(key, options, topDiv, targetOptions) {
787
1151
 
788
1152
  /** editor */
789
1153
  // wysiwyg div or iframe
790
- const wysiwygDiv = domUtils.createElement(!targetOptions.get('iframe') ? 'DIV' : 'IFRAME', {
1154
+ const wysiwygDiv = dom.utils.createElement(!targetOptions.get('iframe') ? 'DIV' : 'IFRAME', {
791
1155
  class: 'se-wrapper-inner se-wrapper-wysiwyg',
792
1156
  'data-root-key': key
793
1157
  });
794
1158
 
795
1159
  if (!targetOptions.get('iframe')) {
796
- wysiwygDiv.setAttribute('contenteditable', true);
1160
+ wysiwygDiv.setAttribute('contenteditable', 'true');
797
1161
  wysiwygDiv.setAttribute('scrolling', 'auto');
798
1162
  wysiwygDiv.className += ' ' + options.get('_editableClass');
799
1163
  wysiwygDiv.style.cssText = editorStyles.frame + editorStyles.editor;
@@ -802,17 +1166,19 @@ function _initTargetElements(key, options, topDiv, targetOptions) {
802
1166
  for (const frameKey in frameAttrs) {
803
1167
  wysiwygDiv.setAttribute(frameKey, frameAttrs[frameKey]);
804
1168
  }
805
- wysiwygDiv.allowFullscreen = true;
806
- wysiwygDiv.frameBorder = 0;
807
- wysiwygDiv.style.cssText = editorStyles.frame;
1169
+
1170
+ const iframeWW = /** @type {HTMLIFrameElement} */ (wysiwygDiv);
1171
+ iframeWW.allowFullscreen = true;
1172
+ iframeWW.frameBorder = '0';
1173
+ iframeWW.style.cssText = editorStyles.frame;
808
1174
  }
809
1175
 
810
1176
  // textarea for code view
811
- const textarea = domUtils.createElement('TEXTAREA', { class: 'se-wrapper-inner se-code-viewer', style: editorStyles.frame });
1177
+ const textarea = dom.utils.createElement('TEXTAREA', { class: 'se-wrapper-inner se-code-viewer', style: editorStyles.frame });
812
1178
  let placeholder = null;
813
1179
  if (targetOptions.get('placeholder')) {
814
- placeholder = domUtils.createElement('SPAN', { class: 'se-placeholder' });
815
- placeholder.innerText = targetOptions.get('placeholder');
1180
+ placeholder = dom.utils.createElement('SPAN', { class: 'se-placeholder' });
1181
+ placeholder.textContent = targetOptions.get('placeholder');
816
1182
  }
817
1183
 
818
1184
  return {
@@ -824,9 +1190,10 @@ function _initTargetElements(key, options, topDiv, targetOptions) {
824
1190
  }
825
1191
 
826
1192
  /**
1193
+ * @private
827
1194
  * @description Check the CodeMirror option to apply the CodeMirror and return the CodeMirror element.
828
- * @param {Object} options options
829
- * @param {Element} textarea textarea element
1195
+ * @param {Map<string, *>} options options
1196
+ * @param {HTMLElement} textarea textarea element
830
1197
  */
831
1198
  function _checkCodeMirror(options, targetOptions, textarea) {
832
1199
  let cmeditor = null;
@@ -877,7 +1244,7 @@ function _checkCodeMirror(options, targetOptions, textarea) {
877
1244
 
878
1245
  options.set('hasCodeMirror', hasCodeMirror);
879
1246
  if (cmeditor) {
880
- domUtils.removeItem(textarea);
1247
+ dom.utils.removeItem(textarea);
881
1248
  cmeditor.className += ' se-code-viewer-mirror';
882
1249
  return cmeditor;
883
1250
  }
@@ -886,6 +1253,7 @@ function _checkCodeMirror(options, targetOptions, textarea) {
886
1253
  }
887
1254
 
888
1255
  /**
1256
+ * @private
889
1257
  * @description create blacklist
890
1258
  * @param {string} blacklist blacklist
891
1259
  * @param {string} defaultLine options.get('defaultLine')
@@ -907,6 +1275,7 @@ function _createBlacklist(blacklist, defaultLine) {
907
1275
  }
908
1276
 
909
1277
  /**
1278
+ * @private
910
1279
  * @description create formats regexp object.
911
1280
  * @param {string} value value
912
1281
  * @param {string} defaultValue default value
@@ -927,8 +1296,9 @@ function _createFormatInfo(value, defaultValue, blacklist) {
927
1296
  }
928
1297
 
929
1298
  /**
1299
+ * @private
930
1300
  * @description create whitelist or blacklist.
931
- * @param {Object} o options
1301
+ * @param {Map<string, *>} o options
932
1302
  * @returns {string} whitelist
933
1303
  */
934
1304
  function _createWhitelist(o) {
@@ -942,8 +1312,9 @@ function _createWhitelist(o) {
942
1312
  }
943
1313
 
944
1314
  /**
1315
+ * @private
945
1316
  * @description Suneditor's Default button list
946
- * @param {Object} options options
1317
+ * @param {Map<string, *>} options options
947
1318
  */
948
1319
  function _defaultButtons(options, icons, lang) {
949
1320
  const isRTL = options.get('_rtl');
@@ -965,22 +1336,29 @@ function _defaultButtons(options, icons, lang) {
965
1336
  redo: ['se-component-enabled', lang.redo, 'redo', '', icons.redo],
966
1337
  preview: ['se-component-enabled', lang.preview, 'preview', '', icons.preview],
967
1338
  print: ['se-component-enabled', lang.print, 'print', '', icons.print],
1339
+ copy: ['', lang.copy, 'copy', '', icons.copy],
968
1340
  dir: ['', lang[isRTL ? 'dir_ltr' : 'dir_rtl'], 'dir', '', icons[isRTL ? 'dir_ltr' : 'dir_rtl']],
969
1341
  dir_ltr: ['', lang.dir_ltr, 'dir_ltr', '', icons.dir_ltr],
970
1342
  dir_rtl: ['', lang.dir_rtl, 'dir_rtl', '', icons.dir_rtl],
971
1343
  save: ['se-component-enabled', lang.save, 'save', '', icons.save],
972
1344
  newDocument: ['se-component-enabled', lang.newDocument, 'newDocument', '', icons.new_document],
973
- selectAll: ['se-component-enabled', lang.selectAll, 'selectAll', '', icons.select_all]
1345
+ selectAll: ['se-component-enabled', lang.selectAll, 'selectAll', '', icons.select_all],
1346
+ pageBreak: ['se-component-enabled', lang.pageBreak, 'pageBreak', '', icons.page_break],
1347
+ // document type buttons
1348
+ pageUp: ['se-component-enabled', lang.pageUp, 'pageUp', '', icons.page_up],
1349
+ pageDown: ['se-component-enabled', lang.pageDown, 'pageDown', '', icons.page_down],
1350
+ pageNavigator: ['se-component-enabled', '', 'pageNavigator', 'input', '']
974
1351
  };
975
1352
  }
976
1353
 
977
1354
  /**
1355
+ * @private
978
1356
  * @description Create a group div containing each module
979
- * @returns {Object}
1357
+ * @returns {{div: Element, ul: Element}}
980
1358
  */
981
1359
  function _createModuleGroup() {
982
- const oUl = domUtils.createElement('UL', { class: 'se-menu-list' });
983
- const oDiv = domUtils.createElement('DIV', { class: 'se-btn-module se-btn-module-border' }, oUl);
1360
+ const oUl = dom.utils.createElement('UL', { class: 'se-menu-list' });
1361
+ const oDiv = dom.utils.createElement('DIV', { class: 'se-btn-module se-btn-module-border' }, oUl);
984
1362
 
985
1363
  return {
986
1364
  div: oDiv,
@@ -989,27 +1367,38 @@ function _createModuleGroup() {
989
1367
  }
990
1368
 
991
1369
  /**
1370
+ * @private
992
1371
  * @description Create a button element
993
1372
  * @param {string} className className in button
994
1373
  * @param {string} title Title in button
995
1374
  * @param {string} dataCommand The data-command property of the button
996
- * @param {string} dataType The data-type property of the button ('modal', 'dropdown', 'command', 'container')
1375
+ * @param {"command"|"dropdown"|"field"|"browser"|"input"|"modal"|"popup"} dataType The data-type property of the button
997
1376
  * @param {string} innerHTML Html in button
998
1377
  * @param {string} _disabled Button disabled
999
- * @param {Object} icons Icons
1000
- * @returns {Object}
1378
+ * @param {Object<string, string>} icons Icons
1379
+ * @returns {{li: HTMLElement, button: HTMLElement}}
1001
1380
  */
1002
1381
  function _createButton(className, title, dataCommand, dataType, innerHTML, _disabled, icons) {
1003
- const oLi = domUtils.createElement('LI');
1382
+ if (!innerHTML) innerHTML = '';
1383
+
1384
+ const oLi = dom.utils.createElement('LI');
1004
1385
  const label = title || '';
1005
- const oButton = domUtils.createElement(/^INPUT|FIELD$/i.test(dataType) ? 'DIV' : 'BUTTON', {
1006
- type: 'button',
1007
- class: 'se-toolbar-btn se-btn se-tooltip' + (className ? ' ' + className : ''),
1008
- 'data-command': dataCommand,
1009
- 'data-type': dataType,
1010
- 'aria-label': label.replace(/<span .+<\/span>/, ''),
1011
- tabindex: '-1'
1012
- });
1386
+ const isDiv = /^INPUT|FIELD$/i.test(dataType);
1387
+ const oButton = /** @type {HTMLButtonElement} */ (
1388
+ 'se-toolbar-separator-vertical' === className
1389
+ ? dom.utils.createElement('DIV', { class: className, tabindex: '-1' }, null)
1390
+ : dom.utils.createElement(isDiv ? 'DIV' : 'BUTTON', {
1391
+ class: 'se-toolbar-btn se-btn se-tooltip' + (className ? ' ' + className : ''),
1392
+ 'data-command': dataCommand,
1393
+ 'data-type': dataType,
1394
+ 'aria-label': label.replace(/<span .+<\/span>/, ''),
1395
+ tabindex: '-1'
1396
+ })
1397
+ );
1398
+
1399
+ if (!isDiv) {
1400
+ oButton.setAttribute('type', 'button');
1401
+ }
1013
1402
 
1014
1403
  if (/^default\./i.test(innerHTML)) {
1015
1404
  innerHTML = icons[innerHTML.replace(/^default\./i, '')];
@@ -1019,11 +1408,11 @@ function _createButton(className, title, dataCommand, dataType, innerHTML, _disa
1019
1408
  oButton.className += ' se-btn-more-text';
1020
1409
  }
1021
1410
 
1022
- if (_disabled) oButton.setAttribute('disabled', true);
1411
+ if (_disabled) oButton.disabled = true;
1023
1412
 
1024
- if (/^FIELD$/i.test(dataType)) domUtils.addClass(oLi, 'se-toolbar-hidden-btn');
1413
+ if (/^FIELD$/i.test(dataType)) dom.utils.addClass(oLi, 'se-toolbar-hidden-btn');
1025
1414
 
1026
- if (label) innerHTML += CreateTooltipInner(label);
1415
+ if (label) innerHTML += dom.utils.createTooltipInner(label);
1027
1416
  if (innerHTML) oButton.innerHTML = innerHTML;
1028
1417
 
1029
1418
  oLi.appendChild(oButton);
@@ -1034,18 +1423,25 @@ function _createButton(className, title, dataCommand, dataType, innerHTML, _disa
1034
1423
  };
1035
1424
  }
1036
1425
 
1037
- export function CreateTooltipInner(text) {
1038
- return `<span class="se-tooltip-inner"><span class="se-tooltip-text">${text}</span></span>`;
1039
- }
1040
-
1426
+ /**
1427
+ * @description Update a button state, attributes, and icons
1428
+ * @param {HTMLElement|null} element Button element
1429
+ * @param {Object<string, *>} plugin Plugin
1430
+ * @param {Object<string, string>} icons Icons
1431
+ * @param {Object<string, string>} lang lang
1432
+ */
1041
1433
  export function UpdateButton(element, plugin, icons, lang) {
1042
1434
  if (!element) return;
1043
1435
 
1044
1436
  const noneInner = plugin.inner === false;
1045
1437
 
1046
- element.innerHTML = noneInner
1047
- ? ''
1048
- : (plugin.inner || icons[plugin.icon] || plugin.icon || '<span class="se-icon-text">!</span>') + '<span class="se-tooltip-inner"><span class="se-tooltip-text">' + (lang[plugin.title] || plugin.title) + '</span></span>';
1438
+ if (plugin.inner?.nodeType === 1) {
1439
+ element.appendChild(plugin.inner);
1440
+ } else {
1441
+ element.innerHTML = noneInner
1442
+ ? ''
1443
+ : (plugin.inner || icons[plugin.icon] || plugin.icon || '<span class="se-icon-text">!</span>') + '<span class="se-tooltip-inner"><span class="se-tooltip-text">' + (lang[plugin.title] || plugin.title) + '</span></span>';
1444
+ }
1049
1445
 
1050
1446
  element.setAttribute('aria-label', plugin.title);
1051
1447
 
@@ -1058,24 +1454,24 @@ export function UpdateButton(element, plugin, icons, lang) {
1058
1454
  }
1059
1455
 
1060
1456
  // side, replace button
1061
- if (plugin.afterButton) {
1062
- domUtils.addClass(plugin.afterButton, 'se-toolbar-btn');
1063
- element.parentElement.appendChild(plugin.afterButton);
1457
+ if (plugin.afterItem) {
1458
+ dom.utils.addClass(plugin.afterItem, 'se-toolbar-btn');
1459
+ element.parentElement.appendChild(plugin.afterItem);
1064
1460
 
1065
- domUtils.addClass(element, 'se-side-btn-a');
1066
- domUtils.addClass(plugin.afterButton, 'se-side-btn-after');
1461
+ dom.utils.addClass(element, 'se-side-btn-a');
1462
+ dom.utils.addClass(plugin.afterItem, 'se-side-btn-after');
1067
1463
  }
1068
- if (plugin.beforeButton) {
1069
- domUtils.addClass(plugin.beforeButton, 'se-toolbar-btn');
1070
- element.parentElement.insertBefore(plugin.beforeButton, element);
1464
+ if (plugin.beforeItem) {
1465
+ dom.utils.addClass(plugin.beforeItem, 'se-toolbar-btn');
1466
+ element.parentElement.insertBefore(plugin.beforeItem, element);
1071
1467
 
1072
- if (plugin.afterButton) {
1073
- domUtils.addClass(element, 'se-side-btn');
1074
- domUtils.removeClass(element, 'se-side-btn-a');
1468
+ if (plugin.afterItem) {
1469
+ dom.utils.addClass(element, 'se-side-btn');
1470
+ dom.utils.removeClass(element, 'se-side-btn-a');
1075
1471
  } else {
1076
- domUtils.addClass(element, 'se-side-btn-b');
1472
+ dom.utils.addClass(element, 'se-side-btn-b');
1077
1473
  }
1078
- domUtils.addClass(plugin.beforeButton, 'se-side-btn-before');
1474
+ dom.utils.addClass(plugin.beforeItem, 'se-side-btn-before');
1079
1475
  }
1080
1476
  if (plugin.replaceButton) {
1081
1477
  element.parentElement.appendChild(plugin.replaceButton);
@@ -1085,10 +1481,10 @@ export function UpdateButton(element, plugin, icons, lang) {
1085
1481
  if (!plugin.replaceButton && /^INPUT$/i.test(element.getAttribute('data-type'))) {
1086
1482
  const inputTarget = element.querySelector('input');
1087
1483
  if (inputTarget) {
1088
- domUtils.addClass(inputTarget, 'se-toolbar-btn');
1484
+ dom.utils.addClass(inputTarget, 'se-toolbar-btn');
1089
1485
  inputTarget.setAttribute('data-command', element.getAttribute('data-command'));
1090
1486
  inputTarget.setAttribute('data-type', element.getAttribute('data-type'));
1091
- if (element.hasAttribute('disabled')) inputTarget.setAttribute('disabled', true);
1487
+ if (element.hasAttribute('disabled')) inputTarget.disabled = true;
1092
1488
  }
1093
1489
  }
1094
1490
  }
@@ -1096,17 +1492,18 @@ export function UpdateButton(element, plugin, icons, lang) {
1096
1492
  /**
1097
1493
  * @description Create editor HTML
1098
1494
  * @param {Array} buttonList option.buttonList
1099
- * @param {Object|null} plugins Plugins
1100
- * @param {Array} options options
1101
- * @param {Object} icons icons
1102
- * @param {Object} lang lang
1495
+ * @param {?Object<string, *>} plugins Plugins
1496
+ * @param {Map<string, *>} options options
1497
+ * @param {Object<string, string>} icons icons
1498
+ * @param {Object<string, string>} lang lang
1103
1499
  * @param {boolean} isUpdate Is update
1104
- * @returns {Object} { element: (Element) Toolbar element, plugins: (Array|null) Plugins Array, pluginCallButtons: (Object), responsiveButtons: (Array) }
1500
+ * @returns {{element: HTMLElement, pluginCallButtons: Object<string, Array<HTMLElement>>, responsiveButtons: Array<HTMLElement>, buttonTray: HTMLElement, updateButtons: Array<{button: HTMLElement, plugin: *, key: string}>}}}
1105
1501
  */
1106
1502
  export function CreateToolBar(buttonList, plugins, options, icons, lang, isUpdate) {
1107
1503
  /** create button list */
1108
1504
  buttonList = JSON.parse(JSON.stringify(buttonList));
1109
1505
  const defaultButtonList = _defaultButtons(options, icons, lang);
1506
+ /** @type {Object<string, Array<HTMLElement>>} */
1110
1507
  const pluginCallButtons = {};
1111
1508
  const responsiveButtons = [];
1112
1509
  const updateButtons = [];
@@ -1116,10 +1513,10 @@ export function CreateToolBar(buttonList, plugins, options, icons, lang, isUpdat
1116
1513
  let plugin = null;
1117
1514
  let moduleElement = null;
1118
1515
  let buttonElement = null;
1119
- let vertical = false;
1120
- const moreLayer = domUtils.createElement('DIV', { class: 'se-toolbar-more-layer' });
1121
- const buttonTray = domUtils.createElement('DIV', { class: 'se-btn-tray' });
1122
- const separator_vertical = domUtils.createElement('DIV', { class: 'se-toolbar-separator-vertical' });
1516
+ // let vertical = false;
1517
+ const moreLayer = dom.utils.createElement('DIV', { class: 'se-toolbar-more-layer' });
1518
+ const buttonTray = dom.utils.createElement('DIV', { class: 'se-btn-tray' });
1519
+ const separator_vertical = dom.utils.createElement('DIV', { class: 'se-toolbar-separator-vertical' });
1123
1520
 
1124
1521
  buttonGroupLoop: for (let i = 0, more, moreContainer, moreCommand, buttonGroup, align; i < buttonList.length; i++) {
1125
1522
  more = false;
@@ -1149,14 +1546,14 @@ export function CreateToolBar(buttonList, plugins, options, icons, lang, isUpdat
1149
1546
  } else {
1150
1547
  // align
1151
1548
  if (/^-/.test(button)) {
1152
- align = button.substr(1);
1549
+ align = button.substring(1);
1153
1550
  moduleElement.div.className += ' module-float-' + align;
1154
1551
  continue;
1155
1552
  }
1156
1553
 
1157
1554
  // rtl fix
1158
1555
  if (/^#/.test(button)) {
1159
- const option = button.substr(1);
1556
+ const option = button.substring(1);
1160
1557
  if (option === 'fix') moduleElement.ul.className += ' se-menu-dir-fix';
1161
1558
  continue;
1162
1559
  }
@@ -1169,8 +1566,15 @@ export function CreateToolBar(buttonList, plugins, options, icons, lang, isUpdat
1169
1566
  const title = matched[1].trim();
1170
1567
  const innerHTML = matched[2].trim();
1171
1568
  modules = ['se-btn-more', /^lang\./i.test(title) ? lang[title.replace(/^lang\./i, '')] : title, moreCommand, 'MORE', innerHTML];
1569
+ } else if (button === '|') {
1570
+ // separator vertical
1571
+ modules = ['se-toolbar-separator-vertical', '', '', 'separator', ''];
1172
1572
  } else {
1173
1573
  // default command
1574
+ if (button === 'copy' && !env.isClipboardSupported) {
1575
+ console.warn('[SUNEDITOR.constructor.warn] Clipboard is not supported in this browser. : [copy] button is not rendered.');
1576
+ continue;
1577
+ }
1174
1578
  modules = defaultButtonList[button];
1175
1579
  }
1176
1580
 
@@ -1199,7 +1603,7 @@ export function CreateToolBar(buttonList, plugins, options, icons, lang, isUpdat
1199
1603
  // more button
1200
1604
  if (moreButton) {
1201
1605
  more = true;
1202
- moreContainer = domUtils.createElement('DIV');
1606
+ moreContainer = dom.utils.createElement('DIV');
1203
1607
  moreContainer.className = 'se-more-layer ' + moreCommand;
1204
1608
  moreContainer.setAttribute('data-ref', moreCommand);
1205
1609
  moreContainer.innerHTML = '<div class="se-more-form"><ul class="se-menu-list"' + (align ? ' style="float: ' + align + ';"' : '') + '></ul></div>';
@@ -1208,18 +1612,23 @@ export function CreateToolBar(buttonList, plugins, options, icons, lang, isUpdat
1208
1612
  }
1209
1613
  }
1210
1614
 
1211
- if (vertical) {
1212
- const sv = separator_vertical.cloneNode(false);
1213
- buttonTray.appendChild(sv);
1214
- }
1615
+ // if (vertical) {
1616
+ // const sv = separator_vertical.cloneNode(false);
1617
+ // buttonTray.appendChild(sv);
1618
+ // }
1215
1619
 
1216
1620
  buttonTray.appendChild(moduleElement.div);
1217
- vertical = true;
1621
+ // vertical = true;
1622
+ } else if (buttonGroup === '|') {
1623
+ // // separator vertical
1624
+ const sv = separator_vertical.cloneNode(false);
1625
+ buttonTray.appendChild(sv);
1626
+ continue;
1218
1627
  } else if (/^\/$/.test(buttonGroup)) {
1219
1628
  /** line break */
1220
- const enterDiv = domUtils.createElement('DIV', { class: 'se-btn-module-enter' });
1629
+ const enterDiv = dom.utils.createElement('DIV', { class: 'se-btn-module-enter' });
1221
1630
  buttonTray.appendChild(enterDiv);
1222
- vertical = false;
1631
+ // vertical = false;
1223
1632
  }
1224
1633
  }
1225
1634
 
@@ -1228,7 +1637,7 @@ export function CreateToolBar(buttonList, plugins, options, icons, lang, isUpdat
1228
1637
  buttonTray.style.display = 'none';
1229
1638
  break;
1230
1639
  case 1:
1231
- domUtils.removeClass(buttonTray.firstElementChild, 'se-btn-module-border');
1640
+ dom.utils.removeClass(buttonTray.firstElementChild, 'se-btn-module-border');
1232
1641
  break;
1233
1642
  }
1234
1643
 
@@ -1236,7 +1645,7 @@ export function CreateToolBar(buttonList, plugins, options, icons, lang, isUpdat
1236
1645
  if (responsiveButtons.length > 0) responsiveButtons.unshift(buttonList);
1237
1646
 
1238
1647
  // rendering toolbar
1239
- const tool_bar = domUtils.createElement('DIV', { class: 'se-toolbar sun-editor-common' + (!options.get('shortcutsHint') ? ' se-shortcut-hide' : '') }, buttonTray);
1648
+ const tool_bar = dom.utils.createElement('DIV', { class: 'se-toolbar sun-editor-common' + (!options.get('shortcutsHint') ? ' se-shortcut-hide' : '') }, buttonTray);
1240
1649
 
1241
1650
  if (options.get('toolbar_hide')) tool_bar.style.display = 'none';
1242
1651