suneditor 2.47.5 → 2.47.7

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