suneditor 2.44.12 → 2.45.0

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 (130) hide show
  1. package/LICENSE.txt +20 -20
  2. package/README.md +1588 -1585
  3. package/dist/suneditor.min.js +2 -2
  4. package/package.json +71 -71
  5. package/src/assets/css/suneditor-contents.css +561 -561
  6. package/src/assets/css/suneditor.css +0 -0
  7. package/src/assets/defaultIcons.js +103 -103
  8. package/src/lang/Lang.d.ts +143 -143
  9. package/src/lang/ckb.d.ts +4 -4
  10. package/src/lang/ckb.js +187 -187
  11. package/src/lang/da.d.ts +4 -4
  12. package/src/lang/da.js +191 -191
  13. package/src/lang/de.d.ts +4 -4
  14. package/src/lang/de.js +188 -188
  15. package/src/lang/en.d.ts +4 -4
  16. package/src/lang/en.js +187 -187
  17. package/src/lang/es.d.ts +4 -4
  18. package/src/lang/es.js +187 -187
  19. package/src/lang/fr.d.ts +4 -4
  20. package/src/lang/fr.js +188 -188
  21. package/src/lang/he.d.ts +4 -4
  22. package/src/lang/he.js +188 -188
  23. package/src/lang/index.d.ts +22 -22
  24. package/src/lang/index.js +25 -25
  25. package/src/lang/it.d.ts +4 -4
  26. package/src/lang/it.js +188 -188
  27. package/src/lang/ja.d.ts +4 -4
  28. package/src/lang/ja.js +187 -187
  29. package/src/lang/ko.d.ts +4 -4
  30. package/src/lang/ko.js +187 -187
  31. package/src/lang/lv.d.ts +4 -4
  32. package/src/lang/lv.js +187 -187
  33. package/src/lang/nl.d.ts +4 -4
  34. package/src/lang/nl.js +187 -187
  35. package/src/lang/pl.d.ts +4 -4
  36. package/src/lang/pl.js +187 -187
  37. package/src/lang/pt_br.d.ts +4 -4
  38. package/src/lang/pt_br.js +189 -189
  39. package/src/lang/ro.d.ts +4 -4
  40. package/src/lang/ro.js +187 -187
  41. package/src/lang/ru.d.ts +4 -4
  42. package/src/lang/ru.js +187 -187
  43. package/src/lang/se.d.ts +4 -4
  44. package/src/lang/se.js +191 -191
  45. package/src/lang/ua.d.ts +5 -5
  46. package/src/lang/ua.js +188 -188
  47. package/src/lang/ur.d.ts +4 -4
  48. package/src/lang/ur.js +187 -187
  49. package/src/lang/zh_cn.d.ts +4 -4
  50. package/src/lang/zh_cn.js +187 -187
  51. package/src/lib/constructor.js +3 -1
  52. package/src/lib/context.d.ts +42 -42
  53. package/src/lib/context.js +0 -0
  54. package/src/lib/core.d.ts +1101 -1101
  55. package/src/lib/core.js +96 -51
  56. package/src/lib/history.d.ts +48 -48
  57. package/src/lib/history.js +218 -218
  58. package/src/lib/util.d.ts +677 -677
  59. package/src/lib/util.js +39 -11
  60. package/src/options.d.ts +608 -608
  61. package/src/plugins/CommandPlugin.d.ts +7 -7
  62. package/src/plugins/DialogPlugin.d.ts +19 -19
  63. package/src/plugins/FileBrowserPlugin.d.ts +29 -29
  64. package/src/plugins/Module.d.ts +14 -14
  65. package/src/plugins/Plugin.d.ts +41 -41
  66. package/src/plugins/SubmenuPlugin.d.ts +7 -7
  67. package/src/plugins/command/blockquote.d.ts +4 -4
  68. package/src/plugins/command/blockquote.js +46 -46
  69. package/src/plugins/dialog/audio.d.ts +4 -4
  70. package/src/plugins/dialog/audio.js +556 -556
  71. package/src/plugins/dialog/image.d.ts +4 -4
  72. package/src/plugins/dialog/image.js +1115 -1115
  73. package/src/plugins/dialog/link.d.ts +4 -4
  74. package/src/plugins/dialog/link.js +223 -223
  75. package/src/plugins/dialog/math.d.ts +4 -4
  76. package/src/plugins/dialog/math.js +295 -295
  77. package/src/plugins/dialog/mention.d.ts +5 -5
  78. package/src/plugins/dialog/mention.js +242 -242
  79. package/src/plugins/dialog/video.d.ts +4 -4
  80. package/src/plugins/dialog/video.js +977 -977
  81. package/src/plugins/fileBrowser/imageGallery.d.ts +4 -4
  82. package/src/plugins/fileBrowser/imageGallery.js +63 -63
  83. package/src/plugins/index.d.ts +79 -79
  84. package/src/plugins/index.js +32 -32
  85. package/src/plugins/modules/_anchor.js +461 -461
  86. package/src/plugins/modules/_colorPicker.d.ts +59 -59
  87. package/src/plugins/modules/_colorPicker.js +0 -0
  88. package/src/plugins/modules/_notice.d.ts +20 -20
  89. package/src/plugins/modules/_notice.js +72 -72
  90. package/src/plugins/modules/_selectMenu.js +118 -118
  91. package/src/plugins/modules/component.d.ts +24 -24
  92. package/src/plugins/modules/component.js +80 -80
  93. package/src/plugins/modules/dialog.d.ts +27 -27
  94. package/src/plugins/modules/dialog.js +174 -174
  95. package/src/plugins/modules/fileBrowser.d.ts +41 -41
  96. package/src/plugins/modules/fileBrowser.js +373 -373
  97. package/src/plugins/modules/fileManager.d.ts +66 -66
  98. package/src/plugins/modules/fileManager.js +325 -325
  99. package/src/plugins/modules/index.d.ts +10 -10
  100. package/src/plugins/modules/index.js +8 -8
  101. package/src/plugins/modules/resizing.d.ts +153 -153
  102. package/src/plugins/modules/resizing.js +895 -895
  103. package/src/plugins/submenu/align.d.ts +4 -4
  104. package/src/plugins/submenu/align.js +160 -160
  105. package/src/plugins/submenu/font.d.ts +4 -4
  106. package/src/plugins/submenu/font.js +120 -120
  107. package/src/plugins/submenu/fontColor.d.ts +4 -4
  108. package/src/plugins/submenu/fontColor.js +0 -0
  109. package/src/plugins/submenu/fontSize.d.ts +4 -4
  110. package/src/plugins/submenu/fontSize.js +112 -112
  111. package/src/plugins/submenu/formatBlock.d.ts +4 -4
  112. package/src/plugins/submenu/formatBlock.js +273 -273
  113. package/src/plugins/submenu/hiliteColor.d.ts +4 -4
  114. package/src/plugins/submenu/hiliteColor.js +0 -0
  115. package/src/plugins/submenu/horizontalRule.d.ts +4 -4
  116. package/src/plugins/submenu/horizontalRule.js +98 -98
  117. package/src/plugins/submenu/lineHeight.d.ts +4 -4
  118. package/src/plugins/submenu/lineHeight.js +104 -104
  119. package/src/plugins/submenu/list.d.ts +4 -4
  120. package/src/plugins/submenu/list.js +456 -456
  121. package/src/plugins/submenu/paragraphStyle.d.ts +4 -4
  122. package/src/plugins/submenu/paragraphStyle.js +135 -135
  123. package/src/plugins/submenu/table.d.ts +4 -4
  124. package/src/plugins/submenu/template.d.ts +4 -4
  125. package/src/plugins/submenu/template.js +71 -71
  126. package/src/plugins/submenu/textStyle.d.ts +4 -4
  127. package/src/plugins/submenu/textStyle.js +167 -167
  128. package/src/suneditor.d.ts +9 -9
  129. package/src/suneditor.js +75 -75
  130. package/src/suneditor_build.js +17 -17
