suneditor 2.46.2 → 3.0.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/.eslintignore +7 -0
  2. package/.eslintrc.json +64 -0
  3. package/CONTRIBUTING.md +36 -0
  4. package/LICENSE.txt +1 -1
  5. package/README.md +11 -1560
  6. package/dist/suneditor.min.css +1 -0
  7. package/dist/suneditor.min.js +1 -2
  8. package/package.json +97 -70
  9. package/src/assets/icons/_default.js +194 -0
  10. package/src/assets/suneditor-contents.css +643 -0
  11. package/src/assets/suneditor.css +3394 -0
  12. package/src/core/base/eventHandlers/handler_toolbar.js +114 -0
  13. package/src/core/base/eventHandlers/handler_ww_clipboard.js +37 -0
  14. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +74 -0
  15. package/src/core/base/eventHandlers/handler_ww_key_input.js +1002 -0
  16. package/src/core/base/eventHandlers/handler_ww_mouse.js +147 -0
  17. package/src/core/base/eventManager.js +1156 -0
  18. package/src/core/base/events.js +320 -0
  19. package/src/core/base/history.js +301 -0
  20. package/src/core/class/char.js +147 -0
  21. package/src/core/class/component.js +639 -0
  22. package/src/core/class/format.js +3258 -0
  23. package/src/core/class/html.js +1710 -0
  24. package/src/core/class/menu.js +260 -0
  25. package/src/core/class/nodeTransform.js +405 -0
  26. package/src/core/class/notice.js +42 -0
  27. package/src/core/class/offset.js +575 -0
  28. package/src/core/class/selection.js +511 -0
  29. package/src/core/class/shortcuts.js +38 -0
  30. package/src/core/class/toolbar.js +440 -0
  31. package/src/core/class/viewer.js +646 -0
  32. package/src/core/editor.js +1601 -0
  33. package/src/core/section/actives.js +145 -0
  34. package/src/core/section/constructor.js +1252 -0
  35. package/src/core/section/context.js +97 -0
  36. package/src/editorInjector/_classes.js +22 -0
  37. package/src/editorInjector/_core.js +28 -0
  38. package/src/editorInjector/index.js +13 -0
  39. package/src/helper/converter.js +388 -0
  40. package/src/helper/domUtils.js +1177 -0
  41. package/src/helper/env.js +250 -0
  42. package/src/helper/index.js +19 -0
  43. package/src/helper/numbers.js +68 -0
  44. package/src/helper/unicode.js +43 -0
  45. package/src/langs/ckb.js +161 -0
  46. package/src/langs/cs.js +161 -0
  47. package/src/langs/da.js +161 -0
  48. package/src/langs/de.js +162 -0
  49. package/src/langs/en.js +210 -0
  50. package/src/langs/es.js +162 -0
  51. package/src/langs/fa.js +159 -0
  52. package/src/langs/fr.js +161 -0
  53. package/src/langs/he.js +162 -0
  54. package/src/{lang → langs}/index.js +0 -2
  55. package/src/langs/it.js +162 -0
  56. package/src/langs/ja.js +162 -0
  57. package/src/langs/ko.js +210 -0
  58. package/src/langs/lv.js +162 -0
  59. package/src/langs/nl.js +162 -0
  60. package/src/langs/pl.js +162 -0
  61. package/src/langs/pt_br.js +162 -0
  62. package/src/langs/ro.js +162 -0
  63. package/src/langs/ru.js +162 -0
  64. package/src/langs/se.js +162 -0
  65. package/src/langs/tr.js +159 -0
  66. package/src/langs/ua.js +162 -0
  67. package/src/langs/ur.js +162 -0
  68. package/src/langs/zh_cn.js +162 -0
  69. package/src/modules/ApiManager.js +168 -0
  70. package/src/modules/ColorPicker.js +302 -0
  71. package/src/modules/Controller.js +323 -0
  72. package/src/modules/Figure.js +1176 -0
  73. package/src/modules/FileBrowser.js +271 -0
  74. package/src/modules/FileManager.js +307 -0
  75. package/src/modules/HueSlider.js +513 -0
  76. package/src/modules/Modal.js +177 -0
  77. package/src/modules/ModalAnchorEditor.js +494 -0
  78. package/src/modules/SelectMenu.js +447 -0
  79. package/src/modules/_DragHandle.js +16 -0
  80. package/src/modules/index.js +14 -0
  81. package/src/plugins/command/blockquote.js +47 -47
  82. package/src/plugins/command/exportPdf.js +168 -0
  83. package/src/plugins/command/fileUpload.js +389 -0
  84. package/src/plugins/command/list_bulleted.js +112 -0
  85. package/src/plugins/command/list_numbered.js +115 -0
  86. package/src/plugins/dropdown/align.js +143 -0
  87. package/src/plugins/dropdown/backgroundColor.js +90 -0
  88. package/src/plugins/dropdown/font.js +113 -0
  89. package/src/plugins/dropdown/fontColor.js +90 -0
  90. package/src/plugins/dropdown/formatBlock.js +141 -0
  91. package/src/plugins/dropdown/hr.js +111 -0
  92. package/src/plugins/dropdown/layout.js +72 -0
  93. package/src/plugins/dropdown/lineHeight.js +114 -0
  94. package/src/plugins/dropdown/list.js +107 -0
  95. package/src/plugins/dropdown/paragraphStyle.js +117 -0
  96. package/src/plugins/dropdown/table.js +2810 -0
  97. package/src/plugins/dropdown/template.js +71 -0
  98. package/src/plugins/dropdown/textStyle.js +137 -0
  99. package/src/plugins/field/mention.js +181 -0
  100. package/src/plugins/fileBrowser/imageGallery.js +76 -59
  101. package/src/plugins/index.js +86 -24
  102. package/src/plugins/input/fontSize.js +357 -0
  103. package/src/plugins/modal/audio.js +492 -0
  104. package/src/plugins/modal/image.js +1064 -0
  105. package/src/plugins/modal/link.js +211 -0
  106. package/src/plugins/modal/math.js +363 -0
  107. package/src/plugins/modal/video.js +870 -0
  108. package/src/suneditor.js +62 -67
  109. package/src/themes/test.css +61 -0
  110. package/typings/CommandPlugin.d.ts +8 -0
  111. package/typings/DialogPlugin.d.ts +20 -0
  112. package/typings/FileBrowserPlugin.d.ts +30 -0
  113. package/typings/Lang.d.ts +124 -0
  114. package/typings/Module.d.ts +15 -0
  115. package/typings/Plugin.d.ts +42 -0
  116. package/typings/SubmenuPlugin.d.ts +8 -0
  117. package/typings/_classes.d.ts +17 -0
  118. package/typings/_colorPicker.d.ts +60 -0
  119. package/typings/_core.d.ts +55 -0
  120. package/typings/align.d.ts +5 -0
  121. package/{src/plugins/dialog → typings}/audio.d.ts +1 -1
  122. package/typings/backgroundColor.d.ts +5 -0
  123. package/{src/plugins/command → typings}/blockquote.d.ts +1 -1
  124. package/typings/char.d.ts +39 -0
  125. package/typings/component.d.ts +38 -0
  126. package/typings/context.d.ts +39 -0
  127. package/typings/converter.d.ts +33 -0
  128. package/typings/dialog.d.ts +28 -0
  129. package/typings/domUtils.d.ts +361 -0
  130. package/typings/editor.d.ts +7 -0
  131. package/typings/editor.ts +542 -0
  132. package/typings/env.d.ts +70 -0
  133. package/typings/eventManager.d.ts +37 -0
  134. package/typings/events.d.ts +262 -0
  135. package/typings/fileBrowser.d.ts +42 -0
  136. package/typings/fileManager.d.ts +67 -0
  137. package/typings/font.d.ts +5 -0
  138. package/typings/fontColor.d.ts +5 -0
  139. package/typings/fontSize.d.ts +5 -0
  140. package/typings/format.d.ts +191 -0
  141. package/typings/formatBlock.d.ts +5 -0
  142. package/typings/history.d.ts +48 -0
  143. package/typings/horizontalRule.d.ts +5 -0
  144. package/{src/plugins/dialog → typings}/image.d.ts +1 -1
  145. package/{src/plugins/fileBrowser → typings}/imageGallery.d.ts +1 -1
  146. package/typings/index.d.ts +21 -0
  147. package/{src/plugins/modules/index.d.ts → typings/index.modules.d.ts} +3 -3
  148. package/typings/index.plugins.d.ts +58 -0
  149. package/typings/lineHeight.d.ts +5 -0
  150. package/{src/plugins/dialog → typings}/link.d.ts +1 -1
  151. package/typings/list.d.ts +5 -0
  152. package/{src/plugins/dialog → typings}/math.d.ts +1 -1
  153. package/typings/mediaContainer.d.ts +25 -0
  154. package/typings/node.d.ts +57 -0
  155. package/typings/notice.d.ts +16 -0
  156. package/typings/numbers.d.ts +29 -0
  157. package/typings/offset.d.ts +24 -0
  158. package/typings/options.d.ts +589 -0
  159. package/typings/paragraphStyle.d.ts +5 -0
  160. package/typings/resizing.d.ts +141 -0
  161. package/typings/selection.d.ts +94 -0
  162. package/typings/shortcuts.d.ts +13 -0
  163. package/typings/suneditor.d.ts +9 -0
  164. package/typings/table.d.ts +5 -0
  165. package/typings/template.d.ts +5 -0
  166. package/typings/textStyle.d.ts +5 -0
  167. package/typings/toolbar.d.ts +32 -0
  168. package/typings/unicode.d.ts +25 -0
  169. package/{src/plugins/dialog → typings}/video.d.ts +1 -1
  170. package/dist/css/suneditor.min.css +0 -1
  171. package/src/assets/css/suneditor-contents.css +0 -562
  172. package/src/assets/css/suneditor.css +0 -566
  173. package/src/assets/defaultIcons.js +0 -103
  174. package/src/lang/Lang.d.ts +0 -144
  175. package/src/lang/ckb.d.ts +0 -5
  176. package/src/lang/ckb.js +0 -188
  177. package/src/lang/cs.d.ts +0 -5
  178. package/src/lang/cs.js +0 -188
  179. package/src/lang/da.d.ts +0 -5
  180. package/src/lang/da.js +0 -191
  181. package/src/lang/de.d.ts +0 -5
  182. package/src/lang/de.js +0 -188
  183. package/src/lang/en.d.ts +0 -5
  184. package/src/lang/en.js +0 -188
  185. package/src/lang/es.d.ts +0 -5
  186. package/src/lang/es.js +0 -188
  187. package/src/lang/fa.d.ts +0 -5
  188. package/src/lang/fa.js +0 -188
  189. package/src/lang/fr.d.ts +0 -5
  190. package/src/lang/fr.js +0 -188
  191. package/src/lang/he.d.ts +0 -5
  192. package/src/lang/he.js +0 -188
  193. package/src/lang/index.d.ts +0 -23
  194. package/src/lang/it.d.ts +0 -5
  195. package/src/lang/it.js +0 -188
  196. package/src/lang/ja.d.ts +0 -5
  197. package/src/lang/ja.js +0 -188
  198. package/src/lang/ko.d.ts +0 -5
  199. package/src/lang/ko.js +0 -188
  200. package/src/lang/lv.d.ts +0 -5
  201. package/src/lang/lv.js +0 -188
  202. package/src/lang/nl.d.ts +0 -5
  203. package/src/lang/nl.js +0 -188
  204. package/src/lang/pl.d.ts +0 -5
  205. package/src/lang/pl.js +0 -188
  206. package/src/lang/pt_br.d.ts +0 -5
  207. package/src/lang/pt_br.js +0 -189
  208. package/src/lang/ro.d.ts +0 -5
  209. package/src/lang/ro.js +0 -188
  210. package/src/lang/ru.d.ts +0 -5
  211. package/src/lang/ru.js +0 -188
  212. package/src/lang/se.d.ts +0 -5
  213. package/src/lang/se.js +0 -191
  214. package/src/lang/tr.d.ts +0 -5
  215. package/src/lang/tr.js +0 -191
  216. package/src/lang/ua.d.ts +0 -5
  217. package/src/lang/ua.js +0 -188
  218. package/src/lang/ur.d.ts +0 -5
  219. package/src/lang/ur.js +0 -188
  220. package/src/lang/zh_cn.d.ts +0 -5
  221. package/src/lang/zh_cn.js +0 -187
  222. package/src/lib/constructor.js +0 -954
  223. package/src/lib/context.d.ts +0 -42
  224. package/src/lib/context.js +0 -71
  225. package/src/lib/core.d.ts +0 -1135
  226. package/src/lib/core.js +0 -9395
  227. package/src/lib/history.d.ts +0 -48
  228. package/src/lib/history.js +0 -219
  229. package/src/lib/util.d.ts +0 -678
  230. package/src/lib/util.js +0 -2131
  231. package/src/options.d.ts +0 -608
  232. package/src/plugins/CommandPlugin.d.ts +0 -8
  233. package/src/plugins/DialogPlugin.d.ts +0 -20
  234. package/src/plugins/FileBrowserPlugin.d.ts +0 -30
  235. package/src/plugins/Module.d.ts +0 -15
  236. package/src/plugins/Plugin.d.ts +0 -42
  237. package/src/plugins/SubmenuPlugin.d.ts +0 -8
  238. package/src/plugins/dialog/audio.js +0 -559
  239. package/src/plugins/dialog/image.js +0 -1126
  240. package/src/plugins/dialog/link.js +0 -223
  241. package/src/plugins/dialog/math.js +0 -295
  242. package/src/plugins/dialog/mention.js +0 -242
  243. package/src/plugins/dialog/video.js +0 -979
  244. package/src/plugins/index.d.ts +0 -79
  245. package/src/plugins/modules/_anchor.js +0 -461
  246. package/src/plugins/modules/_colorPicker.d.ts +0 -60
  247. package/src/plugins/modules/_colorPicker.js +0 -201
  248. package/src/plugins/modules/_notice.d.ts +0 -21
  249. package/src/plugins/modules/_notice.js +0 -72
  250. package/src/plugins/modules/_selectMenu.js +0 -119
  251. package/src/plugins/modules/component.d.ts +0 -25
  252. package/src/plugins/modules/component.js +0 -81
  253. package/src/plugins/modules/dialog.d.ts +0 -28
  254. package/src/plugins/modules/dialog.js +0 -175
  255. package/src/plugins/modules/fileBrowser.d.ts +0 -42
  256. package/src/plugins/modules/fileBrowser.js +0 -374
  257. package/src/plugins/modules/fileManager.d.ts +0 -67
  258. package/src/plugins/modules/fileManager.js +0 -326
  259. package/src/plugins/modules/index.js +0 -9
  260. package/src/plugins/modules/resizing.d.ts +0 -154
  261. package/src/plugins/modules/resizing.js +0 -903
  262. package/src/plugins/submenu/align.d.ts +0 -5
  263. package/src/plugins/submenu/align.js +0 -160
  264. package/src/plugins/submenu/font.d.ts +0 -5
  265. package/src/plugins/submenu/font.js +0 -123
  266. package/src/plugins/submenu/fontColor.d.ts +0 -5
  267. package/src/plugins/submenu/fontColor.js +0 -101
  268. package/src/plugins/submenu/fontSize.d.ts +0 -5
  269. package/src/plugins/submenu/fontSize.js +0 -112
  270. package/src/plugins/submenu/formatBlock.d.ts +0 -5
  271. package/src/plugins/submenu/formatBlock.js +0 -273
  272. package/src/plugins/submenu/hiliteColor.d.ts +0 -5
  273. package/src/plugins/submenu/hiliteColor.js +0 -102
  274. package/src/plugins/submenu/horizontalRule.d.ts +0 -5
  275. package/src/plugins/submenu/horizontalRule.js +0 -98
  276. package/src/plugins/submenu/lineHeight.d.ts +0 -5
  277. package/src/plugins/submenu/lineHeight.js +0 -104
  278. package/src/plugins/submenu/list.d.ts +0 -5
  279. package/src/plugins/submenu/list.js +0 -456
  280. package/src/plugins/submenu/paragraphStyle.d.ts +0 -5
  281. package/src/plugins/submenu/paragraphStyle.js +0 -135
  282. package/src/plugins/submenu/table.d.ts +0 -5
  283. package/src/plugins/submenu/table.js +0 -1431
  284. package/src/plugins/submenu/template.d.ts +0 -5
  285. package/src/plugins/submenu/template.js +0 -72
  286. package/src/plugins/submenu/textStyle.d.ts +0 -5
  287. package/src/plugins/submenu/textStyle.js +0 -167
  288. package/src/suneditor.d.ts +0 -9
  289. package/src/suneditor_build.js +0 -18
  290. /package/{src/plugins/dialog → typings}/mention.d.ts +0 -0
