suneditor 3.0.0-beta.2 → 3.0.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/CONTRIBUTING.md +186 -184
  2. package/LICENSE +21 -21
  3. package/README.md +157 -180
  4. package/dist/suneditor.min.css +1 -1
  5. package/dist/suneditor.min.js +1 -1
  6. package/package.json +126 -123
  7. package/src/assets/design/color.css +131 -121
  8. package/src/assets/design/index.css +3 -3
  9. package/src/assets/design/size.css +37 -35
  10. package/src/assets/design/typography.css +37 -37
  11. package/src/assets/icons/defaultIcons.js +247 -232
  12. package/src/assets/suneditor-contents.css +779 -778
  13. package/src/assets/suneditor.css +43 -35
  14. package/src/core/base/eventHandlers/handler_toolbar.js +135 -135
  15. package/src/core/base/eventHandlers/handler_ww_clipboard.js +56 -56
  16. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +115 -113
  17. package/src/core/base/eventHandlers/handler_ww_key_input.js +1200 -1200
  18. package/src/core/base/eventHandlers/handler_ww_mouse.js +194 -194
  19. package/src/core/base/eventManager.js +1550 -1484
  20. package/src/core/base/history.js +355 -355
  21. package/src/core/class/char.js +163 -162
  22. package/src/core/class/component.js +856 -842
  23. package/src/core/class/format.js +3433 -3422
  24. package/src/core/class/html.js +1927 -1890
  25. package/src/core/class/menu.js +357 -346
  26. package/src/core/class/nodeTransform.js +424 -424
  27. package/src/core/class/offset.js +858 -891
  28. package/src/core/class/selection.js +710 -620
  29. package/src/core/class/shortcuts.js +98 -98
  30. package/src/core/class/toolbar.js +438 -430
  31. package/src/core/class/ui.js +424 -422
  32. package/src/core/class/viewer.js +750 -750
  33. package/src/core/editor.js +1810 -1708
  34. package/src/core/section/actives.js +268 -241
  35. package/src/core/section/constructor.js +1348 -1661
  36. package/src/core/section/context.js +102 -102
  37. package/src/core/section/documentType.js +582 -561
  38. package/src/core/section/options.js +367 -0
  39. package/src/core/util/instanceCheck.js +59 -0
  40. package/src/editorInjector/_classes.js +36 -36
  41. package/src/editorInjector/_core.js +92 -92
  42. package/src/editorInjector/index.js +75 -75
  43. package/src/events.js +634 -622
  44. package/src/helper/clipboard.js +59 -59
  45. package/src/helper/converter.js +586 -564
  46. package/src/helper/dom/domCheck.js +304 -304
  47. package/src/helper/dom/domQuery.js +677 -669
  48. package/src/helper/dom/domUtils.js +618 -557
  49. package/src/helper/dom/index.js +12 -12
  50. package/src/helper/env.js +249 -240
  51. package/src/helper/index.js +25 -25
  52. package/src/helper/keyCodeMap.js +183 -183
  53. package/src/helper/numbers.js +72 -72
  54. package/src/helper/unicode.js +47 -47
  55. package/src/langs/ckb.js +231 -231
  56. package/src/langs/cs.js +231 -231
  57. package/src/langs/da.js +231 -231
  58. package/src/langs/de.js +231 -231
  59. package/src/langs/en.js +230 -230
  60. package/src/langs/es.js +231 -231
  61. package/src/langs/fa.js +231 -231
  62. package/src/langs/fr.js +231 -231
  63. package/src/langs/he.js +231 -231
  64. package/src/langs/hu.js +230 -230
  65. package/src/langs/index.js +28 -28
  66. package/src/langs/it.js +231 -231
  67. package/src/langs/ja.js +230 -230
  68. package/src/langs/km.js +230 -230
  69. package/src/langs/ko.js +230 -230
  70. package/src/langs/lv.js +231 -231
  71. package/src/langs/nl.js +231 -231
  72. package/src/langs/pl.js +231 -231
  73. package/src/langs/pt_br.js +231 -231
  74. package/src/langs/ro.js +231 -231
  75. package/src/langs/ru.js +231 -231
  76. package/src/langs/se.js +231 -231
  77. package/src/langs/tr.js +231 -231
  78. package/src/langs/uk.js +231 -231
  79. package/src/langs/ur.js +231 -231
  80. package/src/langs/zh_cn.js +231 -231
  81. package/src/modules/ApiManager.js +191 -191
  82. package/src/modules/Browser.js +669 -667
  83. package/src/modules/ColorPicker.js +364 -362
  84. package/src/modules/Controller.js +474 -454
  85. package/src/modules/Figure.js +1620 -1617
  86. package/src/modules/FileManager.js +359 -359
  87. package/src/modules/HueSlider.js +577 -565
  88. package/src/modules/Modal.js +346 -346
  89. package/src/modules/ModalAnchorEditor.js +643 -643
  90. package/src/modules/SelectMenu.js +549 -549
  91. package/src/modules/_DragHandle.js +17 -17
  92. package/src/modules/index.js +14 -14
  93. package/src/plugins/browser/audioGallery.js +83 -83
  94. package/src/plugins/browser/fileBrowser.js +103 -103
  95. package/src/plugins/browser/fileGallery.js +83 -83
  96. package/src/plugins/browser/imageGallery.js +81 -81
  97. package/src/plugins/browser/videoGallery.js +103 -103
  98. package/src/plugins/command/blockquote.js +61 -60
  99. package/src/plugins/command/exportPDF.js +134 -134
  100. package/src/plugins/command/fileUpload.js +456 -456
  101. package/src/plugins/command/list_bulleted.js +149 -148
  102. package/src/plugins/command/list_numbered.js +152 -151
  103. package/src/plugins/dropdown/align.js +157 -155
  104. package/src/plugins/dropdown/backgroundColor.js +108 -104
  105. package/src/plugins/dropdown/font.js +141 -137
  106. package/src/plugins/dropdown/fontColor.js +109 -105
  107. package/src/plugins/dropdown/formatBlock.js +170 -178
  108. package/src/plugins/dropdown/hr.js +152 -152
  109. package/src/plugins/dropdown/layout.js +83 -83
  110. package/src/plugins/dropdown/lineHeight.js +131 -130
  111. package/src/plugins/dropdown/list.js +123 -122
  112. package/src/plugins/dropdown/paragraphStyle.js +138 -138
  113. package/src/plugins/dropdown/table.js +4110 -4000
  114. package/src/plugins/dropdown/template.js +83 -83
  115. package/src/plugins/dropdown/textStyle.js +149 -149
  116. package/src/plugins/field/mention.js +242 -242
  117. package/src/plugins/index.js +120 -120
  118. package/src/plugins/input/fontSize.js +414 -410
  119. package/src/plugins/input/pageNavigator.js +71 -70
  120. package/src/plugins/modal/audio.js +677 -677
  121. package/src/plugins/modal/drawing.js +537 -531
  122. package/src/plugins/modal/embed.js +886 -886
  123. package/src/plugins/modal/image.js +1377 -1376
  124. package/src/plugins/modal/link.js +248 -240
  125. package/src/plugins/modal/math.js +563 -563
  126. package/src/plugins/modal/video.js +1226 -1226
  127. package/src/plugins/popup/anchor.js +224 -222
  128. package/src/suneditor.js +114 -107
  129. package/src/themes/dark.css +132 -122
  130. package/src/typedef.js +132 -130
  131. package/types/assets/icons/defaultIcons.d.ts +8 -0
  132. package/types/core/base/eventManager.d.ts +29 -4
  133. package/types/core/class/char.d.ts +2 -1
  134. package/types/core/class/component.d.ts +1 -2
  135. package/types/core/class/format.d.ts +8 -1
  136. package/types/core/class/html.d.ts +8 -0
  137. package/types/core/class/menu.d.ts +8 -0
  138. package/types/core/class/offset.d.ts +24 -26
  139. package/types/core/class/selection.d.ts +2 -0
  140. package/types/core/class/toolbar.d.ts +6 -0
  141. package/types/core/class/ui.d.ts +1 -1
  142. package/types/core/editor.d.ts +34 -12
  143. package/types/core/section/constructor.d.ts +5 -638
  144. package/types/core/section/documentType.d.ts +12 -2
  145. package/types/core/section/options.d.ts +740 -0
  146. package/types/core/util/instanceCheck.d.ts +50 -0
  147. package/types/editorInjector/_core.d.ts +5 -5
  148. package/types/editorInjector/index.d.ts +2 -2
  149. package/types/events.d.ts +2 -0
  150. package/types/helper/converter.d.ts +9 -0
  151. package/types/helper/dom/domQuery.d.ts +5 -5
  152. package/types/helper/dom/domUtils.d.ts +8 -0
  153. package/types/helper/env.d.ts +6 -1
  154. package/types/helper/index.d.ts +4 -1
  155. package/types/index.d.ts +122 -120
  156. package/types/langs/_Lang.d.ts +194 -194
  157. package/types/modules/ColorPicker.d.ts +5 -1
  158. package/types/modules/Controller.d.ts +8 -4
  159. package/types/modules/Figure.d.ts +2 -1
  160. package/types/modules/HueSlider.d.ts +4 -1
  161. package/types/modules/SelectMenu.d.ts +1 -1
  162. package/types/plugins/command/blockquote.d.ts +1 -0
  163. package/types/plugins/command/list_bulleted.d.ts +1 -0
  164. package/types/plugins/command/list_numbered.d.ts +1 -0
  165. package/types/plugins/dropdown/align.d.ts +1 -0
  166. package/types/plugins/dropdown/backgroundColor.d.ts +1 -0
  167. package/types/plugins/dropdown/font.d.ts +1 -0
  168. package/types/plugins/dropdown/fontColor.d.ts +1 -0
  169. package/types/plugins/dropdown/formatBlock.d.ts +3 -2
  170. package/types/plugins/dropdown/lineHeight.d.ts +1 -0
  171. package/types/plugins/dropdown/list.d.ts +1 -0
  172. package/types/plugins/dropdown/table.d.ts +6 -0
  173. package/types/plugins/input/fontSize.d.ts +1 -0
  174. package/types/plugins/modal/drawing.d.ts +4 -0
  175. package/types/plugins/modal/link.d.ts +32 -15
  176. package/types/suneditor.d.ts +13 -9
  177. package/types/typedef.d.ts +8 -0