@@ -1,1115 +1,1115 @@
1
- /*
2
- * wysiwyg web editor
3
- *
4
- * suneditor.js
5
- * Copyright 2017 JiHong Lee.
6
- * MIT license.
7
- */
8
- 'use strict';
9
-
10
- import dialog from '../modules/dialog';
11
- import anchor from '../modules/_anchor';
12
- import component from '../modules/component';
13
- import resizing from '../modules/resizing';
14
- import fileManager from '../modules/fileManager';
15
-
16
- export default {
17
- name: 'image',
18
- display: 'dialog',
19
- add: function (core) {
20
- core.addModule([dialog, anchor, component, resizing, fileManager]);
21
-
22
- const options = core.options;
23
- const context = core.context;
24
- const contextImage = context.image = {
25
- _infoList: [], // @Override fileManager
26
- _infoIndex: 0, // @Override fileManager
27
- _uploadFileLength: 0, // @Override fileManager
28
- focusElement: null, // @Override dialog // This element has focus when the dialog is opened.
29
- sizeUnit: options._imageSizeUnit,
30
- _linkElement: '',
31
- _altText: '',
32
- _align: 'none',
33
- _floatClassRegExp: '__se__float\\-[a-z]+',
34
- _v_src: {_linkValue: ''},
35
- svgDefaultSize: '30%',
36
- base64RenderIndex: 0,
37
- // @require @Override component
38
- _element: null,
39
- _cover: null,
40
- _container: null,
41
- // @Override resizing properties
42
- inputX: null,
43
- inputY: null,
44
- _element_w: 1,
45
- _element_h: 1,
46
- _element_l: 0,
47
- _element_t: 0,
48
- _defaultSizeX: 'auto',
49
- _defaultSizeY: 'auto',
50
- _origin_w: options.imageWidth === 'auto' ? '' : options.imageWidth,
51
- _origin_h: options.imageHeight === 'auto' ? '' : options.imageHeight,
52
- _proportionChecked: true,
53
- _resizing: options.imageResizing,
54
- _resizeDotHide: !options.imageHeightShow,
55
- _rotation: options.imageRotation,
56
- _alignHide: !options.imageAlignShow,
57
- _onlyPercentage: options.imageSizeOnlyPercentage,
58
- _ratio: false,
59
- _ratioX: 1,
60
- _ratioY: 1,
61
- _captionShow: true,
62
- _captionChecked: false,
63
- _caption: null,
64
- captionCheckEl: null
65
- };
66
-
67
- /** image dialog */
68
- let image_dialog = this.setDialog(core);
69
- contextImage.modal = image_dialog;
70
- contextImage.imgInputFile = image_dialog.querySelector('._se_image_file');
71
- contextImage.imgUrlFile = image_dialog.querySelector('._se_image_url');
72
- contextImage.focusElement = contextImage.imgInputFile || contextImage.imgUrlFile;
73
- contextImage.altText = image_dialog.querySelector('._se_image_alt');
74
- contextImage.captionCheckEl = image_dialog.querySelector('._se_image_check_caption');
75
- contextImage.previewSrc = image_dialog.querySelector('._se_tab_content_image .se-link-preview');
76
-
77
- /** add event listeners */
78
- image_dialog.querySelector('.se-dialog-tabs').addEventListener('click', this.openTab.bind(core));
79
- image_dialog.querySelector('form').addEventListener('submit', this.submit.bind(core));
80
- if (contextImage.imgInputFile) image_dialog.querySelector('.se-file-remove').addEventListener('click', this._removeSelectedFiles.bind(contextImage.imgInputFile, contextImage.imgUrlFile, contextImage.previewSrc));
81
- if (contextImage.imgUrlFile) contextImage.imgUrlFile.addEventListener('input', this._onLinkPreview.bind(contextImage.previewSrc, contextImage._v_src, options.linkProtocol));
82
- if (contextImage.imgInputFile && contextImage.imgUrlFile) contextImage.imgInputFile.addEventListener('change', this._fileInputChange.bind(contextImage));
83
-
84
- const imageGalleryButton = image_dialog.querySelector('.__se__gallery');
85
- if (imageGalleryButton) imageGalleryButton.addEventListener('click', this._openGallery.bind(core));
86
-
87
- contextImage.proportion = {};
88
- contextImage.inputX = {};
89
- contextImage.inputY = {};
90
- if (options.imageResizing) {
91
- contextImage.proportion = image_dialog.querySelector('._se_image_check_proportion');
92
- contextImage.inputX = image_dialog.querySelector('._se_image_size_x');
93
- contextImage.inputY = image_dialog.querySelector('._se_image_size_y');
94
- contextImage.inputX.value = options.imageWidth;
95
- contextImage.inputY.value = options.imageHeight;
96
-
97
- contextImage.inputX.addEventListener('keyup', this.setInputSize.bind(core, 'x'));
98
- contextImage.inputY.addEventListener('keyup', this.setInputSize.bind(core, 'y'));
99
-
100
- contextImage.inputX.addEventListener('change', this.setRatio.bind(core));
101
- contextImage.inputY.addEventListener('change', this.setRatio.bind(core));
102
- contextImage.proportion.addEventListener('change', this.setRatio.bind(core));
103
-
104
- image_dialog.querySelector('.se-dialog-btn-revert').addEventListener('click', this.sizeRevert.bind(core));
105
- }
106
-
107
- /** append html */
108
- context.dialog.modal.appendChild(image_dialog);
109
-
110
- /** link event */
111
- core.plugins.anchor.initEvent.call(core, 'image', image_dialog.querySelector('._se_tab_content_url'));
112
- contextImage.anchorCtx = core.context.anchor.caller.image;
113
-
114
- /** empty memory */
115
- image_dialog = null;
116
- },
117
-
118
- /** dialog */
119
- setDialog: function (core) {
120
- const option = core.options;
121
- const lang = core.lang;
122
- const dialog = core.util.createElement('DIV');
123
-
124
- dialog.className = 'se-dialog-content se-dialog-image';
125
- dialog.style.display = 'none';
126
-
127
- let html = '' +
128
- '<div class="se-dialog-header">' +
129
- '<button type="button" data-command="close" class="se-btn se-dialog-close" class="close" title="' + lang.dialogBox.close + '" aria-label="' + lang.dialogBox.close + '">' +
130
- core.icons.cancel +
131
- '</button>' +
132
- '<span class="se-modal-title">' + lang.dialogBox.imageBox.title + '</span>' +
133
- '</div>' +
134
- '<div class="se-dialog-tabs">' +
135
- '<button type="button" class="_se_tab_link active" data-tab-link="image">' + lang.toolbar.image + '</button>' +
136
- '<button type="button" class="_se_tab_link" data-tab-link="url">' + lang.toolbar.link + '</button>' +
137
- '</div>' +
138
- '<form method="post" enctype="multipart/form-data">' +
139
- '<div class="_se_tab_content _se_tab_content_image">' +
140
- '<div class="se-dialog-body"><div style="border-bottom: 1px dashed #ccc;">';
141
-
142
- if (option.imageFileInput) {
143
- html += '' +
144
- '<div class="se-dialog-form">' +
145
- '<label>' + lang.dialogBox.imageBox.file + '</label>' +
146
- '<div class="se-dialog-form-files">' +
147
- '<input class="se-input-form _se_image_file" type="file" accept="' + option.imageAccept + '"' + (option.imageMultipleFile ? ' multiple="multiple"' : '') + '/>' +
148
- '<button type="button" class="se-btn se-dialog-files-edge-button se-file-remove" title="' + lang.controller.remove + '" aria-label="' + lang.controller.remove + '">' + core.icons.cancel + '</button>' +
149
- '</div>' +
150
- '</div>' ;
151
- }
152
-
153
- if (option.imageUrlInput) {
154
- html += '' +
155
- '<div class="se-dialog-form">' +
156
- '<label>' + lang.dialogBox.imageBox.url + '</label>' +
157
- '<div class="se-dialog-form-files">' +
158
- '<input class="se-input-form se-input-url _se_image_url" type="text" />' +
159
- ((option.imageGalleryUrl && core.plugins.imageGallery) ? '<button type="button" class="se-btn se-dialog-files-edge-button __se__gallery" title="' + lang.toolbar.imageGallery + '" aria-label="' + lang.toolbar.imageGallery + '">' + core.icons.image_gallery + '</button>' : '') +
160
- '</div>' +
161
- '<pre class="se-link-preview"></pre>' +
162
- '</div>';
163
- }
164
-
165
- html += '</div>' +
166
- '<div class="se-dialog-form">' +
167
- '<label>' + lang.dialogBox.imageBox.altText + '</label><input class="se-input-form _se_image_alt" type="text" />' +
168
- '</div>';
169
-
170
- if (option.imageResizing) {
171
- const onlyPercentage = option.imageSizeOnlyPercentage;
172
- const onlyPercentDisplay = onlyPercentage ? ' style="display: none !important;"' : '';
173
- const heightDisplay = !option.imageHeightShow ? ' style="display: none !important;"' : '';
174
- html += '<div class="se-dialog-form">';
175
- if (onlyPercentage || !option.imageHeightShow) {
176
- html += '' +
177
- '<div class="se-dialog-size-text">' +
178
- '<label class="size-w">' + lang.dialogBox.size + '</label>' +
179
- '</div>';
180
- } else {
181
- html += '' +
182
- '<div class="se-dialog-size-text">' +
183
- '<label class="size-w">' + lang.dialogBox.width + '</label>' +
184
- '<label class="se-dialog-size-x">&nbsp;</label>' +
185
- '<label class="size-h">' + lang.dialogBox.height + '</label>' +
186
- '</div>';
187
- }
188
- html += '' +
189
- '<input class="se-input-control _se_image_size_x" placeholder="auto"' + (onlyPercentage ? ' type="number" min="1"' : 'type="text"') + (onlyPercentage ? ' max="100"' : '') + ' />' +
190
- '<label class="se-dialog-size-x"' + heightDisplay + '>' + (onlyPercentage ? '%' : 'x') + '</label>' +
191
- '<input type="text" class="se-input-control _se_image_size_y" placeholder="auto"' + onlyPercentDisplay + (onlyPercentage ? ' max="100"' : '') + heightDisplay + '/>' +
192
- '<label' + onlyPercentDisplay + heightDisplay + '><input type="checkbox" class="se-dialog-btn-check _se_image_check_proportion" checked/>&nbsp;' + lang.dialogBox.proportion + '</label>' +
193
- '<button type="button" title="' + lang.dialogBox.revertButton + '" aria-label="' + lang.dialogBox.revertButton + '" class="se-btn se-dialog-btn-revert" style="float: right;">' + core.icons.revert + '</button>' +
194
- '</div>' ;
195
- }
196
-
197
- html += '' +
198
- '<div class="se-dialog-form se-dialog-form-footer">' +
199
- '<label><input type="checkbox" class="se-dialog-btn-check _se_image_check_caption" />&nbsp;' + lang.dialogBox.caption + '</label>' +
200
- '</div>' +
201
- '</div>' +
202
- '</div>' +
203
- '<div class="_se_tab_content _se_tab_content_url" style="display: none">' +
204
- core.context.anchor.forms.innerHTML +
205
- '</div>' +
206
- '<div class="se-dialog-footer">' +
207
- '<div' + (option.imageAlignShow ? '' : ' style="display: none"') + '>' +
208
- '<label><input type="radio" name="suneditor_image_radio" class="se-dialog-btn-radio" value="none" checked>' + lang.dialogBox.basic + '</label>' +
209
- '<label><input type="radio" name="suneditor_image_radio" class="se-dialog-btn-radio" value="left">' + lang.dialogBox.left + '</label>' +
210
- '<label><input type="radio" name="suneditor_image_radio" class="se-dialog-btn-radio" value="center">' + lang.dialogBox.center + '</label>' +
211
- '<label><input type="radio" name="suneditor_image_radio" class="se-dialog-btn-radio" value="right">' + lang.dialogBox.right + '</label>' +
212
- '</div>' +
213
- '<button type="submit" class="se-btn-primary" title="' + lang.dialogBox.submitButton + '" aria-label="' + lang.dialogBox.submitButton + '"><span>' + lang.dialogBox.submitButton + '</span></button>' +
214
- '</div>' +
215
- '</form>';
216
-
217
- dialog.innerHTML = html;
218
-
219
- return dialog;
220
- },
221
-
222
- _fileInputChange: function () {
223
- if (!this.imgInputFile.value) {
224
- this.imgUrlFile.removeAttribute('disabled');
225
- this.previewSrc.style.textDecoration = '';
226
- } else {
227
- this.imgUrlFile.setAttribute('disabled', true);
228
- this.previewSrc.style.textDecoration = 'line-through';
229
- }
230
- },
231
-
232
- _removeSelectedFiles: function (urlInput, previewSrc) {
233
- this.value = '';
234
- if (urlInput) {
235
- urlInput.removeAttribute('disabled');
236
- previewSrc.style.textDecoration = '';
237
- }
238
- },
239
-
240
- _openGallery: function () {
241
- this.callPlugin('imageGallery', this.plugins.imageGallery.open.bind(this, this.plugins.image._setUrlInput.bind(this.context.image)), null);
242
- },
243
-
244
- _setUrlInput: function (target) {
245
- this.altText.value = target.alt;
246
- this._v_src._linkValue = this.previewSrc.textContent = this.imgUrlFile.value = target.src;
247
- this.imgUrlFile.focus();
248
- },
249
-
250
- _onLinkPreview: function (context, protocol, e) {
251
- const value = e.target.value.trim();
252
- context._linkValue = this.textContent = !value ? '' : (protocol && value.indexOf('://') === -1 && value.indexOf('#') !== 0) ? protocol + value : value.indexOf('://') === -1 ? '/' + value : value;
253
- },
254
-
255
- /**
256
- * @Override @Required fileManager
257
- */
258
- fileTags: ['img'],
259
-
260
- /**
261
- * @Override core, fileManager, resizing
262
- * @description It is called from core.selectComponent.
263
- * @param {Element} element Target element
264
- */
265
- select: function (element) {
266
- this.plugins.image.onModifyMode.call(this, element, this.plugins.resizing.call_controller_resize.call(this, element, 'image'));
267
- },
268
-
269
- /**
270
- * @Override fileManager, resizing
271
- */
272
- destroy: function (element) {
273
- const imageEl = element || this.context.image._element;
274
- const imageContainer = this.util.getParentElement(imageEl, this.util.isMediaComponent) || imageEl;
275
- const dataIndex = imageEl.getAttribute('data-index') * 1;
276
- let focusEl = (imageContainer.previousElementSibling || imageContainer.nextElementSibling);
277
-
278
- const emptyDiv = imageContainer.parentNode;
279
- this.util.removeItem(imageContainer);
280
- this.plugins.image.init.call(this);
281
- this.controllersOff();
282
-
283
- if (emptyDiv !== this.context.element.wysiwyg) this.util.removeItemAllParents(emptyDiv, function (current) { return current.childNodes.length === 0; }, null);
284
-
285
- // focus
286
- this.focusEdge(focusEl);
287
-
288
- // event
289
- this.plugins.fileManager.deleteInfo.call(this, 'image', dataIndex, this.functions.onImageUpload);
290
-
291
- // history stack
292
- this.history.push(false);
293
- },
294
-
295
- /**
296
- * @Required @Override dialog
297
- */
298
- on: function (update) {
299
- const contextImage = this.context.image;
300
-
301
- if (!update) {
302
- contextImage.inputX.value = contextImage._origin_w = this.options.imageWidth === contextImage._defaultSizeX ? '' : this.options.imageWidth;
303
- contextImage.inputY.value = contextImage._origin_h = this.options.imageHeight === contextImage._defaultSizeY ? '' : this.options.imageHeight;
304
- if (contextImage.imgInputFile && this.options.imageMultipleFile) contextImage.imgInputFile.setAttribute('multiple', 'multiple');
305
- } else {
306
- if (contextImage.imgInputFile && this.options.imageMultipleFile) contextImage.imgInputFile.removeAttribute('multiple');
307
- }
308
- this.plugins.anchor.on.call(this, contextImage.anchorCtx, update);
309
- },
310
-
311
- /**
312
- * @Required @Override dialog
313
- */
314
- open: function () {
315
- this.plugins.dialog.open.call(this, 'image', 'image' === this.currentControllerName);
316
- },
317
-
318
- openTab: function (e) {
319
- const modal = this.context.image.modal;
320
- const targetElement = (e === 'init' ? modal.querySelector('._se_tab_link') : e.target);
321
-
322
- if (!/^BUTTON$/i.test(targetElement.tagName)) {
323
- return false;
324
- }
325
-
326
- // Declare all variables
327
- const tabName = targetElement.getAttribute('data-tab-link');
328
- const contentClassName = '_se_tab_content';
329
- let i, tabContent, tabLinks;
330
-
331
- // Get all elements with class="tabcontent" and hide them
332
- tabContent = modal.getElementsByClassName(contentClassName);
333
- for (i = 0; i < tabContent.length; i++) {
334
- tabContent[i].style.display = 'none';
335
- }
336
-
337
- // Get all elements with class="tablinks" and remove the class "active"
338
- tabLinks = modal.getElementsByClassName('_se_tab_link');
339
- for (i = 0; i < tabLinks.length; i++) {
340
- this.util.removeClass(tabLinks[i], 'active');
341
- }
342
-
343
- // Show the current tab, and add an "active" class to the button that opened the tab
344
- modal.querySelector('.' + contentClassName + '_' + tabName).style.display = 'block';
345
- this.util.addClass(targetElement, 'active');
346
-
347
- // focus
348
- if (tabName === 'image' && this.context.image.focusElement) {
349
- this.context.image.focusElement.focus();
350
- } else if (tabName === 'url') {
351
- this.context.anchor.caller.image.urlInput.focus();
352
- }
353
-
354
- return false;
355
- },
356
-
357
- submit: function (e) {
358
- const contextImage = this.context.image;
359
- const imagePlugin = this.plugins.image;
360
-
361
- e.preventDefault();
362
- e.stopPropagation();
363
-
364
- contextImage._altText = contextImage.altText.value;
365
- contextImage._align = contextImage.modal.querySelector('input[name="suneditor_image_radio"]:checked').value;
366
- contextImage._captionChecked = contextImage.captionCheckEl.checked;
367
- if (contextImage._resizing) contextImage._proportionChecked = contextImage.proportion.checked;
368
-
369
- try {
370
- if (this.context.dialog.updateModal) {
371
- imagePlugin.update_image.call(this, false, true, false);
372
- }
373
-
374
- if (contextImage.imgInputFile && contextImage.imgInputFile.files.length > 0) {
375
- this.showLoading();
376
- imagePlugin.submitAction.call(this, this.context.image.imgInputFile.files);
377
- } else if (contextImage.imgUrlFile && contextImage._v_src._linkValue.length > 0) {
378
- this.showLoading();
379
- imagePlugin.onRender_imgUrl.call(this, contextImage._v_src._linkValue);
380
- }
381
- } catch (error) {
382
- this.closeLoading();
383
- throw Error('[SUNEDITOR.image.submit.fail] cause : "' + error.message + '"');
384
- } finally {
385
- this.plugins.dialog.close.call(this);
386
- }
387
-
388
- return false;
389
- },
390
-
391
- submitAction: function (fileList) {
392
- if (fileList.length === 0) return;
393
-
394
- let fileSize = 0;
395
- let files = [];
396
- for (let i = 0, len = fileList.length; i < len; i++) {
397
- if (/image/i.test(fileList[i].type)) {
398
- files.push(fileList[i]);
399
- fileSize += fileList[i].size;
400
- }
401
- }
402
-
403
- const limitSize = this.options.imageUploadSizeLimit;
404
- if (limitSize > 0) {
405
- let infoSize = 0;
406
- const imagesInfo = this.context.image._infoList;
407
- for (let i = 0, len = imagesInfo.length; i < len; i++) {
408
- infoSize += imagesInfo[i].size * 1;
409
- }
410
-
411
- if ((fileSize + infoSize) > limitSize) {
412
- this.closeLoading();
413
- const err = '[SUNEDITOR.imageUpload.fail] Size of uploadable total images: ' + (limitSize/1000) + 'KB';
414
- if (typeof this.functions.onImageUploadError !== 'function' || this.functions.onImageUploadError(err, { 'limitSize': limitSize, 'currentSize': infoSize, 'uploadSize': fileSize }, this)) {
415
- this.functions.noticeOpen(err);
416
- }
417
- return;
418
- }
419
- }
420
-
421
- const contextImage = this.context.image;
422
- contextImage._uploadFileLength = files.length;
423
-
424
- const anchor = this.plugins.anchor.createAnchor.call(this, contextImage.anchorCtx, true);
425
- const info = {
426
- anchor: anchor,
427
- inputWidth: contextImage.inputX.value,
428
- inputHeight: contextImage.inputY.value,
429
- align: contextImage._align,
430
- isUpdate: this.context.dialog.updateModal,
431
- alt: contextImage._altText,
432
- element: contextImage._element
433
- };
434
-
435
- if (typeof this.functions.onImageUploadBefore === 'function') {
436
- const result = this.functions.onImageUploadBefore(files, info, this, function (data) {
437
- if (data && this._w.Array.isArray(data.result)) {
438
- this.plugins.image.register.call(this, info, data);
439
- } else {
440
- this.plugins.image.upload.call(this, info, data);
441
- }
442
- }.bind(this));
443
-
444
- if (typeof result === 'undefined') return;
445
- if (!result) {
446
- this.closeLoading();
447
- return;
448
- }
449
- if (this._w.Array.isArray(result) && result.length > 0) files = result;
450
- }
451
-
452
- this.plugins.image.upload.call(this, info, files);
453
- },
454
-
455
- error: function (message, response) {
456
- this.closeLoading();
457
- if (typeof this.functions.onImageUploadError !== 'function' || this.functions.onImageUploadError(message, response, this)) {
458
- this.functions.noticeOpen(message);
459
- throw Error('[SUNEDITOR.plugin.image.error] response: ' + message);
460
- }
461
- },
462
-
463
- upload: function (info, files) {
464
- if (!files) {
465
- this.closeLoading();
466
- return;
467
- }
468
- if (typeof files === 'string') {
469
- this.plugins.image.error.call(this, files, null);
470
- return;
471
- }
472
-
473
- const imageUploadUrl = this.options.imageUploadUrl;
474
- const filesLen = this.context.dialog.updateModal ? 1 : files.length;
475
-
476
- // server upload
477
- if (typeof imageUploadUrl === 'string' && imageUploadUrl.length > 0) {
478
- const formData = new FormData();
479
- for (let i = 0; i < filesLen; i++) {
480
- formData.append('file-' + i, files[i]);
481
- }
482
- this.plugins.fileManager.upload.call(this, imageUploadUrl, this.options.imageUploadHeader, formData, this.plugins.image.callBack_imgUpload.bind(this, info), this.functions.onImageUploadError);
483
- } else { // base64
484
- this.plugins.image.setup_reader.call(this, files, info.anchor, info.inputWidth, info.inputHeight, info.align, info.alt, filesLen, info.isUpdate);
485
- }
486
- },
487
-
488
- callBack_imgUpload: function (info, xmlHttp) {
489
- if (typeof this.functions.imageUploadHandler === 'function') {
490
- this.functions.imageUploadHandler(xmlHttp, info, this);
491
- } else {
492
- const response = JSON.parse(xmlHttp.responseText);
493
- if (response.errorMessage) {
494
- this.plugins.image.error.call(this, response.errorMessage, response);
495
- } else {
496
- this.plugins.image.register.call(this, info, response);
497
- }
498
- }
499
- },
500
-
501
- register: function (info, response) {
502
- const fileList = response.result;
503
-
504
- for (let i = 0, len = fileList.length, file; i < len; i++) {
505
- file = { name: fileList[i].name, size: fileList[i].size };
506
- if (info.isUpdate) {
507
- this.plugins.image.update_src.call(this, fileList[i].url, info.element, file);
508
- break;
509
- } else {
510
- this.plugins.image.create_image.call(this, fileList[i].url, info.anchor, info.inputWidth, info.inputHeight, info.align, file, info.alt);
511
- }
512
- }
513
-
514
- this.closeLoading();
515
- },
516
-
517
- setup_reader: function (files, anchor, width, height, align, alt, filesLen, isUpdate) {
518
- try {
519
- this.context.image.base64RenderIndex = filesLen;
520
- const wFileReader = this._w.FileReader;
521
- const filesStack = [filesLen];
522
- this.context.image.inputX.value = width;
523
- this.context.image.inputY.value = height;
524
-
525
- for (let i = 0, reader, file; i < filesLen; i++) {
526
- reader = new wFileReader();
527
- file = files[i];
528
-
529
- reader.onload = function (reader, update, updateElement, file, index) {
530
- filesStack[index] = { result: reader.result, file: file };
531
-
532
- if (--this.context.image.base64RenderIndex === 0) {
533
- this.plugins.image.onRender_imgBase64.call(this, update, filesStack, updateElement, anchor, width, height, align, alt);
534
- this.closeLoading();
535
- }
536
- }.bind(this, reader, isUpdate, this.context.image._element, file, i);
537
-
538
- reader.readAsDataURL(file);
539
- }
540
- } catch (e) {
541
- this.closeLoading();
542
- throw Error('[SUNEDITOR.image.setup_reader.fail] cause : "' + e.message + '"');
543
- }
544
- },
545
-
546
- onRender_imgBase64: function (update, filesStack, updateElement, anchor, width, height, align, alt) {
547
- const updateMethod = this.plugins.image.update_src;
548
- const createMethod = this.plugins.image.create_image;
549
-
550
- for (let i = 0, len = filesStack.length; i < len; i++) {
551
- if (update) {
552
- this.context.image._element.setAttribute('data-file-name', filesStack[i].file.name);
553
- this.context.image._element.setAttribute('data-file-size', filesStack[i].file.size);
554
- updateMethod.call(this, filesStack[i].result, updateElement, filesStack[i].file);
555
- } else {
556
- createMethod.call(this, filesStack[i].result, anchor, width, height, align, filesStack[i].file, alt);
557
- }
558
- }
559
- },
560
-
561
- onRender_imgUrl: function (url) {
562
- if (!url) url = this.context.image._v_src._linkValue;
563
- if (!url) return false;
564
- const contextImage = this.context.image;
565
-
566
- try {
567
- const file = {name: url.split('/').pop(), size: 0};
568
- if (this.context.dialog.updateModal) this.plugins.image.update_src.call(this, url, contextImage._element, file);
569
- else this.plugins.image.create_image.call(this, url, this.plugins.anchor.createAnchor.call(this, contextImage.anchorCtx, true), contextImage.inputX.value, contextImage.inputY.value, contextImage._align, file, contextImage._altText);
570
- } catch (e) {
571
- throw Error('[SUNEDITOR.image.URLRendering.fail] cause : "' + e.message + '"');
572
- } finally {
573
- this.closeLoading();
574
- }
575
- },
576
-
577
- onRender_link: function (imgTag, anchor) {
578
- if (anchor) {
579
- anchor.setAttribute('data-image-link', 'image');
580
- imgTag.setAttribute('data-image-link', anchor.href);
581
- anchor.appendChild(imgTag);
582
- return anchor;
583
- }
584
-
585
- return imgTag;
586
- },
587
-
588
- /**
589
- * @Override resizing
590
- * @param {String} xy 'x': width, 'y': height
591
- * @param {KeyboardEvent} e Event object
592
- */
593
- setInputSize: function (xy, e) {
594
- if (e && e.keyCode === 32) {
595
- e.preventDefault();
596
- return;
597
- }
598
-
599
- this.plugins.resizing._module_setInputSize.call(this, this.context.image, xy);
600
- },
601
-
602
- /**
603
- * @Override resizing
604
- */
605
- setRatio: function () {
606
- this.plugins.resizing._module_setRatio.call(this, this.context.image);
607
- },
608
-
609
- /**
610
- * @Override fileManager
611
- */
612
- checkFileInfo: function () {
613
- const imagePlugin = this.plugins.image;
614
- const contextImage = this.context.image;
615
-
616
- const modifyHandler = function (tag) {
617
- imagePlugin.onModifyMode.call(this, tag, null);
618
- imagePlugin.openModify.call(this, true);
619
- // get size
620
- contextImage.inputX.value = contextImage._origin_w;
621
- contextImage.inputY.value = contextImage._origin_h;
622
- // get align
623
- const format = this.util.getFormatElement(tag);
624
- if (format) contextImage._align = format.style.textAlign || format.style.float;
625
- // link
626
- if (this.util.isAnchor(tag.parentNode) && !contextImage.anchorCtx.linkValue) contextImage.anchorCtx.linkValue = ' ';
627
-
628
- imagePlugin.update_image.call(this, true, false, true);
629
- imagePlugin.init.call(this);
630
- }.bind(this);
631
-
632
- this.plugins.fileManager.checkInfo.call(this, 'image', ['img'], this.functions.onImageUpload, modifyHandler, true);
633
- },
634
-
635
- /**
636
- * @Override fileManager
637
- */
638
- resetFileInfo: function () {
639
- this.plugins.fileManager.resetInfo.call(this, 'image', this.functions.onImageUpload);
640
- },
641
-
642
- create_image: function (src, anchor, width, height, align, file, alt) {
643
- const imagePlugin = this.plugins.image;
644
- const contextImage = this.context.image;
645
- this.context.resizing._resize_plugin = 'image';
646
-
647
- let oImg = this.util.createElement('IMG');
648
- oImg.src = src;
649
- oImg.alt = alt;
650
- oImg.setAttribute('data-rotate', '0');
651
- anchor = imagePlugin.onRender_link.call(this, oImg, anchor ? anchor.cloneNode(false) : null);
652
-
653
- if (contextImage._resizing) {
654
- oImg.setAttribute('data-proportion', contextImage._proportionChecked);
655
- }
656
-
657
- const cover = this.plugins.component.set_cover.call(this, anchor);
658
- const container = this.plugins.component.set_container.call(this, cover, 'se-image-container');
659
-
660
- // caption
661
- if (contextImage._captionChecked) {
662
- contextImage._caption = this.plugins.component.create_caption.call(this);
663
- cover.appendChild(contextImage._caption);
664
- }
665
-
666
- contextImage._element = oImg;
667
- contextImage._cover = cover;
668
- contextImage._container = container;
669
-
670
- // set size
671
- imagePlugin.applySize.call(this, width, height);
672
-
673
- // align
674
- imagePlugin.setAlign.call(this, align, oImg, cover, container);
675
-
676
- oImg.onload = imagePlugin._image_create_onload.bind(this, oImg, contextImage.svgDefaultSize, container);
677
- if (this.insertComponent(container, true, true, true)) this.plugins.fileManager.setInfo.call(this, 'image', oImg, this.functions.onImageUpload, file, true);
678
- this.context.resizing._resize_plugin = '';
679
- },
680
-
681
- _image_create_onload: function (oImg, svgDefaultSize, container) {
682
- // svg exception handling
683
- if (oImg.offsetWidth === 0) this.plugins.image.applySize.call(this, svgDefaultSize, '');
684
- if (this.options.mediaAutoSelect) {
685
- this.selectComponent(oImg, 'image');
686
- } else {
687
- const line = this.appendFormatTag(container, null);
688
- if (line) this.setRange(line, 0, line, 0);
689
- }
690
- },
691
-
692
- update_image: function (init, openController, notHistoryPush) {
693
- const contextImage = this.context.image;
694
- let imageEl = contextImage._element;
695
- let cover = contextImage._cover;
696
- let container = contextImage._container;
697
- let isNewContainer = false;
698
-
699
- if (cover === null) {
700
- isNewContainer = true;
701
- imageEl = contextImage._element.cloneNode(true);
702
- cover = this.plugins.component.set_cover.call(this, imageEl);
703
- }
704
-
705
- if (container === null) {
706
- cover = cover.cloneNode(true);
707
- imageEl = cover.querySelector('img');
708
- isNewContainer = true;
709
- container = this.plugins.component.set_container.call(this, cover, 'se-image-container');
710
- } else if (isNewContainer) {
711
- container.innerHTML = '';
712
- container.appendChild(cover);
713
- contextImage._cover = cover;
714
- contextImage._element = imageEl;
715
- isNewContainer = false;
716
- }
717
-
718
- // check size
719
- let changeSize;
720
- const x = this.util.isNumber(contextImage.inputX.value) ? contextImage.inputX.value + contextImage.sizeUnit : contextImage.inputX.value;
721
- const y = this.util.isNumber(contextImage.inputY.value) ? contextImage.inputY.value + contextImage.sizeUnit : contextImage.inputY.value;
722
- if (/%$/.test(imageEl.style.width)) {
723
- changeSize = x !== container.style.width || y !== container.style.height;
724
- } else {
725
- changeSize = x !== imageEl.style.width || y !== imageEl.style.height;
726
- }
727
-
728
- // alt
729
- imageEl.alt = contextImage._altText;
730
-
731
- // caption
732
- let modifiedCaption = false;
733
- if (contextImage._captionChecked) {
734
- if (!contextImage._caption) {
735
- contextImage._caption = this.plugins.component.create_caption.call(this);
736
- cover.appendChild(contextImage._caption);
737
- modifiedCaption = true;
738
- }
739
- } else {
740
- if (contextImage._caption) {
741
- this.util.removeItem(contextImage._caption);
742
- contextImage._caption = null;
743
- modifiedCaption = true;
744
- }
745
- }
746
-
747
- // link
748
- let isNewAnchor = null;
749
- const anchor = this.plugins.anchor.createAnchor.call(this, contextImage.anchorCtx, true);
750
- if (anchor) {
751
- if (contextImage._linkElement !== anchor || (isNewContainer && !container.contains(anchor))) {
752
- contextImage._linkElement = anchor.cloneNode(false);
753
- cover.insertBefore(this.plugins.image.onRender_link.call(this, imageEl, contextImage._linkElement), contextImage._caption);
754
- isNewAnchor = contextImage._element;
755
- } else {
756
- contextImage._linkElement.setAttribute('data-image-link', 'image');
757
- }
758
- } else if (contextImage._linkElement !== null) {
759
- const imageElement = imageEl;
760
- imageElement.setAttribute('data-image-link', '');
761
- if (cover.contains(contextImage._linkElement)) {
762
- const newEl = imageElement.cloneNode(true);
763
- cover.removeChild(contextImage._linkElement);
764
- cover.insertBefore(newEl, contextImage._caption);
765
- contextImage._element = imageEl = newEl;
766
- }
767
- }
768
-
769
- let existElement = null;
770
- if (isNewContainer) {
771
- existElement = (this.util.isRangeFormatElement(contextImage._element.parentNode) || this.util.isWysiwygDiv(contextImage._element.parentNode)) ?
772
- contextImage._element :
773
- this.util.isAnchor(contextImage._element.parentNode) ? contextImage._element.parentNode : this.util.getFormatElement(contextImage._element) || contextImage._element;
774
-
775
- if (this.util.getParentElement(contextImage._element, this.util.isNotCheckingNode)) {
776
- existElement = isNewAnchor ? anchor : contextImage._element;
777
- existElement.parentNode.replaceChild(container, existElement);
778
- } else if (this.util.isListCell(existElement)) {
779
- const refer = this.util.getParentElement(contextImage._element, function (current) { return current.parentNode === existElement; });
780
- existElement.insertBefore(container, refer);
781
- this.util.removeItem(contextImage._element);
782
- this.util.removeEmptyNode(refer, null, true);
783
- } else if (this.util.isFormatElement(existElement)) {
784
- const refer = this.util.getParentElement(contextImage._element, function (current) { return current.parentNode === existElement; });
785
- existElement = this.util.splitElement(existElement, refer);
786
- existElement.parentNode.insertBefore(container, existElement);
787
- this.util.removeItem(contextImage._element);
788
- this.util.removeEmptyNode(existElement, null, true);
789
- if (existElement.children.length === 0) existElement.innerHTML = this.util.htmlRemoveWhiteSpace(existElement.innerHTML);
790
- } else {
791
- if (this.util.isFormatElement(existElement.parentNode)) {
792
- const formats = existElement.parentNode;
793
- formats.parentNode.insertBefore(container, existElement.previousSibling ? formats.nextElementSibling : formats);
794
- if (contextImage.__updateTags.map(function (current) { return existElement.contains(current); }).length === 0) this.util.removeItem(existElement);
795
- } else {
796
- existElement.parentNode.replaceChild(container, existElement);
797
- }
798
- }
799
-
800
- imageEl = container.querySelector('img');
801
-
802
- contextImage._element = imageEl;
803
- contextImage._cover = cover;
804
- contextImage._container = container;
805
- }
806
-
807
- if (isNewAnchor) {
808
- if (!isNewContainer) {
809
- this.util.removeItem(anchor);
810
- } else {
811
- this.util.removeItem(isNewAnchor);
812
- if (this.util.getListChildren(anchor, function (current) { return /IMG/i.test(current.tagName); }).length === 0) {
813
- this.util.removeItem(anchor);
814
- }
815
- }
816
- }
817
-
818
- // transform
819
- if (modifiedCaption || (!contextImage._onlyPercentage && changeSize)) {
820
- if (!init && (/\d+/.test(imageEl.style.height) || (this.context.resizing._rotateVertical && contextImage._captionChecked))) {
821
- if (/%$/.test(contextImage.inputX.value) || /%$/.test(contextImage.inputY.value)) {
822
- this.plugins.resizing.resetTransform.call(this, imageEl);
823
- } else {
824
- this.plugins.resizing.setTransformSize.call(this, imageEl, this.util.getNumber(contextImage.inputX.value, 0), this.util.getNumber(contextImage.inputY.value, 0));
825
- }
826
- }
827
- }
828
-
829
- // size
830
- if (contextImage._resizing) {
831
- imageEl.setAttribute('data-proportion', contextImage._proportionChecked);
832
- if (changeSize) {
833
- this.plugins.image.applySize.call(this);
834
- }
835
- }
836
-
837
- // align
838
- this.plugins.image.setAlign.call(this, null, imageEl, null, null);
839
-
840
- // set imagesInfo
841
- if (init) {
842
- this.plugins.fileManager.setInfo.call(this, 'image', imageEl, this.functions.onImageUpload, null, true);
843
- }
844
-
845
- if (openController) {
846
- this.selectComponent(imageEl, 'image');
847
- }
848
-
849
- // history stack
850
- if (!notHistoryPush) this.history.push(false);
851
- },
852
-
853
- update_src: function (src, element, file) {
854
- element.src = src;
855
- this._w.setTimeout(this.plugins.fileManager.setInfo.bind(this, 'image', element, this.functions.onImageUpload, file, true));
856
- this.selectComponent(element, 'image');
857
- },
858
-
859
- /**
860
- * @Required @Override fileManager, resizing
861
- */
862
- onModifyMode: function (element, size) {
863
- if (!element) return;
864
-
865
- const contextImage = this.context.image;
866
- contextImage._linkElement = contextImage.anchorCtx.linkAnchor = this.util.isAnchor(element.parentNode) ? element.parentNode : null;
867
- contextImage._element = element;
868
- contextImage._cover = this.util.getParentElement(element, 'FIGURE');
869
- contextImage._container = this.util.getParentElement(element, this.util.isMediaComponent);
870
- contextImage._caption = this.util.getChildElement(contextImage._cover, 'FIGCAPTION');
871
- contextImage._align = element.getAttribute('data-align') || element.style.float || 'none';
872
- element.style.float = '';
873
- this.plugins.anchor.setCtx(contextImage._linkElement, contextImage.anchorCtx);
874
-
875
- if (size) {
876
- contextImage._element_w = size.w;
877
- contextImage._element_h = size.h;
878
- contextImage._element_t = size.t;
879
- contextImage._element_l = size.l;
880
- }
881
-
882
- let userSize = contextImage._element.getAttribute('data-size') || contextImage._element.getAttribute('data-origin');
883
- let w, h;
884
- if (userSize) {
885
- userSize = userSize.split(',');
886
- w = userSize[0];
887
- h = userSize[1];
888
- } else if (size) {
889
- w = size.w;
890
- h = size.h;
891
- }
892
-
893
- contextImage._origin_w = w || element.style.width || element.width || '';
894
- contextImage._origin_h = h || element.style.height || element.height || '';
895
- },
896
-
897
- /**
898
- * @Required @Override fileManager, resizing
899
- */
900
- openModify: function (notOpen) {
901
- const contextImage = this.context.image;
902
- if (contextImage.imgUrlFile) {
903
- contextImage._v_src._linkValue = contextImage.previewSrc.textContent = contextImage.imgUrlFile.value = contextImage._element.src;
904
- }
905
- contextImage._altText = contextImage.altText.value = contextImage._element.alt;
906
- (contextImage.modal.querySelector('input[name="suneditor_image_radio"][value="' + contextImage._align + '"]') || contextImage.modal.querySelector('input[name="suneditor_image_radio"][value="none"]')).checked = true;
907
- contextImage._align = contextImage.modal.querySelector('input[name="suneditor_image_radio"]:checked').value;
908
- contextImage._captionChecked = contextImage.captionCheckEl.checked = !!contextImage._caption;
909
-
910
- if (contextImage._resizing) {
911
- this.plugins.resizing._module_setModifyInputSize.call(this, contextImage, this.plugins.image);
912
- }
913
-
914
- if (!notOpen) this.plugins.dialog.open.call(this, 'image', true);
915
- },
916
-
917
- /**
918
- * @Override fileManager
919
- */
920
- applySize: function (w, h) {
921
- const contextImage = this.context.image;
922
-
923
- if (!w) w = contextImage.inputX.value || this.options.imageWidth;
924
- if (!h) h = contextImage.inputY.value || this.options.imageHeight;
925
-
926
- if ((contextImage._onlyPercentage && !!w) || /%$/.test(w)) {
927
- this.plugins.image.setPercentSize.call(this, w, h);
928
- return true;
929
- } else if ((!w || w === 'auto') && (!h || h === 'auto')) {
930
- this.plugins.image.setAutoSize.call(this);
931
- } else {
932
- this.plugins.image.setSize.call(this, w, h, false);
933
- }
934
-
935
- return false;
936
- },
937
-
938
- /**
939
- * @Override resizing
940
- */
941
- sizeRevert: function () {
942
- this.plugins.resizing._module_sizeRevert.call(this, this.context.image);
943
- },
944
-
945
- /**
946
- * @Override resizing
947
- */
948
- setSize: function (w, h, notResetPercentage, direction) {
949
- const contextImage = this.context.image;
950
- const onlyW = /^(rw|lw)$/.test(direction) && /\d+/.test(contextImage._element.style.height);
951
- const onlyH = /^(th|bh)$/.test(direction) && /\d+/.test(contextImage._element.style.width);
952
-
953
- if (!onlyH) {
954
- contextImage._element.style.width = this.util.isNumber(w) ? w + contextImage.sizeUnit : w;
955
- this.plugins.image.cancelPercentAttr.call(this);
956
- }
957
- if (!onlyW) {
958
- contextImage._element.style.height = this.util.isNumber(h) ? h + contextImage.sizeUnit : /%$/.test(h) ? '' : h;
959
- }
960
-
961
- if (contextImage._align === 'center') this.plugins.image.setAlign.call(this, null, null, null, null);
962
- if (!notResetPercentage) contextImage._element.removeAttribute('data-percentage');
963
-
964
- // save current size
965
- this.plugins.resizing._module_saveCurrentSize.call(this, contextImage);
966
- },
967
-
968
- /**
969
- * @Override resizing
970
- */
971
- setAutoSize: function () {
972
- const contextImage = this.context.image;
973
-
974
- if (contextImage._caption) contextImage._caption.style.marginTop = '';
975
- this.plugins.resizing.resetTransform.call(this, contextImage._element);
976
- this.plugins.image.cancelPercentAttr.call(this);
977
-
978
- contextImage._element.style.maxWidth = '';
979
- contextImage._element.style.width = '';
980
- contextImage._element.style.height = '';
981
- contextImage._cover.style.width = '';
982
- contextImage._cover.style.height = '';
983
-
984
- this.plugins.image.setAlign.call(this, null, null, null, null);
985
- contextImage._element.setAttribute('data-percentage', 'auto,auto');
986
-
987
- // save current size
988
- this.plugins.resizing._module_saveCurrentSize.call(this, contextImage);
989
- },
990
-
991
- /**
992
- * @Override resizing
993
- */
994
- setOriginSize: function () {
995
- const contextImage = this.context.image;
996
- contextImage._element.removeAttribute('data-percentage');
997
-
998
- this.plugins.resizing.resetTransform.call(this, contextImage._element);
999
- this.plugins.image.cancelPercentAttr.call(this);
1000
-
1001
- const originSize = (contextImage._element.getAttribute('data-origin') || '').split(',');
1002
- const w = originSize[0];
1003
- const h = originSize[1];
1004
-
1005
- if (originSize) {
1006
- if (contextImage._onlyPercentage || (/%$/.test(w) && (/%$/.test(h) || !/\d/.test(h)))) {
1007
- this.plugins.image.setPercentSize.call(this, w, h);
1008
- } else {
1009
- this.plugins.image.setSize.call(this, w, h);
1010
- }
1011
-
1012
- // save current size
1013
- this.plugins.resizing._module_saveCurrentSize.call(this, contextImage);
1014
- }
1015
- },
1016
-
1017
- /**
1018
- * @Override resizing
1019
- */
1020
- setPercentSize: function (w, h) {
1021
- const contextImage = this.context.image;
1022
- h = !!h && !/%$/.test(h) && !this.util.getNumber(h, 0) ? this.util.isNumber(h) ? h + '%' : h : this.util.isNumber(h) ? h + contextImage.sizeUnit : (h || '');
1023
- const heightPercentage = /%$/.test(h);
1024
-
1025
- contextImage._container.style.width = this.util.isNumber(w) ? w + '%' : w;
1026
- contextImage._container.style.height = '';
1027
- contextImage._cover.style.width = '100%';
1028
- contextImage._cover.style.height = !heightPercentage ? '' : h;
1029
- contextImage._element.style.width = '100%';
1030
- contextImage._element.style.height = heightPercentage ? '' : h;
1031
- contextImage._element.style.maxWidth = '';
1032
-
1033
- if (contextImage._align === 'center') this.plugins.image.setAlign.call(this, null, null, null, null);
1034
-
1035
- contextImage._element.setAttribute('data-percentage', w + ',' + h);
1036
- this.plugins.resizing.setCaptionPosition.call(this, contextImage._element);
1037
-
1038
- // save current size
1039
- this.plugins.resizing._module_saveCurrentSize.call(this, contextImage);
1040
- },
1041
-
1042
- /**
1043
- * @Override resizing
1044
- */
1045
- cancelPercentAttr: function () {
1046
- const contextImage = this.context.image;
1047
-
1048
- contextImage._cover.style.width = '';
1049
- contextImage._cover.style.height = '';
1050
- contextImage._container.style.width = '';
1051
- contextImage._container.style.height = '';
1052
-
1053
- this.util.removeClass(contextImage._container, this.context.image._floatClassRegExp);
1054
- this.util.addClass(contextImage._container, '__se__float-' + contextImage._align);
1055
-
1056
- if (contextImage._align === 'center') this.plugins.image.setAlign.call(this, null, null, null, null);
1057
- },
1058
-
1059
- /**
1060
- * @Override resizing
1061
- */
1062
- setAlign: function (align, element, cover, container) {
1063
- const contextImage = this.context.image;
1064
-
1065
- if (!align) align = contextImage._align;
1066
- if (!element) element = contextImage._element;
1067
- if (!cover) cover = contextImage._cover;
1068
- if (!container) container = contextImage._container;
1069
-
1070
- if (/%$/.test(element.style.width) && align === 'center') {
1071
- container.style.minWidth = '100%';
1072
- cover.style.width = container.style.width;
1073
- } else {
1074
- container.style.minWidth = '';
1075
- cover.style.width = this.context.resizing._rotateVertical ? (element.style.height || element.offsetHeight) : ((!element.style.width || element.style.width === 'auto') ? '' : element.style.width || '100%');
1076
- }
1077
-
1078
- if (!this.util.hasClass(container, '__se__float-' + align)) {
1079
- this.util.removeClass(container, contextImage._floatClassRegExp);
1080
- this.util.addClass(container, '__se__float-' + align);
1081
- }
1082
-
1083
- element.setAttribute('data-align', align);
1084
- },
1085
-
1086
- /**
1087
- * @Override dialog
1088
- */
1089
- init: function () {
1090
- const contextImage = this.context.image;
1091
- if (contextImage.imgInputFile) contextImage.imgInputFile.value = '';
1092
- if (contextImage.imgUrlFile) contextImage._v_src._linkValue = contextImage.previewSrc.textContent = contextImage.imgUrlFile.value = '';
1093
- if (contextImage.imgInputFile && contextImage.imgUrlFile) {
1094
- contextImage.imgUrlFile.removeAttribute('disabled');
1095
- contextImage.previewSrc.style.textDecoration = '';
1096
- }
1097
-
1098
- contextImage.altText.value = '';
1099
- contextImage.modal.querySelector('input[name="suneditor_image_radio"][value="none"]').checked = true;
1100
- contextImage.captionCheckEl.checked = false;
1101
- contextImage._element = null;
1102
- this.plugins.image.openTab.call(this, 'init');
1103
-
1104
- if (contextImage._resizing) {
1105
- contextImage.inputX.value = this.options.imageWidth === contextImage._defaultSizeX ? '' : this.options.imageWidth;
1106
- contextImage.inputY.value = this.options.imageHeight === contextImage._defaultSizeY ? '' : this.options.imageHeight;
1107
- contextImage.proportion.checked = true;
1108
- contextImage._ratio = false;
1109
- contextImage._ratioX = 1;
1110
- contextImage._ratioY = 1;
1111
- }
1112
-
1113
- this.plugins.anchor.init.call(this, contextImage.anchorCtx);
1114
- }
1115
- };
1
+ /*
2
+ * wysiwyg web editor
3
+ *
4
+ * suneditor.js
5
+ * Copyright 2017 JiHong Lee.
6
+ * MIT license.
7
+ */
8
+ 'use strict';
9
+
10
+ import dialog from '../modules/dialog';
11
+ import anchor from '../modules/_anchor';
12
+ import component from '../modules/component';
13
+ import resizing from '../modules/resizing';
14
+ import fileManager from '../modules/fileManager';
15
+
16
+ export default {
17
+ name: 'image',
18
+ display: 'dialog',
19
+ add: function (core) {
20
+ core.addModule([dialog, anchor, component, resizing, fileManager]);
21
+
22
+ const options = core.options;
23
+ const context = core.context;
24
+ const contextImage = context.image = {
25
+ _infoList: [], // @Override fileManager
26
+ _infoIndex: 0, // @Override fileManager
27
+ _uploadFileLength: 0, // @Override fileManager
28
+ focusElement: null, // @Override dialog // This element has focus when the dialog is opened.
29
+ sizeUnit: options._imageSizeUnit,
30
+ _linkElement: '',
31
+ _altText: '',
32
+ _align: 'none',
33
+ _floatClassRegExp: '__se__float\\-[a-z]+',
34
+ _v_src: {_linkValue: ''},
35
+ svgDefaultSize: '30%',
36
+ base64RenderIndex: 0,
37
+ // @require @Override component
38
+ _element: null,
39
+ _cover: null,
40
+ _container: null,
41
+ // @Override resizing properties
42
+ inputX: null,
43
+ inputY: null,
44
+ _element_w: 1,
45
+ _element_h: 1,
46
+ _element_l: 0,
47
+ _element_t: 0,
48
+ _defaultSizeX: 'auto',
49
+ _defaultSizeY: 'auto',
50
+ _origin_w: options.imageWidth === 'auto' ? '' : options.imageWidth,
51
+ _origin_h: options.imageHeight === 'auto' ? '' : options.imageHeight,
52
+ _proportionChecked: true,
53
+ _resizing: options.imageResizing,
54
+ _resizeDotHide: !options.imageHeightShow,
55
+ _rotation: options.imageRotation,
56
+ _alignHide: !options.imageAlignShow,
57
+ _onlyPercentage: options.imageSizeOnlyPercentage,
58
+ _ratio: false,
59
+ _ratioX: 1,
60
+ _ratioY: 1,
61
+ _captionShow: true,
62
+ _captionChecked: false,
63
+ _caption: null,
64
+ captionCheckEl: null
65
+ };
66
+
67
+ /** image dialog */
68
+ let image_dialog = this.setDialog(core);
69
+ contextImage.modal = image_dialog;
70
+ contextImage.imgInputFile = image_dialog.querySelector('._se_image_file');
71
+ contextImage.imgUrlFile = image_dialog.querySelector('._se_image_url');
72
+ contextImage.focusElement = contextImage.imgInputFile || contextImage.imgUrlFile;
73
+ contextImage.altText = image_dialog.querySelector('._se_image_alt');
74
+ contextImage.captionCheckEl = image_dialog.querySelector('._se_image_check_caption');
75
+ contextImage.previewSrc = image_dialog.querySelector('._se_tab_content_image .se-link-preview');
76
+
77
+ /** add event listeners */
78
+ image_dialog.querySelector('.se-dialog-tabs').addEventListener('click', this.openTab.bind(core));
79
+ image_dialog.querySelector('form').addEventListener('submit', this.submit.bind(core));
80
+ if (contextImage.imgInputFile) image_dialog.querySelector('.se-file-remove').addEventListener('click', this._removeSelectedFiles.bind(contextImage.imgInputFile, contextImage.imgUrlFile, contextImage.previewSrc));
81
+ if (contextImage.imgUrlFile) contextImage.imgUrlFile.addEventListener('input', this._onLinkPreview.bind(contextImage.previewSrc, contextImage._v_src, options.linkProtocol));
82
+ if (contextImage.imgInputFile && contextImage.imgUrlFile) contextImage.imgInputFile.addEventListener('change', this._fileInputChange.bind(contextImage));
83
+
84
+ const imageGalleryButton = image_dialog.querySelector('.__se__gallery');
85
+ if (imageGalleryButton) imageGalleryButton.addEventListener('click', this._openGallery.bind(core));
86
+
87
+ contextImage.proportion = {};
88
+ contextImage.inputX = {};
89
+ contextImage.inputY = {};
90
+ if (options.imageResizing) {
91
+ contextImage.proportion = image_dialog.querySelector('._se_image_check_proportion');
92
+ contextImage.inputX = image_dialog.querySelector('._se_image_size_x');
93
+ contextImage.inputY = image_dialog.querySelector('._se_image_size_y');
94
+ contextImage.inputX.value = options.imageWidth;
95
+ contextImage.inputY.value = options.imageHeight;
96
+
97
+ contextImage.inputX.addEventListener('keyup', this.setInputSize.bind(core, 'x'));
98
+ contextImage.inputY.addEventListener('keyup', this.setInputSize.bind(core, 'y'));
99
+
100
+ contextImage.inputX.addEventListener('change', this.setRatio.bind(core));
101
+ contextImage.inputY.addEventListener('change', this.setRatio.bind(core));
102
+ contextImage.proportion.addEventListener('change', this.setRatio.bind(core));
103
+
104
+ image_dialog.querySelector('.se-dialog-btn-revert').addEventListener('click', this.sizeRevert.bind(core));
105
+ }
106
+
107
+ /** append html */
108
+ context.dialog.modal.appendChild(image_dialog);
109
+
110
+ /** link event */
111
+ core.plugins.anchor.initEvent.call(core, 'image', image_dialog.querySelector('._se_tab_content_url'));
112
+ contextImage.anchorCtx = core.context.anchor.caller.image;
113
+
114
+ /** empty memory */
115
+ image_dialog = null;
116
+ },
117
+
118
+ /** dialog */
119
+ setDialog: function (core) {
120
+ const option = core.options;
121
+ const lang = core.lang;
122
+ const dialog = core.util.createElement('DIV');
123
+
124
+ dialog.className = 'se-dialog-content se-dialog-image';
125
+ dialog.style.display = 'none';
126
+
127
+ let html = '' +
128
+ '<div class="se-dialog-header">' +
129
+ '<button type="button" data-command="close" class="se-btn se-dialog-close" class="close" title="' + lang.dialogBox.close + '" aria-label="' + lang.dialogBox.close + '">' +
130
+ core.icons.cancel +
131
+ '</button>' +
132
+ '<span class="se-modal-title">' + lang.dialogBox.imageBox.title + '</span>' +
133
+ '</div>' +
134
+ '<div class="se-dialog-tabs">' +
135
+ '<button type="button" class="_se_tab_link active" data-tab-link="image">' + lang.toolbar.image + '</button>' +
136
+ '<button type="button" class="_se_tab_link" data-tab-link="url">' + lang.toolbar.link + '</button>' +
137
+ '</div>' +
138
+ '<form method="post" enctype="multipart/form-data">' +
139
+ '<div class="_se_tab_content _se_tab_content_image">' +
140
+ '<div class="se-dialog-body"><div style="border-bottom: 1px dashed #ccc;">';
141
+
142
+ if (option.imageFileInput) {
143
+ html += '' +
144
+ '<div class="se-dialog-form">' +
145
+ '<label>' + lang.dialogBox.imageBox.file + '</label>' +
146
+ '<div class="se-dialog-form-files">' +
147
+ '<input class="se-input-form _se_image_file" type="file" accept="' + option.imageAccept + '"' + (option.imageMultipleFile ? ' multiple="multiple"' : '') + '/>' +
148
+ '<button type="button" class="se-btn se-dialog-files-edge-button se-file-remove" title="' + lang.controller.remove + '" aria-label="' + lang.controller.remove + '">' + core.icons.cancel + '</button>' +
149
+ '</div>' +
150
+ '</div>' ;
151
+ }
152
+
153
+ if (option.imageUrlInput) {
154
+ html += '' +
155
+ '<div class="se-dialog-form">' +
156
+ '<label>' + lang.dialogBox.imageBox.url + '</label>' +
157
+ '<div class="se-dialog-form-files">' +
158
+ '<input class="se-input-form se-input-url _se_image_url" type="text" />' +
159
+ ((option.imageGalleryUrl && core.plugins.imageGallery) ? '<button type="button" class="se-btn se-dialog-files-edge-button __se__gallery" title="' + lang.toolbar.imageGallery + '" aria-label="' + lang.toolbar.imageGallery + '">' + core.icons.image_gallery + '</button>' : '') +
160
+ '</div>' +
161
+ '<pre class="se-link-preview"></pre>' +
162
+ '</div>';
163
+ }
164
+
165
+ html += '</div>' +
166
+ '<div class="se-dialog-form">' +
167
+ '<label>' + lang.dialogBox.imageBox.altText + '</label><input class="se-input-form _se_image_alt" type="text" />' +
168
+ '</div>';
169
+
170
+ if (option.imageResizing) {
171
+ const onlyPercentage = option.imageSizeOnlyPercentage;
172
+ const onlyPercentDisplay = onlyPercentage ? ' style="display: none !important;"' : '';
173
+ const heightDisplay = !option.imageHeightShow ? ' style="display: none !important;"' : '';
174
+ html += '<div class="se-dialog-form">';
175
+ if (onlyPercentage || !option.imageHeightShow) {
176
+ html += '' +
177
+ '<div class="se-dialog-size-text">' +
178
+ '<label class="size-w">' + lang.dialogBox.size + '</label>' +
179
+ '</div>';
180
+ } else {
181
+ html += '' +
182
+ '<div class="se-dialog-size-text">' +
183
+ '<label class="size-w">' + lang.dialogBox.width + '</label>' +
184
+ '<label class="se-dialog-size-x">&nbsp;</label>' +
185
+ '<label class="size-h">' + lang.dialogBox.height + '</label>' +
186
+ '</div>';
187
+ }
188
+ html += '' +
189
+ '<input class="se-input-control _se_image_size_x" placeholder="auto"' + (onlyPercentage ? ' type="number" min="1"' : 'type="text"') + (onlyPercentage ? ' max="100"' : '') + ' />' +
190
+ '<label class="se-dialog-size-x"' + heightDisplay + '>' + (onlyPercentage ? '%' : 'x') + '</label>' +
191
+ '<input type="text" class="se-input-control _se_image_size_y" placeholder="auto"' + onlyPercentDisplay + (onlyPercentage ? ' max="100"' : '') + heightDisplay + '/>' +
192
+ '<label' + onlyPercentDisplay + heightDisplay + '><input type="checkbox" class="se-dialog-btn-check _se_image_check_proportion" checked/>&nbsp;' + lang.dialogBox.proportion + '</label>' +
193
+ '<button type="button" title="' + lang.dialogBox.revertButton + '" aria-label="' + lang.dialogBox.revertButton + '" class="se-btn se-dialog-btn-revert" style="float: right;">' + core.icons.revert + '</button>' +
194
+ '</div>' ;
195
+ }
196
+
197
+ html += '' +
198
+ '<div class="se-dialog-form se-dialog-form-footer">' +
199
+ '<label><input type="checkbox" class="se-dialog-btn-check _se_image_check_caption" />&nbsp;' + lang.dialogBox.caption + '</label>' +
200
+ '</div>' +
201
+ '</div>' +
202
+ '</div>' +
203
+ '<div class="_se_tab_content _se_tab_content_url" style="display: none">' +
204
+ core.context.anchor.forms.innerHTML +
205
+ '</div>' +
206
+ '<div class="se-dialog-footer">' +
207
+ '<div' + (option.imageAlignShow ? '' : ' style="display: none"') + '>' +
208
+ '<label><input type="radio" name="suneditor_image_radio" class="se-dialog-btn-radio" value="none" checked>' + lang.dialogBox.basic + '</label>' +
209
+ '<label><input type="radio" name="suneditor_image_radio" class="se-dialog-btn-radio" value="left">' + lang.dialogBox.left + '</label>' +
210
+ '<label><input type="radio" name="suneditor_image_radio" class="se-dialog-btn-radio" value="center">' + lang.dialogBox.center + '</label>' +
211
+ '<label><input type="radio" name="suneditor_image_radio" class="se-dialog-btn-radio" value="right">' + lang.dialogBox.right + '</label>' +
212
+ '</div>' +
213
+ '<button type="submit" class="se-btn-primary" title="' + lang.dialogBox.submitButton + '" aria-label="' + lang.dialogBox.submitButton + '"><span>' + lang.dialogBox.submitButton + '</span></button>' +
214
+ '</div>' +
215
+ '</form>';
216
+
217
+ dialog.innerHTML = html;
218
+
219
+ return dialog;
220
+ },
221
+
222
+ _fileInputChange: function () {
223
+ if (!this.imgInputFile.value) {
224
+ this.imgUrlFile.removeAttribute('disabled');
225
+ this.previewSrc.style.textDecoration = '';
226
+ } else {
227
+ this.imgUrlFile.setAttribute('disabled', true);
228
+ this.previewSrc.style.textDecoration = 'line-through';
229
+ }
230
+ },
231
+
232
+ _removeSelectedFiles: function (urlInput, previewSrc) {
233
+ this.value = '';
234
+ if (urlInput) {
235
+ urlInput.removeAttribute('disabled');
236
+ previewSrc.style.textDecoration = '';
237
+ }
238
+ },
239
+
240
+ _openGallery: function () {
241
+ this.callPlugin('imageGallery', this.plugins.imageGallery.open.bind(this, this.plugins.image._setUrlInput.bind(this.context.image)), null);
242
+ },
243
+
244
+ _setUrlInput: function (target) {
245
+ this.altText.value = target.alt;
246
+ this._v_src._linkValue = this.previewSrc.textContent = this.imgUrlFile.value = target.src;
247
+ this.imgUrlFile.focus();
248
+ },
249
+
250
+ _onLinkPreview: function (context, protocol, e) {
251
+ const value = e.target.value.trim();
252
+ context._linkValue = this.textContent = !value ? '' : (protocol && value.indexOf('://') === -1 && value.indexOf('#') !== 0) ? protocol + value : value.indexOf('://') === -1 ? '/' + value : value;
253
+ },
254
+
255
+ /**
256
+ * @Override @Required fileManager
257
+ */
258
+ fileTags: ['img'],
259
+
260
+ /**
261
+ * @Override core, fileManager, resizing
262
+ * @description It is called from core.selectComponent.
263
+ * @param {Element} element Target element
264
+ */
265
+ select: function (element) {
266
+ this.plugins.image.onModifyMode.call(this, element, this.plugins.resizing.call_controller_resize.call(this, element, 'image'));
267
+ },
268
+
269
+ /**
270
+ * @Override fileManager, resizing
271
+ */
272
+ destroy: function (element) {
273
+ const imageEl = element || this.context.image._element;
274
+ const imageContainer = this.util.getParentElement(imageEl, this.util.isMediaComponent) || imageEl;
275
+ const dataIndex = imageEl.getAttribute('data-index') * 1;
276
+ let focusEl = (imageContainer.previousElementSibling || imageContainer.nextElementSibling);
277
+
278
+ const emptyDiv = imageContainer.parentNode;
279
+ this.util.removeItem(imageContainer);
280
+ this.plugins.image.init.call(this);
281
+ this.controllersOff();
282
+
283
+ if (emptyDiv !== this.context.element.wysiwyg) this.util.removeItemAllParents(emptyDiv, function (current) { return current.childNodes.length === 0; }, null);
284
+
285
+ // focus
286
+ this.focusEdge(focusEl);
287
+
288
+ // event
289
+ this.plugins.fileManager.deleteInfo.call(this, 'image', dataIndex, this.functions.onImageUpload);
290
+
291
+ // history stack
292
+ this.history.push(false);
293
+ },
294
+
295
+ /**
296
+ * @Required @Override dialog
297
+ */
298
+ on: function (update) {
299
+ const contextImage = this.context.image;
300
+
301
+ if (!update) {
302
+ contextImage.inputX.value = contextImage._origin_w = this.options.imageWidth === contextImage._defaultSizeX ? '' : this.options.imageWidth;
303
+ contextImage.inputY.value = contextImage._origin_h = this.options.imageHeight === contextImage._defaultSizeY ? '' : this.options.imageHeight;
304
+ if (contextImage.imgInputFile && this.options.imageMultipleFile) contextImage.imgInputFile.setAttribute('multiple', 'multiple');
305
+ } else {
306
+ if (contextImage.imgInputFile && this.options.imageMultipleFile) contextImage.imgInputFile.removeAttribute('multiple');
307
+ }
308
+ this.plugins.anchor.on.call(this, contextImage.anchorCtx, update);
309
+ },
310
+
311
+ /**
312
+ * @Required @Override dialog
313
+ */
314
+ open: function () {
315
+ this.plugins.dialog.open.call(this, 'image', 'image' === this.currentControllerName);
316
+ },
317
+
318
+ openTab: function (e) {
319
+ const modal = this.context.image.modal;
320
+ const targetElement = (e === 'init' ? modal.querySelector('._se_tab_link') : e.target);
321
+
322
+ if (!/^BUTTON$/i.test(targetElement.tagName)) {
323
+ return false;
324
+ }
325
+
326
+ // Declare all variables
327
+ const tabName = targetElement.getAttribute('data-tab-link');
328
+ const contentClassName = '_se_tab_content';
329
+ let i, tabContent, tabLinks;
330
+
331
+ // Get all elements with class="tabcontent" and hide them
332
+ tabContent = modal.getElementsByClassName(contentClassName);
333
+ for (i = 0; i < tabContent.length; i++) {
334
+ tabContent[i].style.display = 'none';
335
+ }
336
+
337
+ // Get all elements with class="tablinks" and remove the class "active"
338
+ tabLinks = modal.getElementsByClassName('_se_tab_link');
339
+ for (i = 0; i < tabLinks.length; i++) {
340
+ this.util.removeClass(tabLinks[i], 'active');
341
+ }
342
+
343
+ // Show the current tab, and add an "active" class to the button that opened the tab
344
+ modal.querySelector('.' + contentClassName + '_' + tabName).style.display = 'block';
345
+ this.util.addClass(targetElement, 'active');
346
+
347
+ // focus
348
+ if (tabName === 'image' && this.context.image.focusElement) {
349
+ this.context.image.focusElement.focus();
350
+ } else if (tabName === 'url') {
351
+ this.context.anchor.caller.image.urlInput.focus();
352
+ }
353
+
354
+ return false;
355
+ },
356
+
357
+ submit: function (e) {
358
+ const contextImage = this.context.image;
359
+ const imagePlugin = this.plugins.image;
360
+
361
+ e.preventDefault();
362
+ e.stopPropagation();
363
+
364
+ contextImage._altText = contextImage.altText.value;
365
+ contextImage._align = contextImage.modal.querySelector('input[name="suneditor_image_radio"]:checked').value;
366
+ contextImage._captionChecked = contextImage.captionCheckEl.checked;
367
+ if (contextImage._resizing) contextImage._proportionChecked = contextImage.proportion.checked;
368
+
369
+ try {
370
+ if (this.context.dialog.updateModal) {
371
+ imagePlugin.update_image.call(this, false, true, false);
372
+ }
373
+
374
+ if (contextImage.imgInputFile && contextImage.imgInputFile.files.length > 0) {
375
+ this.showLoading();
376
+ imagePlugin.submitAction.call(this, this.context.image.imgInputFile.files);
377
+ } else if (contextImage.imgUrlFile && contextImage._v_src._linkValue.length > 0) {
378
+ this.showLoading();
379
+ imagePlugin.onRender_imgUrl.call(this, contextImage._v_src._linkValue);
380
+ }
381
+ } catch (error) {
382
+ this.closeLoading();
383
+ throw Error('[SUNEDITOR.image.submit.fail] cause : "' + error.message + '"');
384
+ } finally {
385
+ this.plugins.dialog.close.call(this);
386
+ }
387
+
388
+ return false;
389
+ },
390
+
391
+ submitAction: function (fileList) {
392
+ if (fileList.length === 0) return;
393
+
394
+ let fileSize = 0;
395
+ let files = [];
396
+ for (let i = 0, len = fileList.length; i < len; i++) {
397
+ if (/image/i.test(fileList[i].type)) {
398
+ files.push(fileList[i]);
399
+ fileSize += fileList[i].size;
400
+ }
401
+ }
402
+
403
+ const limitSize = this.options.imageUploadSizeLimit;
404
+ if (limitSize > 0) {
405
+ let infoSize = 0;
406
+ const imagesInfo = this.context.image._infoList;
407
+ for (let i = 0, len = imagesInfo.length; i < len; i++) {
408
+ infoSize += imagesInfo[i].size * 1;
409
+ }
410
+
411
+ if ((fileSize + infoSize) > limitSize) {
412
+ this.closeLoading();
413
+ const err = '[SUNEDITOR.imageUpload.fail] Size of uploadable total images: ' + (limitSize/1000) + 'KB';
414
+ if (typeof this.functions.onImageUploadError !== 'function' || this.functions.onImageUploadError(err, { 'limitSize': limitSize, 'currentSize': infoSize, 'uploadSize': fileSize }, this)) {
415
+ this.functions.noticeOpen(err);
416
+ }
417
+ return;
418
+ }
419
+ }
420
+
421
+ const contextImage = this.context.image;
422
+ contextImage._uploadFileLength = files.length;
423
+
424
+ const anchor = this.plugins.anchor.createAnchor.call(this, contextImage.anchorCtx, true);
425
+ const info = {
426
+ anchor: anchor,
427
+ inputWidth: contextImage.inputX.value,
428
+ inputHeight: contextImage.inputY.value,
429
+ align: contextImage._align,
430
+ isUpdate: this.context.dialog.updateModal,
431
+ alt: contextImage._altText,
432
+ element: contextImage._element
433
+ };
434
+
435
+ if (typeof this.functions.onImageUploadBefore === 'function') {
436
+ const result = this.functions.onImageUploadBefore(files, info, this, function (data) {
437
+ if (data && this._w.Array.isArray(data.result)) {
438
+ this.plugins.image.register.call(this, info, data);
439
+ } else {
440
+ this.plugins.image.upload.call(this, info, data);
441
+ }
442
+ }.bind(this));
443
+
444
+ if (typeof result === 'undefined') return;
445
+ if (!result) {
446
+ this.closeLoading();
447
+ return;
448
+ }
449
+ if (this._w.Array.isArray(result) && result.length > 0) files = result;
450
+ }
451
+
452
+ this.plugins.image.upload.call(this, info, files);
453
+ },
454
+
455
+ error: function (message, response) {
456
+ this.closeLoading();
457
+ if (typeof this.functions.onImageUploadError !== 'function' || this.functions.onImageUploadError(message, response, this)) {
458
+ this.functions.noticeOpen(message);
459
+ throw Error('[SUNEDITOR.plugin.image.error] response: ' + message);
460
+ }
461
+ },
462
+
463
+ upload: function (info, files) {
464
+ if (!files) {
465
+ this.closeLoading();
466
+ return;
467
+ }
468
+ if (typeof files === 'string') {
469
+ this.plugins.image.error.call(this, files, null);
470
+ return;
471
+ }
472
+
473
+ const imageUploadUrl = this.options.imageUploadUrl;
474
+ const filesLen = this.context.dialog.updateModal ? 1 : files.length;
475
+
476
+ // server upload
477
+ if (typeof imageUploadUrl === 'string' && imageUploadUrl.length > 0) {
478
+ const formData = new FormData();
479
+ for (let i = 0; i < filesLen; i++) {
480
+ formData.append('file-' + i, files[i]);
481
+ }
482
+ this.plugins.fileManager.upload.call(this, imageUploadUrl, this.options.imageUploadHeader, formData, this.plugins.image.callBack_imgUpload.bind(this, info), this.functions.onImageUploadError);
483
+ } else { // base64
484
+ this.plugins.image.setup_reader.call(this, files, info.anchor, info.inputWidth, info.inputHeight, info.align, info.alt, filesLen, info.isUpdate);
485
+ }
486
+ },
487
+
488
+ callBack_imgUpload: function (info, xmlHttp) {
489
+ if (typeof this.functions.imageUploadHandler === 'function') {
490
+ this.functions.imageUploadHandler(xmlHttp, info, this);
491
+ } else {
492
+ const response = JSON.parse(xmlHttp.responseText);
493
+ if (response.errorMessage) {
494
+ this.plugins.image.error.call(this, response.errorMessage, response);
495
+ } else {
496
+ this.plugins.image.register.call(this, info, response);
497
+ }
498
+ }
499
+ },
500
+
501
+ register: function (info, response) {
502
+ const fileList = response.result;
503
+
504
+ for (let i = 0, len = fileList.length, file; i < len; i++) {
505
+ file = { name: fileList[i].name, size: fileList[i].size };
506
+ if (info.isUpdate) {
507
+ this.plugins.image.update_src.call(this, fileList[i].url, info.element, file);
508
+ break;
509
+ } else {
510
+ this.plugins.image.create_image.call(this, fileList[i].url, info.anchor, info.inputWidth, info.inputHeight, info.align, file, info.alt);
511
+ }
512
+ }
513
+
514
+ this.closeLoading();
515
+ },
516
+
517
+ setup_reader: function (files, anchor, width, height, align, alt, filesLen, isUpdate) {
518
+ try {
519
+ this.context.image.base64RenderIndex = filesLen;
520
+ const wFileReader = this._w.FileReader;
521
+ const filesStack = [filesLen];
522
+ this.context.image.inputX.value = width;
523
+ this.context.image.inputY.value = height;
524
+
525
+ for (let i = 0, reader, file; i < filesLen; i++) {
526
+ reader = new wFileReader();
527
+ file = files[i];
528
+
529
+ reader.onload = function (reader, update, updateElement, file, index) {
530
+ filesStack[index] = { result: reader.result, file: file };
531
+
532
+ if (--this.context.image.base64RenderIndex === 0) {
533
+ this.plugins.image.onRender_imgBase64.call(this, update, filesStack, updateElement, anchor, width, height, align, alt);
534
+ this.closeLoading();
535
+ }
536
+ }.bind(this, reader, isUpdate, this.context.image._element, file, i);
537
+
538
+ reader.readAsDataURL(file);
539
+ }
540
+ } catch (e) {
541
+ this.closeLoading();
542
+ throw Error('[SUNEDITOR.image.setup_reader.fail] cause : "' + e.message + '"');
543
+ }
544
+ },
545
+
546
+ onRender_imgBase64: function (update, filesStack, updateElement, anchor, width, height, align, alt) {
547
+ const updateMethod = this.plugins.image.update_src;
548
+ const createMethod = this.plugins.image.create_image;
549
+
550
+ for (let i = 0, len = filesStack.length; i < len; i++) {
551
+ if (update) {
552
+ this.context.image._element.setAttribute('data-file-name', filesStack[i].file.name);
553
+ this.context.image._element.setAttribute('data-file-size', filesStack[i].file.size);
554
+ updateMethod.call(this, filesStack[i].result, updateElement, filesStack[i].file);
555
+ } else {
556
+ createMethod.call(this, filesStack[i].result, anchor, width, height, align, filesStack[i].file, alt);
557
+ }
558
+ }
559
+ },
560
+
561
+ onRender_imgUrl: function (url) {
562
+ if (!url) url = this.context.image._v_src._linkValue;
563
+ if (!url) return false;
564
+ const contextImage = this.context.image;
565
+
566
+ try {
567
+ const file = {name: url.split('/').pop(), size: 0};
568
+ if (this.context.dialog.updateModal) this.plugins.image.update_src.call(this, url, contextImage._element, file);
569
+ else this.plugins.image.create_image.call(this, url, this.plugins.anchor.createAnchor.call(this, contextImage.anchorCtx, true), contextImage.inputX.value, contextImage.inputY.value, contextImage._align, file, contextImage._altText);
570
+ } catch (e) {
571
+ throw Error('[SUNEDITOR.image.URLRendering.fail] cause : "' + e.message + '"');
572
+ } finally {
573
+ this.closeLoading();
574
+ }
575
+ },
576
+
577
+ onRender_link: function (imgTag, anchor) {
578
+ if (anchor) {
579
+ anchor.setAttribute('data-image-link', 'image');
580
+ imgTag.setAttribute('data-image-link', anchor.href);
581
+ anchor.appendChild(imgTag);
582
+ return anchor;
583
+ }
584
+
585
+ return imgTag;
586
+ },
587
+
588
+ /**
589
+ * @Override resizing
590
+ * @param {String} xy 'x': width, 'y': height
591
+ * @param {KeyboardEvent} e Event object
592
+ */
593
+ setInputSize: function (xy, e) {
594
+ if (e && e.keyCode === 32) {
595
+ e.preventDefault();
596
+ return;
597
+ }
598
+
599
+ this.plugins.resizing._module_setInputSize.call(this, this.context.image, xy);
600
+ },
601
+
602
+ /**
603
+ * @Override resizing
604
+ */
605
+ setRatio: function () {
606
+ this.plugins.resizing._module_setRatio.call(this, this.context.image);
607
+ },
608
+
609
+ /**
610
+ * @Override fileManager
611
+ */
612
+ checkFileInfo: function () {
613
+ const imagePlugin = this.plugins.image;
614
+ const contextImage = this.context.image;
615
+
616
+ const modifyHandler = function (tag) {
617
+ imagePlugin.onModifyMode.call(this, tag, null);
618
+ imagePlugin.openModify.call(this, true);
619
+ // get size
620
+ contextImage.inputX.value = contextImage._origin_w;
621
+ contextImage.inputY.value = contextImage._origin_h;
622
+ // get align
623
+ const format = this.util.getFormatElement(tag);
624
+ if (format) contextImage._align = format.style.textAlign || format.style.float;
625
+ // link
626
+ if (this.util.isAnchor(tag.parentNode) && !contextImage.anchorCtx.linkValue) contextImage.anchorCtx.linkValue = ' ';
627
+
628
+ imagePlugin.update_image.call(this, true, false, true);
629
+ imagePlugin.init.call(this);
630
+ }.bind(this);
631
+
632
+ this.plugins.fileManager.checkInfo.call(this, 'image', ['img'], this.functions.onImageUpload, modifyHandler, true);
633
+ },
634
+
635
+ /**
636
+ * @Override fileManager
637
+ */
638
+ resetFileInfo: function () {
639
+ this.plugins.fileManager.resetInfo.call(this, 'image', this.functions.onImageUpload);
640
+ },
641
+
642
+ create_image: function (src, anchor, width, height, align, file, alt) {
643
+ const imagePlugin = this.plugins.image;
644
+ const contextImage = this.context.image;
645
+ this.context.resizing._resize_plugin = 'image';
646
+
647
+ let oImg = this.util.createElement('IMG');
648
+ oImg.src = src;
649
+ oImg.alt = alt;
650
+ oImg.setAttribute('data-rotate', '0');
651
+ anchor = imagePlugin.onRender_link.call(this, oImg, anchor ? anchor.cloneNode(false) : null);
652
+
653
+ if (contextImage._resizing) {
654
+ oImg.setAttribute('data-proportion', contextImage._proportionChecked);
655
+ }
656
+
657
+ const cover = this.plugins.component.set_cover.call(this, anchor);
658
+ const container = this.plugins.component.set_container.call(this, cover, 'se-image-container');
659
+
660
+ // caption
661
+ if (contextImage._captionChecked) {
662
+ contextImage._caption = this.plugins.component.create_caption.call(this);
663
+ cover.appendChild(contextImage._caption);
664
+ }
665
+
666
+ contextImage._element = oImg;
667
+ contextImage._cover = cover;
668
+ contextImage._container = container;
669
+
670
+ // set size
671
+ imagePlugin.applySize.call(this, width, height);
672
+
673
+ // align
674
+ imagePlugin.setAlign.call(this, align, oImg, cover, container);
675
+
676
+ oImg.onload = imagePlugin._image_create_onload.bind(this, oImg, contextImage.svgDefaultSize, container);
677
+ if (this.insertComponent(container, true, true, true)) this.plugins.fileManager.setInfo.call(this, 'image', oImg, this.functions.onImageUpload, file, true);
678
+ this.context.resizing._resize_plugin = '';
679
+ },
680
+
681
+ _image_create_onload: function (oImg, svgDefaultSize, container) {
682
+ // svg exception handling
683
+ if (oImg.offsetWidth === 0) this.plugins.image.applySize.call(this, svgDefaultSize, '');
684
+ if (this.options.mediaAutoSelect) {
685
+ this.selectComponent(oImg, 'image');
686
+ } else {
687
+ const line = this.appendFormatTag(container, null);
688
+ if (line) this.setRange(line, 0, line, 0);
689
+ }
690
+ },
691
+
692
+ update_image: function (init, openController, notHistoryPush) {
693
+ const contextImage = this.context.image;
694
+ let imageEl = contextImage._element;
695
+ let cover = contextImage._cover;
696
+ let container = contextImage._container;
697
+ let isNewContainer = false;
698
+
699
+ if (cover === null) {
700
+ isNewContainer = true;
701
+ imageEl = contextImage._element.cloneNode(true);
702
+ cover = this.plugins.component.set_cover.call(this, imageEl);
703
+ }
704
+
705
+ if (container === null) {
706
+ cover = cover.cloneNode(true);
707
+ imageEl = cover.querySelector('img');
708
+ isNewContainer = true;
709
+ container = this.plugins.component.set_container.call(this, cover, 'se-image-container');
710
+ } else if (isNewContainer) {
711
+ container.innerHTML = '';
712
+ container.appendChild(cover);
713
+ contextImage._cover = cover;
714
+ contextImage._element = imageEl;
715
+ isNewContainer = false;
716
+ }
717
+
718
+ // check size
719
+ let changeSize;
720
+ const x = this.util.isNumber(contextImage.inputX.value) ? contextImage.inputX.value + contextImage.sizeUnit : contextImage.inputX.value;
721
+ const y = this.util.isNumber(contextImage.inputY.value) ? contextImage.inputY.value + contextImage.sizeUnit : contextImage.inputY.value;
722
+ if (/%$/.test(imageEl.style.width)) {
723
+ changeSize = x !== container.style.width || y !== container.style.height;
724
+ } else {
725
+ changeSize = x !== imageEl.style.width || y !== imageEl.style.height;
726
+ }
727
+
728
+ // alt
729
+ imageEl.alt = contextImage._altText;
730
+
731
+ // caption
732
+ let modifiedCaption = false;
733
+ if (contextImage._captionChecked) {
734
+ if (!contextImage._caption) {
735
+ contextImage._caption = this.plugins.component.create_caption.call(this);
736
+ cover.appendChild(contextImage._caption);
737
+ modifiedCaption = true;
738
+ }
739
+ } else {
740
+ if (contextImage._caption) {
741
+ this.util.removeItem(contextImage._caption);
742
+ contextImage._caption = null;
743
+ modifiedCaption = true;
744
+ }
745
+ }
746
+
747
+ // link
748
+ let isNewAnchor = null;
749
+ const anchor = this.plugins.anchor.createAnchor.call(this, contextImage.anchorCtx, true);
750
+ if (anchor) {
751
+ if (contextImage._linkElement !== anchor || (isNewContainer && !container.contains(anchor))) {
752
+ contextImage._linkElement = anchor.cloneNode(false);
753
+ cover.insertBefore(this.plugins.image.onRender_link.call(this, imageEl, contextImage._linkElement), contextImage._caption);
754
+ isNewAnchor = contextImage._element;
755
+ } else {
756
+ contextImage._linkElement.setAttribute('data-image-link', 'image');
757
+ }
758
+ } else if (contextImage._linkElement !== null) {
759
+ const imageElement = imageEl;
760
+ imageElement.setAttribute('data-image-link', '');
761
+ if (cover.contains(contextImage._linkElement)) {
762
+ const newEl = imageElement.cloneNode(true);
763
+ cover.removeChild(contextImage._linkElement);
764
+ cover.insertBefore(newEl, contextImage._caption);
765
+ contextImage._element = imageEl = newEl;
766
+ }
767
+ }
768
+
769
+ let existElement = null;
770
+ if (isNewContainer) {
771
+ existElement = (this.util.isRangeFormatElement(contextImage._element.parentNode) || this.util.isWysiwygDiv(contextImage._element.parentNode)) ?
772
+ contextImage._element :
773
+ this.util.isAnchor(contextImage._element.parentNode) ? contextImage._element.parentNode : this.util.getFormatElement(contextImage._element) || contextImage._element;
774
+
775
+ if (this.util.getParentElement(contextImage._element, this.util.isNotCheckingNode)) {
776
+ existElement = isNewAnchor ? anchor : contextImage._element;
777
+ existElement.parentNode.replaceChild(container, existElement);
778
+ } else if (this.util.isListCell(existElement)) {
779
+ const refer = this.util.getParentElement(contextImage._element, function (current) { return current.parentNode === existElement; });
780
+ existElement.insertBefore(container, refer);
781
+ this.util.removeItem(contextImage._element);
782
+ this.util.removeEmptyNode(refer, null, true);
783
+ } else if (this.util.isFormatElement(existElement)) {
784
+ const refer = this.util.getParentElement(contextImage._element, function (current) { return current.parentNode === existElement; });
785
+ existElement = this.util.splitElement(existElement, refer);
786
+ existElement.parentNode.insertBefore(container, existElement);
787
+ this.util.removeItem(contextImage._element);
788
+ this.util.removeEmptyNode(existElement, null, true);
789
+ if (existElement.children.length === 0) existElement.innerHTML = this.util.htmlRemoveWhiteSpace(existElement.innerHTML);
790
+ } else {
791
+ if (this.util.isFormatElement(existElement.parentNode)) {
792
+ const formats = existElement.parentNode;
793
+ formats.parentNode.insertBefore(container, existElement.previousSibling ? formats.nextElementSibling : formats);
794
+ if (contextImage.__updateTags.map(function (current) { return existElement.contains(current); }).length === 0) this.util.removeItem(existElement);
795
+ } else {
796
+ existElement.parentNode.replaceChild(container, existElement);
797
+ }
798
+ }
799
+
800
+ imageEl = container.querySelector('img');
801
+
802
+ contextImage._element = imageEl;
803
+ contextImage._cover = cover;
804
+ contextImage._container = container;
805
+ }
806
+
807
+ if (isNewAnchor) {
808
+ if (!isNewContainer) {
809
+ this.util.removeItem(anchor);
810
+ } else {
811
+ this.util.removeItem(isNewAnchor);
812
+ if (this.util.getListChildren(anchor, function (current) { return /IMG/i.test(current.tagName); }).length === 0) {
813
+ this.util.removeItem(anchor);
814
+ }
815
+ }
816
+ }
817
+
818
+ // transform
819
+ if (modifiedCaption || (!contextImage._onlyPercentage && changeSize)) {
820
+ if (!init && (/\d+/.test(imageEl.style.height) || (this.context.resizing._rotateVertical && contextImage._captionChecked))) {
821
+ if (/%$/.test(contextImage.inputX.value) || /%$/.test(contextImage.inputY.value)) {
822
+ this.plugins.resizing.resetTransform.call(this, imageEl);
823
+ } else {
824
+ this.plugins.resizing.setTransformSize.call(this, imageEl, this.util.getNumber(contextImage.inputX.value, 0), this.util.getNumber(contextImage.inputY.value, 0));
825
+ }
826
+ }
827
+ }
828
+
829
+ // size
830
+ if (contextImage._resizing) {
831
+ imageEl.setAttribute('data-proportion', contextImage._proportionChecked);
832
+ if (changeSize) {
833
+ this.plugins.image.applySize.call(this);
834
+ }
835
+ }
836
+
837
+ // align
838
+ this.plugins.image.setAlign.call(this, null, imageEl, null, null);
839
+
840
+ // set imagesInfo
841
+ if (init) {
842
+ this.plugins.fileManager.setInfo.call(this, 'image', imageEl, this.functions.onImageUpload, null, true);
843
+ }
844
+
845
+ if (openController) {
846
+ this.selectComponent(imageEl, 'image');
847
+ }
848
+
849
+ // history stack
850
+ if (!notHistoryPush) this.history.push(false);
851
+ },
852
+
853
+ update_src: function (src, element, file) {
854
+ element.src = src;
855
+ this._w.setTimeout(this.plugins.fileManager.setInfo.bind(this, 'image', element, this.functions.onImageUpload, file, true));
856
+ this.selectComponent(element, 'image');
857
+ },
858
+
859
+ /**
860
+ * @Required @Override fileManager, resizing
861
+ */
862
+ onModifyMode: function (element, size) {
863
+ if (!element) return;
864
+
865
+ const contextImage = this.context.image;
866
+ contextImage._linkElement = contextImage.anchorCtx.linkAnchor = this.util.isAnchor(element.parentNode) ? element.parentNode : null;
867
+ contextImage._element = element;
868
+ contextImage._cover = this.util.getParentElement(element, 'FIGURE');
869
+ contextImage._container = this.util.getParentElement(element, this.util.isMediaComponent);
870
+ contextImage._caption = this.util.getChildElement(contextImage._cover, 'FIGCAPTION');
871
+ contextImage._align = element.getAttribute('data-align') || element.style.float || 'none';
872
+ element.style.float = '';
873
+ this.plugins.anchor.setCtx(contextImage._linkElement, contextImage.anchorCtx);
874
+
875
+ if (size) {
876
+ contextImage._element_w = size.w;
877
+ contextImage._element_h = size.h;
878
+ contextImage._element_t = size.t;
879
+ contextImage._element_l = size.l;
880
+ }
881
+
882
+ let userSize = contextImage._element.getAttribute('data-size') || contextImage._element.getAttribute('data-origin');
883
+ let w, h;
884
+ if (userSize) {
885
+ userSize = userSize.split(',');
886
+ w = userSize[0];
887
+ h = userSize[1];
888
+ } else if (size) {
889
+ w = size.w;
890
+ h = size.h;
891
+ }
892
+
893
+ contextImage._origin_w = w || element.style.width || element.width || '';
894
+ contextImage._origin_h = h || element.style.height || element.height || '';
895
+ },
896
+
897
+ /**
898
+ * @Required @Override fileManager, resizing
899
+ */
900
+ openModify: function (notOpen) {
901
+ const contextImage = this.context.image;
902
+ if (contextImage.imgUrlFile) {
903
+ contextImage._v_src._linkValue = contextImage.previewSrc.textContent = contextImage.imgUrlFile.value = contextImage._element.src;
904
+ }
905
+ contextImage._altText = contextImage.altText.value = contextImage._element.alt;
906
+ (contextImage.modal.querySelector('input[name="suneditor_image_radio"][value="' + contextImage._align + '"]') || contextImage.modal.querySelector('input[name="suneditor_image_radio"][value="none"]')).checked = true;
907
+ contextImage._align = contextImage.modal.querySelector('input[name="suneditor_image_radio"]:checked').value;
908
+ contextImage._captionChecked = contextImage.captionCheckEl.checked = !!contextImage._caption;
909
+
910
+ if (contextImage._resizing) {
911
+ this.plugins.resizing._module_setModifyInputSize.call(this, contextImage, this.plugins.image);
912
+ }
913
+
914
+ if (!notOpen) this.plugins.dialog.open.call(this, 'image', true);
915
+ },
916
+
917
+ /**
918
+ * @Override fileManager
919
+ */
920
+ applySize: function (w, h) {
921
+ const contextImage = this.context.image;
922
+
923
+ if (!w) w = contextImage.inputX.value || this.options.imageWidth;
924
+ if (!h) h = contextImage.inputY.value || this.options.imageHeight;
925
+
926
+ if ((contextImage._onlyPercentage && !!w) || /%$/.test(w)) {
927
+ this.plugins.image.setPercentSize.call(this, w, h);
928
+ return true;
929
+ } else if ((!w || w === 'auto') && (!h || h === 'auto')) {
930
+ this.plugins.image.setAutoSize.call(this);
931
+ } else {
932
+ this.plugins.image.setSize.call(this, w, h, false);
933
+ }
934
+
935
+ return false;
936
+ },
937
+
938
+ /**
939
+ * @Override resizing
940
+ */
941
+ sizeRevert: function () {
942
+ this.plugins.resizing._module_sizeRevert.call(this, this.context.image);
943
+ },
944
+
945
+ /**
946
+ * @Override resizing
947
+ */
948
+ setSize: function (w, h, notResetPercentage, direction) {
949
+ const contextImage = this.context.image;
950
+ const onlyW = /^(rw|lw)$/.test(direction) && /\d+/.test(contextImage._element.style.height);
951
+ const onlyH = /^(th|bh)$/.test(direction) && /\d+/.test(contextImage._element.style.width);
952
+
953
+ if (!onlyH) {
954
+ contextImage._element.style.width = this.util.isNumber(w) ? w + contextImage.sizeUnit : w;
955
+ this.plugins.image.cancelPercentAttr.call(this);
956
+ }
957
+ if (!onlyW) {
958
+ contextImage._element.style.height = this.util.isNumber(h) ? h + contextImage.sizeUnit : /%$/.test(h) ? '' : h;
959
+ }
960
+
961
+ if (contextImage._align === 'center') this.plugins.image.setAlign.call(this, null, null, null, null);
962
+ if (!notResetPercentage) contextImage._element.removeAttribute('data-percentage');
963
+
964
+ // save current size
965
+ this.plugins.resizing._module_saveCurrentSize.call(this, contextImage);
966
+ },
967
+
968
+ /**
969
+ * @Override resizing
970
+ */
971
+ setAutoSize: function () {
972
+ const contextImage = this.context.image;
973
+
974
+ if (contextImage._caption) contextImage._caption.style.marginTop = '';
975
+ this.plugins.resizing.resetTransform.call(this, contextImage._element);
976
+ this.plugins.image.cancelPercentAttr.call(this);
977
+
978
+ contextImage._element.style.maxWidth = '';
979
+ contextImage._element.style.width = '';
980
+ contextImage._element.style.height = '';
981
+ contextImage._cover.style.width = '';
982
+ contextImage._cover.style.height = '';
983
+
984
+ this.plugins.image.setAlign.call(this, null, null, null, null);
985
+ contextImage._element.setAttribute('data-percentage', 'auto,auto');
986
+
987
+ // save current size
988
+ this.plugins.resizing._module_saveCurrentSize.call(this, contextImage);
989
+ },
990
+
991
+ /**
992
+ * @Override resizing
993
+ */
994
+ setOriginSize: function () {
995
+ const contextImage = this.context.image;
996
+ contextImage._element.removeAttribute('data-percentage');
997
+
998
+ this.plugins.resizing.resetTransform.call(this, contextImage._element);
999
+ this.plugins.image.cancelPercentAttr.call(this);
1000
+
1001
+ const originSize = (contextImage._element.getAttribute('data-origin') || '').split(',');
1002
+ const w = originSize[0];
1003
+ const h = originSize[1];
1004
+
1005
+ if (originSize) {
1006
+ if (contextImage._onlyPercentage || (/%$/.test(w) && (/%$/.test(h) || !/\d/.test(h)))) {
1007
+ this.plugins.image.setPercentSize.call(this, w, h);
1008
+ } else {
1009
+ this.plugins.image.setSize.call(this, w, h);
1010
+ }
1011
+
1012
+ // save current size
1013
+ this.plugins.resizing._module_saveCurrentSize.call(this, contextImage);
1014
+ }
1015
+ },
1016
+
1017
+ /**
1018
+ * @Override resizing
1019
+ */
1020
+ setPercentSize: function (w, h) {
1021
+ const contextImage = this.context.image;
1022
+ h = !!h && !/%$/.test(h) && !this.util.getNumber(h, 0) ? this.util.isNumber(h) ? h + '%' : h : this.util.isNumber(h) ? h + contextImage.sizeUnit : (h || '');
1023
+ const heightPercentage = /%$/.test(h);
1024
+
1025
+ contextImage._container.style.width = this.util.isNumber(w) ? w + '%' : w;
1026
+ contextImage._container.style.height = '';
1027
+ contextImage._cover.style.width = '100%';
1028
+ contextImage._cover.style.height = !heightPercentage ? '' : h;
1029
+ contextImage._element.style.width = '100%';
1030
+ contextImage._element.style.height = heightPercentage ? '' : h;
1031
+ contextImage._element.style.maxWidth = '';
1032
+
1033
+ if (contextImage._align === 'center') this.plugins.image.setAlign.call(this, null, null, null, null);
1034
+
1035
+ contextImage._element.setAttribute('data-percentage', w + ',' + h);
1036
+ this.plugins.resizing.setCaptionPosition.call(this, contextImage._element);
1037
+
1038
+ // save current size
1039
+ this.plugins.resizing._module_saveCurrentSize.call(this, contextImage);
1040
+ },
1041
+
1042
+ /**
1043
+ * @Override resizing
1044
+ */
1045
+ cancelPercentAttr: function () {
1046
+ const contextImage = this.context.image;
1047
+
1048
+ contextImage._cover.style.width = '';
1049
+ contextImage._cover.style.height = '';
1050
+ contextImage._container.style.width = '';
1051
+ contextImage._container.style.height = '';
1052
+
1053
+ this.util.removeClass(contextImage._container, this.context.image._floatClassRegExp);
1054
+ this.util.addClass(contextImage._container, '__se__float-' + contextImage._align);
1055
+
1056
+ if (contextImage._align === 'center') this.plugins.image.setAlign.call(this, null, null, null, null);
1057
+ },
1058
+
1059
+ /**
1060
+ * @Override resizing
1061
+ */
1062
+ setAlign: function (align, element, cover, container) {
1063
+ const contextImage = this.context.image;
1064
+
1065
+ if (!align) align = contextImage._align;
1066
+ if (!element) element = contextImage._element;
1067
+ if (!cover) cover = contextImage._cover;
1068
+ if (!container) container = contextImage._container;
1069
+
1070
+ if (/%$/.test(element.style.width) && align === 'center') {
1071
+ container.style.minWidth = '100%';
1072
+ cover.style.width = container.style.width;
1073
+ } else {
1074
+ container.style.minWidth = '';
1075
+ cover.style.width = this.context.resizing._rotateVertical ? (element.style.height || element.offsetHeight) : ((!element.style.width || element.style.width === 'auto') ? '' : element.style.width || '100%');
1076
+ }
1077
+
1078
+ if (!this.util.hasClass(container, '__se__float-' + align)) {
1079
+ this.util.removeClass(container, contextImage._floatClassRegExp);
1080
+ this.util.addClass(container, '__se__float-' + align);
1081
+ }
1082
+
1083
+ element.setAttribute('data-align', align);
1084
+ },
1085
+
1086
+ /**
1087
+ * @Override dialog
1088
+ */
1089
+ init: function () {
1090
+ const contextImage = this.context.image;
1091
+ if (contextImage.imgInputFile) contextImage.imgInputFile.value = '';
1092
+ if (contextImage.imgUrlFile) contextImage._v_src._linkValue = contextImage.previewSrc.textContent = contextImage.imgUrlFile.value = '';
1093
+ if (contextImage.imgInputFile && contextImage.imgUrlFile) {
1094
+ contextImage.imgUrlFile.removeAttribute('disabled');
1095
+ contextImage.previewSrc.style.textDecoration = '';
1096
+ }
1097
+
1098
+ contextImage.altText.value = '';
1099
+ contextImage.modal.querySelector('input[name="suneditor_image_radio"][value="none"]').checked = true;
1100
+ contextImage.captionCheckEl.checked = false;
1101
+ contextImage._element = null;
1102
+ this.plugins.image.openTab.call(this, 'init');
1103
+
1104
+ if (contextImage._resizing) {
1105
+ contextImage.inputX.value = this.options.imageWidth === contextImage._defaultSizeX ? '' : this.options.imageWidth;
1106
+ contextImage.inputY.value = this.options.imageHeight === contextImage._defaultSizeY ? '' : this.options.imageHeight;
1107
+ contextImage.proportion.checked = true;
1108
+ contextImage._ratio = false;
1109
+ contextImage._ratioX = 1;
1110
+ contextImage._ratioY = 1;
1111
+ }
1112
+
1113
+ this.plugins.anchor.init.call(this, contextImage.anchorCtx);
1114
+ }
1115
+ };