@@ -0,0 +1,1601 @@
1
+ import { env, converter, domUtils, numbers } from '../helper';
2
+ import Constructor, { InitOptions, UpdateButton, CreateShortcuts, CreateStatusbar, RO_UNAVAILABD } from './section/constructor';
3
+ import { UpdateStatusbarContext } from './section/context';
4
+ import { BASIC_COMMANDS, ACTIVE_EVENT_COMMANDS, SELECT_ALL, DIR_BTN_ACTIVE, SAVE, COPY_FORMAT, FONT_STYLE } from './section/actives';
5
+ import History from './base/history';
6
+ import EventManager from './base/eventManager';
7
+ import Events from './base/events';
8
+
9
+ // class injector
10
+ import ClassInjector from '../editorInjector/_classes';
11
+
12
+ // classes
13
+ import Char from './class/char';
14
+ import Component from './class/component';
15
+ import Format from './class/format';
16
+ import HTML from './class/html';
17
+ import Menu from './class/menu';
18
+ import NodeTransform from './class/nodeTransform';
19
+ import Notice from './class/notice';
20
+ import Offset from './class/offset';
21
+ import Selection from './class/selection';
22
+ import Shortcuts from './class/shortcuts';
23
+ import Toolbar from './class/toolbar';
24
+ import Viewer from './class/viewer';
25
+
26
+ const COMMAND_BUTTONS = '.se-menu-list .se-toolbar-btn[data-command]';
27
+ const DISABLE_BUTTONS_CODEVIEW = `${COMMAND_BUTTONS}:not([class~="se-code-view-enabled"]):not([data-type="MORE"])`;
28
+ const DISABLE_BUTTONS_CONTROLLER = `${COMMAND_BUTTONS}:not([class~="se-component-enabled"]):not([data-type="MORE"])`;
29
+
30
+ /**
31
+ * @description SunEditor constructor function.
32
+ * @param {Array.<Element>} multiTargets Target textarea
33
+ * @param {Object} options options
34
+ * @returns {Object}
35
+ */
36
+ const Editor = function (multiTargets, options) {
37
+ const _d = multiTargets[0].target.ownerDocument || env._d;
38
+ const _w = _d.defaultView || env._w;
39
+ const product = Constructor(multiTargets, options);
40
+
41
+ // properties
42
+ this.rootKeys = product.rootKeys;
43
+ this.frameRoots = product.frameRoots;
44
+ this.context = product.context;
45
+ this.frameContext = new Map();
46
+ this.frameOptions = new Map();
47
+ this._lineBreaker_t = null;
48
+ this._lineBreaker_b = null;
49
+
50
+ /**
51
+ * @description Document object
52
+ * @type {Document}
53
+ */
54
+ this._d = _d;
55
+
56
+ /**
57
+ * @description Window object
58
+ * @type {Window}
59
+ */
60
+ this._w = _w;
61
+
62
+ /**
63
+ * @description Controllers carrier
64
+ */
65
+ this.carrierWrapper = product.carrierWrapper;
66
+
67
+ /**
68
+ * @description Editor options
69
+ * @type {Object.<string, any>}
70
+ */
71
+ this.options = product.options;
72
+
73
+ /**
74
+ * @description Plugins
75
+ * @type {Object.<string, any>}
76
+ */
77
+ this.plugins = product.plugins || {};
78
+
79
+ /**
80
+ * @description Events object, call by triggerEvent function
81
+ * @type {Object.<string, any>}
82
+ */
83
+ this.events = null;
84
+
85
+ /**
86
+ * @description Call the event function by injecting self: this.
87
+ * @type {Function}
88
+ */
89
+ this.triggerEvent = null;
90
+
91
+ /**
92
+ * @description Default icons object
93
+ * @type {Object.<string, string>}
94
+ */
95
+ this.icons = product.icons;
96
+
97
+ /**
98
+ * @description loaded language
99
+ * @type {Object.<string, any>}
100
+ */
101
+ this.lang = product.lang;
102
+
103
+ /**
104
+ * @description Variables used internally in editor operation
105
+ * @property {boolean} hasFocus Boolean value of whether the editor has focus
106
+ * @property {number} tabSize Indent size of tab (4)
107
+ * @property {number} indentSize Indent size (25)px
108
+ * @property {number} codeIndentSize Indent size of Code view mode (2)
109
+ * @property {Array} currentNodes An element array of the current cursor's node structure
110
+ * @property {Array} currentNodesMap An element name array of the current cursor's node structure
111
+ * @property {boolean} componentSelected Boolean value of whether component is selected
112
+ * @property {number} rootKey Current root key
113
+ */
114
+ this.status = {
115
+ hasFocus: false,
116
+ tabSize: 4,
117
+ indentSize: 25,
118
+ codeIndentSize: 2,
119
+ currentNodes: [],
120
+ currentNodesMap: [],
121
+ componentSelected: false,
122
+ rootKey: product.rootId,
123
+ _range: null
124
+ };
125
+
126
+ /**
127
+ * @description Is classic mode?
128
+ */
129
+ this.isClassic = null;
130
+
131
+ /**
132
+ * @description Is inline mode?
133
+ */
134
+ this.isInline = null;
135
+
136
+ /**
137
+ * @description Is balloon|balloon-always mode?
138
+ */
139
+ this.isBalloon = null;
140
+
141
+ /**
142
+ * @description Is balloon-always mode?
143
+ */
144
+ this.isBalloonAlways = null;
145
+
146
+ /**
147
+ * @description Is subToolbar balloon|balloon-always mode?
148
+ */
149
+ this.isSubBalloon = null;
150
+
151
+ /**
152
+ * @description Is subToolbar balloon-always mode?
153
+ */
154
+ this.isSubBalloonAlways = null;
155
+
156
+ // ----- Properties not shared with _core -----
157
+ /**
158
+ * @description All command buttons map
159
+ */
160
+ this.allCommandButtons = new Map();
161
+ this.subAllCommandButtons = new Map();
162
+
163
+ /**
164
+ * @description Shoutcuts key map
165
+ */
166
+ this.shortcutsKeyMap = new Map();
167
+ this.reverseKeys = [];
168
+
169
+ /**
170
+ * @description A map with the plugin's buttons having an "active" method and the default command buttons with an "active" action.
171
+ * Each button is contained in an array.
172
+ */
173
+ this.commandTargets = new Map();
174
+
175
+ /**
176
+ * @description Plugins array with "active" method.
177
+ * "activeCommands" runs the "add" method when creating the editor.
178
+ */
179
+ this.activeCommands = null;
180
+
181
+ /**
182
+ * @description The selection node (selection.getNode()) to which the effect was last applied
183
+ */
184
+ this.effectNode = null;
185
+
186
+ // ------------------------------------------------------- private properties -------------------------------------------------------
187
+ /**
188
+ * @description Closest ShadowRoot to editor if found
189
+ * @type {ShadowRoot}
190
+ * @private
191
+ */
192
+ this._shadowRoot = null;
193
+
194
+ /**
195
+ * @description Plugin call event map
196
+ * @private
197
+ */
198
+ this._onPluginEvents = null;
199
+
200
+ /**
201
+ * @description Copy format info
202
+ * @private
203
+ */
204
+ this._onCopyFormatInfo = null;
205
+ this._onCopyFormatInitMethod = null;
206
+
207
+ /**
208
+ * @description Controller, modal relative
209
+ * @private
210
+ */
211
+ this.opendModal = null;
212
+ this.opendControllers = [];
213
+ this.currentControllerName = '';
214
+ this._controllerTargetContext = null;
215
+ this.selectMenuOn = false;
216
+ this._backWrapper = product.carrierWrapper.querySelector('.se-back-wrapper');
217
+
218
+ this._controllerOnDisabledButtons = [];
219
+ this._codeViewDisabledButtons = [];
220
+
221
+ /**
222
+ * @description Button List in Responsive Toolbar.
223
+ * @private
224
+ */
225
+ this._pluginCallButtons = product.pluginCallButtons;
226
+ this._pluginCallButtons_sub = product.pluginCallButtons_sub;
227
+ this._responsiveButtons = product.responsiveButtons;
228
+ this._responsiveButtons_sub = product.responsiveButtons_sub;
229
+
230
+ /**
231
+ * @description Variable that controls the "blur" event in the editor of inline or balloon mode when the focus is moved to dropdown
232
+ * @private
233
+ */
234
+ this._notHideToolbar = false;
235
+
236
+ /**
237
+ * @description Variables for controlling focus and blur events
238
+ * @private
239
+ */
240
+ this._antiBlur = false;
241
+
242
+ /**
243
+ * @description If true, (initialize, reset) all indexes of image, video information
244
+ * @private
245
+ */
246
+ this._componentsInfoInit = true;
247
+ this._componentsInfoReset = false;
248
+
249
+ /**
250
+ * @description plugin retainFormat info Map()
251
+ * @private
252
+ */
253
+ this._MELInfo = null;
254
+
255
+ /**
256
+ * @description Properties for managing files in the "FileManager" module
257
+ * @private
258
+ */
259
+ this._fileInfoPluginsCheck = null;
260
+
261
+ /**
262
+ * @description Properties for managing files in the "FileManager" module
263
+ * @private
264
+ */
265
+ this._fileInfoPluginsReset = null;
266
+
267
+ /**
268
+ * @description Variables for file component management
269
+ * @private
270
+ */
271
+ this._fileManager = {
272
+ tags: null,
273
+ regExp: null,
274
+ pluginRegExp: null,
275
+ pluginMap: null
276
+ };
277
+ this._componentManager = [];
278
+
279
+ /**
280
+ * @description Current Figure container.
281
+ * @private
282
+ */
283
+ this._figureContainer = null;
284
+
285
+ /**
286
+ * @description Origin options
287
+ * @private
288
+ */
289
+ this._originOptions = options;
290
+
291
+ /** ----- Create editor ------------------------------------------------------------ */
292
+ this.__Create(options);
293
+ };
294
+
295
+ Editor.prototype = {
296
+ /**
297
+ * @description If the plugin is not added, add the plugin and call the 'add' function.
298
+ * If the plugin is added call callBack function.
299
+ * @param {string} pluginName The name of the plugin to call
300
+ * @param {Array.<Element>|null} targets Plugin target button (This is not necessary if you have a button list when creating the editor)
301
+ * @param {object|null} pluginOptions Plugin's options
302
+ * @param {object} shortcuts this.options.get('shortcuts')
303
+ */
304
+ registerPlugin(pluginName, targets, pluginOptions, shortcuts) {
305
+ let plugin = this.plugins[pluginName];
306
+ if (!plugin) {
307
+ throw Error(`[SUNEDITOR.registerPlugin.fail] The called plugin does not exist or is in an invalid format. (pluginName: "${pluginName}")`);
308
+ } else if (typeof this.plugins[pluginName] === 'function') {
309
+ plugin = this.plugins[pluginName] = new this.plugins[pluginName](this, pluginOptions || {});
310
+ if (typeof plugin.init === 'function') plugin.init();
311
+ }
312
+
313
+ if (targets) {
314
+ for (let i = 0, len = targets.length; i < len; i++) {
315
+ UpdateButton(targets[i], plugin, this.icons, this.lang, shortcuts);
316
+ }
317
+
318
+ if (!this.activeCommands.includes(pluginName) && typeof this.plugins[pluginName].active === 'function') {
319
+ this.activeCommands.push(pluginName);
320
+ }
321
+ }
322
+ },
323
+
324
+ /**
325
+ * @description Run plugin calls and basic commands.
326
+ * @param {string} command Command string
327
+ * @param {string} type Display type string ('command', 'dropdown', 'modal', 'container')
328
+ * @param {Element|null} button The element of command button
329
+ */
330
+ run(command, type, button) {
331
+ if (type) {
332
+ if (/more/i.test(type)) {
333
+ const toolbar = domUtils.getParentElement(button, '.se-toolbar');
334
+ const toolInst = domUtils.hasClass(toolbar, 'se-toolbar-sub') ? this.subToolbar : this.toolbar;
335
+ if (button !== toolInst.currentMoreLayerActiveButton) {
336
+ const layer = toolbar.querySelector('.' + command);
337
+ if (layer) {
338
+ toolInst._moreLayerOn(button, layer);
339
+ toolInst._showBalloon();
340
+ toolInst._showInline();
341
+ }
342
+ domUtils.addClass(button, 'on');
343
+ } else if (toolInst.currentMoreLayerActiveButton) {
344
+ toolInst._moreLayerOff();
345
+ toolInst._showBalloon();
346
+ toolInst._showInline();
347
+ }
348
+
349
+ this.viewer._resetFullScreenHeight();
350
+ return;
351
+ }
352
+
353
+ if (/container/.test(type) && (this.menu.targetMap[command] === null || button !== this.menu.currentContainerActiveButton)) {
354
+ this.menu.containerOn(button);
355
+ return;
356
+ }
357
+
358
+ if (this.frameContext.get('isReadOnly') && domUtils.arrayIncludes(this._controllerOnDisabledButtons, button)) return;
359
+ if (/dropdown/.test(type) && (this.menu.targetMap[command] === null || button !== this.menu.currentDropdownActiveButton)) {
360
+ this.menu.dropdownOn(button);
361
+ return;
362
+ } else if (/modal/.test(type)) {
363
+ this.plugins[command].open(button);
364
+ return;
365
+ } else if (/command/.test(type)) {
366
+ this.plugins[command].action(button);
367
+ } else if (/fileBrowser/.test(type)) {
368
+ this.plugins[command].open(null);
369
+ }
370
+ } else if (command) {
371
+ this.commandHandler(command, button);
372
+ }
373
+
374
+ if (/dropdown/.test(type)) {
375
+ this.menu.dropdownOff();
376
+ } else if (!/command/.test(type)) {
377
+ this.menu.dropdownOff();
378
+ this.menu.containerOff();
379
+ }
380
+ },
381
+
382
+ /**
383
+ * @description Execute default command of command button
384
+ * (selectAll, codeView, fullScreen, indent, outdent, undo, redo, removeFormat, print, preview, showBlocks, save, bold, underline, italic, strike, subscript, superscript, copy, cut, paste)
385
+ * @param {string} command Property of command button (data-value)
386
+ * @param {Element} button Command button
387
+ */
388
+ async commandHandler(command, button) {
389
+ if (this.frameContext.get('isReadOnly') && !/copy|cut|selectAll|codeView|fullScreen|print|preview|showBlocks/.test(command)) return;
390
+
391
+ switch (command) {
392
+ case 'copy':
393
+ case 'cut':
394
+ this.execCommand(command);
395
+ break;
396
+ case 'paste':
397
+ // @todo
398
+ break;
399
+ case 'selectAll':
400
+ SELECT_ALL(this);
401
+ break;
402
+ case 'newDocument':
403
+ this.html.set(`<${this.options.get('defaultLine')}><br></${this.options.get('defaultLine')}>`);
404
+ this.focus();
405
+ this.history.push(false);
406
+ break;
407
+ case 'codeView':
408
+ this.viewer.codeView(!this.frameContext.get('isCodeView'));
409
+ break;
410
+ case 'fullScreen':
411
+ this.viewer.fullScreen(!this.frameContext.get('isFullScreen'));
412
+ break;
413
+ case 'indent':
414
+ this.format.indent();
415
+ break;
416
+ case 'outdent':
417
+ this.format.outdent();
418
+ break;
419
+ case 'undo':
420
+ this.history.undo();
421
+ break;
422
+ case 'redo':
423
+ this.history.redo();
424
+ break;
425
+ case 'removeFormat':
426
+ this.format.removeTextStyle();
427
+ this.focus();
428
+ break;
429
+ case 'print':
430
+ this.viewer.print();
431
+ break;
432
+ case 'preview':
433
+ this.viewer.preview();
434
+ break;
435
+ case 'showBlocks':
436
+ this.viewer.showBlocks(!this.frameContext.get('isShowBlocks'));
437
+ break;
438
+ case 'dir':
439
+ this.setDir(this.options.get('_rtl') ? 'ltr' : 'rtl');
440
+ break;
441
+ case 'dir_ltr':
442
+ this.setDir('ltr');
443
+ break;
444
+ case 'dir_rtl':
445
+ this.setDir('rtl');
446
+ break;
447
+ case 'save':
448
+ await SAVE(this);
449
+ break;
450
+ case 'copyFormat':
451
+ COPY_FORMAT(this, button);
452
+ break;
453
+ default:
454
+ FONT_STYLE(this, command);
455
+ }
456
+ },
457
+
458
+ /**
459
+ * @description Execute "editor.run" with command button.
460
+ * @param {Element} target Command target
461
+ */
462
+ runFromTarget(target) {
463
+ const isInput = domUtils.isInputElement(target);
464
+ if (isInput || !(target = domUtils.getCommandTarget(target))) return;
465
+
466
+ const command = target.getAttribute('data-command');
467
+ const type = target.getAttribute('data-type');
468
+
469
+ if (!command && !type) return;
470
+ if (target.disabled) return;
471
+
472
+ this.run(command, type, target);
473
+ },
474
+
475
+ /**
476
+ * @description It is executed by inserting the button of commandTargets as the argument value of the "f" function.
477
+ * "f" is called as long as the button array's length.
478
+ * @param {string} cmd data-command
479
+ * @param {Function} f Function.
480
+ */
481
+ applyCommandTargets(cmd, f) {
482
+ if (this.commandTargets.has(cmd)) {
483
+ this.commandTargets.get(cmd).forEach(f);
484
+ }
485
+ },
486
+
487
+ /**
488
+ * @description Executes a function by traversing all root targets.
489
+ * @param {Function} f Function
490
+ */
491
+ applyFrameRoots(f) {
492
+ this.frameRoots.forEach(f);
493
+ },
494
+
495
+ /**
496
+ * @description Checks if the content of the editor is empty.
497
+ * Display criteria for "placeholder".
498
+ * @param {frameContext|null} fc Frame context, if not present, currently selected frame context.
499
+ * @returns {boolean}
500
+ */
501
+ isEmpty(fc) {
502
+ fc = fc || this.frameContext;
503
+ const wysiwyg = fc.get('wysiwyg');
504
+ return domUtils.isZeroWith(wysiwyg.textContent) && !wysiwyg.querySelector(env._allowedEmptyNodeList) && (wysiwyg.innerText.match(/\n/g) || '').length <= 1;
505
+ },
506
+
507
+ /**
508
+ * @description Set direction to "rtl" or "ltr".
509
+ * @param {string} dir "rtl" or "ltr"
510
+ */
511
+ setDir(dir) {
512
+ const rtl = dir === 'rtl';
513
+ if (this.options.get('_rtl') === rtl) return;
514
+
515
+ try {
516
+ this.options.set('_rtl', rtl);
517
+ this._offCurrentController();
518
+
519
+ const fc = this.frameContext;
520
+ const plugins = this.plugins;
521
+ for (const k in plugins) {
522
+ if (typeof plugins[k].setDir === 'function') plugins[k].setDir(dir);
523
+ }
524
+
525
+ const toolbarWrapper = this.context.get('toolbar._wrapper');
526
+ const statusbarWrapper = this.context.get('statusbar._wrapper');
527
+ if (rtl) {
528
+ this.applyFrameRoots((e) => {
529
+ domUtils.addClass([e.get('topArea'), e.get('wysiwyg')], 'se-rtl');
530
+ });
531
+ domUtils.addClass([this.carrierWrapper, toolbarWrapper, statusbarWrapper], 'se-rtl');
532
+ } else {
533
+ this.applyFrameRoots((e) => {
534
+ domUtils.removeClass([e.get('topArea'), e.get('wysiwyg')], 'se-rtl');
535
+ });
536
+ domUtils.removeClass([this.carrierWrapper, toolbarWrapper, statusbarWrapper], 'se-rtl');
537
+ }
538
+
539
+ const lineNodes = domUtils.getListChildren(fc.wysiwyg, (current) => {
540
+ return this.format.isLine(current) && (current.style.marginRight || current.style.marginLeft || current.style.textAlign);
541
+ });
542
+
543
+ for (let i = 0, n, l, r; (n = lineNodes[i]); i++) {
544
+ n = lineNodes[i];
545
+ // indent margin
546
+ r = n.style.marginRight;
547
+ l = n.style.marginLeft;
548
+ if (r || l) {
549
+ n.style.marginRight = l;
550
+ n.style.marginLeft = r;
551
+ }
552
+ // text align
553
+ r = n.style.textAlign;
554
+ if (r === 'left') n.style.textAlign = 'right';
555
+ else if (r === 'right') n.style.textAlign = 'left';
556
+ }
557
+
558
+ DIR_BTN_ACTIVE(this, rtl);
559
+
560
+ if (this.isBalloon) this.toolbar._showBalloon();
561
+ else if (this.isSubBalloon) this.subToolbar._showBalloon();
562
+ } catch (e) {
563
+ this.options.set('_rtl', !rtl);
564
+ console.warn(`[SUNEDITOR.setDir.fail] ${e.toString()}`);
565
+ }
566
+
567
+ this.effectNode = null;
568
+ this.eventManager.applyTagEffect();
569
+ },
570
+
571
+ /**
572
+ * @description Add or reset option property (Editor is reloaded)
573
+ * @param {Object} newOptions Options
574
+ */
575
+ resetOptions(newOptions) {
576
+ const _keys = Object.keys;
577
+ this.viewer.codeView(false);
578
+ this.viewer.showBlocks(false);
579
+
580
+ const newOptionKeys = _keys(newOptions);
581
+ CheckResetKeys(newOptionKeys, this.plugins, '');
582
+ if (newOptionKeys.length === 0) return;
583
+
584
+ // option merge
585
+ const rootDiff = {};
586
+ const rootKeys = this.rootKeys;
587
+ const frameRoots = this.frameRoots;
588
+ const newRoots = [];
589
+ const newRootKeys = {};
590
+ this._originOptions = [newOptions, this._originOptions].reduce(function (init, option) {
591
+ for (const key in option) {
592
+ if (rootKeys.includes(key) && option[key]) {
593
+ const nro = option[key];
594
+ const newKeys = _keys(nro);
595
+ CheckResetKeys(newKeys, null, key + '.');
596
+ if (newKeys.length === 0) continue;
597
+
598
+ rootDiff[key] = new Map();
599
+ const o = frameRoots.get(key).get('options').get('_origin');
600
+ for (const rk in nro) {
601
+ const roV = nro[rk];
602
+ if (!newKeys.includes(rk) || o[rk] === roV) continue;
603
+ rootDiff[key].set(GetResetDiffKey(rk), true);
604
+ o[rk] = roV;
605
+ }
606
+ newRoots.push((newRootKeys[key] = { options: o }));
607
+ } else {
608
+ init[key] = option[key];
609
+ }
610
+ }
611
+ return init;
612
+ }, {});
613
+
614
+ // init options
615
+ const options = this.options;
616
+ const newMap = InitOptions(this._originOptions, newRoots, this.plugins).o;
617
+ /** --------- root start --------- */
618
+ for (let i = 0, k; (k = newOptionKeys[i]); i++) {
619
+ if (newRootKeys[k]) {
620
+ const diff = rootDiff[k];
621
+ const fc = frameRoots.get(k);
622
+ const originOptions = fc.get('options');
623
+ const newRootOptions = newRootKeys[k].options;
624
+
625
+ // statusbar
626
+ if (diff.has('statusbar')) {
627
+ domUtils.removeItem(fc.get('statusbar'));
628
+ if (newRootOptions.get('statusbar')) {
629
+ const statusbar = CreateStatusbar(newRootOptions, null).statusbar;
630
+ fc.get('container').appendChild(statusbar);
631
+ UpdateStatusbarContext(statusbar, fc);
632
+ this.eventManager.__addStatusbarEvent(fc, newRootOptions);
633
+ } else {
634
+ this.eventManager.removeEvent(originOptions.get('__statusbarEvent'));
635
+ newRootOptions.set('__statusbarEvent', null);
636
+ UpdateStatusbarContext(null, fc);
637
+ }
638
+ }
639
+
640
+ // iframe's options
641
+ if (diff.has('iframe_attributes')) {
642
+ const frame = fc.get('wysiwygFrame');
643
+ const originAttr = originOptions.get('iframe_attributes');
644
+ const newAttr = newRootOptions.get('iframe_attributes');
645
+ for (const origin_k in originAttr) frame.removeAttribute(origin_k, originAttr[origin_k]);
646
+ for (const new_k in newAttr) frame.setAttribute(new_k, newAttr[new_k]);
647
+ }
648
+ if (diff.has('iframe_cssFileName')) {
649
+ const docHead = fc.get('_wd').head;
650
+ const links = docHead.getElementsByTagName('link');
651
+ while (links[0]) docHead.removeChild(links[0]);
652
+ const parseDocument = new DOMParser().parseFromString(converter._setIframeStyleLinks(newRootOptions.get('iframe_cssFileName')), 'text/html');
653
+ const newLinks = parseDocument.head.children;
654
+ const sTag = docHead.querySelector('style');
655
+ while (newLinks[0]) docHead.insertBefore(newLinks[0], sTag);
656
+ }
657
+
658
+ // --- options set ---
659
+ fc.set('options', newRootOptions);
660
+
661
+ // frame styles
662
+ this.setEditorStyle(newRootOptions.get('_defaultStyles'), fc);
663
+
664
+ // frame attributes
665
+ const frame = fc.get('wysiwyg');
666
+ const originAttr = originOptions.get('editableFrameAttributes');
667
+ const newAttr = newRootOptions.get('editableFrameAttributes');
668
+ for (const origin_k in originAttr) frame.removeAttribute(origin_k, originAttr[origin_k]);
669
+ for (const new_k in newAttr) frame.setAttribute(new_k, newAttr[new_k]);
670
+
671
+ continue;
672
+ }
673
+ /** --------- root end --------- */
674
+
675
+ options.set(k, newMap.get(k));
676
+
677
+ /** apply option */
678
+ // history delay time
679
+ if (k === 'historyStackDelayTime') {
680
+ this.history.resetDelayTime(options.get('historyStackDelayTime'));
681
+ continue;
682
+ }
683
+ // set dir
684
+ if (k === 'textDirection') {
685
+ this.setDir(options.get('_rtl') ? 'ltr' : 'rtl');
686
+ continue;
687
+ }
688
+ }
689
+
690
+ /** apply options */
691
+ // toolbar
692
+ const toolbar = this.context.get('toolbar.main');
693
+ // width
694
+ if (/inline|balloon/i.test(options.get('mode')) && newOptionKeys.includes('toolbar_width')) {
695
+ toolbar.style.width = options.get('toolbar_width');
696
+ }
697
+ // hide
698
+ if (options.get('toolbar_hide')) {
699
+ toolbar.style.display = 'none';
700
+ }
701
+ // shortcuts hint
702
+ if (options.get('shortcutsHint')) {
703
+ domUtils.removeClass(toolbar, 'se-shortcut-hide');
704
+ } else {
705
+ domUtils.addClass(toolbar, 'se-shortcut-hide');
706
+ }
707
+
708
+ this.effectNode = null;
709
+ this._setFrameInfo(this.frameRoots.get(this.status.rootKey));
710
+ },
711
+
712
+ /**
713
+ * @description Change the current root index.
714
+ * @param {number} rootKey
715
+ */
716
+ changeFrameContext(rootKey) {
717
+ if (rootKey === this.status.rootKey) return;
718
+
719
+ this.status.rootKey = rootKey;
720
+ this._setFrameInfo(this.frameRoots.get(rootKey));
721
+ this.toolbar._resetSticky();
722
+ },
723
+
724
+ /**
725
+ * @description javascript execCommand
726
+ * @param {string} command javascript execCommand function property
727
+ * @param {Boolean|undefined} showDefaultUI javascript execCommand function property
728
+ * @param {string|undefined} value javascript execCommand function property
729
+ */
730
+ execCommand(command, showDefaultUI, value) {
731
+ this.frameContext.get('_wd').execCommand(command, showDefaultUI, command === 'formatBlock' ? '<' + value + '>' : value);
732
+ this.history.push(true);
733
+ },
734
+
735
+ /**
736
+ * @description Focus to wysiwyg area
737
+ * @param {number|undefined} rootKey Root index
738
+ */
739
+ focus(rootKey) {
740
+ if (rootKey) this.changeFrameContext(rootKey);
741
+ if (this.frameContext.get('wysiwygFrame').style.display === 'none') return;
742
+ this._antiBlur = false;
743
+
744
+ if (this.frameOptions.get('iframe') || !this.frameContext.get('wysiwyg').contains(this.selection.getNode())) {
745
+ this._nativeFocus();
746
+ } else {
747
+ try {
748
+ const range = this.selection.getRange();
749
+ if (range.startContainer === range.endContainer && domUtils.isWysiwygFrame(range.startContainer)) {
750
+ const currentNode = range.commonAncestorContainer.children[range.startOffset];
751
+ if (!this.format.isLine(currentNode) && !this.component.is(currentNode)) {
752
+ const br = domUtils.createElement('BR');
753
+ const format = domUtils.createElement(this.options.get('defaultLine'), null, br);
754
+ this.frameContext.get('wysiwyg').insertBefore(format, currentNode);
755
+ this.selection.setRange(br, 0, br, 0);
756
+ return;
757
+ }
758
+ }
759
+ this.selection.setRange(range.startContainer, range.startOffset, range.endContainer, range.endOffset);
760
+ } catch (e) {
761
+ console.warn('[SUNEDITOR.focus.warn] ', e);
762
+ this._nativeFocus();
763
+ }
764
+ }
765
+
766
+ if (this.isBalloon) this.eventManager._toggleToolbarBalloon();
767
+ },
768
+
769
+ /**
770
+ * @description If "focusEl" is a component, then that component is selected; if it is a format element, the last text is selected
771
+ * If "focusEdge" is null, then selected last element
772
+ * @param {Element|null} focusEl Focus element
773
+ */
774
+ focusEdge(focusEl) {
775
+ this._antiBlur = false;
776
+ if (!focusEl) focusEl = this.frameContext.get('wysiwyg').lastElementChild;
777
+
778
+ const fileComponentInfo = this.component.get(focusEl);
779
+ if (fileComponentInfo) {
780
+ this.component.select(fileComponentInfo.target, fileComponentInfo.pluginName, false);
781
+ } else if (focusEl) {
782
+ if (focusEl.nodeType !== 3) {
783
+ focusEl = domUtils.getEdgeChild(
784
+ focusEl,
785
+ function (current) {
786
+ return current.childNodes.length === 0 || current.nodeType === 3;
787
+ },
788
+ true
789
+ );
790
+ }
791
+ if (!focusEl) this._nativeFocus();
792
+ else this.selection.setRange(focusEl, focusEl.textContent.length, focusEl, focusEl.textContent.length);
793
+ } else {
794
+ this.focus();
795
+ }
796
+ },
797
+
798
+ /**
799
+ * @description Focusout to wysiwyg area (.blur())
800
+ */
801
+ blur() {
802
+ if (this.frameOptions.get('iframe')) {
803
+ this.frameContext.get('wysiwygFrame').blur();
804
+ } else {
805
+ this.frameContext.get('wysiwyg').blur();
806
+ }
807
+ },
808
+
809
+ /**
810
+ * @description Set "options.get('editorStyle')" style.
811
+ * Define the style of the edit area
812
+ * It can also be defined with the "setOptions" method, but the "setEditorStyle" method does not render the editor again.
813
+ * @param {string} style Style string
814
+ * @param {FrameContext|null} fc Frame context
815
+ */
816
+ setEditorStyle(style, fc) {
817
+ fc = fc || this.frameContext;
818
+ const fo = fc.get('options');
819
+
820
+ const newStyles = converter._setDefaultOptionStyle(fo, style);
821
+ fo.set('_defaultStyles', newStyles);
822
+
823
+ // top area
824
+ fc.get('topArea').style.cssText = newStyles.top;
825
+
826
+ // code view
827
+ const code = fc.get('code');
828
+ code.style.cssText = fo.get('_defaultStyles').frame;
829
+ code.style.display = 'none';
830
+
831
+ // wysiwyg frame
832
+ if (!fo.get('iframe')) {
833
+ fc.get('wysiwygFrame').style.cssText = newStyles.frame + newStyles.editor;
834
+ } else {
835
+ fc.get('wysiwygFrame').style.cssText = newStyles.frame;
836
+ fc.get('wysiwyg').style.cssText = newStyles.editor;
837
+ }
838
+ },
839
+
840
+ /**
841
+ * @description Switch to or off "ReadOnly" mode.
842
+ * @param {boolean} value "readOnly" boolean value.
843
+ * @param {string|undefined} rootKey Root key
844
+ */
845
+ readOnly(value, rootKey) {
846
+ const fc = rootKey ? this.frameRoots.get(rootKey) : this.frameContext;
847
+
848
+ fc.set('isReadOnly', !!value);
849
+ domUtils.setDisabled(this._controllerOnDisabledButtons, !!value);
850
+
851
+ if (value) {
852
+ this._offCurrentController();
853
+ this._offCurrentModal();
854
+
855
+ if (this.toolbar?.currentMoreLayerActiveButton?.disabled) this.toolbar.moreLayerOff();
856
+ if (this.subToolbar?.currentMoreLayerActiveButton?.disabled) this.subToolbar.moreLayerOff();
857
+ if (this.menu?.currentDropdownActiveButton?.disabled) this.menu.dropdownOff();
858
+ if (this.menu?.currentContainerActiveButton?.disabled) this.menu.containerOff();
859
+ if (this.modalForm) this.plugins.modal.close.call(this);
860
+
861
+ fc.get('code').setAttribute('readOnly', 'true');
862
+ domUtils.addClass(fc.get('wysiwygFrame'), 'se-read-only');
863
+ } else {
864
+ fc.get('code').removeAttribute('readOnly');
865
+ domUtils.removeClass(fc.get('wysiwygFrame'), 'se-read-only');
866
+ }
867
+
868
+ if (this.options.get('hasCodeMirror')) {
869
+ this.viewer._codeMirrorEditor('readonly', !!value, rootKey);
870
+ }
871
+ },
872
+
873
+ /**
874
+ * @description Disable the suneditor
875
+ * @param {string|undefined} rootKey Root key
876
+ */
877
+ disable(rootKey) {
878
+ const fc = rootKey ? this.frameRoots.get(rootKey) : this.frameContext;
879
+
880
+ this.toolbar.disable();
881
+ this._offCurrentController();
882
+ this._offCurrentModal();
883
+
884
+ if (this.modalForm) this.plugins.modal.close.call(this);
885
+
886
+ fc.get('wysiwyg').setAttribute('contenteditable', false);
887
+ fc.set('isDisabled', true);
888
+
889
+ if (this.options.get('hasCodeMirror')) {
890
+ this.viewer._codeMirrorEditor('readonly', true, rootKey);
891
+ } else {
892
+ fc.get('code').setAttribute('disabled', true);
893
+ }
894
+ },
895
+
896
+ /**
897
+ * @description Enable the suneditor
898
+ * @param {string|undefined} rootKey Root key
899
+ */
900
+ enable(rootKey) {
901
+ const fc = rootKey ? this.frameRoots.get(rootKey) : this.frameContext;
902
+
903
+ this.toolbar.enable();
904
+ fc.get('wysiwyg').setAttribute('contenteditable', true);
905
+ fc.set('isDisabled', false);
906
+
907
+ if (this.options.get('hasCodeMirror')) {
908
+ this.viewer._codeMirrorEditor('readonly', false, rootKey);
909
+ } else {
910
+ fc.get('code').removeAttribute('disabled');
911
+ }
912
+ },
913
+
914
+ /**
915
+ * @description Show the suneditor
916
+ * @param {string|undefined} rootKey Root key
917
+ */
918
+ show(rootKey) {
919
+ const fc = rootKey ? this.frameRoots.get(rootKey) : this.frameContext;
920
+ const topAreaStyle = fc.get('topArea').style;
921
+ if (topAreaStyle.display === 'none') topAreaStyle.display = 'block';
922
+ },
923
+
924
+ /**
925
+ * @description Hide the suneditor
926
+ * @param {string|undefined} rootKey Root key
927
+ */
928
+ hide(rootKey) {
929
+ const fc = rootKey ? this.frameRoots.get(rootKey) : this.frameContext;
930
+ fc.get('topArea').style.display = 'none';
931
+ },
932
+
933
+ /**
934
+ * @description Show loading box
935
+ * @param {string|undefined} rootKey Root key
936
+ */
937
+ showLoading(rootKey) {
938
+ (rootKey ? this.frameRoots.get(rootKey).get('container') : this.carrierWrapper).querySelector('.se-loading-box').style.display = 'block';
939
+ },
940
+
941
+ /**
942
+ * @description Hide loading box
943
+ * @param {string|undefined} rootKey Root key
944
+ */
945
+ hideLoading(rootKey) {
946
+ (rootKey ? this.frameRoots.get(rootKey).get('container') : this.carrierWrapper).querySelector('.se-loading-box').style.display = 'none';
947
+ },
948
+
949
+ /**
950
+ * @description Activate the transparent background "div" so that other elements are not affected during resizing.
951
+ * @param {cursor} cursor cursor css property
952
+ */
953
+ enableBackWrapper(cursor) {
954
+ this._backWrapper.style.cursor = cursor;
955
+ this._backWrapper.style.display = 'block';
956
+ },
957
+
958
+ /**
959
+ * @description Disabled background "div"
960
+ */
961
+ disableBackWrapper() {
962
+ this._backWrapper.style.display = 'none';
963
+ this._backWrapper.style.cursor = 'default';
964
+ },
965
+
966
+ /**
967
+ * @description Destroy the suneditor
968
+ */
969
+ destroy() {
970
+ /** remove history */
971
+ this.history.destroy();
972
+
973
+ /** remove event listeners */
974
+ this.eventManager._removeAllEvents();
975
+
976
+ /** destroy external library */
977
+ if (this.options.get('codeMirror6Editor')) {
978
+ this.options.get('codeMirror6Editor').destroy();
979
+ }
980
+
981
+ /** remove element */
982
+ domUtils.removeItem(this.carrierWrapper);
983
+ domUtils.removeItem(this.context.get('toolbar._wrapper'));
984
+ domUtils.removeItem(this.context.get('toolbar.sub._wrapper'));
985
+ domUtils.removeItem(this.context.get('statusbar._wrapper'));
986
+ this.applyFrameRoots((e) => {
987
+ domUtils.removeItem(e.get('topArea'));
988
+ e.get('options').clear();
989
+ e.clear();
990
+ });
991
+
992
+ /** remove object reference */
993
+ this.options.clear();
994
+ this.context.clear();
995
+
996
+ let obj = this.plugins;
997
+ for (const k in obj) {
998
+ const p = obj[k];
999
+ if (typeof p._destroy === 'function') p._destroy();
1000
+ for (const pk in p) {
1001
+ delete p[pk];
1002
+ }
1003
+ delete obj[k];
1004
+ }
1005
+ obj = this.events;
1006
+ for (const k in obj) {
1007
+ delete obj[k];
1008
+ }
1009
+
1010
+ obj = ['eventManager', 'char', 'component', 'format', 'html', 'menu', 'nodeTransform', 'notice', 'offset', 'selection', 'shortcuts', 'toolbar', 'viewer'];
1011
+ for (let i = 0, len = obj.length, c; i < len; i++) {
1012
+ c = this[obj[i]];
1013
+ for (const k in c) {
1014
+ delete c[k];
1015
+ }
1016
+ }
1017
+ obj = this.subToolbar;
1018
+ if (obj) {
1019
+ for (const k in obj) {
1020
+ delete obj[k];
1021
+ }
1022
+ }
1023
+
1024
+ obj = null;
1025
+ for (const k in this) {
1026
+ delete this[k];
1027
+ }
1028
+
1029
+ return null;
1030
+ },
1031
+
1032
+ /** ----- private methods ----------------------------------------------------------------------------------------------------------------------------- */
1033
+ /**
1034
+ * @description Set frameContext, frameOptions
1035
+ * @param {rootTarget} rt Root target
1036
+ */
1037
+ _setFrameInfo(rt) {
1038
+ this.frameContext = rt;
1039
+ this.frameOptions = rt.get('options');
1040
+ rt.set('_editorHeight', rt.get('wysiwygFrame').offsetHeight);
1041
+ this._lineBreaker_t = rt.get('lineBreaker_t');
1042
+ this._lineBreaker_b = rt.get('lineBreaker_b');
1043
+ },
1044
+
1045
+ /**
1046
+ * @description visible controllers
1047
+ * @param {boolean} value hidden/show
1048
+ * @param {boolean?} lineBreakShow Line break hidden/show (default: Follows the value "value".)
1049
+ * @private
1050
+ */
1051
+ _visibleControllers(value, lineBreakShow) {
1052
+ const visible = value ? '' : 'hidden';
1053
+ const breakerVisible = lineBreakShow ?? visible ? '' : 'hidden';
1054
+
1055
+ const cont = this.opendControllers;
1056
+ for (let i = 0, c; i < cont.length; i++) {
1057
+ c = cont[i];
1058
+ if (c.form) c.form.style.visibility = visible;
1059
+ }
1060
+
1061
+ this._lineBreaker_t.style.visibility = breakerVisible;
1062
+ this._lineBreaker_b.style.visibility = breakerVisible;
1063
+ },
1064
+
1065
+ /**
1066
+ * @description Off current controllers
1067
+ * @private
1068
+ */
1069
+ _offCurrentController() {
1070
+ this.__offControllers();
1071
+ this.component.__deselect();
1072
+ },
1073
+
1074
+ /**
1075
+ * @description Off controllers
1076
+ * @private
1077
+ */
1078
+ __offControllers() {
1079
+ const cont = this.opendControllers;
1080
+ const fixedCont = [];
1081
+ for (let i = 0, c; i < cont.length; i++) {
1082
+ c = cont[i];
1083
+ if (c.fixed) {
1084
+ fixedCont.push(c);
1085
+ continue;
1086
+ }
1087
+ if (typeof c.inst.close === 'function') c.inst.close();
1088
+ if (c.form) c.form.style.display = 'none';
1089
+ }
1090
+ this.opendControllers = fixedCont;
1091
+ this.currentControllerName = '';
1092
+ this._antiBlur = false;
1093
+ },
1094
+
1095
+ /**
1096
+ * @description Off current modal
1097
+ * @private
1098
+ */
1099
+ _offCurrentModal() {
1100
+ if (this.opendModal) {
1101
+ this.opendModal.close();
1102
+ }
1103
+ },
1104
+
1105
+ /**
1106
+ * @description Focus to wysiwyg area using "native focus function"
1107
+ * @private
1108
+ */
1109
+ _nativeFocus() {
1110
+ this.selection.__focus();
1111
+ this.selection._init();
1112
+ },
1113
+
1114
+ /**
1115
+ * @description Check the components such as image and video and modify them according to the format.
1116
+ * @private
1117
+ */
1118
+ _checkComponents(loaded) {
1119
+ for (let i = 0, len = this._fileInfoPluginsCheck.length; i < len; i++) {
1120
+ this._fileInfoPluginsCheck[i](loaded);
1121
+ }
1122
+ },
1123
+
1124
+ /**
1125
+ * @description Initialize the information of the components.
1126
+ * @private
1127
+ */
1128
+ _resetComponents() {
1129
+ for (let i = 0, len = this._fileInfoPluginsReset.length; i < len; i++) {
1130
+ this._fileInfoPluginsReset[i]();
1131
+ }
1132
+ },
1133
+
1134
+ /**
1135
+ * @description Initializ wysiwyg area (Only called from core._init)
1136
+ * @param {Map} e frameContext
1137
+ * @param {string} value initial html string
1138
+ * @private
1139
+ */
1140
+ _initWysiwygArea(e, value) {
1141
+ e.get('wysiwyg').innerHTML =
1142
+ this.html.clean(typeof value === 'string' ? value : (/^TEXTAREA$/i.test(e.get('originElement').nodeName) ? e.get('originElement').value : e.get('originElement').innerHTML) || '', true, null, null) ||
1143
+ '<' + this.options.get('defaultLine') + '><br></' + this.options.get('defaultLine') + '>';
1144
+
1145
+ if (e.has('charCounter')) e.get('charCounter').textContent = this.char.getLength();
1146
+ },
1147
+
1148
+ /**
1149
+ * @description Called when there are changes to tags in the wysiwyg region.
1150
+ * @private
1151
+ */
1152
+ _resourcesStateChange(fc) {
1153
+ this._iframeAutoHeight(fc);
1154
+ this._checkPlaceholder(fc);
1155
+ },
1156
+
1157
+ /**
1158
+ * @description Modify the height value of the iframe when the height of the iframe is automatic.
1159
+ * @private
1160
+ */
1161
+ _iframeAutoHeight(fc) {
1162
+ const autoFrame = fc.get('_iframeAuto');
1163
+
1164
+ if (autoFrame) {
1165
+ this._w.setTimeout(() => {
1166
+ const h = autoFrame.offsetHeight;
1167
+ fc.get('wysiwygFrame').style.height = h + 'px';
1168
+ if (!env.isResizeObserverSupported) this.__callResizeFunction(fc, h, null);
1169
+ }, 0);
1170
+ } else if (!env.isResizeObserverSupported) {
1171
+ this.__callResizeFunction(fc, fc.get('wysiwygFrame').offsetHeight, null);
1172
+ }
1173
+ },
1174
+
1175
+ __callResizeFunction(fc, h, resizeObserverEntry) {
1176
+ h =
1177
+ h === -1
1178
+ ? resizeObserverEntry?.borderBoxSize && resizeObserverEntry.borderBoxSize[0]
1179
+ ? resizeObserverEntry.borderBoxSize[0].blockSize
1180
+ : resizeObserverEntry.contentRect.height + numbers.get(fc.get('wwComputedStyle').getPropertyValue('padding-left')) + numbers.get(fc.get('wwComputedStyle').getPropertyValue('padding-right'))
1181
+ : h;
1182
+ if (fc.get('_editorHeight') !== h) {
1183
+ this.triggerEvent('onResizeEditor', { height: h, prevHeight: fc.get('_editorHeight'), frameContext: fc, observerEntry: resizeObserverEntry });
1184
+ fc.set('_editorHeight', h);
1185
+ }
1186
+ },
1187
+
1188
+ /**
1189
+ * @description Set display property when there is placeholder.
1190
+ * @private
1191
+ */
1192
+ _checkPlaceholder(fc) {
1193
+ fc = fc || this.frameContext;
1194
+ const placeholder = fc.get('placeholder');
1195
+
1196
+ if (placeholder) {
1197
+ if (fc.get('isCodeView')) {
1198
+ placeholder.style.display = 'none';
1199
+ return;
1200
+ }
1201
+
1202
+ if (this.isEmpty(fc)) {
1203
+ placeholder.style.display = 'block';
1204
+ } else {
1205
+ placeholder.style.display = 'none';
1206
+ }
1207
+ }
1208
+ },
1209
+
1210
+ /**
1211
+ * @description Initializ editor
1212
+ * @private
1213
+ */
1214
+ __editorInit(options) {
1215
+ this.applyFrameRoots((e) => {
1216
+ this.__setEditorParams(e);
1217
+ });
1218
+
1219
+ // initialize core and add event listeners
1220
+ this._setFrameInfo(this.frameRoots.get(this.status.rootKey));
1221
+ this.__init(options);
1222
+ for (const v of this._onPluginEvents.values()) {
1223
+ v.sort((a, b) => a.index - b.index);
1224
+ }
1225
+
1226
+ this.applyFrameRoots((e) => {
1227
+ this.eventManager._addFrameEvents(e);
1228
+ this._initWysiwygArea(e, e.get('options').get('value'));
1229
+ });
1230
+
1231
+ this.eventManager.__eventDoc = null;
1232
+ this._componentsInfoInit = false;
1233
+ this._componentsInfoReset = false;
1234
+ this._checkComponents(true);
1235
+
1236
+ this._w.setTimeout(() => {
1237
+ // toolbar visibility
1238
+ this.context.get('toolbar.main').style.visibility = '';
1239
+ // roots
1240
+ this.applyFrameRoots((e) => {
1241
+ if (typeof this._resourcesStateChange !== 'function') return;
1242
+ // observer
1243
+ if (this.eventManager._wwFrameObserver) this.eventManager._wwFrameObserver.observe(e.get('wysiwygFrame'));
1244
+ if (this.eventManager._toolbarObserver) this.eventManager._toolbarObserver.observe(e.get('_toolbarShadow'));
1245
+ // resource state
1246
+ this._resourcesStateChange(e);
1247
+ });
1248
+ // history reset
1249
+ this.history.reset();
1250
+ // user event
1251
+ this.triggerEvent('onload', {});
1252
+ }, 0);
1253
+ },
1254
+
1255
+ /**
1256
+ * @description Initializ core variable
1257
+ * @private
1258
+ */
1259
+ __init(options) {
1260
+ // file components
1261
+ this._fileInfoPluginsCheck = [];
1262
+ this._fileInfoPluginsReset = [];
1263
+
1264
+ // text components
1265
+ this._MELInfo = new Map();
1266
+
1267
+ // Command and file plugins registration
1268
+ this.activeCommands = ACTIVE_EVENT_COMMANDS;
1269
+ this._onPluginEvents = new Map([
1270
+ ['onMouseMove', []],
1271
+ ['onMouseLeave', []],
1272
+ ['onMouseDown', []],
1273
+ ['onMouseUp', []],
1274
+ ['onScroll', []],
1275
+ ['onClick', []],
1276
+ ['onInput', []],
1277
+ ['onKeyDown', []],
1278
+ ['onKeyUp', []],
1279
+ ['onFocus', []],
1280
+ ['onBlur', []],
1281
+ ['onPastAndDrop', []]
1282
+ ]);
1283
+ this._fileManager.tags = [];
1284
+ this._fileManager.pluginMap = {};
1285
+ this._fileManager.tagAttrs = {};
1286
+
1287
+ const plugins = this.plugins;
1288
+ const isArray = Array.isArray;
1289
+ const shortcuts = this.options.get('shortcuts');
1290
+ const filePluginRegExp = [];
1291
+ let plugin;
1292
+ for (const key in plugins) {
1293
+ this.registerPlugin(key, this._pluginCallButtons[key], options[key], shortcuts[key]);
1294
+ this.registerPlugin(key, this._pluginCallButtons_sub[key], options[key], shortcuts[key]);
1295
+ plugin = this.plugins[key];
1296
+
1297
+ // Filemanager
1298
+ if (typeof plugin.__fileManagement === 'object') {
1299
+ const fm = plugin.__fileManagement;
1300
+ this._fileInfoPluginsCheck.push(fm._checkInfo.bind(fm));
1301
+ this._fileInfoPluginsReset.push(fm._resetInfo.bind(fm));
1302
+ if (isArray(fm.tagNames)) {
1303
+ const tagNames = fm.tagNames;
1304
+ this._fileManager.tags = this._fileManager.tags.concat(tagNames);
1305
+ filePluginRegExp.push(key);
1306
+ for (let tag = 0, tLen = tagNames.length, t; tag < tLen; tag++) {
1307
+ t = tagNames[tag].toLowerCase();
1308
+ this._fileManager.pluginMap[t] = key;
1309
+ if (fm.tagAttrs) {
1310
+ this._fileManager.tagAttrs[t] = fm.tagAttrs;
1311
+ }
1312
+ }
1313
+ }
1314
+ }
1315
+
1316
+ // Not file component
1317
+ if (typeof plugin.constructor.component === 'function') {
1318
+ this._componentManager.push(
1319
+ function (launcher, element) {
1320
+ if (!(element = launcher.component?.call(this, element))) return null;
1321
+ return {
1322
+ target: element,
1323
+ pluginName: launcher.key,
1324
+ options: launcher.options
1325
+ };
1326
+ }.bind(plugin, plugin.constructor)
1327
+ );
1328
+ }
1329
+
1330
+ // plugin event
1331
+ const pluginOptions = plugin.constructor.options || {};
1332
+ this._onPluginEvents.forEach((v, k) => {
1333
+ if (typeof plugin[k] === 'function') {
1334
+ const f = plugin[k].bind(plugin);
1335
+ f.index = pluginOptions[`eventIndex_${k}`] || pluginOptions.eventIndex || 0;
1336
+ v.push(f);
1337
+ }
1338
+ });
1339
+
1340
+ // plugin maintain
1341
+ if (plugin.retainFormat) {
1342
+ const info = plugin.retainFormat();
1343
+ this._MELInfo.set(info.query, info.method);
1344
+ }
1345
+ }
1346
+
1347
+ this._fileManager.regExp = new RegExp(`^(${this._fileManager.tags.join('|') || '\\^'})$`, 'i');
1348
+ this._fileManager.pluginRegExp = new RegExp(`^(${filePluginRegExp.length === 0 ? '\\^' : filePluginRegExp.join('|')})$`, 'i');
1349
+
1350
+ delete this._pluginCallButtons;
1351
+ delete this._pluginCallButtons_sub;
1352
+
1353
+ this.__cachingButtons();
1354
+ },
1355
+
1356
+ /**
1357
+ * @description Caching basic buttons to use
1358
+ * @private
1359
+ */
1360
+ __cachingButtons() {
1361
+ const ctx = this.context;
1362
+ this.__setDisabledButtons();
1363
+ this.__saveCommandButtons(this.allCommandButtons, ctx.get('toolbar.buttonTray'));
1364
+ if (this.options.has('_subMode')) {
1365
+ this.__saveCommandButtons(this.subAllCommandButtons, ctx.get('toolbar.sub.buttonTray'));
1366
+ }
1367
+ },
1368
+
1369
+ __setDisabledButtons() {
1370
+ const ctx = this.context;
1371
+
1372
+ this._codeViewDisabledButtons = converter.nodeListToArray(ctx.get('toolbar.buttonTray').querySelectorAll(DISABLE_BUTTONS_CODEVIEW));
1373
+ this._controllerOnDisabledButtons = converter.nodeListToArray(ctx.get('toolbar.buttonTray').querySelectorAll(DISABLE_BUTTONS_CONTROLLER));
1374
+
1375
+ if (this.options.has('_subMode')) {
1376
+ this._codeViewDisabledButtons = this._codeViewDisabledButtons.concat(converter.nodeListToArray(ctx.get('toolbar.sub.buttonTray').querySelectorAll(DISABLE_BUTTONS_CODEVIEW)));
1377
+ this._controllerOnDisabledButtons = this._controllerOnDisabledButtons.concat(converter.nodeListToArray(ctx.get('toolbar.sub.buttonTray').querySelectorAll(DISABLE_BUTTONS_CONTROLLER)));
1378
+ }
1379
+ },
1380
+
1381
+ /**
1382
+ * @description Save the current buttons
1383
+ * @private
1384
+ */
1385
+ __saveCommandButtons(cmdButtons, tray) {
1386
+ const currentButtons = tray.querySelectorAll(COMMAND_BUTTONS);
1387
+ const shortcuts = this.options.get('shortcuts');
1388
+ const reverseCommandArray = this.options.get('_reverseCommandArray');
1389
+ const keyMap = this.shortcutsKeyMap;
1390
+ const reverseKeys = this.reverseKeys;
1391
+
1392
+ for (let i = 0, len = currentButtons.length, e, c; i < len; i++) {
1393
+ e = currentButtons[i];
1394
+ c = e.getAttribute('data-command');
1395
+ // command set
1396
+ cmdButtons.set(c, e);
1397
+ this.__setCommandTargets(c, e);
1398
+ // shortcuts
1399
+ CreateShortcuts(c, e, shortcuts[c], keyMap, reverseCommandArray, reverseKeys);
1400
+ }
1401
+ },
1402
+
1403
+ __setCommandTargets(cmd, target) {
1404
+ if (!cmd || !target) return;
1405
+
1406
+ const isBasicCmd = BASIC_COMMANDS.includes(cmd);
1407
+ if (!isBasicCmd && !this.plugins[cmd]) return;
1408
+
1409
+ if (!this.commandTargets.get(cmd)) {
1410
+ this.commandTargets.set(cmd, [target]);
1411
+ } else if (!this.commandTargets.get(cmd).includes(target)) {
1412
+ this.commandTargets.get(cmd).push(target);
1413
+ }
1414
+ },
1415
+
1416
+ __setIframeDocument(frame, originOptions, targetOptions) {
1417
+ frame.setAttribute('scrolling', 'auto');
1418
+ frame.contentDocument.head.innerHTML =
1419
+ '<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">' +
1420
+ converter._setIframeStyleLinks(targetOptions.get('iframe_cssFileName')) +
1421
+ converter._setAutoHeightStyle(targetOptions.get('height'));
1422
+ frame.contentDocument.body.className = originOptions.get('_editableClass');
1423
+ frame.contentDocument.body.setAttribute('contenteditable', true);
1424
+ },
1425
+
1426
+ __setEditorParams(e) {
1427
+ const frameOptions = e.get('options');
1428
+ const _w = this._w;
1429
+
1430
+ e.set('wwComputedStyle', _w.getComputedStyle(e.get('wysiwyg')));
1431
+
1432
+ if (!frameOptions.get('iframe') && typeof ShadowRoot === 'function') {
1433
+ let child = e.get('wysiwygFrame');
1434
+ while (child) {
1435
+ if (child.shadowRoot) {
1436
+ this._shadowRoot = child.shadowRoot;
1437
+ break;
1438
+ } else if (child instanceof ShadowRoot) {
1439
+ this._shadowRoot = child;
1440
+ break;
1441
+ }
1442
+ child = child.parentNode;
1443
+ }
1444
+ }
1445
+
1446
+ // wisywig attributes
1447
+ const attr = frameOptions.get('editableFrameAttributes');
1448
+ for (const k in attr) {
1449
+ e.get('wysiwyg').setAttribute(k, attr[k]);
1450
+ }
1451
+
1452
+ // init, validate
1453
+ if (frameOptions.get('iframe')) {
1454
+ e.set('_ww', e.get('wysiwygFrame').contentWindow);
1455
+ e.set('_wd', e.get('wysiwygFrame').contentDocument);
1456
+ e.set('wysiwyg', e.get('_wd').body);
1457
+ // e.get('wysiwyg').className += ' ' + options.get('_editableClass');
1458
+ if (frameOptions.get('_defaultStyles').editor) e.get('wysiwyg').style.cssText = frameOptions.get('_defaultStyles').editor;
1459
+ if (frameOptions.get('height') === 'auto') e.set('_iframeAuto', e.get('_wd').body);
1460
+ } else {
1461
+ e.set('_ww', _w);
1462
+ e.set('_wd', this._d);
1463
+ }
1464
+ },
1465
+
1466
+ __registerClass() {
1467
+ // use events
1468
+ this.events = { ...Events(), ...this.options.get('events') };
1469
+ this.triggerEvent = async (eventName, eventData) => {
1470
+ const eventHandler = this.events[eventName];
1471
+ if (typeof eventHandler === 'function') {
1472
+ return await eventHandler({ editor: this, ...eventData });
1473
+ }
1474
+ return env.NO_EVENT;
1475
+ };
1476
+
1477
+ // history function
1478
+ this.history = History(this);
1479
+
1480
+ // eventManager
1481
+ this.eventManager = new EventManager(this);
1482
+
1483
+ // util classes
1484
+ this.offset = new Offset(this);
1485
+ this.shortcuts = new Shortcuts(this);
1486
+ this.notice = new Notice(this);
1487
+ // main classes
1488
+ this.toolbar = new Toolbar(this, { keyName: 'toolbar', balloon: this.isBalloon, balloonAlways: this.isBalloonAlways, inline: this.isInline, res: this._responsiveButtons });
1489
+ if (this.options.has('_subMode'))
1490
+ this.subToolbar = new Toolbar(this, {
1491
+ keyName: 'toolbar.sub',
1492
+ balloon: this.isSubBalloon,
1493
+ balloonAlways: this.isSubBalloonAlways,
1494
+ inline: false,
1495
+ res: this._responsiveButtons_sub
1496
+ });
1497
+ this.selection = new Selection(this);
1498
+ this.html = new HTML(this);
1499
+ this.nodeTransform = new NodeTransform(this);
1500
+ this.component = new Component(this);
1501
+ this.format = new Format(this);
1502
+ this.menu = new Menu(this);
1503
+ this.char = new Char(this);
1504
+ this.viewer = new Viewer(this);
1505
+
1506
+ // register classes to the eventManager
1507
+ ClassInjector.call(this.eventManager, this);
1508
+ // register main classes
1509
+ ClassInjector.call(this.char, this);
1510
+ ClassInjector.call(this.component, this);
1511
+ ClassInjector.call(this.format, this);
1512
+ ClassInjector.call(this.html, this);
1513
+ ClassInjector.call(this.menu, this);
1514
+ ClassInjector.call(this.nodeTransform, this);
1515
+ ClassInjector.call(this.selection, this);
1516
+ ClassInjector.call(this.toolbar, this);
1517
+ ClassInjector.call(this.viewer, this);
1518
+ if (this.options.has('_subMode')) ClassInjector.call(this.subToolbar, this);
1519
+
1520
+ // delete self reference
1521
+ delete this.char.char;
1522
+ delete this.component.component;
1523
+ delete this.format.format;
1524
+ delete this.html.html;
1525
+ delete this.menu.menu;
1526
+ delete this.nodeTransform.nodeTransform;
1527
+ delete this.selection.selection;
1528
+ delete this.toolbar.toolbar;
1529
+ delete this.viewer.viewer;
1530
+ if (this.subToolbar) delete this.subToolbar.subToolbar;
1531
+
1532
+ this._responsiveButtons = this._responsiveButtons_res = null;
1533
+ },
1534
+
1535
+ async __Create(originOptions) {
1536
+ // set modes
1537
+ this.isInline = /inline/i.test(this.options.get('mode'));
1538
+ this.isBalloon = /balloon/i.test(this.options.get('mode'));
1539
+ this.isBalloonAlways = /balloon-always/i.test(this.options.get('mode'));
1540
+ this.isClassic = /classic/i.test(this.options.get('mode'));
1541
+ // set subToolbar modes
1542
+ this.isSubBalloon = /balloon/i.test(this.options.get('_subMode'));
1543
+ this.isSubBalloonAlways = /balloon-always/i.test(this.options.get('_subMode'));
1544
+
1545
+ // register class
1546
+ this.__registerClass();
1547
+
1548
+ // common events
1549
+ this.eventManager._addCommonEvents();
1550
+
1551
+ // init
1552
+ const iframePromises = [];
1553
+ this.applyFrameRoots((e) => {
1554
+ const o = e.get('originElement');
1555
+ const t = e.get('topArea');
1556
+ o.style.display = 'none';
1557
+ t.style.display = 'block';
1558
+ o.parentNode.insertBefore(t, o.nextElementSibling);
1559
+
1560
+ if (e.get('options').get('iframe')) {
1561
+ const iframeLoaded = new Promise((resolve) => {
1562
+ this.eventManager.addEvent(e.get('wysiwygFrame'), 'load', ({ target }) => {
1563
+ this.__setIframeDocument(target, this.options, e.get('options'));
1564
+ resolve();
1565
+ });
1566
+ });
1567
+ iframePromises.push(iframeLoaded);
1568
+ }
1569
+ });
1570
+
1571
+ this.applyFrameRoots((e) => {
1572
+ e.get('wrapper').appendChild(e.get('wysiwygFrame'));
1573
+ });
1574
+
1575
+ if (iframePromises.length > 0) {
1576
+ await Promise.all(iframePromises);
1577
+ }
1578
+
1579
+ this.__editorInit(originOptions);
1580
+ },
1581
+
1582
+ Constructor: Editor
1583
+ };
1584
+
1585
+ function GetResetDiffKey(key) {
1586
+ if (/^statusbar/i.test(key)) return 'statusbar';
1587
+ return key;
1588
+ }
1589
+
1590
+ function CheckResetKeys(keys, plugins, root) {
1591
+ for (let i = 0, len = keys.length, k; i < len; i++) {
1592
+ k = keys[i];
1593
+ if (RO_UNAVAILABD.includes(k) || (plugins && plugins[k])) {
1594
+ console.warn(`[SUNEDITOR.warn.resetOptions] "[${root + k}]" options not available in resetOptions have no effect.`);
1595
+ keys.splice(i--, 1);
1596
+ len--;
1597
+ }
1598
+ }
1599
+ }
1600
+
1601
+ export default Editor;