@@ -1,563 +1,563 @@
1
- import EditorInjector from '../../editorInjector';
2
- import { Modal, Controller } from '../../modules';
3
- import { dom, env, converter } from '../../helper';
4
-
5
- const { _w } = env;
6
-
7
- /**
8
- * @typedef {Object} MathPluginOptions
9
- * @property {boolean} [canResize=true] - Whether the math modal can be resized.
10
- * @property {boolean} [autoHeight=false] - Whether to automatically adjust the height of the modal.
11
- * @property {Array<object>} [fontSizeList] - A list of font size options for rendering math expressions.
12
- * @property {(...args: *) => *} [onPaste] - A callback function to handle paste events in the math input area.
13
- * @property {Object} [formSize={}] - An object specifying the dimensions for the math modal.
14
- * @property {string} [formSize.width="460px"] - The default width of the math modal.
15
- * @property {string} [formSize.height="14em"] - The default height of the math modal.
16
- * @property {string} [formSize.maxWidth] - The maximum width of the math modal.
17
- * @property {string} [formSize.maxHeight] - The maximum height of the math modal.
18
- * @property {string} [formSize.minWidth="400px"] - The minimum width of the math modal.
19
- * @property {string} [formSize.minHeight="40px"] - The minimum height of the math modal.
20
- */
21
-
22
- /**
23
- * @class
24
- * @description Math plugin.
25
- * - This plugin provides support for rendering mathematical expressions using either the KaTeX or MathJax libraries.
26
- * - If external library is provided, a warning is issued.
27
- */
28
- class Math_ extends EditorInjector {
29
- static key = 'math';
30
- static type = 'modal';
31
- static className = '';
32
- /**
33
- * @this {Math_}
34
- * @param {HTMLElement} node - The node to check.
35
- * @returns {HTMLElement|null} Returns a node if the node is a valid component.
36
- */
37
- static component(node) {
38
- return dom.utils.hasClass(node, 'se-math|katex') && dom.utils.hasClass(node, 'se-component') ? node : null;
39
- }
40
-
41
- /**
42
- * @constructor
43
- * @param {__se__EditorCore} editor - The root editor instance
44
- * @param {MathPluginOptions} pluginOptions
45
- */
46
- constructor(editor, pluginOptions) {
47
- // plugin basic properties
48
- super(editor);
49
- this.title = this.lang.math;
50
- this.icon = 'math';
51
-
52
- // external library
53
- this.katex = null;
54
- this.mathjax = null;
55
-
56
- // exception
57
- if (!(this.katex = this.#CheckKatex(editor.options.get('externalLibs').katex)) && !(this.mathjax = this.#CheckMathJax(editor.options.get('externalLibs').mathjax, editor))) {
58
- console.warn('[SUNEDITOR.plugins.math.warn] The math plugin must need either "KaTeX" or "MathJax" library. Please add the katex or mathjax option.');
59
- }
60
-
61
- this.pluginOptions = {
62
- formSize: {
63
- width: '460px',
64
- height: '14em',
65
- maxWidth: '',
66
- maxHeight: '',
67
- minWidth: '400px',
68
- minHeight: '40px',
69
- ...pluginOptions.formSize
70
- },
71
- canResize: pluginOptions.canResize ?? true,
72
- autoHeight: !!pluginOptions.autoHeight,
73
- fontSizeList: pluginOptions.fontSizeList || [
74
- {
75
- text: '1',
76
- value: '1em'
77
- },
78
- {
79
- text: '1.5',
80
- value: '1.5em'
81
- },
82
- {
83
- text: '2',
84
- value: '2em'
85
- },
86
- {
87
- text: '2.5',
88
- value: '2.5em'
89
- }
90
- ],
91
- onPaste: typeof pluginOptions.onPaste === 'function' ? pluginOptions.onPaste : null
92
- };
93
- if (this.pluginOptions.autoHeight) {
94
- this.pluginOptions.formSize.height = this.pluginOptions.formSize.minHeight;
95
- }
96
-
97
- // create HTML
98
- this.defaultFontSize = null;
99
- const modalEl = CreateHTML_modal(this);
100
- const controllerEl = CreateHTML_controller(editor);
101
-
102
- // modules
103
- this.modal = new Modal(this, modalEl);
104
- this.controller = new Controller(this, controllerEl, { position: 'bottom', disabled: true });
105
-
106
- // members
107
- /** @type {HTMLTextAreaElement} */
108
- this.textArea = modalEl.querySelector('.se-math-exp');
109
- /** @type {HTMLPreElement} */
110
- this.previewElement = modalEl.querySelector('.se-math-preview');
111
- /** @type {HTMLSelectElement} */
112
- this.fontSizeElement = modalEl.querySelector('.se-math-size');
113
-
114
- this.isUpdateState = false;
115
- this._element = null;
116
-
117
- // init
118
- this.previewElement.style.fontSize = this.defaultFontSize;
119
- this.eventManager.addEvent(this.textArea, 'input', this.#RenderMathExp.bind(this));
120
- this.eventManager.addEvent(
121
- this.fontSizeElement,
122
- 'change',
123
- function (e) {
124
- this.fontSize = e.target.value;
125
- }.bind(this.previewElement.style)
126
- );
127
- if (this.pluginOptions.onPaste) {
128
- this.eventManager.addEvent(this.textArea, 'paste', this.pluginOptions.onPaste.bind(this));
129
- }
130
- }
131
-
132
- /**
133
- * @editorMethod Editor.component
134
- * @description Executes the method that is called when a component of a plugin is selected.
135
- * @param {HTMLElement} target Target component element
136
- */
137
- select(target) {
138
- if (dom.utils.hasClass(target, 'se-math|katex') && getValue(target)) {
139
- this._element = target;
140
- this.controller.open(target, null, { isWWTarget: false, initMethod: null, addOffset: null });
141
- return;
142
- }
143
- }
144
-
145
- /**
146
- * @editorMethod Modules.Controller
147
- * @description This function is called before the "controller" before it is closed.
148
- */
149
- close() {
150
- this._element = null;
151
- }
152
-
153
- /**
154
- * @editorMethod Editor.core
155
- * @description This method is used to validate and preserve the format of the component within the editor.
156
- * - It ensures that the structure and attributes of the element are maintained and secure.
157
- * - The method checks if the element is already wrapped in a valid container and updates its attributes if necessary.
158
- * - If the element isn't properly contained, a new container is created to retain the format.
159
- * @returns {{query: string, method: (element: HTMLElement) => void}} The format retention object containing the query and method to process the element.
160
- * - query: The selector query to identify the relevant elements (in this case, 'audio').
161
- * - method:The function to execute on the element to validate and preserve its format.
162
- * - The function takes the element as an argument, checks if it is contained correctly, and applies necessary adjustments.
163
- */
164
- retainFormat() {
165
- return {
166
- query: '.se-math, .katex, .MathJax',
167
- method: (element) => {
168
- if (!this.katex && !this.mathjax) return;
169
-
170
- const value = getValue(element);
171
- if (!value) return;
172
-
173
- const domParser = this._d.createRange().createContextualFragment(this._renderer(converter.entityToHTML(this._escapeBackslashes(value, true))));
174
- element.innerHTML = domParser.querySelector('.se-math, .katex').innerHTML;
175
- element.setAttribute('contenteditable', 'false');
176
- dom.utils.addClass(element, 'se-component|se-inline-component|se-disable-pointer|se-math');
177
-
178
- if (this.katex) {
179
- dom.utils.addClass(element, 'katex');
180
- } else {
181
- dom.utils.removeClass(element, 'katex');
182
- }
183
-
184
- if (this.mathjax) {
185
- this.#renderMathJax(this.mathjax);
186
- }
187
- }
188
- };
189
- }
190
-
191
- /**
192
- * @editorMethod Modules.Modal
193
- * @description Executes the method that is called when a "Modal" module's is opened.
194
- */
195
- open() {
196
- this.modal.open();
197
- }
198
-
199
- /**
200
- * @editorMethod Modules.Modal
201
- * @description Executes the method that is called when a plugin's modal is opened.
202
- * @param {boolean} isUpdate "Indicates whether the modal is for editing an existing component (true) or registering a new one (false)."
203
- */
204
- on(isUpdate) {
205
- this.isUpdateState = isUpdate;
206
- if (!isUpdate) {
207
- this.init();
208
- } else if (this.controller.currentTarget) {
209
- const currentTarget = this.controller.currentTarget;
210
- const exp = converter.entityToHTML(this._escapeBackslashes(getValue(currentTarget), true));
211
- const fontSize = getType(currentTarget) || '1em';
212
- this.textArea.value = exp;
213
- this.fontSizeElement.value = fontSize;
214
- this.previewElement.innerHTML = this._renderer(exp);
215
- this.previewElement.style.fontSize = fontSize;
216
- }
217
- }
218
-
219
- /**
220
- * @editorMethod Modules.Modal
221
- * @description This function is called when a form within a modal window is "submit".
222
- * @returns {boolean} Success or failure
223
- */
224
- modalAction() {
225
- if (this.textArea.value.trim().length === 0 || dom.utils.hasClass(this.textArea, 'se-error')) {
226
- this.textArea.focus();
227
- return false;
228
- }
229
-
230
- const mathExp = this.textArea.value;
231
-
232
- /** @type {HTMLSpanElement} */
233
- const mathEl = this.previewElement.querySelector('.se-math, .katex');
234
-
235
- if (!mathEl) return false;
236
- dom.utils.addClass(mathEl, 'se-component|se-inline-component|se-disable-pointer|se-math');
237
- mathEl.setAttribute('contenteditable', 'false');
238
- mathEl.setAttribute('data-se-value', converter.htmlToEntity(this._escapeBackslashes(mathExp, false)));
239
- mathEl.setAttribute('data-se-type', this.fontSizeElement.value);
240
- mathEl.style.fontSize = this.fontSizeElement.value;
241
-
242
- if (this.katex) {
243
- dom.utils.addClass(mathEl, 'katex');
244
- dom.utils.removeClass(mathEl, 'MathJax');
245
- } else {
246
- dom.utils.removeClass(mathEl, 'katex');
247
- }
248
-
249
- if (!this.isUpdateState) {
250
- const selectedFormats = this.format.getLines();
251
-
252
- if (selectedFormats.length > 1) {
253
- const oFormat = dom.utils.createElement(selectedFormats[0].nodeName, null, mathEl);
254
- this.component.insert(oFormat, { skipCharCount: false, skipSelection: true, skipHistory: false });
255
- } else {
256
- this.component.insert(mathEl, { skipCharCount: false, skipSelection: true, skipHistory: false });
257
- }
258
- } else {
259
- const containerEl = dom.query.getParentElement(this.controller.currentTarget, '.se-component');
260
- containerEl.replaceWith(mathEl);
261
- const compInfo = this.component.get(mathEl);
262
- this.component.select(compInfo.target, compInfo.pluginName);
263
- return true;
264
- }
265
-
266
- if (this.mathjax) {
267
- this.#renderMathJax(this.mathjax);
268
- }
269
-
270
- const r = this.selection.getNearRange(mathEl);
271
- if (r) {
272
- this.selection.setRange(r.container, r.offset, r.container, r.offset);
273
- } else {
274
- this.component.select(mathEl, Math_.key);
275
- }
276
-
277
- return true;
278
- }
279
-
280
- /**
281
- * @editorMethod Modules.Modal
282
- * @description This function is called before the modal window is opened, but before it is closed.
283
- */
284
- init() {
285
- this.textArea.value = '';
286
- this.previewElement.innerHTML = '';
287
- dom.utils.removeClass(this.textArea, 'se-error');
288
- }
289
-
290
- /**
291
- * @editorMethod Modules.Controller
292
- * @description Executes the method that is called when a button is clicked in the "controller".
293
- * @param {HTMLButtonElement} target Target button element
294
- */
295
- controllerAction(target) {
296
- const command = target.getAttribute('data-command');
297
- switch (command) {
298
- case 'update':
299
- this.modal.open();
300
- break;
301
- case 'copy':
302
- this.#copyTextToClipboard(this._element);
303
- break;
304
- case 'delete':
305
- this.destroy(this.controller.currentTarget);
306
- }
307
- }
308
-
309
- /**
310
- * @editorMethod Editor.Component
311
- * @description Method to delete a component of a plugin, called by the "FileManager", "Controller" module.
312
- * @param {Node} target Target element
313
- */
314
- destroy(target) {
315
- dom.utils.removeItem(target);
316
- this.controller.close();
317
- this.editor.focus();
318
- this.history.push(false);
319
- }
320
-
321
- /**
322
- * @private
323
- * @description Renders the given math expression using KaTeX or MathJax.
324
- * @param {string} exp - The math expression to render.
325
- * @returns {string} - The rendered math expression as HTML.
326
- */
327
- _renderer(exp) {
328
- let result = '';
329
- try {
330
- dom.utils.removeClass(this.textArea, 'se-error');
331
- if (this.katex) {
332
- result = this.katex.src.renderToString(exp, { throwOnError: true, displayMode: true });
333
- } else if (this.mathjax) {
334
- result = this.mathjax.convert(exp).outerHTML;
335
- if (/<mjx-merror/.test(result)) {
336
- dom.utils.addClass(this.textArea, 'se-error');
337
- result = `<span class="se-math-error">${result}</span>`;
338
- } else {
339
- result = `<span class="se-math">${result}</span>`;
340
- }
341
- }
342
- } catch (error) {
343
- dom.utils.addClass(this.textArea, 'se-error');
344
- result = `<span class="se-math-error">Math syntax error. (Refer ${this.katex ? `<a href="${env.KATEX_WEBSITE}" target="_blank">KaTeX</a>` : `<a href="${env.MATHJAX_WEBSITE}" target="_blank">MathJax</a>`})</span>`;
345
- console.warn('[SUNEDITOR.math.error] ', error.message);
346
- }
347
- return result;
348
- }
349
-
350
- /**
351
- * @private
352
- * @description Escapes or unescapes backslashes in a given string.
353
- * @param {string} str - The input string.
354
- * @param {boolean} decode - If true, decodes escaped backslashes; otherwise, encodes them.
355
- * @returns {string} - The processed string.
356
- */
357
- _escapeBackslashes(str, decode) {
358
- return str.replace(/\\{2}/g, decode ? '\\' : '\\\\');
359
- }
360
-
361
- /**
362
- * @description Copies the math expression text to clipboard.
363
- * @param {Node} element - The math expression element.
364
- * @returns {Promise<void>}
365
- */
366
- async #copyTextToClipboard(element) {
367
- if (!navigator.clipboard || !element) return;
368
-
369
- try {
370
- const text = getValue(element);
371
- await this.html.copy(text);
372
- dom.utils.addClass(element, 'se-copy');
373
- // copy effect
374
- _w.setTimeout(() => {
375
- dom.utils.removeClass(element, 'se-copy');
376
- }, 120);
377
- } catch (err) {
378
- console.error('[SUNEDITOR.math.copy.fail]', err);
379
- }
380
- }
381
-
382
- /**
383
- * @description Handles rendering of math expressions in the preview.
384
- * @param {InputEvent} e - The input event.
385
- */
386
- #RenderMathExp(e) {
387
- /** @type {HTMLInputElement} */
388
- const eventTarget = dom.query.getEventTarget(e);
389
- if (this.pluginOptions.autoHeight) {
390
- eventTarget.style.height = '5px';
391
- eventTarget.style.height = eventTarget.scrollHeight + 5 + 'px';
392
- }
393
-
394
- this.previewElement.innerHTML = this._renderer(eventTarget.value);
395
- if (this.mathjax) this.#renderMathJax(this.mathjax);
396
- }
397
-
398
- /**
399
- * @param {*} mathjax - The MathJax instance.
400
- */
401
- #renderMathJax(mathjax) {
402
- mathjax.clear();
403
- mathjax.updateDocument();
404
- }
405
-
406
- /**
407
- * @param {*} katex - The KaTeX instance.
408
- * @returns {*} - The KaTeX instance or null if the instance is invalid.
409
- */
410
- #CheckKatex(katex) {
411
- if (!katex) return null;
412
- if (!katex.src) {
413
- console.warn('[SUNEDITOR.math.katex.fail] The katex option is set incorrectly.');
414
- return null;
415
- }
416
-
417
- const katexOptions = [
418
- {
419
- throwOnError: false
420
- },
421
- katex.options || {}
422
- ].reduce((init, option) => {
423
- for (const key in option) {
424
- init[key] = option[key];
425
- }
426
- return init;
427
- }, {});
428
-
429
- katex.options = katexOptions;
430
- return katex;
431
- }
432
-
433
- /**
434
- * @param {*} mathjax - The MathJax instance.
435
- * @param {__se__EditorCore} editor - The root editor instance.
436
- * @returns {*}
437
- */
438
- #CheckMathJax(mathjax, editor) {
439
- if (!mathjax) return null;
440
- if (editor.frameOptions.get('iframe')) {
441
- console.warn('[SUNEDITOR.math.mathjax.fail] The MathJax option is not supported in the iframe.');
442
- }
443
-
444
- try {
445
- const adaptor = mathjax.browserAdaptor();
446
- mathjax.RegisterHTMLHandler(adaptor);
447
-
448
- const tex = new mathjax.TeX();
449
- const chtml = new mathjax.CHTML();
450
-
451
- return mathjax.src.document(document, {
452
- InputJax: tex,
453
- OutputJax: chtml
454
- });
455
- } catch (error) {
456
- console.warn('[SUNEDITOR.math.mathjax.fail] The MathJax option is set incorrectly.', error);
457
- return null;
458
- }
459
- }
460
- }
461
-
462
- function CreateHTML_modal(inst) {
463
- const { lang, icons, pluginOptions, katex } = inst;
464
- const { formSize, fontSizeList, canResize, autoHeight } = pluginOptions;
465
- const { width, height, maxWidth, maxHeight, minWidth, minHeight } = formSize;
466
- const resizeType = !canResize ? 'none' : autoHeight ? 'horizontal' : 'auto';
467
-
468
- let defaultFontSize = fontSizeList[0].value;
469
- let html = /*html*/ `
470
- <form>
471
- <div class="se-modal-header">
472
- <button type="button" data-command="close" class="se-btn se-close-btn" title="${lang.close}" aria-label="${lang.close}">
473
- ${icons.cancel}
474
- </button>
475
- <span class="se-modal-title">${lang.math_modal_title}</span>
476
- </div>
477
- <div class="se-modal-body">
478
- <div class="se-modal-form">
479
- <label>${lang.math_modal_inputLabel} ${katex ? `(<a href="${env.KATEX_WEBSITE}" target="_blank">KaTeX</a>)` : `(<a href="${env.MATHJAX_WEBSITE}" target="_blank">MathJax</a>)`}</label>
480
- <textarea class="se-input-form se-math-exp se-modal-resize-form" type="text" data-focus style="width: ${width}; height: ${height}; min-width: ${minWidth}; min-height: ${minHeight}; resize: ${resizeType};"></textarea>
481
- </div>
482
- <div class="se-modal-form">
483
- <label>${lang.math_modal_fontSizeLabel}</label>
484
- <select class="se-input-select se-math-size">`;
485
-
486
- for (let i = 0, len = fontSizeList.length, f; i < len; i++) {
487
- f = fontSizeList[i];
488
- if (f.default) defaultFontSize = f.value;
489
- html += /*html*/ `<option value="${f.value}"${f.default ? ' selected' : ''}>${f.text}</option>`;
490
- }
491
-
492
- html += /*html*/ `</select>
493
- </div>
494
- <div class="se-modal-form">
495
- <label>${lang.math_modal_previewLabel}</label>
496
- <p class="se-math-preview"></p>
497
- </div>
498
- </div>
499
- <div class="se-modal-footer">
500
- <button type="submit" class="se-btn-primary" title="${lang.submitButton}" aria-label="${lang.submitButton}">
501
- <span>${lang.submitButton}</span>
502
- </button>
503
- </div>
504
- </form>`;
505
-
506
- inst.defaultFontSize = defaultFontSize;
507
- return dom.utils.createElement('DIV', { class: 'se-modal-content se-modal-responsive', style: `max-width: ${maxWidth}; max-height: ${maxHeight};` }, html);
508
- }
509
-
510
- function CreateHTML_controller({ lang, icons }) {
511
- const html = /*html*/ `
512
- <div class="se-arrow se-arrow-up"></div>
513
- <div class="link-content">
514
- <div class="se-btn-group">
515
- <button type="button" data-command="update" tabindex="-1" class="se-btn se-tooltip">
516
- ${icons.edit}
517
- <span class="se-tooltip-inner">
518
- <span class="se-tooltip-text">${lang.edit}</span>
519
- </span>
520
- </button>
521
- <button type="button" data-command="copy" tabindex="-1" class="se-btn se-tooltip">
522
- ${icons.copy}
523
- <span class="se-tooltip-inner">
524
- <span class="se-tooltip-text">${lang.copy}</span>
525
- </span>
526
- </button>
527
- <button type="button" data-command="delete" tabindex="-1" class="se-btn se-tooltip">
528
- ${icons.delete}
529
- <span class="se-tooltip-inner">
530
- <span class="se-tooltip-text">${lang.remove}</span>
531
- </span>
532
- </button>
533
- </div>
534
- </div>`;
535
-
536
- return dom.utils.createElement('DIV', { class: 'se-controller se-controller-link' }, html);
537
- }
538
-
539
- function getValue(element) {
540
- const seAttr = element.getAttribute('data-se-value');
541
- if (seAttr) return seAttr;
542
-
543
- // v2-migration
544
- const v2SeAttr = element.getAttribute(`data-exp`);
545
- if (!v2SeAttr) return null;
546
- element.removeAttribute(`data-exp`);
547
- element.setAttribute(`data-se-value`, v2SeAttr);
548
- return v2SeAttr;
549
- }
550
-
551
- function getType(element) {
552
- const seAttr = element.getAttribute('data-se-type');
553
- if (seAttr) return seAttr;
554
-
555
- // v2-migration
556
- const v2SeAttr = element.getAttribute(`data-exp`);
557
- if (!v2SeAttr) return null;
558
- element.removeAttribute(`data-font-size`);
559
- element.setAttribute(`data-se-type`, v2SeAttr);
560
- return v2SeAttr;
561
- }
562
-
563
- export default Math_;
1
+ import EditorInjector from '../../editorInjector';
2
+ import { Modal, Controller } from '../../modules';
3
+ import { dom, env, converter } from '../../helper';
4
+
5
+ const { _w } = env;
6
+
7
+ /**
8
+ * @typedef {Object} MathPluginOptions
9
+ * @property {boolean} [canResize=true] - Whether the math modal can be resized.
10
+ * @property {boolean} [autoHeight=false] - Whether to automatically adjust the height of the modal.
11
+ * @property {Array<object>} [fontSizeList] - A list of font size options for rendering math expressions.
12
+ * @property {(...args: *) => *} [onPaste] - A callback function to handle paste events in the math input area.
13
+ * @property {Object} [formSize={}] - An object specifying the dimensions for the math modal.
14
+ * @property {string} [formSize.width="460px"] - The default width of the math modal.
15
+ * @property {string} [formSize.height="14em"] - The default height of the math modal.
16
+ * @property {string} [formSize.maxWidth] - The maximum width of the math modal.
17
+ * @property {string} [formSize.maxHeight] - The maximum height of the math modal.
18
+ * @property {string} [formSize.minWidth="400px"] - The minimum width of the math modal.
19
+ * @property {string} [formSize.minHeight="40px"] - The minimum height of the math modal.
20
+ */
21
+
22
+ /**
23
+ * @class
24
+ * @description Math plugin.
25
+ * - This plugin provides support for rendering mathematical expressions using either the KaTeX or MathJax libraries.
26
+ * - If external library is provided, a warning is issued.
27
+ */
28
+ class Math_ extends EditorInjector {
29
+ static key = 'math';
30
+ static type = 'modal';
31
+ static className = '';
32
+ /**
33
+ * @this {Math_}
34
+ * @param {HTMLElement} node - The node to check.
35
+ * @returns {HTMLElement|null} Returns a node if the node is a valid component.
36
+ */
37
+ static component(node) {
38
+ return dom.utils.hasClass(node, 'se-math|katex') && dom.utils.hasClass(node, 'se-component') ? node : null;
39
+ }
40
+
41
+ /**
42
+ * @constructor
43
+ * @param {__se__EditorCore} editor - The root editor instance
44
+ * @param {MathPluginOptions} pluginOptions
45
+ */
46
+ constructor(editor, pluginOptions) {
47
+ // plugin basic properties
48
+ super(editor);
49
+ this.title = this.lang.math;
50
+ this.icon = 'math';
51
+
52
+ // external library
53
+ this.katex = null;
54
+ this.mathjax = null;
55
+
56
+ // exception
57
+ if (!(this.katex = this.#CheckKatex(editor.options.get('externalLibs').katex)) && !(this.mathjax = this.#CheckMathJax(editor.options.get('externalLibs').mathjax, editor))) {
58
+ console.warn('[SUNEDITOR.plugins.math.warn] The math plugin must need either "KaTeX" or "MathJax" library. Please add the katex or mathjax option.');
59
+ }
60
+
61
+ this.pluginOptions = {
62
+ formSize: {
63
+ width: '460px',
64
+ height: '14em',
65
+ maxWidth: '',
66
+ maxHeight: '',
67
+ minWidth: '400px',
68
+ minHeight: '40px',
69
+ ...pluginOptions.formSize
70
+ },
71
+ canResize: pluginOptions.canResize ?? true,
72
+ autoHeight: !!pluginOptions.autoHeight,
73
+ fontSizeList: pluginOptions.fontSizeList || [
74
+ {
75
+ text: '1',
76
+ value: '1em'
77
+ },
78
+ {
79
+ text: '1.5',
80
+ value: '1.5em'
81
+ },
82
+ {
83
+ text: '2',
84
+ value: '2em'
85
+ },
86
+ {
87
+ text: '2.5',
88
+ value: '2.5em'
89
+ }
90
+ ],
91
+ onPaste: typeof pluginOptions.onPaste === 'function' ? pluginOptions.onPaste : null
92
+ };
93
+ if (this.pluginOptions.autoHeight) {
94
+ this.pluginOptions.formSize.height = this.pluginOptions.formSize.minHeight;
95
+ }
96
+
97
+ // create HTML
98
+ this.defaultFontSize = null;
99
+ const modalEl = CreateHTML_modal(this);
100
+ const controllerEl = CreateHTML_controller(editor);
101
+
102
+ // modules
103
+ this.modal = new Modal(this, modalEl);
104
+ this.controller = new Controller(this, controllerEl, { position: 'bottom', disabled: true });
105
+
106
+ // members
107
+ /** @type {HTMLTextAreaElement} */
108
+ this.textArea = modalEl.querySelector('.se-math-exp');
109
+ /** @type {HTMLPreElement} */
110
+ this.previewElement = modalEl.querySelector('.se-math-preview');
111
+ /** @type {HTMLSelectElement} */
112
+ this.fontSizeElement = modalEl.querySelector('.se-math-size');
113
+
114
+ this.isUpdateState = false;
115
+ this._element = null;
116
+
117
+ // init
118
+ this.previewElement.style.fontSize = this.defaultFontSize;
119
+ this.eventManager.addEvent(this.textArea, 'input', this.#RenderMathExp.bind(this));
120
+ this.eventManager.addEvent(
121
+ this.fontSizeElement,
122
+ 'change',
123
+ function (e) {
124
+ this.fontSize = e.target.value;
125
+ }.bind(this.previewElement.style)
126
+ );
127
+ if (this.pluginOptions.onPaste) {
128
+ this.eventManager.addEvent(this.textArea, 'paste', this.pluginOptions.onPaste.bind(this));
129
+ }
130
+ }
131
+
132
+ /**
133
+ * @editorMethod Editor.component
134
+ * @description Executes the method that is called when a component of a plugin is selected.
135
+ * @param {HTMLElement} target Target component element
136
+ */
137
+ select(target) {
138
+ if (dom.utils.hasClass(target, 'se-math|katex') && getValue(target)) {
139
+ this._element = target;
140
+ this.controller.open(target, null, { isWWTarget: false, initMethod: null, addOffset: null });
141
+ return;
142
+ }
143
+ }
144
+
145
+ /**
146
+ * @editorMethod Modules.Controller
147
+ * @description This function is called before the "controller" before it is closed.
148
+ */
149
+ close() {
150
+ this._element = null;
151
+ }
152
+
153
+ /**
154
+ * @editorMethod Editor.core
155
+ * @description This method is used to validate and preserve the format of the component within the editor.
156
+ * - It ensures that the structure and attributes of the element are maintained and secure.
157
+ * - The method checks if the element is already wrapped in a valid container and updates its attributes if necessary.
158
+ * - If the element isn't properly contained, a new container is created to retain the format.
159
+ * @returns {{query: string, method: (element: HTMLElement) => void}} The format retention object containing the query and method to process the element.
160
+ * - query: The selector query to identify the relevant elements (in this case, 'audio').
161
+ * - method:The function to execute on the element to validate and preserve its format.
162
+ * - The function takes the element as an argument, checks if it is contained correctly, and applies necessary adjustments.
163
+ */
164
+ retainFormat() {
165
+ return {
166
+ query: '.se-math, .katex, .MathJax',
167
+ method: (element) => {
168
+ if (!this.katex && !this.mathjax) return;
169
+
170
+ const value = getValue(element);
171
+ if (!value) return;
172
+
173
+ const domParser = this._d.createRange().createContextualFragment(this._renderer(converter.entityToHTML(this._escapeBackslashes(value, true))));
174
+ element.innerHTML = domParser.querySelector('.se-math, .katex').innerHTML;
175
+ element.setAttribute('contenteditable', 'false');
176
+ dom.utils.addClass(element, 'se-component|se-inline-component|se-disable-pointer|se-math');
177
+
178
+ if (this.katex) {
179
+ dom.utils.addClass(element, 'katex');
180
+ } else {
181
+ dom.utils.removeClass(element, 'katex');
182
+ }
183
+
184
+ if (this.mathjax) {
185
+ this.#renderMathJax(this.mathjax);
186
+ }
187
+ }
188
+ };
189
+ }
190
+
191
+ /**
192
+ * @editorMethod Modules.Modal
193
+ * @description Executes the method that is called when a "Modal" module's is opened.
194
+ */
195
+ open() {
196
+ this.modal.open();
197
+ }
198
+
199
+ /**
200
+ * @editorMethod Modules.Modal
201
+ * @description Executes the method that is called when a plugin's modal is opened.
202
+ * @param {boolean} isUpdate "Indicates whether the modal is for editing an existing component (true) or registering a new one (false)."
203
+ */
204
+ on(isUpdate) {
205
+ this.isUpdateState = isUpdate;
206
+ if (!isUpdate) {
207
+ this.init();
208
+ } else if (this.controller.currentTarget) {
209
+ const currentTarget = this.controller.currentTarget;
210
+ const exp = converter.entityToHTML(this._escapeBackslashes(getValue(currentTarget), true));
211
+ const fontSize = getType(currentTarget) || '1em';
212
+ this.textArea.value = exp;
213
+ this.fontSizeElement.value = fontSize;
214
+ this.previewElement.innerHTML = this._renderer(exp);
215
+ this.previewElement.style.fontSize = fontSize;
216
+ }
217
+ }
218
+
219
+ /**
220
+ * @editorMethod Modules.Modal
221
+ * @description This function is called when a form within a modal window is "submit".
222
+ * @returns {boolean} Success or failure
223
+ */
224
+ modalAction() {
225
+ if (this.textArea.value.trim().length === 0 || dom.utils.hasClass(this.textArea, 'se-error')) {
226
+ this.textArea.focus();
227
+ return false;
228
+ }
229
+
230
+ const mathExp = this.textArea.value;
231
+
232
+ /** @type {HTMLSpanElement} */
233
+ const mathEl = this.previewElement.querySelector('.se-math, .katex');
234
+
235
+ if (!mathEl) return false;
236
+ dom.utils.addClass(mathEl, 'se-component|se-inline-component|se-disable-pointer|se-math');
237
+ mathEl.setAttribute('contenteditable', 'false');
238
+ mathEl.setAttribute('data-se-value', converter.htmlToEntity(this._escapeBackslashes(mathExp, false)));
239
+ mathEl.setAttribute('data-se-type', this.fontSizeElement.value);
240
+ mathEl.style.fontSize = this.fontSizeElement.value;
241
+
242
+ if (this.katex) {
243
+ dom.utils.addClass(mathEl, 'katex');
244
+ dom.utils.removeClass(mathEl, 'MathJax');
245
+ } else {
246
+ dom.utils.removeClass(mathEl, 'katex');
247
+ }
248
+
249
+ if (!this.isUpdateState) {
250
+ const selectedFormats = this.format.getLines();
251
+
252
+ if (selectedFormats.length > 1) {
253
+ const oFormat = dom.utils.createElement(selectedFormats[0].nodeName, null, mathEl);
254
+ this.component.insert(oFormat, { skipCharCount: false, skipSelection: true, skipHistory: false });
255
+ } else {
256
+ this.component.insert(mathEl, { skipCharCount: false, skipSelection: true, skipHistory: false });
257
+ }
258
+ } else {
259
+ const containerEl = dom.query.getParentElement(this.controller.currentTarget, '.se-component');
260
+ containerEl.replaceWith(mathEl);
261
+ const compInfo = this.component.get(mathEl);
262
+ this.component.select(compInfo.target, compInfo.pluginName);
263
+ return true;
264
+ }
265
+
266
+ if (this.mathjax) {
267
+ this.#renderMathJax(this.mathjax);
268
+ }
269
+
270
+ const r = this.selection.getNearRange(mathEl);
271
+ if (r) {
272
+ this.selection.setRange(r.container, r.offset, r.container, r.offset);
273
+ } else {
274
+ this.component.select(mathEl, Math_.key);
275
+ }
276
+
277
+ return true;
278
+ }
279
+
280
+ /**
281
+ * @editorMethod Modules.Modal
282
+ * @description This function is called before the modal window is opened, but before it is closed.
283
+ */
284
+ init() {
285
+ this.textArea.value = '';
286
+ this.previewElement.innerHTML = '';
287
+ dom.utils.removeClass(this.textArea, 'se-error');
288
+ }
289
+
290
+ /**
291
+ * @editorMethod Modules.Controller
292
+ * @description Executes the method that is called when a button is clicked in the "controller".
293
+ * @param {HTMLButtonElement} target Target button element
294
+ */
295
+ controllerAction(target) {
296
+ const command = target.getAttribute('data-command');
297
+ switch (command) {
298
+ case 'update':
299
+ this.modal.open();
300
+ break;
301
+ case 'copy':
302
+ this.#copyTextToClipboard(this._element);
303
+ break;
304
+ case 'delete':
305
+ this.destroy(this.controller.currentTarget);
306
+ }
307
+ }
308
+
309
+ /**
310
+ * @editorMethod Editor.Component
311
+ * @description Method to delete a component of a plugin, called by the "FileManager", "Controller" module.
312
+ * @param {Node} target Target element
313
+ */
314
+ destroy(target) {
315
+ dom.utils.removeItem(target);
316
+ this.controller.close();
317
+ this.editor.focus();
318
+ this.history.push(false);
319
+ }
320
+
321
+ /**
322
+ * @private
323
+ * @description Renders the given math expression using KaTeX or MathJax.
324
+ * @param {string} exp - The math expression to render.
325
+ * @returns {string} - The rendered math expression as HTML.
326
+ */
327
+ _renderer(exp) {
328
+ let result = '';
329
+ try {
330
+ dom.utils.removeClass(this.textArea, 'se-error');
331
+ if (this.katex) {
332
+ result = this.katex.src.renderToString(exp, { throwOnError: true, displayMode: true });
333
+ } else if (this.mathjax) {
334
+ result = this.mathjax.convert(exp).outerHTML;
335
+ if (/<mjx-merror/.test(result)) {
336
+ dom.utils.addClass(this.textArea, 'se-error');
337
+ result = `<span class="se-math-error">${result}</span>`;
338
+ } else {
339
+ result = `<span class="se-math">${result}</span>`;
340
+ }
341
+ }
342
+ } catch (error) {
343
+ dom.utils.addClass(this.textArea, 'se-error');
344
+ result = `<span class="se-math-error">Math syntax error. (Refer ${this.katex ? `<a href="${env.KATEX_WEBSITE}" target="_blank">KaTeX</a>` : `<a href="${env.MATHJAX_WEBSITE}" target="_blank">MathJax</a>`})</span>`;
345
+ console.warn('[SUNEDITOR.math.error] ', error.message);
346
+ }
347
+ return result;
348
+ }
349
+
350
+ /**
351
+ * @private
352
+ * @description Escapes or unescapes backslashes in a given string.
353
+ * @param {string} str - The input string.
354
+ * @param {boolean} decode - If true, decodes escaped backslashes; otherwise, encodes them.
355
+ * @returns {string} - The processed string.
356
+ */
357
+ _escapeBackslashes(str, decode) {
358
+ return str.replace(/\\{2}/g, decode ? '\\' : '\\\\');
359
+ }
360
+
361
+ /**
362
+ * @description Copies the math expression text to clipboard.
363
+ * @param {Node} element - The math expression element.
364
+ * @returns {Promise<void>}
365
+ */
366
+ async #copyTextToClipboard(element) {
367
+ if (!navigator.clipboard || !element) return;
368
+
369
+ try {
370
+ const text = getValue(element);
371
+ await this.html.copy(text);
372
+ dom.utils.addClass(element, 'se-copy');
373
+ // copy effect
374
+ _w.setTimeout(() => {
375
+ dom.utils.removeClass(element, 'se-copy');
376
+ }, 120);
377
+ } catch (err) {
378
+ console.error('[SUNEDITOR.math.copy.fail]', err);
379
+ }
380
+ }
381
+
382
+ /**
383
+ * @description Handles rendering of math expressions in the preview.
384
+ * @param {InputEvent} e - The input event.
385
+ */
386
+ #RenderMathExp(e) {
387
+ /** @type {HTMLInputElement} */
388
+ const eventTarget = dom.query.getEventTarget(e);
389
+ if (this.pluginOptions.autoHeight) {
390
+ eventTarget.style.height = '5px';
391
+ eventTarget.style.height = eventTarget.scrollHeight + 5 + 'px';
392
+ }
393
+
394
+ this.previewElement.innerHTML = this._renderer(eventTarget.value);
395
+ if (this.mathjax) this.#renderMathJax(this.mathjax);
396
+ }
397
+
398
+ /**
399
+ * @param {*} mathjax - The MathJax instance.
400
+ */
401
+ #renderMathJax(mathjax) {
402
+ mathjax.clear();
403
+ mathjax.updateDocument();
404
+ }
405
+
406
+ /**
407
+ * @param {*} katex - The KaTeX instance.
408
+ * @returns {*} - The KaTeX instance or null if the instance is invalid.
409
+ */
410
+ #CheckKatex(katex) {
411
+ if (!katex) return null;
412
+ if (!katex.src) {
413
+ console.warn('[SUNEDITOR.math.katex.fail] The katex option is set incorrectly.');
414
+ return null;
415
+ }
416
+
417
+ const katexOptions = [
418
+ {
419
+ throwOnError: false
420
+ },
421
+ katex.options || {}
422
+ ].reduce((init, option) => {
423
+ for (const key in option) {
424
+ init[key] = option[key];
425
+ }
426
+ return init;
427
+ }, {});
428
+
429
+ katex.options = katexOptions;
430
+ return katex;
431
+ }
432
+
433
+ /**
434
+ * @param {*} mathjax - The MathJax instance.
435
+ * @param {__se__EditorCore} editor - The root editor instance.
436
+ * @returns {*}
437
+ */
438
+ #CheckMathJax(mathjax, editor) {
439
+ if (!mathjax) return null;
440
+ if (editor.frameOptions.get('iframe')) {
441
+ console.warn('[SUNEDITOR.math.mathjax.fail] The MathJax option is not supported in the iframe.');
442
+ }
443
+
444
+ try {
445
+ const adaptor = mathjax.browserAdaptor();
446
+ mathjax.RegisterHTMLHandler(adaptor);
447
+
448
+ const tex = new mathjax.TeX();
449
+ const chtml = new mathjax.CHTML();
450
+
451
+ return mathjax.src.document(document, {
452
+ InputJax: tex,
453
+ OutputJax: chtml
454
+ });
455
+ } catch (error) {
456
+ console.warn('[SUNEDITOR.math.mathjax.fail] The MathJax option is set incorrectly.', error);
457
+ return null;
458
+ }
459
+ }
460
+ }
461
+
462
+ function CreateHTML_modal(inst) {
463
+ const { lang, icons, pluginOptions, katex } = inst;
464
+ const { formSize, fontSizeList, canResize, autoHeight } = pluginOptions;
465
+ const { width, height, maxWidth, maxHeight, minWidth, minHeight } = formSize;
466
+ const resizeType = !canResize ? 'none' : autoHeight ? 'horizontal' : 'auto';
467
+
468
+ let defaultFontSize = fontSizeList[0].value;
469
+ let html = /*html*/ `
470
+ <form>
471
+ <div class="se-modal-header">
472
+ <button type="button" data-command="close" class="se-btn se-close-btn" title="${lang.close}" aria-label="${lang.close}">
473
+ ${icons.cancel}
474
+ </button>
475
+ <span class="se-modal-title">${lang.math_modal_title}</span>
476
+ </div>
477
+ <div class="se-modal-body">
478
+ <div class="se-modal-form">
479
+ <label>${lang.math_modal_inputLabel} ${katex ? `(<a href="${env.KATEX_WEBSITE}" target="_blank">KaTeX</a>)` : `(<a href="${env.MATHJAX_WEBSITE}" target="_blank">MathJax</a>)`}</label>
480
+ <textarea class="se-input-form se-math-exp se-modal-resize-form" type="text" data-focus style="width: ${width}; height: ${height}; min-width: ${minWidth}; min-height: ${minHeight}; resize: ${resizeType};"></textarea>
481
+ </div>
482
+ <div class="se-modal-form">
483
+ <label>${lang.math_modal_fontSizeLabel}</label>
484
+ <select class="se-input-select se-math-size">`;
485
+
486
+ for (let i = 0, len = fontSizeList.length, f; i < len; i++) {
487
+ f = fontSizeList[i];
488
+ if (f.default) defaultFontSize = f.value;
489
+ html += /*html*/ `<option value="${f.value}"${f.default ? ' selected' : ''}>${f.text}</option>`;
490
+ }
491
+
492
+ html += /*html*/ `</select>
493
+ </div>
494
+ <div class="se-modal-form">
495
+ <label>${lang.math_modal_previewLabel}</label>
496
+ <p class="se-math-preview"></p>
497
+ </div>
498
+ </div>
499
+ <div class="se-modal-footer">
500
+ <button type="submit" class="se-btn-primary" title="${lang.submitButton}" aria-label="${lang.submitButton}">
501
+ <span>${lang.submitButton}</span>
502
+ </button>
503
+ </div>
504
+ </form>`;
505
+
506
+ inst.defaultFontSize = defaultFontSize;
507
+ return dom.utils.createElement('DIV', { class: 'se-modal-content se-modal-responsive', style: `max-width: ${maxWidth}; max-height: ${maxHeight};` }, html);
508
+ }
509
+
510
+ function CreateHTML_controller({ lang, icons }) {
511
+ const html = /*html*/ `
512
+ <div class="se-arrow se-arrow-up"></div>
513
+ <div class="link-content">
514
+ <div class="se-btn-group">
515
+ <button type="button" data-command="update" tabindex="-1" class="se-btn se-tooltip">
516
+ ${icons.edit}
517
+ <span class="se-tooltip-inner">
518
+ <span class="se-tooltip-text">${lang.edit}</span>
519
+ </span>
520
+ </button>
521
+ <button type="button" data-command="copy" tabindex="-1" class="se-btn se-tooltip">
522
+ ${icons.copy}
523
+ <span class="se-tooltip-inner">
524
+ <span class="se-tooltip-text">${lang.copy}</span>
525
+ </span>
526
+ </button>
527
+ <button type="button" data-command="delete" tabindex="-1" class="se-btn se-tooltip">
528
+ ${icons.delete}
529
+ <span class="se-tooltip-inner">
530
+ <span class="se-tooltip-text">${lang.remove}</span>
531
+ </span>
532
+ </button>
533
+ </div>
534
+ </div>`;
535
+
536
+ return dom.utils.createElement('DIV', { class: 'se-controller se-controller-link' }, html);
537
+ }
538
+
539
+ function getValue(element) {
540
+ const seAttr = element.getAttribute('data-se-value');
541
+ if (seAttr) return seAttr;
542
+
543
+ // v2-migration
544
+ const v2SeAttr = element.getAttribute(`data-exp`);
545
+ if (!v2SeAttr) return null;
546
+ element.removeAttribute(`data-exp`);
547
+ element.setAttribute(`data-se-value`, v2SeAttr);
548
+ return v2SeAttr;
549
+ }
550
+
551
+ function getType(element) {
552
+ const seAttr = element.getAttribute('data-se-type');
553
+ if (seAttr) return seAttr;
554
+
555
+ // v2-migration
556
+ const v2SeAttr = element.getAttribute(`data-exp`);
557
+ if (!v2SeAttr) return null;
558
+ element.removeAttribute(`data-font-size`);
559
+ element.setAttribute(`data-se-type`, v2SeAttr);
560
+ return v2SeAttr;
561
+ }
562
+
563
+ export default Math_;