suneditor 2.44.6 → 2.44.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 (130) hide show
  1. package/LICENSE.txt +20 -20
  2. package/README.md +1585 -1585
  3. package/dist/suneditor.min.js +2 -2
  4. package/package.json +71 -71
  5. package/src/assets/css/suneditor-contents.css +556 -556
  6. package/src/assets/css/suneditor.css +0 -0
  7. package/src/assets/defaultIcons.js +103 -103
  8. package/src/lang/Lang.d.ts +143 -143
  9. package/src/lang/ckb.d.ts +4 -4
  10. package/src/lang/ckb.js +187 -187
  11. package/src/lang/da.d.ts +4 -4
  12. package/src/lang/da.js +191 -191
  13. package/src/lang/de.d.ts +4 -4
  14. package/src/lang/de.js +187 -187
  15. package/src/lang/en.d.ts +4 -4
  16. package/src/lang/en.js +187 -187
  17. package/src/lang/es.d.ts +4 -4
  18. package/src/lang/es.js +187 -187
  19. package/src/lang/fr.d.ts +4 -4
  20. package/src/lang/fr.js +188 -188
  21. package/src/lang/he.d.ts +4 -4
  22. package/src/lang/he.js +188 -188
  23. package/src/lang/index.d.ts +22 -22
  24. package/src/lang/index.js +25 -25
  25. package/src/lang/it.d.ts +4 -4
  26. package/src/lang/it.js +188 -188
  27. package/src/lang/ja.d.ts +4 -4
  28. package/src/lang/ja.js +187 -187
  29. package/src/lang/ko.d.ts +4 -4
  30. package/src/lang/ko.js +187 -187
  31. package/src/lang/lv.d.ts +4 -4
  32. package/src/lang/lv.js +187 -187
  33. package/src/lang/nl.d.ts +4 -4
  34. package/src/lang/nl.js +187 -187
  35. package/src/lang/pl.d.ts +4 -4
  36. package/src/lang/pl.js +187 -187
  37. package/src/lang/pt_br.d.ts +4 -4
  38. package/src/lang/pt_br.js +189 -189
  39. package/src/lang/ro.d.ts +4 -4
  40. package/src/lang/ro.js +187 -187
  41. package/src/lang/ru.d.ts +4 -4
  42. package/src/lang/ru.js +187 -187
  43. package/src/lang/se.d.ts +4 -4
  44. package/src/lang/se.js +191 -191
  45. package/src/lang/ua.d.ts +5 -5
  46. package/src/lang/ua.js +188 -188
  47. package/src/lang/ur.d.ts +4 -4
  48. package/src/lang/ur.js +187 -187
  49. package/src/lang/zh_cn.d.ts +4 -4
  50. package/src/lang/zh_cn.js +187 -187
  51. package/src/lib/constructor.js +0 -0
  52. package/src/lib/context.d.ts +42 -42
  53. package/src/lib/context.js +0 -0
  54. package/src/lib/core.d.ts +1101 -1101
  55. package/src/lib/core.js +12 -22
  56. package/src/lib/history.d.ts +48 -48
  57. package/src/lib/history.js +218 -218
  58. package/src/lib/util.d.ts +677 -677
  59. package/src/lib/util.js +19 -0
  60. package/src/options.d.ts +608 -608
  61. package/src/plugins/CommandPlugin.d.ts +7 -7
  62. package/src/plugins/DialogPlugin.d.ts +19 -19
  63. package/src/plugins/FileBrowserPlugin.d.ts +29 -29
  64. package/src/plugins/Module.d.ts +14 -14
  65. package/src/plugins/Plugin.d.ts +41 -41
  66. package/src/plugins/SubmenuPlugin.d.ts +7 -7
  67. package/src/plugins/command/blockquote.d.ts +4 -4
  68. package/src/plugins/command/blockquote.js +46 -46
  69. package/src/plugins/dialog/audio.d.ts +4 -4
  70. package/src/plugins/dialog/audio.js +556 -556
  71. package/src/plugins/dialog/image.d.ts +4 -4
  72. package/src/plugins/dialog/image.js +1122 -1122
  73. package/src/plugins/dialog/link.d.ts +4 -4
  74. package/src/plugins/dialog/link.js +223 -223
  75. package/src/plugins/dialog/math.d.ts +4 -4
  76. package/src/plugins/dialog/math.js +294 -294
  77. package/src/plugins/dialog/mention.d.ts +5 -5
  78. package/src/plugins/dialog/mention.js +242 -242
  79. package/src/plugins/dialog/video.d.ts +4 -4
  80. package/src/plugins/dialog/video.js +983 -983
  81. package/src/plugins/fileBrowser/imageGallery.d.ts +4 -4
  82. package/src/plugins/fileBrowser/imageGallery.js +63 -63
  83. package/src/plugins/index.d.ts +79 -79
  84. package/src/plugins/index.js +32 -32
  85. package/src/plugins/modules/_anchor.js +461 -461
  86. package/src/plugins/modules/_colorPicker.d.ts +59 -59
  87. package/src/plugins/modules/_colorPicker.js +0 -0
  88. package/src/plugins/modules/_notice.d.ts +20 -20
  89. package/src/plugins/modules/_notice.js +72 -72
  90. package/src/plugins/modules/_selectMenu.js +118 -118
  91. package/src/plugins/modules/component.d.ts +24 -24
  92. package/src/plugins/modules/component.js +82 -82
  93. package/src/plugins/modules/dialog.d.ts +27 -27
  94. package/src/plugins/modules/dialog.js +174 -174
  95. package/src/plugins/modules/fileBrowser.d.ts +41 -41
  96. package/src/plugins/modules/fileBrowser.js +373 -373
  97. package/src/plugins/modules/fileManager.d.ts +66 -66
  98. package/src/plugins/modules/fileManager.js +325 -325
  99. package/src/plugins/modules/index.d.ts +10 -10
  100. package/src/plugins/modules/index.js +8 -8
  101. package/src/plugins/modules/resizing.d.ts +153 -153
  102. package/src/plugins/modules/resizing.js +895 -895
  103. package/src/plugins/submenu/align.d.ts +4 -4
  104. package/src/plugins/submenu/align.js +160 -160
  105. package/src/plugins/submenu/font.d.ts +4 -4
  106. package/src/plugins/submenu/font.js +120 -120
  107. package/src/plugins/submenu/fontColor.d.ts +4 -4
  108. package/src/plugins/submenu/fontColor.js +0 -0
  109. package/src/plugins/submenu/fontSize.d.ts +4 -4
  110. package/src/plugins/submenu/fontSize.js +112 -112
  111. package/src/plugins/submenu/formatBlock.d.ts +4 -4
  112. package/src/plugins/submenu/formatBlock.js +273 -273
  113. package/src/plugins/submenu/hiliteColor.d.ts +4 -4
  114. package/src/plugins/submenu/hiliteColor.js +0 -0
  115. package/src/plugins/submenu/horizontalRule.d.ts +4 -4
  116. package/src/plugins/submenu/horizontalRule.js +98 -98
  117. package/src/plugins/submenu/lineHeight.d.ts +4 -4
  118. package/src/plugins/submenu/lineHeight.js +104 -104
  119. package/src/plugins/submenu/list.d.ts +4 -4
  120. package/src/plugins/submenu/list.js +456 -456
  121. package/src/plugins/submenu/paragraphStyle.d.ts +4 -4
  122. package/src/plugins/submenu/paragraphStyle.js +135 -135
  123. package/src/plugins/submenu/table.d.ts +4 -4
  124. package/src/plugins/submenu/template.d.ts +4 -4
  125. package/src/plugins/submenu/template.js +71 -71
  126. package/src/plugins/submenu/textStyle.d.ts +4 -4
  127. package/src/plugins/submenu/textStyle.js +167 -167
  128. package/src/suneditor.d.ts +9 -9
  129. package/src/suneditor.js +75 -75
  130. package/src/suneditor_build.js +17 -17
@@ -1,983 +1,983 @@
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 component from '../modules/component';
12
- import resizing from '../modules/resizing';
13
- import fileManager from '../modules/fileManager';
14
-
15
- export default {
16
- name: 'video',
17
- display: 'dialog',
18
- add: function (core) {
19
- core.addModule([dialog, component, resizing, fileManager]);
20
-
21
- const options = core.options;
22
- const context = core.context;
23
- const contextVideo = context.video = {
24
- _infoList: [], // @Override fileManager
25
- _infoIndex: 0, // @Override fileManager
26
- _uploadFileLength: 0, // @Override fileManager
27
- focusElement: null, // @Override dialog // This element has focus when the dialog is opened.
28
- sizeUnit: options._videoSizeUnit,
29
- _align: 'none',
30
- _floatClassRegExp: '__se__float\\-[a-z]+',
31
- _youtubeQuery: options.youtubeQuery,
32
- _videoRatio: (options.videoRatio * 100) + '%',
33
- _defaultRatio: (options.videoRatio * 100) + '%',
34
- _linkValue: '',
35
- // @require @Override component
36
- _element: null,
37
- _cover: null,
38
- _container: null,
39
- // @Override resizing properties
40
- inputX: null,
41
- inputY: null,
42
- _element_w: 1,
43
- _element_h: 1,
44
- _element_l: 0,
45
- _element_t: 0,
46
- _defaultSizeX: '100%',
47
- _defaultSizeY: (options.videoRatio * 100) + '%',
48
- _origin_w: options.videoWidth === '100%' ? '' : options.videoWidth,
49
- _origin_h: options.videoHeight === '56.25%' ? '' : options.videoHeight,
50
- _proportionChecked: true,
51
- _resizing: options.videoResizing,
52
- _resizeDotHide: !options.videoHeightShow,
53
- _rotation: options.videoRotation,
54
- _alignHide: !options.videoAlignShow,
55
- _onlyPercentage: options.videoSizeOnlyPercentage,
56
- _ratio: false,
57
- _ratioX: 1,
58
- _ratioY: 1,
59
- _captionShow: false
60
- };
61
-
62
- /** video dialog */
63
- let video_dialog = this.setDialog(core);
64
- contextVideo.modal = video_dialog;
65
- contextVideo.videoInputFile = video_dialog.querySelector('._se_video_file');
66
- contextVideo.videoUrlFile = video_dialog.querySelector('.se-input-url');
67
- contextVideo.focusElement = contextVideo.videoUrlFile || contextVideo.videoInputFile;
68
- contextVideo.preview = video_dialog.querySelector('.se-link-preview');
69
-
70
- /** add event listeners */
71
- video_dialog.querySelector('form').addEventListener('submit', this.submit.bind(core));
72
- if (contextVideo.videoInputFile) video_dialog.querySelector('.se-dialog-files-edge-button').addEventListener('click', this._removeSelectedFiles.bind(contextVideo.videoInputFile, contextVideo.videoUrlFile, contextVideo.preview));
73
- if (contextVideo.videoInputFile && contextVideo.videoUrlFile) contextVideo.videoInputFile.addEventListener('change', this._fileInputChange.bind(contextVideo));
74
- if (contextVideo.videoUrlFile) contextVideo.videoUrlFile.addEventListener('input', this._onLinkPreview.bind(contextVideo.preview, contextVideo, options.linkProtocol));
75
-
76
- contextVideo.proportion = {};
77
- contextVideo.videoRatioOption = {};
78
- contextVideo.inputX = {};
79
- contextVideo.inputY = {};
80
- if (options.videoResizing) {
81
- contextVideo.proportion = video_dialog.querySelector('._se_video_check_proportion');
82
- contextVideo.videoRatioOption = video_dialog.querySelector('.se-video-ratio');
83
- contextVideo.inputX = video_dialog.querySelector('._se_video_size_x');
84
- contextVideo.inputY = video_dialog.querySelector('._se_video_size_y');
85
- contextVideo.inputX.value = options.videoWidth;
86
- contextVideo.inputY.value = options.videoHeight;
87
-
88
- contextVideo.inputX.addEventListener('keyup', this.setInputSize.bind(core, 'x'));
89
- contextVideo.inputY.addEventListener('keyup', this.setInputSize.bind(core, 'y'));
90
-
91
- contextVideo.inputX.addEventListener('change', this.setRatio.bind(core));
92
- contextVideo.inputY.addEventListener('change', this.setRatio.bind(core));
93
- contextVideo.proportion.addEventListener('change', this.setRatio.bind(core));
94
- contextVideo.videoRatioOption.addEventListener('change', this.setVideoRatio.bind(core));
95
-
96
- video_dialog.querySelector('.se-dialog-btn-revert').addEventListener('click', this.sizeRevert.bind(core));
97
- }
98
-
99
- /** append html */
100
- context.dialog.modal.appendChild(video_dialog);
101
-
102
- /** empty memory */
103
- video_dialog = null;
104
- },
105
-
106
- /** dialog */
107
- setDialog: function (core) {
108
- const option = core.options;
109
- const lang = core.lang;
110
- const dialog = core.util.createElement('DIV');
111
-
112
- dialog.className = 'se-dialog-content';
113
- dialog.style.display = 'none';
114
- let html = '' +
115
- '<form method="post" enctype="multipart/form-data">' +
116
- '<div class="se-dialog-header">' +
117
- '<button type="button" data-command="close" class="se-btn se-dialog-close" title="' + lang.dialogBox.close + '" aria-label="' + lang.dialogBox.close + '">' +
118
- core.icons.cancel +
119
- '</button>' +
120
- '<span class="se-modal-title">' + lang.dialogBox.videoBox.title + '</span>' +
121
- '</div>' +
122
- '<div class="se-dialog-body">';
123
-
124
- if (option.videoFileInput) {
125
- html += '' +
126
- '<div class="se-dialog-form">' +
127
- '<label>' + lang.dialogBox.videoBox.file + '</label>' +
128
- '<div class="se-dialog-form-files">' +
129
- '<input class="se-input-form _se_video_file" type="file" accept="' + option.videoAccept + '"' + (option.videoMultipleFile ? ' multiple="multiple"' : '') + '/>' +
130
- '<button type="button" data-command="filesRemove" class="se-btn se-dialog-files-edge-button se-file-remove" title="' + lang.controller.remove + '" aria-label="' + lang.controller.remove + '">' + core.icons.cancel + '</button>' +
131
- '</div>' +
132
- '</div>' ;
133
- }
134
-
135
- if (option.videoUrlInput) {
136
- html += '' +
137
- '<div class="se-dialog-form">' +
138
- '<label>' + lang.dialogBox.videoBox.url + '</label>' +
139
- '<input class="se-input-form se-input-url" type="text" />' +
140
- '<pre class="se-link-preview"></pre>' +
141
- '</div>';
142
- }
143
-
144
- if (option.videoResizing) {
145
- const ratioList = option.videoRatioList || [{name: '16:9', value: 0.5625}, {name: '4:3', value: 0.75}, {name: '21:9', value: 0.4285}];
146
- const ratio = option.videoRatio;
147
- const onlyPercentage = option.videoSizeOnlyPercentage;
148
- const onlyPercentDisplay = onlyPercentage ? ' style="display: none !important;"' : '';
149
- const heightDisplay = !option.videoHeightShow ? ' style="display: none !important;"' : '';
150
- const ratioDisplay = !option.videoRatioShow ? ' style="display: none !important;"' : '';
151
- const onlyWidthDisplay = !onlyPercentage && !option.videoHeightShow && !option.videoRatioShow ? ' style="display: none !important;"' : '';
152
- html += '' +
153
- '<div class="se-dialog-form">' +
154
- '<div class="se-dialog-size-text">' +
155
- '<label class="size-w">' + lang.dialogBox.width + '</label>' +
156
- '<label class="se-dialog-size-x">&nbsp;</label>' +
157
- '<label class="size-h"' + heightDisplay + '>' + lang.dialogBox.height + '</label>' +
158
- '<label class="size-h"' + ratioDisplay + '>(' + lang.dialogBox.ratio + ')</label>' +
159
- '</div>' +
160
- '<input class="se-input-control _se_video_size_x" placeholder="100%"' + (onlyPercentage ? ' type="number" min="1"' : 'type="text"') + (onlyPercentage ? ' max="100"' : '') + '/>' +
161
- '<label class="se-dialog-size-x"' + onlyWidthDisplay + '>' + (onlyPercentage ? '%' : 'x') + '</label>' +
162
- '<input class="se-input-control _se_video_size_y" placeholder="' + (option.videoRatio * 100) + '%"' + (onlyPercentage ? ' type="number" min="1"' : 'type="text"') + (onlyPercentage ? ' max="100"' : '') + heightDisplay + '/>' +
163
- '<select class="se-input-select se-video-ratio" title="' + lang.dialogBox.ratio + '" aria-label="' + lang.dialogBox.ratio + '"' + ratioDisplay + '>';
164
- if (!heightDisplay) html += '<option value=""> - </option>';
165
- for (let i = 0, len = ratioList.length; i < len; i++) {
166
- html += '<option value="' + ratioList[i].value + '"' + (ratio.toString() === ratioList[i].value.toString() ? ' selected' : '') + '>' + ratioList[i].name + '</option>';
167
- }
168
- html += '</select>' +
169
- '<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>' +
170
- '</div>' +
171
- '<div class="se-dialog-form se-dialog-form-footer"' + onlyPercentDisplay + onlyWidthDisplay + '>' +
172
- '<label><input type="checkbox" class="se-dialog-btn-check _se_video_check_proportion" checked/>&nbsp;' + lang.dialogBox.proportion + '</label>' +
173
- '</div>';
174
- }
175
-
176
- html += '' +
177
- '</div>' +
178
- '<div class="se-dialog-footer">' +
179
- '<div' + (option.videoAlignShow ? '' : ' style="display: none"') + '>' +
180
- '<label><input type="radio" name="suneditor_video_radio" class="se-dialog-btn-radio" value="none" checked>' + lang.dialogBox.basic + '</label>' +
181
- '<label><input type="radio" name="suneditor_video_radio" class="se-dialog-btn-radio" value="left">' + lang.dialogBox.left + '</label>' +
182
- '<label><input type="radio" name="suneditor_video_radio" class="se-dialog-btn-radio" value="center">' + lang.dialogBox.center + '</label>' +
183
- '<label><input type="radio" name="suneditor_video_radio" class="se-dialog-btn-radio" value="right">' + lang.dialogBox.right + '</label>' +
184
- '</div>' +
185
- '<button type="submit" class="se-btn-primary" title="' + lang.dialogBox.submitButton + '" aria-label="' + lang.dialogBox.submitButton + '"><span>' + lang.dialogBox.submitButton + '</span></button>' +
186
- '</div>' +
187
- '</form>';
188
-
189
- dialog.innerHTML = html;
190
-
191
- return dialog;
192
- },
193
-
194
- _fileInputChange: function () {
195
- if (!this.videoInputFile.value) {
196
- this.videoUrlFile.removeAttribute('disabled');
197
- this.preview.style.textDecoration = '';
198
- } else {
199
- this.videoUrlFile.setAttribute('disabled', true);
200
- this.preview.style.textDecoration = 'line-through';
201
- }
202
- },
203
-
204
- _removeSelectedFiles: function (urlInput, preview) {
205
- this.value = '';
206
- if (urlInput) {
207
- urlInput.removeAttribute('disabled');
208
- preview.style.textDecoration = '';
209
- }
210
- },
211
-
212
- _onLinkPreview: function (context, protocol, e) {
213
- const value = e.target.value.trim();
214
- if (/^<iframe.*\/iframe>$/.test(value)) {
215
- context._linkValue = value;
216
- this.textContent = '<IFrame :src=".."></IFrame>';
217
- } else {
218
- context._linkValue = this.textContent = !value ? '' : (protocol && value.indexOf('://') === -1 && value.indexOf('#') !== 0) ? protocol + value : value.indexOf('://') === -1 ? '/' + value : value;
219
- }
220
- },
221
-
222
- _setTagAttrs: function (element) {
223
- element.setAttribute('controls', true);
224
-
225
- const attrs = this.options.videoTagAttrs;
226
- if (!attrs) return;
227
-
228
- for (let key in attrs) {
229
- if (!this.util.hasOwn(attrs, key)) continue;
230
- element.setAttribute(key, attrs[key]);
231
- }
232
- },
233
-
234
- createVideoTag: function () {
235
- const videoTag = this.util.createElement('VIDEO');
236
- this.plugins.video._setTagAttrs.call(this, videoTag);
237
- return videoTag;
238
- },
239
-
240
- _setIframeAttrs: function (element) {
241
- element.frameBorder = '0';
242
- element.allowFullscreen = true;
243
-
244
- const attrs = this.options.videoIframeAttrs;
245
- if (!attrs) return;
246
-
247
- for (let key in attrs) {
248
- if (!this.util.hasOwn(attrs, key)) continue;
249
- element.setAttribute(key, attrs[key]);
250
- }
251
- },
252
-
253
- createIframeTag: function () {
254
- const iframeTag = this.util.createElement('IFRAME');
255
- this.plugins.video._setIframeAttrs.call(this, iframeTag);
256
- return iframeTag;
257
- },
258
-
259
- /**
260
- * @Override @Required fileManager
261
- */
262
- fileTags: ['iframe', 'video'],
263
-
264
- /**
265
- * @Override core, resizing, fileManager
266
- * @description It is called from core.selectComponent.
267
- * @param {Element} element Target element
268
- */
269
- select: function (element) {
270
- this.plugins.video.onModifyMode.call(this, element, this.plugins.resizing.call_controller_resize.call(this, element, 'video'));
271
- },
272
-
273
- /**
274
- * @Override fileManager, resizing
275
- */
276
- destroy: function (element) {
277
- const frame = element || this.context.video._element;
278
- const container = this.context.video._container;
279
- const dataIndex = frame.getAttribute('data-index') * 1;
280
- let focusEl = (container.previousElementSibling || container.nextElementSibling);
281
-
282
- const emptyDiv = container.parentNode;
283
- this.util.removeItem(container);
284
- this.plugins.video.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, 'video', dataIndex, this.functions.onVideoUpload);
294
-
295
- // history stack
296
- this.history.push(false);
297
- },
298
-
299
- /**
300
- * @Required @Override dialog
301
- */
302
- on: function (update) {
303
- const contextVideo = this.context.video;
304
-
305
- if (!update) {
306
- contextVideo.inputX.value = contextVideo._origin_w = this.options.videoWidth === contextVideo._defaultSizeX ? '' : this.options.videoWidth;
307
- contextVideo.inputY.value = contextVideo._origin_h = this.options.videoHeight === contextVideo._defaultSizeY ? '' : this.options.videoHeight;
308
- contextVideo.proportion.disabled = true;
309
- if (contextVideo.videoInputFile && this.options.videoMultipleFile) contextVideo.videoInputFile.setAttribute('multiple', 'multiple');
310
- } else {
311
- if (contextVideo.videoInputFile && this.options.videoMultipleFile) contextVideo.videoInputFile.removeAttribute('multiple');
312
- }
313
-
314
- if (contextVideo._resizing) {
315
- this.plugins.video.setVideoRatioSelect.call(this, contextVideo._origin_h || contextVideo._defaultRatio);
316
- }
317
- },
318
-
319
- /**
320
- * @Required @Override dialog
321
- */
322
- open: function () {
323
- this.plugins.dialog.open.call(this, 'video', 'video' === this.currentControllerName);
324
- },
325
-
326
- setVideoRatio: function (e) {
327
- const contextVideo = this.context.video;
328
- const value = e.target.options[e.target.selectedIndex].value;
329
-
330
- contextVideo._defaultSizeY = contextVideo._videoRatio = !value ? contextVideo._defaultSizeY : (value * 100) + '%';
331
- contextVideo.inputY.placeholder = !value ? '' : (value * 100) + '%';
332
- contextVideo.inputY.value = '';
333
- },
334
-
335
- /**
336
- * @Override resizing
337
- * @param {String} xy 'x': width, 'y': height
338
- * @param {KeyboardEvent} e Event object
339
- */
340
- setInputSize: function (xy, e) {
341
- if (e && e.keyCode === 32) {
342
- e.preventDefault();
343
- return;
344
- }
345
-
346
- const contextVideo = this.context.video;
347
- this.plugins.resizing._module_setInputSize.call(this, contextVideo, xy);
348
-
349
- if (xy === 'y') {
350
- this.plugins.video.setVideoRatioSelect.call(this, e.target.value || contextVideo._defaultRatio);
351
- }
352
- },
353
-
354
- /**
355
- * @Override resizing
356
- */
357
- setRatio: function () {
358
- this.plugins.resizing._module_setRatio.call(this, this.context.video);
359
- },
360
-
361
- submit: function (e) {
362
- const contextVideo = this.context.video;
363
- const videoPlugin = this.plugins.video;
364
-
365
- e.preventDefault();
366
- e.stopPropagation();
367
-
368
- contextVideo._align = contextVideo.modal.querySelector('input[name="suneditor_video_radio"]:checked').value;
369
-
370
- try {
371
- if (contextVideo.videoInputFile && contextVideo.videoInputFile.files.length > 0) {
372
- this.showLoading();
373
- videoPlugin.submitAction.call(this, this.context.video.videoInputFile.files);
374
- } else if (contextVideo.videoUrlFile && contextVideo._linkValue.length > 0) {
375
- this.showLoading();
376
- videoPlugin.setup_url.call(this, contextVideo._linkValue);
377
- }
378
- } catch (error) {
379
- this.closeLoading();
380
- throw Error('[SUNEDITOR.video.submit.fail] cause : "' + error.message + '"');
381
- } finally {
382
- this.plugins.dialog.close.call(this);
383
- }
384
-
385
- return false;
386
- },
387
-
388
- submitAction: function (fileList) {
389
- if (fileList.length === 0) return;
390
-
391
- let fileSize = 0;
392
- let files = [];
393
- for (let i = 0, len = fileList.length; i < len; i++) {
394
- if (/video/i.test(fileList[i].type)) {
395
- files.push(fileList[i]);
396
- fileSize += fileList[i].size;
397
- }
398
- }
399
-
400
- const limitSize = this.options.videoUploadSizeLimit;
401
- if (limitSize > 0) {
402
- let infoSize = 0;
403
- const videosInfo = this.context.video._infoList;
404
- for (let i = 0, len = videosInfo.length; i < len; i++) {
405
- infoSize += videosInfo[i].size * 1;
406
- }
407
-
408
- if ((fileSize + infoSize) > limitSize) {
409
- this.closeLoading();
410
- const err = '[SUNEDITOR.videoUpload.fail] Size of uploadable total videos: ' + (limitSize/1000) + 'KB';
411
- if (typeof this.functions.onVideoUploadError !== 'function' || this.functions.onVideoUploadError(err, { 'limitSize': limitSize, 'currentSize': infoSize, 'uploadSize': fileSize }, this)) {
412
- this.functions.noticeOpen(err);
413
- }
414
- return;
415
- }
416
- }
417
-
418
- const contextVideo = this.context.video;
419
- contextVideo._uploadFileLength = files.length;
420
-
421
- const info = {
422
- inputWidth: contextVideo.inputX.value,
423
- inputHeight: contextVideo.inputY.value,
424
- align: contextVideo._align,
425
- isUpdate: this.context.dialog.updateModal,
426
- element: contextVideo._element
427
- };
428
-
429
- if (typeof this.functions.onVideoUploadBefore === 'function') {
430
- const result = this.functions.onVideoUploadBefore(files, info, this, function (data) {
431
- if (data && this._w.Array.isArray(data.result)) {
432
- this.plugins.video.register.call(this, info, data);
433
- } else {
434
- this.plugins.video.upload.call(this, info, data);
435
- }
436
- }.bind(this));
437
-
438
- if (typeof result === 'undefined') return;
439
- if (!result) {
440
- this.closeLoading();
441
- return;
442
- }
443
- if (typeof result === 'object' && result.length > 0) files = result;
444
- }
445
-
446
- this.plugins.video.upload.call(this, info, files);
447
- },
448
-
449
- error: function (message, response) {
450
- this.closeLoading();
451
- if (typeof this.functions.onVideoUploadError !== 'function' || this.functions.onVideoUploadError(message, response, this)) {
452
- this.functions.noticeOpen(message);
453
- throw Error('[SUNEDITOR.plugin.video.error] response: ' + message);
454
- }
455
- },
456
-
457
- upload: function (info, files) {
458
- if (!files) {
459
- this.closeLoading();
460
- return;
461
- }
462
- if (typeof files === 'string') {
463
- this.plugins.video.error.call(this, files, null);
464
- return;
465
- }
466
-
467
- const videoUploadUrl = this.options.videoUploadUrl;
468
- const filesLen = this.context.dialog.updateModal ? 1 : files.length;
469
-
470
- // server upload
471
- if (typeof videoUploadUrl === 'string' && videoUploadUrl.length > 0) {
472
- const formData = new FormData();
473
- for (let i = 0; i < filesLen; i++) {
474
- formData.append('file-' + i, files[i]);
475
- }
476
- this.plugins.fileManager.upload.call(this, videoUploadUrl, this.options.videoUploadHeader, formData, this.plugins.video.callBack_videoUpload.bind(this, info), this.functions.onVideoUploadError);
477
- } else {
478
- throw Error('[SUNEDITOR.videoUpload.fail] cause : There is no "videoUploadUrl" option.');
479
- }
480
- },
481
-
482
- callBack_videoUpload: function (info, xmlHttp) {
483
- if (typeof this.functions.videoUploadHandler === 'function') {
484
- this.functions.videoUploadHandler(xmlHttp, info, this);
485
- } else {
486
- const response = JSON.parse(xmlHttp.responseText);
487
- if (response.errorMessage) {
488
- this.plugins.video.error.call(this, response.errorMessage, response);
489
- } else {
490
- this.plugins.video.register.call(this, info, response);
491
- }
492
- }
493
- },
494
-
495
- register: function (info, response) {
496
- const fileList = response.result;
497
- const videoTag = this.plugins.video.createVideoTag.call(this);
498
-
499
- for (let i = 0, len = fileList.length, file; i < len; i++) {
500
- file = { name: fileList[i].name, size: fileList[i].size };
501
- this.plugins.video.create_video.call(this, (info.isUpdate ? info.element : videoTag.cloneNode(false)), fileList[i].url, info.inputWidth, info.inputHeight, info.align, file, info.isUpdate);
502
- }
503
-
504
- this.closeLoading();
505
- },
506
-
507
- setup_url: function (url) {
508
- try {
509
- const contextVideo = this.context.video;
510
- if (!url) url = contextVideo._linkValue;
511
- if (!url) return false;
512
-
513
- /** iframe source */
514
- if (/^<iframe.*\/iframe>$/.test(url)) {
515
- const oIframe = (new this._w.DOMParser()).parseFromString(url, 'text/html').querySelector('iframe');
516
- url = oIframe.src;
517
- if (url.length === 0) return false;
518
- }
519
-
520
- /** youtube */
521
- if (/youtu\.?be/.test(url)) {
522
- if (!/^http/.test(url)) url = 'https://' + url;
523
- url = url.replace('watch?v=', '');
524
- if (!/^\/\/.+\/embed\//.test(url)) {
525
- url = url.replace(url.match(/\/\/.+\//)[0], '//www.youtube.com/embed/').replace('&', '?&');
526
- }
527
-
528
- if (contextVideo._youtubeQuery.length > 0) {
529
- if (/\?/.test(url)) {
530
- const splitUrl = url.split('?');
531
- url = splitUrl[0] + '?' + contextVideo._youtubeQuery + '&' + splitUrl[1];
532
- } else {
533
- url += '?' + contextVideo._youtubeQuery;
534
- }
535
- }
536
- } else if (/vimeo\.com/.test(url)) {
537
- if (url.endsWith('/')) {
538
- url = url.slice(0, -1);
539
- }
540
- url = 'https://player.vimeo.com/video/' + url.slice(url.lastIndexOf('/') + 1);
541
- }
542
-
543
- this.plugins.video.create_video.call(this, this.plugins.video[(!/embed|iframe|player|\/e\/|\.php|\.html?/.test(url) && !/vimeo\.com/.test(url) ? "createVideoTag" : "createIframeTag")].call(this), url, contextVideo.inputX.value, contextVideo.inputY.value, contextVideo._align, null, this.context.dialog.updateModal);
544
- } catch (error) {
545
- throw Error('[SUNEDITOR.video.upload.fail] cause : "' + error.message + '"');
546
- } finally {
547
- this.closeLoading();
548
- }
549
- },
550
-
551
- create_video: function (oFrame, src, width, height, align, file, isUpdate) {
552
- this.context.resizing._resize_plugin = 'video';
553
- const contextVideo = this.context.video;
554
-
555
- let cover = null;
556
- let container = null;
557
- let init = false;
558
-
559
- /** update */
560
- if (isUpdate) {
561
- oFrame = contextVideo._element;
562
- if (oFrame.src !== src) {
563
- init = true;
564
- const isYoutube = /youtu\.?be/.test(src);
565
- const isVimeo = /vimeo\.com/.test(src);
566
- if ((isYoutube || isVimeo) && !/^iframe$/i.test(oFrame.nodeName)) {
567
- const newTag = this.plugins.video.createIframeTag.call(this);
568
- newTag.src = src;
569
- oFrame.parentNode.replaceChild(newTag, oFrame);
570
- contextVideo._element = oFrame = newTag;
571
- } else if (!isYoutube && !isVimeo && !/^videoo$/i.test(oFrame.nodeName)) {
572
- const newTag = this.plugins.video.createVideoTag.call(this);
573
- newTag.src = src;
574
- oFrame.parentNode.replaceChild(newTag, oFrame);
575
- contextVideo._element = oFrame = newTag;
576
- } else {
577
- oFrame.src = src;
578
- }
579
- }
580
- container = contextVideo._container;
581
- cover = this.util.getParentElement(oFrame, 'FIGURE');
582
- }
583
- /** create */
584
- else {
585
- init = true;
586
- oFrame.src = src;
587
- contextVideo._element = oFrame;
588
- cover = this.plugins.component.set_cover.call(this, oFrame);
589
- container = this.plugins.component.set_container.call(this, cover, 'se-video-container');
590
- }
591
-
592
- /** rendering */
593
- contextVideo._cover = cover;
594
- contextVideo._container = container;
595
-
596
- const inputUpdate = (this.plugins.resizing._module_getSizeX.call(this, contextVideo) !== (width || contextVideo._defaultSizeX)) || (this.plugins.resizing._module_getSizeY.call(this, contextVideo) !== (height || contextVideo._videoRatio));
597
- const changeSize = !isUpdate || inputUpdate;
598
-
599
- if (contextVideo._resizing) {
600
- this.context.video._proportionChecked = contextVideo.proportion.checked;
601
- oFrame.setAttribute('data-proportion', contextVideo._proportionChecked);
602
- }
603
-
604
- // size
605
- let isPercent = false;
606
- if (changeSize) {
607
- isPercent = this.plugins.video.applySize.call(this);
608
- }
609
-
610
- // align
611
- if (!(isPercent && align === 'center')) {
612
- this.plugins.video.setAlign.call(this, null, oFrame, cover, container);
613
- }
614
-
615
- let changed = true;
616
- if (!isUpdate) {
617
- changed = this.insertComponent(container, false, true, !this.options.mediaAutoSelect);
618
- if (!this.options.mediaAutoSelect) {
619
- const line = this.appendFormatTag(container, null);
620
- if (line) this.setRange(line, 0, line, 0);
621
- }
622
- } else if (contextVideo._resizing && this.context.resizing._rotateVertical && changeSize) {
623
- this.plugins.resizing.setTransformSize.call(this, oFrame, null, null);
624
- }
625
-
626
- if (changed) {
627
- if (init) {
628
- this.plugins.fileManager.setInfo.call(this, 'video', oFrame, this.functions.onVideoUpload, file, true);
629
- }
630
- if (isUpdate) {
631
- this.selectComponent(oFrame, 'video');
632
- // history stack
633
- this.history.push(false);
634
- }
635
- }
636
-
637
- this.context.resizing._resize_plugin = '';
638
- },
639
-
640
- _update_videoCover: function (oFrame) {
641
- if (!oFrame) return;
642
-
643
- const contextVideo = this.context.video;
644
-
645
- if (/^video$/i.test(oFrame.nodeName)) this.plugins.video._setTagAttrs.call(this, oFrame);
646
- else this.plugins.video._setIframeAttrs.call(this, oFrame);
647
-
648
- let existElement = (this.util.isRangeFormatElement(oFrame.parentNode) || this.util.isWysiwygDiv(oFrame.parentNode)) ?
649
- oFrame : this.util.getFormatElement(oFrame) || oFrame;
650
-
651
- const prevFrame = oFrame;
652
- contextVideo._element = oFrame = oFrame.cloneNode(true);
653
- const cover = contextVideo._cover = this.plugins.component.set_cover.call(this, oFrame);
654
- const container = contextVideo._container = this.plugins.component.set_container.call(this, cover, 'se-video-container');
655
-
656
- try {
657
- const figcaption = existElement.querySelector('figcaption');
658
- let caption = null;
659
- if (!!figcaption) {
660
- caption = this.util.createElement('DIV');
661
- caption.innerHTML = figcaption.innerHTML;
662
- this.util.removeItem(figcaption);
663
- }
664
-
665
- // size
666
- const size = (oFrame.getAttribute('data-size') || oFrame.getAttribute('data-origin') || '').split(',');
667
- this.plugins.video.applySize.call(this, (size[0] || prevFrame.style.width || prevFrame.width || ''), (size[1] || prevFrame.style.height || prevFrame.height || ''));
668
-
669
- // align
670
- const format = this.util.getFormatElement(prevFrame);
671
- if (format) contextVideo._align = format.style.textAlign || format.style.float;
672
- this.plugins.video.setAlign.call(this, null, oFrame, cover, container);
673
-
674
- if (this.util.getParentElement(prevFrame, this.util.isNotCheckingNode)) {
675
- prevFrame.parentNode.replaceChild(container, prevFrame);
676
- } else if (this.util.isListCell(existElement)) {
677
- const refer = this.util.getParentElement(prevFrame, function (current) { return current.parentNode === existElement; });
678
- existElement.insertBefore(container, refer);
679
- this.util.removeItem(prevFrame);
680
- this.util.removeEmptyNode(refer, null, true);
681
- } else if (this.util.isFormatElement(existElement)) {
682
- const refer = this.util.getParentElement(prevFrame, function (current) { return current.parentNode === existElement; });
683
- existElement = this.util.splitElement(existElement, refer);
684
- existElement.parentNode.insertBefore(container, existElement);
685
- this.util.removeItem(prevFrame);
686
- this.util.removeEmptyNode(existElement, null, true);
687
- if (existElement.children.length === 0) existElement.innerHTML = this.util.htmlRemoveWhiteSpace(existElement.innerHTML);
688
- } else {
689
- existElement.parentNode.replaceChild(container, existElement);
690
- }
691
-
692
- if (!!caption) existElement.parentNode.insertBefore(caption, container.nextElementSibling);
693
- } catch (error) {
694
- console.warn('[SUNEDITOR.video.error] Maybe the video tag is nested.', error);
695
- }
696
-
697
- this.plugins.fileManager.setInfo.call(this, 'video', oFrame, this.functions.onVideoUpload, null, true);
698
- this.plugins.video.init.call(this);
699
- },
700
-
701
- /**
702
- * @Required @Override fileManager, resizing
703
- */
704
- onModifyMode: function (element, size) {
705
- const contextVideo = this.context.video;
706
- contextVideo._element = element;
707
- contextVideo._cover = this.util.getParentElement(element, 'FIGURE');
708
- contextVideo._container = this.util.getParentElement(element, this.util.isMediaComponent);
709
- contextVideo._align = element.style.float || element.getAttribute('data-align') || 'none';
710
- element.style.float = '';
711
-
712
- if (size) {
713
- contextVideo._element_w = size.w;
714
- contextVideo._element_h = size.h;
715
- contextVideo._element_t = size.t;
716
- contextVideo._element_l = size.l;
717
- }
718
-
719
- let origin = contextVideo._element.getAttribute('data-size') || contextVideo._element.getAttribute('data-origin');
720
- let w, h;
721
- if (origin) {
722
- origin = origin.split(',');
723
- w = origin[0];
724
- h = origin[1];
725
- } else if (size) {
726
- w = size.w;
727
- h = size.h;
728
- }
729
-
730
- contextVideo._origin_w = w || element.style.width || element.width || '';
731
- contextVideo._origin_h = h || element.style.height || element.height || '';
732
- },
733
-
734
- /**
735
- * @Required @Override fileManager, resizing
736
- */
737
- openModify: function (notOpen) {
738
- const contextVideo = this.context.video;
739
-
740
- if (contextVideo.videoUrlFile) contextVideo._linkValue = contextVideo.preview.textContent = contextVideo.videoUrlFile.value = (contextVideo._element.src || (contextVideo._element.querySelector('source') || '').src || '');
741
- (contextVideo.modal.querySelector('input[name="suneditor_video_radio"][value="' + contextVideo._align + '"]') || contextVideo.modal.querySelector('input[name="suneditor_video_radio"][value="none"]')).checked = true;
742
-
743
- if (contextVideo._resizing) {
744
- this.plugins.resizing._module_setModifyInputSize.call(this, contextVideo, this.plugins.video);
745
-
746
- const y = contextVideo._videoRatio = this.plugins.resizing._module_getSizeY.call(this, contextVideo);
747
- const ratioSelected = this.plugins.video.setVideoRatioSelect.call(this, y);
748
- if (!ratioSelected) contextVideo.inputY.value = contextVideo._onlyPercentage ? this.util.getNumber(y, 2) : y;
749
- }
750
-
751
- if (!notOpen) this.plugins.dialog.open.call(this, 'video', true);
752
- },
753
-
754
- setVideoRatioSelect: function (value) {
755
- let ratioSelected = false;
756
- const contextVideo = this.context.video;
757
- const ratioOptions = contextVideo.videoRatioOption.options;
758
-
759
- if (/%$/.test(value) || contextVideo._onlyPercentage) value = (this.util.getNumber(value, 2) / 100) + '';
760
- else if (!this.util.isNumber(value) || (value * 1) >= 1) value = '';
761
-
762
- contextVideo.inputY.placeholder = '';
763
- for (let i = 0, len = ratioOptions.length; i < len; i++) {
764
- if (ratioOptions[i].value === value) {
765
- ratioSelected = ratioOptions[i].selected = true;
766
- contextVideo.inputY.placeholder = !value ? '' : (value * 100) + '%';
767
- }
768
- else ratioOptions[i].selected = false;
769
- }
770
-
771
- return ratioSelected;
772
- },
773
-
774
- /**
775
- * @Override fileManager
776
- */
777
- checkFileInfo: function () {
778
- this.plugins.fileManager.checkInfo.call(this, 'video', ['iframe', 'video'], this.functions.onVideoUpload, this.plugins.video._update_videoCover.bind(this), true);
779
- },
780
-
781
- /**
782
- * @Override fileManager
783
- */
784
- resetFileInfo: function () {
785
- this.plugins.fileManager.resetInfo.call(this, 'video', this.functions.onVideoUpload);
786
- },
787
-
788
- /**
789
- * @Override fileManager
790
- */
791
- applySize: function (w, h) {
792
- const contextVideo = this.context.video;
793
-
794
- if (!w) w = contextVideo.inputX.value || this.options.videoWidth;
795
- if (!h) h = contextVideo.inputY.value || this.options.videoHeight;
796
-
797
- if (contextVideo._onlyPercentage || /%$/.test(w) || !w) {
798
- this.plugins.video.setPercentSize.call(this, (w || '100%'), (h || (/%$/.test(contextVideo._videoRatio) ? contextVideo._videoRatio : contextVideo._defaultRatio)));
799
- return true;
800
- } else if ((!w || w === 'auto') && (!h || h === 'auto')) {
801
- this.plugins.video.setAutoSize.call(this);
802
- } else {
803
- this.plugins.video.setSize.call(this, w, (h || contextVideo._videoRatio || contextVideo._defaultRatio), false);
804
- }
805
-
806
- return false;
807
- },
808
-
809
- /**
810
- * @Override resizing
811
- */
812
- sizeRevert: function () {
813
- this.plugins.resizing._module_sizeRevert.call(this, this.context.video);
814
- },
815
-
816
- /**
817
- * @Override resizing
818
- */
819
- setSize: function (w, h, notResetPercentage, direction) {
820
- const contextVideo = this.context.video;
821
- const onlyW = /^(rw|lw)$/.test(direction);
822
- const onlyH = /^(th|bh)$/.test(direction);
823
-
824
- if (!onlyH) w = this.util.getNumber(w, 0);
825
- if (!onlyW) h = this.util.isNumber(h) ? h + contextVideo.sizeUnit : !h ? '' : h;
826
- w = w ? w + contextVideo.sizeUnit : '';
827
-
828
- if (!onlyH) contextVideo._element.style.width = w;
829
- if (!onlyW) contextVideo._cover.style.paddingBottom = contextVideo._cover.style.height = h;
830
-
831
- if (!onlyH && !/%$/.test(w)) {
832
- contextVideo._cover.style.width = w;
833
- contextVideo._container.style.width = '';
834
- }
835
-
836
- if (!onlyW && !/%$/.test(h)) {
837
- contextVideo._element.style.height = h;
838
- } else {
839
- contextVideo._element.style.height = '';
840
- }
841
-
842
- if (!notResetPercentage) contextVideo._element.removeAttribute('data-percentage');
843
-
844
- // save current size
845
- this.plugins.resizing._module_saveCurrentSize.call(this, contextVideo);
846
- },
847
-
848
- /**
849
- * @Override resizing
850
- */
851
- setAutoSize: function () {
852
- this.plugins.video.setPercentSize.call(this, 100, this.context.video._defaultRatio);
853
- },
854
-
855
- /**
856
- * @Override resizing
857
- */
858
- setOriginSize: function (dataSize) {
859
- const contextVideo = this.context.video;
860
- contextVideo._element.removeAttribute('data-percentage');
861
-
862
- this.plugins.resizing.resetTransform.call(this, contextVideo._element);
863
- this.plugins.video.cancelPercentAttr.call(this);
864
-
865
- const originSize = ((dataSize ? contextVideo._element.getAttribute('data-size') : '') || contextVideo._element.getAttribute('data-origin') || '').split(',');
866
-
867
- if (originSize) {
868
- const w = originSize[0];
869
- const h = originSize[1];
870
-
871
- if (contextVideo._onlyPercentage || (/%$/.test(w) && (/%$/.test(h) || !/\d/.test(h)))) {
872
- this.plugins.video.setPercentSize.call(this, w, h);
873
- } else {
874
- this.plugins.video.setSize.call(this, w, h);
875
- }
876
-
877
- // save current size
878
- this.plugins.resizing._module_saveCurrentSize.call(this, contextVideo);
879
- }
880
- },
881
-
882
- /**
883
- * @Override resizing
884
- */
885
- setPercentSize: function (w, h) {
886
- const contextVideo = this.context.video;
887
- h = !!h && !/%$/.test(h) && !this.util.getNumber(h, 0) ? this.util.isNumber(h) ? h + '%' : h : this.util.isNumber(h) ? h + contextVideo.sizeUnit : (h || contextVideo._defaultRatio);
888
-
889
- contextVideo._container.style.width = this.util.isNumber(w) ? w + '%' : w;
890
- contextVideo._container.style.height = '';
891
- contextVideo._cover.style.width = '100%';
892
- contextVideo._cover.style.height = h;
893
- contextVideo._cover.style.paddingBottom = h;
894
- contextVideo._element.style.width = '100%';
895
- contextVideo._element.style.height = '100%';
896
- contextVideo._element.style.maxWidth = '';
897
-
898
- if (contextVideo._align === 'center') this.plugins.video.setAlign.call(this, null, null, null, null);
899
- contextVideo._element.setAttribute('data-percentage', w + ',' + h);
900
-
901
- // save current size
902
- this.plugins.resizing._module_saveCurrentSize.call(this, contextVideo);
903
- },
904
-
905
- /**
906
- * @Override resizing
907
- */
908
- cancelPercentAttr: function () {
909
- const contextVideo = this.context.video;
910
-
911
- contextVideo._cover.style.width = '';
912
- contextVideo._cover.style.height = '';
913
- contextVideo._cover.style.paddingBottom = '';
914
- contextVideo._container.style.width = '';
915
- contextVideo._container.style.height = '';
916
-
917
- this.util.removeClass(contextVideo._container, this.context.video._floatClassRegExp);
918
- this.util.addClass(contextVideo._container, '__se__float-' + contextVideo._align);
919
-
920
- if (contextVideo._align === 'center') this.plugins.video.setAlign.call(this, null, null, null, null);
921
- },
922
-
923
- /**
924
- * @Override resizing
925
- */
926
- setAlign: function (align, element, cover, container) {
927
- const contextVideo = this.context.video;
928
-
929
- if (!align) align = contextVideo._align;
930
- if (!element) element = contextVideo._element;
931
- if (!cover) cover = contextVideo._cover;
932
- if (!container) container = contextVideo._container;
933
-
934
- if (align && align !== 'none') {
935
- cover.style.margin = 'auto';
936
- } else {
937
- cover.style.margin = '0';
938
- }
939
-
940
- if (/%$/.test(element.style.width) && align === 'center') {
941
- container.style.minWidth = '100%';
942
- cover.style.width = container.style.width;
943
- cover.style.height = cover.style.height;
944
- cover.style.paddingBottom = !/%$/.test(cover.style.height) ? cover.style.height : this.util.getNumber((this.util.getNumber(cover.style.height, 2) / 100) * this.util.getNumber(cover.style.width, 2), 2) + '%';
945
- } else {
946
- container.style.minWidth = '';
947
- cover.style.width = this.context.resizing._rotateVertical ? (element.style.height || element.offsetHeight) : (element.style.width || '100%');
948
- cover.style.paddingBottom = cover.style.height;
949
- }
950
-
951
- if (!this.util.hasClass(container, '__se__float-' + align)) {
952
- this.util.removeClass(container, contextVideo._floatClassRegExp);
953
- this.util.addClass(container, '__se__float-' + align);
954
- }
955
-
956
- element.setAttribute('data-align', align);
957
- },
958
-
959
- /**
960
- * @Override dialog
961
- */
962
- init: function () {
963
- const contextVideo = this.context.video;
964
- if (contextVideo.videoInputFile) contextVideo.videoInputFile.value = '';
965
- if (contextVideo.videoUrlFile) contextVideo._linkValue = contextVideo.preview.textContent = contextVideo.videoUrlFile.value = '';
966
- if (contextVideo.videoInputFile && contextVideo.videoUrlFile) {
967
- contextVideo.videoUrlFile.removeAttribute('disabled');
968
- contextVideo.preview.style.textDecoration = '';
969
- }
970
-
971
- contextVideo._origin_w = this.options.videoWidth;
972
- contextVideo._origin_h = this.options.videoHeight;
973
- contextVideo.modal.querySelector('input[name="suneditor_video_radio"][value="none"]').checked = true;
974
-
975
- if (contextVideo._resizing) {
976
- contextVideo.inputX.value = this.options.videoWidth === contextVideo._defaultSizeX ? '' : this.options.videoWidth;
977
- contextVideo.inputY.value = this.options.videoHeight === contextVideo._defaultSizeY ? '' : this.options.videoHeight;
978
- contextVideo.proportion.checked = true;
979
- contextVideo.proportion.disabled = true;
980
- this.plugins.video.setVideoRatioSelect.call(this, contextVideo._defaultRatio);
981
- }
982
- }
983
- };
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 component from '../modules/component';
12
+ import resizing from '../modules/resizing';
13
+ import fileManager from '../modules/fileManager';
14
+
15
+ export default {
16
+ name: 'video',
17
+ display: 'dialog',
18
+ add: function (core) {
19
+ core.addModule([dialog, component, resizing, fileManager]);
20
+
21
+ const options = core.options;
22
+ const context = core.context;
23
+ const contextVideo = context.video = {
24
+ _infoList: [], // @Override fileManager
25
+ _infoIndex: 0, // @Override fileManager
26
+ _uploadFileLength: 0, // @Override fileManager
27
+ focusElement: null, // @Override dialog // This element has focus when the dialog is opened.
28
+ sizeUnit: options._videoSizeUnit,
29
+ _align: 'none',
30
+ _floatClassRegExp: '__se__float\\-[a-z]+',
31
+ _youtubeQuery: options.youtubeQuery,
32
+ _videoRatio: (options.videoRatio * 100) + '%',
33
+ _defaultRatio: (options.videoRatio * 100) + '%',
34
+ _linkValue: '',
35
+ // @require @Override component
36
+ _element: null,
37
+ _cover: null,
38
+ _container: null,
39
+ // @Override resizing properties
40
+ inputX: null,
41
+ inputY: null,
42
+ _element_w: 1,
43
+ _element_h: 1,
44
+ _element_l: 0,
45
+ _element_t: 0,
46
+ _defaultSizeX: '100%',
47
+ _defaultSizeY: (options.videoRatio * 100) + '%',
48
+ _origin_w: options.videoWidth === '100%' ? '' : options.videoWidth,
49
+ _origin_h: options.videoHeight === '56.25%' ? '' : options.videoHeight,
50
+ _proportionChecked: true,
51
+ _resizing: options.videoResizing,
52
+ _resizeDotHide: !options.videoHeightShow,
53
+ _rotation: options.videoRotation,
54
+ _alignHide: !options.videoAlignShow,
55
+ _onlyPercentage: options.videoSizeOnlyPercentage,
56
+ _ratio: false,
57
+ _ratioX: 1,
58
+ _ratioY: 1,
59
+ _captionShow: false
60
+ };
61
+
62
+ /** video dialog */
63
+ let video_dialog = this.setDialog(core);
64
+ contextVideo.modal = video_dialog;
65
+ contextVideo.videoInputFile = video_dialog.querySelector('._se_video_file');
66
+ contextVideo.videoUrlFile = video_dialog.querySelector('.se-input-url');
67
+ contextVideo.focusElement = contextVideo.videoUrlFile || contextVideo.videoInputFile;
68
+ contextVideo.preview = video_dialog.querySelector('.se-link-preview');
69
+
70
+ /** add event listeners */
71
+ video_dialog.querySelector('form').addEventListener('submit', this.submit.bind(core));
72
+ if (contextVideo.videoInputFile) video_dialog.querySelector('.se-dialog-files-edge-button').addEventListener('click', this._removeSelectedFiles.bind(contextVideo.videoInputFile, contextVideo.videoUrlFile, contextVideo.preview));
73
+ if (contextVideo.videoInputFile && contextVideo.videoUrlFile) contextVideo.videoInputFile.addEventListener('change', this._fileInputChange.bind(contextVideo));
74
+ if (contextVideo.videoUrlFile) contextVideo.videoUrlFile.addEventListener('input', this._onLinkPreview.bind(contextVideo.preview, contextVideo, options.linkProtocol));
75
+
76
+ contextVideo.proportion = {};
77
+ contextVideo.videoRatioOption = {};
78
+ contextVideo.inputX = {};
79
+ contextVideo.inputY = {};
80
+ if (options.videoResizing) {
81
+ contextVideo.proportion = video_dialog.querySelector('._se_video_check_proportion');
82
+ contextVideo.videoRatioOption = video_dialog.querySelector('.se-video-ratio');
83
+ contextVideo.inputX = video_dialog.querySelector('._se_video_size_x');
84
+ contextVideo.inputY = video_dialog.querySelector('._se_video_size_y');
85
+ contextVideo.inputX.value = options.videoWidth;
86
+ contextVideo.inputY.value = options.videoHeight;
87
+
88
+ contextVideo.inputX.addEventListener('keyup', this.setInputSize.bind(core, 'x'));
89
+ contextVideo.inputY.addEventListener('keyup', this.setInputSize.bind(core, 'y'));
90
+
91
+ contextVideo.inputX.addEventListener('change', this.setRatio.bind(core));
92
+ contextVideo.inputY.addEventListener('change', this.setRatio.bind(core));
93
+ contextVideo.proportion.addEventListener('change', this.setRatio.bind(core));
94
+ contextVideo.videoRatioOption.addEventListener('change', this.setVideoRatio.bind(core));
95
+
96
+ video_dialog.querySelector('.se-dialog-btn-revert').addEventListener('click', this.sizeRevert.bind(core));
97
+ }
98
+
99
+ /** append html */
100
+ context.dialog.modal.appendChild(video_dialog);
101
+
102
+ /** empty memory */
103
+ video_dialog = null;
104
+ },
105
+
106
+ /** dialog */
107
+ setDialog: function (core) {
108
+ const option = core.options;
109
+ const lang = core.lang;
110
+ const dialog = core.util.createElement('DIV');
111
+
112
+ dialog.className = 'se-dialog-content';
113
+ dialog.style.display = 'none';
114
+ let html = '' +
115
+ '<form method="post" enctype="multipart/form-data">' +
116
+ '<div class="se-dialog-header">' +
117
+ '<button type="button" data-command="close" class="se-btn se-dialog-close" title="' + lang.dialogBox.close + '" aria-label="' + lang.dialogBox.close + '">' +
118
+ core.icons.cancel +
119
+ '</button>' +
120
+ '<span class="se-modal-title">' + lang.dialogBox.videoBox.title + '</span>' +
121
+ '</div>' +
122
+ '<div class="se-dialog-body">';
123
+
124
+ if (option.videoFileInput) {
125
+ html += '' +
126
+ '<div class="se-dialog-form">' +
127
+ '<label>' + lang.dialogBox.videoBox.file + '</label>' +
128
+ '<div class="se-dialog-form-files">' +
129
+ '<input class="se-input-form _se_video_file" type="file" accept="' + option.videoAccept + '"' + (option.videoMultipleFile ? ' multiple="multiple"' : '') + '/>' +
130
+ '<button type="button" data-command="filesRemove" class="se-btn se-dialog-files-edge-button se-file-remove" title="' + lang.controller.remove + '" aria-label="' + lang.controller.remove + '">' + core.icons.cancel + '</button>' +
131
+ '</div>' +
132
+ '</div>' ;
133
+ }
134
+
135
+ if (option.videoUrlInput) {
136
+ html += '' +
137
+ '<div class="se-dialog-form">' +
138
+ '<label>' + lang.dialogBox.videoBox.url + '</label>' +
139
+ '<input class="se-input-form se-input-url" type="text" />' +
140
+ '<pre class="se-link-preview"></pre>' +
141
+ '</div>';
142
+ }
143
+
144
+ if (option.videoResizing) {
145
+ const ratioList = option.videoRatioList || [{name: '16:9', value: 0.5625}, {name: '4:3', value: 0.75}, {name: '21:9', value: 0.4285}];
146
+ const ratio = option.videoRatio;
147
+ const onlyPercentage = option.videoSizeOnlyPercentage;
148
+ const onlyPercentDisplay = onlyPercentage ? ' style="display: none !important;"' : '';
149
+ const heightDisplay = !option.videoHeightShow ? ' style="display: none !important;"' : '';
150
+ const ratioDisplay = !option.videoRatioShow ? ' style="display: none !important;"' : '';
151
+ const onlyWidthDisplay = !onlyPercentage && !option.videoHeightShow && !option.videoRatioShow ? ' style="display: none !important;"' : '';
152
+ html += '' +
153
+ '<div class="se-dialog-form">' +
154
+ '<div class="se-dialog-size-text">' +
155
+ '<label class="size-w">' + lang.dialogBox.width + '</label>' +
156
+ '<label class="se-dialog-size-x">&nbsp;</label>' +
157
+ '<label class="size-h"' + heightDisplay + '>' + lang.dialogBox.height + '</label>' +
158
+ '<label class="size-h"' + ratioDisplay + '>(' + lang.dialogBox.ratio + ')</label>' +
159
+ '</div>' +
160
+ '<input class="se-input-control _se_video_size_x" placeholder="100%"' + (onlyPercentage ? ' type="number" min="1"' : 'type="text"') + (onlyPercentage ? ' max="100"' : '') + '/>' +
161
+ '<label class="se-dialog-size-x"' + onlyWidthDisplay + '>' + (onlyPercentage ? '%' : 'x') + '</label>' +
162
+ '<input class="se-input-control _se_video_size_y" placeholder="' + (option.videoRatio * 100) + '%"' + (onlyPercentage ? ' type="number" min="1"' : 'type="text"') + (onlyPercentage ? ' max="100"' : '') + heightDisplay + '/>' +
163
+ '<select class="se-input-select se-video-ratio" title="' + lang.dialogBox.ratio + '" aria-label="' + lang.dialogBox.ratio + '"' + ratioDisplay + '>';
164
+ if (!heightDisplay) html += '<option value=""> - </option>';
165
+ for (let i = 0, len = ratioList.length; i < len; i++) {
166
+ html += '<option value="' + ratioList[i].value + '"' + (ratio.toString() === ratioList[i].value.toString() ? ' selected' : '') + '>' + ratioList[i].name + '</option>';
167
+ }
168
+ html += '</select>' +
169
+ '<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>' +
170
+ '</div>' +
171
+ '<div class="se-dialog-form se-dialog-form-footer"' + onlyPercentDisplay + onlyWidthDisplay + '>' +
172
+ '<label><input type="checkbox" class="se-dialog-btn-check _se_video_check_proportion" checked/>&nbsp;' + lang.dialogBox.proportion + '</label>' +
173
+ '</div>';
174
+ }
175
+
176
+ html += '' +
177
+ '</div>' +
178
+ '<div class="se-dialog-footer">' +
179
+ '<div' + (option.videoAlignShow ? '' : ' style="display: none"') + '>' +
180
+ '<label><input type="radio" name="suneditor_video_radio" class="se-dialog-btn-radio" value="none" checked>' + lang.dialogBox.basic + '</label>' +
181
+ '<label><input type="radio" name="suneditor_video_radio" class="se-dialog-btn-radio" value="left">' + lang.dialogBox.left + '</label>' +
182
+ '<label><input type="radio" name="suneditor_video_radio" class="se-dialog-btn-radio" value="center">' + lang.dialogBox.center + '</label>' +
183
+ '<label><input type="radio" name="suneditor_video_radio" class="se-dialog-btn-radio" value="right">' + lang.dialogBox.right + '</label>' +
184
+ '</div>' +
185
+ '<button type="submit" class="se-btn-primary" title="' + lang.dialogBox.submitButton + '" aria-label="' + lang.dialogBox.submitButton + '"><span>' + lang.dialogBox.submitButton + '</span></button>' +
186
+ '</div>' +
187
+ '</form>';
188
+
189
+ dialog.innerHTML = html;
190
+
191
+ return dialog;
192
+ },
193
+
194
+ _fileInputChange: function () {
195
+ if (!this.videoInputFile.value) {
196
+ this.videoUrlFile.removeAttribute('disabled');
197
+ this.preview.style.textDecoration = '';
198
+ } else {
199
+ this.videoUrlFile.setAttribute('disabled', true);
200
+ this.preview.style.textDecoration = 'line-through';
201
+ }
202
+ },
203
+
204
+ _removeSelectedFiles: function (urlInput, preview) {
205
+ this.value = '';
206
+ if (urlInput) {
207
+ urlInput.removeAttribute('disabled');
208
+ preview.style.textDecoration = '';
209
+ }
210
+ },
211
+
212
+ _onLinkPreview: function (context, protocol, e) {
213
+ const value = e.target.value.trim();
214
+ if (/^<iframe.*\/iframe>$/.test(value)) {
215
+ context._linkValue = value;
216
+ this.textContent = '<IFrame :src=".."></IFrame>';
217
+ } else {
218
+ context._linkValue = this.textContent = !value ? '' : (protocol && value.indexOf('://') === -1 && value.indexOf('#') !== 0) ? protocol + value : value.indexOf('://') === -1 ? '/' + value : value;
219
+ }
220
+ },
221
+
222
+ _setTagAttrs: function (element) {
223
+ element.setAttribute('controls', true);
224
+
225
+ const attrs = this.options.videoTagAttrs;
226
+ if (!attrs) return;
227
+
228
+ for (let key in attrs) {
229
+ if (!this.util.hasOwn(attrs, key)) continue;
230
+ element.setAttribute(key, attrs[key]);
231
+ }
232
+ },
233
+
234
+ createVideoTag: function () {
235
+ const videoTag = this.util.createElement('VIDEO');
236
+ this.plugins.video._setTagAttrs.call(this, videoTag);
237
+ return videoTag;
238
+ },
239
+
240
+ _setIframeAttrs: function (element) {
241
+ element.frameBorder = '0';
242
+ element.allowFullscreen = true;
243
+
244
+ const attrs = this.options.videoIframeAttrs;
245
+ if (!attrs) return;
246
+
247
+ for (let key in attrs) {
248
+ if (!this.util.hasOwn(attrs, key)) continue;
249
+ element.setAttribute(key, attrs[key]);
250
+ }
251
+ },
252
+
253
+ createIframeTag: function () {
254
+ const iframeTag = this.util.createElement('IFRAME');
255
+ this.plugins.video._setIframeAttrs.call(this, iframeTag);
256
+ return iframeTag;
257
+ },
258
+
259
+ /**
260
+ * @Override @Required fileManager
261
+ */
262
+ fileTags: ['iframe', 'video'],
263
+
264
+ /**
265
+ * @Override core, resizing, fileManager
266
+ * @description It is called from core.selectComponent.
267
+ * @param {Element} element Target element
268
+ */
269
+ select: function (element) {
270
+ this.plugins.video.onModifyMode.call(this, element, this.plugins.resizing.call_controller_resize.call(this, element, 'video'));
271
+ },
272
+
273
+ /**
274
+ * @Override fileManager, resizing
275
+ */
276
+ destroy: function (element) {
277
+ const frame = element || this.context.video._element;
278
+ const container = this.context.video._container;
279
+ const dataIndex = frame.getAttribute('data-index') * 1;
280
+ let focusEl = (container.previousElementSibling || container.nextElementSibling);
281
+
282
+ const emptyDiv = container.parentNode;
283
+ this.util.removeItem(container);
284
+ this.plugins.video.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, 'video', dataIndex, this.functions.onVideoUpload);
294
+
295
+ // history stack
296
+ this.history.push(false);
297
+ },
298
+
299
+ /**
300
+ * @Required @Override dialog
301
+ */
302
+ on: function (update) {
303
+ const contextVideo = this.context.video;
304
+
305
+ if (!update) {
306
+ contextVideo.inputX.value = contextVideo._origin_w = this.options.videoWidth === contextVideo._defaultSizeX ? '' : this.options.videoWidth;
307
+ contextVideo.inputY.value = contextVideo._origin_h = this.options.videoHeight === contextVideo._defaultSizeY ? '' : this.options.videoHeight;
308
+ contextVideo.proportion.disabled = true;
309
+ if (contextVideo.videoInputFile && this.options.videoMultipleFile) contextVideo.videoInputFile.setAttribute('multiple', 'multiple');
310
+ } else {
311
+ if (contextVideo.videoInputFile && this.options.videoMultipleFile) contextVideo.videoInputFile.removeAttribute('multiple');
312
+ }
313
+
314
+ if (contextVideo._resizing) {
315
+ this.plugins.video.setVideoRatioSelect.call(this, contextVideo._origin_h || contextVideo._defaultRatio);
316
+ }
317
+ },
318
+
319
+ /**
320
+ * @Required @Override dialog
321
+ */
322
+ open: function () {
323
+ this.plugins.dialog.open.call(this, 'video', 'video' === this.currentControllerName);
324
+ },
325
+
326
+ setVideoRatio: function (e) {
327
+ const contextVideo = this.context.video;
328
+ const value = e.target.options[e.target.selectedIndex].value;
329
+
330
+ contextVideo._defaultSizeY = contextVideo._videoRatio = !value ? contextVideo._defaultSizeY : (value * 100) + '%';
331
+ contextVideo.inputY.placeholder = !value ? '' : (value * 100) + '%';
332
+ contextVideo.inputY.value = '';
333
+ },
334
+
335
+ /**
336
+ * @Override resizing
337
+ * @param {String} xy 'x': width, 'y': height
338
+ * @param {KeyboardEvent} e Event object
339
+ */
340
+ setInputSize: function (xy, e) {
341
+ if (e && e.keyCode === 32) {
342
+ e.preventDefault();
343
+ return;
344
+ }
345
+
346
+ const contextVideo = this.context.video;
347
+ this.plugins.resizing._module_setInputSize.call(this, contextVideo, xy);
348
+
349
+ if (xy === 'y') {
350
+ this.plugins.video.setVideoRatioSelect.call(this, e.target.value || contextVideo._defaultRatio);
351
+ }
352
+ },
353
+
354
+ /**
355
+ * @Override resizing
356
+ */
357
+ setRatio: function () {
358
+ this.plugins.resizing._module_setRatio.call(this, this.context.video);
359
+ },
360
+
361
+ submit: function (e) {
362
+ const contextVideo = this.context.video;
363
+ const videoPlugin = this.plugins.video;
364
+
365
+ e.preventDefault();
366
+ e.stopPropagation();
367
+
368
+ contextVideo._align = contextVideo.modal.querySelector('input[name="suneditor_video_radio"]:checked').value;
369
+
370
+ try {
371
+ if (contextVideo.videoInputFile && contextVideo.videoInputFile.files.length > 0) {
372
+ this.showLoading();
373
+ videoPlugin.submitAction.call(this, this.context.video.videoInputFile.files);
374
+ } else if (contextVideo.videoUrlFile && contextVideo._linkValue.length > 0) {
375
+ this.showLoading();
376
+ videoPlugin.setup_url.call(this, contextVideo._linkValue);
377
+ }
378
+ } catch (error) {
379
+ this.closeLoading();
380
+ throw Error('[SUNEDITOR.video.submit.fail] cause : "' + error.message + '"');
381
+ } finally {
382
+ this.plugins.dialog.close.call(this);
383
+ }
384
+
385
+ return false;
386
+ },
387
+
388
+ submitAction: function (fileList) {
389
+ if (fileList.length === 0) return;
390
+
391
+ let fileSize = 0;
392
+ let files = [];
393
+ for (let i = 0, len = fileList.length; i < len; i++) {
394
+ if (/video/i.test(fileList[i].type)) {
395
+ files.push(fileList[i]);
396
+ fileSize += fileList[i].size;
397
+ }
398
+ }
399
+
400
+ const limitSize = this.options.videoUploadSizeLimit;
401
+ if (limitSize > 0) {
402
+ let infoSize = 0;
403
+ const videosInfo = this.context.video._infoList;
404
+ for (let i = 0, len = videosInfo.length; i < len; i++) {
405
+ infoSize += videosInfo[i].size * 1;
406
+ }
407
+
408
+ if ((fileSize + infoSize) > limitSize) {
409
+ this.closeLoading();
410
+ const err = '[SUNEDITOR.videoUpload.fail] Size of uploadable total videos: ' + (limitSize/1000) + 'KB';
411
+ if (typeof this.functions.onVideoUploadError !== 'function' || this.functions.onVideoUploadError(err, { 'limitSize': limitSize, 'currentSize': infoSize, 'uploadSize': fileSize }, this)) {
412
+ this.functions.noticeOpen(err);
413
+ }
414
+ return;
415
+ }
416
+ }
417
+
418
+ const contextVideo = this.context.video;
419
+ contextVideo._uploadFileLength = files.length;
420
+
421
+ const info = {
422
+ inputWidth: contextVideo.inputX.value,
423
+ inputHeight: contextVideo.inputY.value,
424
+ align: contextVideo._align,
425
+ isUpdate: this.context.dialog.updateModal,
426
+ element: contextVideo._element
427
+ };
428
+
429
+ if (typeof this.functions.onVideoUploadBefore === 'function') {
430
+ const result = this.functions.onVideoUploadBefore(files, info, this, function (data) {
431
+ if (data && this._w.Array.isArray(data.result)) {
432
+ this.plugins.video.register.call(this, info, data);
433
+ } else {
434
+ this.plugins.video.upload.call(this, info, data);
435
+ }
436
+ }.bind(this));
437
+
438
+ if (typeof result === 'undefined') return;
439
+ if (!result) {
440
+ this.closeLoading();
441
+ return;
442
+ }
443
+ if (typeof result === 'object' && result.length > 0) files = result;
444
+ }
445
+
446
+ this.plugins.video.upload.call(this, info, files);
447
+ },
448
+
449
+ error: function (message, response) {
450
+ this.closeLoading();
451
+ if (typeof this.functions.onVideoUploadError !== 'function' || this.functions.onVideoUploadError(message, response, this)) {
452
+ this.functions.noticeOpen(message);
453
+ throw Error('[SUNEDITOR.plugin.video.error] response: ' + message);
454
+ }
455
+ },
456
+
457
+ upload: function (info, files) {
458
+ if (!files) {
459
+ this.closeLoading();
460
+ return;
461
+ }
462
+ if (typeof files === 'string') {
463
+ this.plugins.video.error.call(this, files, null);
464
+ return;
465
+ }
466
+
467
+ const videoUploadUrl = this.options.videoUploadUrl;
468
+ const filesLen = this.context.dialog.updateModal ? 1 : files.length;
469
+
470
+ // server upload
471
+ if (typeof videoUploadUrl === 'string' && videoUploadUrl.length > 0) {
472
+ const formData = new FormData();
473
+ for (let i = 0; i < filesLen; i++) {
474
+ formData.append('file-' + i, files[i]);
475
+ }
476
+ this.plugins.fileManager.upload.call(this, videoUploadUrl, this.options.videoUploadHeader, formData, this.plugins.video.callBack_videoUpload.bind(this, info), this.functions.onVideoUploadError);
477
+ } else {
478
+ throw Error('[SUNEDITOR.videoUpload.fail] cause : There is no "videoUploadUrl" option.');
479
+ }
480
+ },
481
+
482
+ callBack_videoUpload: function (info, xmlHttp) {
483
+ if (typeof this.functions.videoUploadHandler === 'function') {
484
+ this.functions.videoUploadHandler(xmlHttp, info, this);
485
+ } else {
486
+ const response = JSON.parse(xmlHttp.responseText);
487
+ if (response.errorMessage) {
488
+ this.plugins.video.error.call(this, response.errorMessage, response);
489
+ } else {
490
+ this.plugins.video.register.call(this, info, response);
491
+ }
492
+ }
493
+ },
494
+
495
+ register: function (info, response) {
496
+ const fileList = response.result;
497
+ const videoTag = this.plugins.video.createVideoTag.call(this);
498
+
499
+ for (let i = 0, len = fileList.length, file; i < len; i++) {
500
+ file = { name: fileList[i].name, size: fileList[i].size };
501
+ this.plugins.video.create_video.call(this, (info.isUpdate ? info.element : videoTag.cloneNode(false)), fileList[i].url, info.inputWidth, info.inputHeight, info.align, file, info.isUpdate);
502
+ }
503
+
504
+ this.closeLoading();
505
+ },
506
+
507
+ setup_url: function (url) {
508
+ try {
509
+ const contextVideo = this.context.video;
510
+ if (!url) url = contextVideo._linkValue;
511
+ if (!url) return false;
512
+
513
+ /** iframe source */
514
+ if (/^<iframe.*\/iframe>$/.test(url)) {
515
+ const oIframe = (new this._w.DOMParser()).parseFromString(url, 'text/html').querySelector('iframe');
516
+ url = oIframe.src;
517
+ if (url.length === 0) return false;
518
+ }
519
+
520
+ /** youtube */
521
+ if (/youtu\.?be/.test(url)) {
522
+ if (!/^http/.test(url)) url = 'https://' + url;
523
+ url = url.replace('watch?v=', '');
524
+ if (!/^\/\/.+\/embed\//.test(url)) {
525
+ url = url.replace(url.match(/\/\/.+\//)[0], '//www.youtube.com/embed/').replace('&', '?&');
526
+ }
527
+
528
+ if (contextVideo._youtubeQuery.length > 0) {
529
+ if (/\?/.test(url)) {
530
+ const splitUrl = url.split('?');
531
+ url = splitUrl[0] + '?' + contextVideo._youtubeQuery + '&' + splitUrl[1];
532
+ } else {
533
+ url += '?' + contextVideo._youtubeQuery;
534
+ }
535
+ }
536
+ } else if (/vimeo\.com/.test(url)) {
537
+ if (url.endsWith('/')) {
538
+ url = url.slice(0, -1);
539
+ }
540
+ url = 'https://player.vimeo.com/video/' + url.slice(url.lastIndexOf('/') + 1);
541
+ }
542
+
543
+ this.plugins.video.create_video.call(this, this.plugins.video[(!/embed|iframe|player|\/e\/|\.php|\.html?/.test(url) && !/vimeo\.com/.test(url) ? "createVideoTag" : "createIframeTag")].call(this), url, contextVideo.inputX.value, contextVideo.inputY.value, contextVideo._align, null, this.context.dialog.updateModal);
544
+ } catch (error) {
545
+ throw Error('[SUNEDITOR.video.upload.fail] cause : "' + error.message + '"');
546
+ } finally {
547
+ this.closeLoading();
548
+ }
549
+ },
550
+
551
+ create_video: function (oFrame, src, width, height, align, file, isUpdate) {
552
+ this.context.resizing._resize_plugin = 'video';
553
+ const contextVideo = this.context.video;
554
+
555
+ let cover = null;
556
+ let container = null;
557
+ let init = false;
558
+
559
+ /** update */
560
+ if (isUpdate) {
561
+ oFrame = contextVideo._element;
562
+ if (oFrame.src !== src) {
563
+ init = true;
564
+ const isYoutube = /youtu\.?be/.test(src);
565
+ const isVimeo = /vimeo\.com/.test(src);
566
+ if ((isYoutube || isVimeo) && !/^iframe$/i.test(oFrame.nodeName)) {
567
+ const newTag = this.plugins.video.createIframeTag.call(this);
568
+ newTag.src = src;
569
+ oFrame.parentNode.replaceChild(newTag, oFrame);
570
+ contextVideo._element = oFrame = newTag;
571
+ } else if (!isYoutube && !isVimeo && !/^videoo$/i.test(oFrame.nodeName)) {
572
+ const newTag = this.plugins.video.createVideoTag.call(this);
573
+ newTag.src = src;
574
+ oFrame.parentNode.replaceChild(newTag, oFrame);
575
+ contextVideo._element = oFrame = newTag;
576
+ } else {
577
+ oFrame.src = src;
578
+ }
579
+ }
580
+ container = contextVideo._container;
581
+ cover = this.util.getParentElement(oFrame, 'FIGURE');
582
+ }
583
+ /** create */
584
+ else {
585
+ init = true;
586
+ oFrame.src = src;
587
+ contextVideo._element = oFrame;
588
+ cover = this.plugins.component.set_cover.call(this, oFrame);
589
+ container = this.plugins.component.set_container.call(this, cover, 'se-video-container');
590
+ }
591
+
592
+ /** rendering */
593
+ contextVideo._cover = cover;
594
+ contextVideo._container = container;
595
+
596
+ const inputUpdate = (this.plugins.resizing._module_getSizeX.call(this, contextVideo) !== (width || contextVideo._defaultSizeX)) || (this.plugins.resizing._module_getSizeY.call(this, contextVideo) !== (height || contextVideo._videoRatio));
597
+ const changeSize = !isUpdate || inputUpdate;
598
+
599
+ if (contextVideo._resizing) {
600
+ this.context.video._proportionChecked = contextVideo.proportion.checked;
601
+ oFrame.setAttribute('data-proportion', contextVideo._proportionChecked);
602
+ }
603
+
604
+ // size
605
+ let isPercent = false;
606
+ if (changeSize) {
607
+ isPercent = this.plugins.video.applySize.call(this);
608
+ }
609
+
610
+ // align
611
+ if (!(isPercent && align === 'center')) {
612
+ this.plugins.video.setAlign.call(this, null, oFrame, cover, container);
613
+ }
614
+
615
+ let changed = true;
616
+ if (!isUpdate) {
617
+ changed = this.insertComponent(container, false, true, !this.options.mediaAutoSelect);
618
+ if (!this.options.mediaAutoSelect) {
619
+ const line = this.appendFormatTag(container, null);
620
+ if (line) this.setRange(line, 0, line, 0);
621
+ }
622
+ } else if (contextVideo._resizing && this.context.resizing._rotateVertical && changeSize) {
623
+ this.plugins.resizing.setTransformSize.call(this, oFrame, null, null);
624
+ }
625
+
626
+ if (changed) {
627
+ if (init) {
628
+ this.plugins.fileManager.setInfo.call(this, 'video', oFrame, this.functions.onVideoUpload, file, true);
629
+ }
630
+ if (isUpdate) {
631
+ this.selectComponent(oFrame, 'video');
632
+ // history stack
633
+ this.history.push(false);
634
+ }
635
+ }
636
+
637
+ this.context.resizing._resize_plugin = '';
638
+ },
639
+
640
+ _update_videoCover: function (oFrame) {
641
+ if (!oFrame) return;
642
+
643
+ const contextVideo = this.context.video;
644
+
645
+ if (/^video$/i.test(oFrame.nodeName)) this.plugins.video._setTagAttrs.call(this, oFrame);
646
+ else this.plugins.video._setIframeAttrs.call(this, oFrame);
647
+
648
+ let existElement = (this.util.isRangeFormatElement(oFrame.parentNode) || this.util.isWysiwygDiv(oFrame.parentNode)) ?
649
+ oFrame : this.util.getFormatElement(oFrame) || oFrame;
650
+
651
+ const prevFrame = oFrame;
652
+ contextVideo._element = oFrame = oFrame.cloneNode(true);
653
+ const cover = contextVideo._cover = this.plugins.component.set_cover.call(this, oFrame);
654
+ const container = contextVideo._container = this.plugins.component.set_container.call(this, cover, 'se-video-container');
655
+
656
+ try {
657
+ const figcaption = existElement.querySelector('figcaption');
658
+ let caption = null;
659
+ if (!!figcaption) {
660
+ caption = this.util.createElement('DIV');
661
+ caption.innerHTML = figcaption.innerHTML;
662
+ this.util.removeItem(figcaption);
663
+ }
664
+
665
+ // size
666
+ const size = (oFrame.getAttribute('data-size') || oFrame.getAttribute('data-origin') || '').split(',');
667
+ this.plugins.video.applySize.call(this, (size[0] || prevFrame.style.width || prevFrame.width || ''), (size[1] || prevFrame.style.height || prevFrame.height || ''));
668
+
669
+ // align
670
+ const format = this.util.getFormatElement(prevFrame);
671
+ if (format) contextVideo._align = format.style.textAlign || format.style.float;
672
+ this.plugins.video.setAlign.call(this, null, oFrame, cover, container);
673
+
674
+ if (this.util.getParentElement(prevFrame, this.util.isNotCheckingNode)) {
675
+ prevFrame.parentNode.replaceChild(container, prevFrame);
676
+ } else if (this.util.isListCell(existElement)) {
677
+ const refer = this.util.getParentElement(prevFrame, function (current) { return current.parentNode === existElement; });
678
+ existElement.insertBefore(container, refer);
679
+ this.util.removeItem(prevFrame);
680
+ this.util.removeEmptyNode(refer, null, true);
681
+ } else if (this.util.isFormatElement(existElement)) {
682
+ const refer = this.util.getParentElement(prevFrame, function (current) { return current.parentNode === existElement; });
683
+ existElement = this.util.splitElement(existElement, refer);
684
+ existElement.parentNode.insertBefore(container, existElement);
685
+ this.util.removeItem(prevFrame);
686
+ this.util.removeEmptyNode(existElement, null, true);
687
+ if (existElement.children.length === 0) existElement.innerHTML = this.util.htmlRemoveWhiteSpace(existElement.innerHTML);
688
+ } else {
689
+ existElement.parentNode.replaceChild(container, existElement);
690
+ }
691
+
692
+ if (!!caption) existElement.parentNode.insertBefore(caption, container.nextElementSibling);
693
+ } catch (error) {
694
+ console.warn('[SUNEDITOR.video.error] Maybe the video tag is nested.', error);
695
+ }
696
+
697
+ this.plugins.fileManager.setInfo.call(this, 'video', oFrame, this.functions.onVideoUpload, null, true);
698
+ this.plugins.video.init.call(this);
699
+ },
700
+
701
+ /**
702
+ * @Required @Override fileManager, resizing
703
+ */
704
+ onModifyMode: function (element, size) {
705
+ const contextVideo = this.context.video;
706
+ contextVideo._element = element;
707
+ contextVideo._cover = this.util.getParentElement(element, 'FIGURE');
708
+ contextVideo._container = this.util.getParentElement(element, this.util.isMediaComponent);
709
+ contextVideo._align = element.style.float || element.getAttribute('data-align') || 'none';
710
+ element.style.float = '';
711
+
712
+ if (size) {
713
+ contextVideo._element_w = size.w;
714
+ contextVideo._element_h = size.h;
715
+ contextVideo._element_t = size.t;
716
+ contextVideo._element_l = size.l;
717
+ }
718
+
719
+ let origin = contextVideo._element.getAttribute('data-size') || contextVideo._element.getAttribute('data-origin');
720
+ let w, h;
721
+ if (origin) {
722
+ origin = origin.split(',');
723
+ w = origin[0];
724
+ h = origin[1];
725
+ } else if (size) {
726
+ w = size.w;
727
+ h = size.h;
728
+ }
729
+
730
+ contextVideo._origin_w = w || element.style.width || element.width || '';
731
+ contextVideo._origin_h = h || element.style.height || element.height || '';
732
+ },
733
+
734
+ /**
735
+ * @Required @Override fileManager, resizing
736
+ */
737
+ openModify: function (notOpen) {
738
+ const contextVideo = this.context.video;
739
+
740
+ if (contextVideo.videoUrlFile) contextVideo._linkValue = contextVideo.preview.textContent = contextVideo.videoUrlFile.value = (contextVideo._element.src || (contextVideo._element.querySelector('source') || '').src || '');
741
+ (contextVideo.modal.querySelector('input[name="suneditor_video_radio"][value="' + contextVideo._align + '"]') || contextVideo.modal.querySelector('input[name="suneditor_video_radio"][value="none"]')).checked = true;
742
+
743
+ if (contextVideo._resizing) {
744
+ this.plugins.resizing._module_setModifyInputSize.call(this, contextVideo, this.plugins.video);
745
+
746
+ const y = contextVideo._videoRatio = this.plugins.resizing._module_getSizeY.call(this, contextVideo);
747
+ const ratioSelected = this.plugins.video.setVideoRatioSelect.call(this, y);
748
+ if (!ratioSelected) contextVideo.inputY.value = contextVideo._onlyPercentage ? this.util.getNumber(y, 2) : y;
749
+ }
750
+
751
+ if (!notOpen) this.plugins.dialog.open.call(this, 'video', true);
752
+ },
753
+
754
+ setVideoRatioSelect: function (value) {
755
+ let ratioSelected = false;
756
+ const contextVideo = this.context.video;
757
+ const ratioOptions = contextVideo.videoRatioOption.options;
758
+
759
+ if (/%$/.test(value) || contextVideo._onlyPercentage) value = (this.util.getNumber(value, 2) / 100) + '';
760
+ else if (!this.util.isNumber(value) || (value * 1) >= 1) value = '';
761
+
762
+ contextVideo.inputY.placeholder = '';
763
+ for (let i = 0, len = ratioOptions.length; i < len; i++) {
764
+ if (ratioOptions[i].value === value) {
765
+ ratioSelected = ratioOptions[i].selected = true;
766
+ contextVideo.inputY.placeholder = !value ? '' : (value * 100) + '%';
767
+ }
768
+ else ratioOptions[i].selected = false;
769
+ }
770
+
771
+ return ratioSelected;
772
+ },
773
+
774
+ /**
775
+ * @Override fileManager
776
+ */
777
+ checkFileInfo: function () {
778
+ this.plugins.fileManager.checkInfo.call(this, 'video', ['iframe', 'video'], this.functions.onVideoUpload, this.plugins.video._update_videoCover.bind(this), true);
779
+ },
780
+
781
+ /**
782
+ * @Override fileManager
783
+ */
784
+ resetFileInfo: function () {
785
+ this.plugins.fileManager.resetInfo.call(this, 'video', this.functions.onVideoUpload);
786
+ },
787
+
788
+ /**
789
+ * @Override fileManager
790
+ */
791
+ applySize: function (w, h) {
792
+ const contextVideo = this.context.video;
793
+
794
+ if (!w) w = contextVideo.inputX.value || this.options.videoWidth;
795
+ if (!h) h = contextVideo.inputY.value || this.options.videoHeight;
796
+
797
+ if (contextVideo._onlyPercentage || /%$/.test(w) || !w) {
798
+ this.plugins.video.setPercentSize.call(this, (w || '100%'), (h || (/%$/.test(contextVideo._videoRatio) ? contextVideo._videoRatio : contextVideo._defaultRatio)));
799
+ return true;
800
+ } else if ((!w || w === 'auto') && (!h || h === 'auto')) {
801
+ this.plugins.video.setAutoSize.call(this);
802
+ } else {
803
+ this.plugins.video.setSize.call(this, w, (h || contextVideo._videoRatio || contextVideo._defaultRatio), false);
804
+ }
805
+
806
+ return false;
807
+ },
808
+
809
+ /**
810
+ * @Override resizing
811
+ */
812
+ sizeRevert: function () {
813
+ this.plugins.resizing._module_sizeRevert.call(this, this.context.video);
814
+ },
815
+
816
+ /**
817
+ * @Override resizing
818
+ */
819
+ setSize: function (w, h, notResetPercentage, direction) {
820
+ const contextVideo = this.context.video;
821
+ const onlyW = /^(rw|lw)$/.test(direction);
822
+ const onlyH = /^(th|bh)$/.test(direction);
823
+
824
+ if (!onlyH) w = this.util.getNumber(w, 0);
825
+ if (!onlyW) h = this.util.isNumber(h) ? h + contextVideo.sizeUnit : !h ? '' : h;
826
+ w = w ? w + contextVideo.sizeUnit : '';
827
+
828
+ if (!onlyH) contextVideo._element.style.width = w;
829
+ if (!onlyW) contextVideo._cover.style.paddingBottom = contextVideo._cover.style.height = h;
830
+
831
+ if (!onlyH && !/%$/.test(w)) {
832
+ contextVideo._cover.style.width = w;
833
+ contextVideo._container.style.width = '';
834
+ }
835
+
836
+ if (!onlyW && !/%$/.test(h)) {
837
+ contextVideo._element.style.height = h;
838
+ } else {
839
+ contextVideo._element.style.height = '';
840
+ }
841
+
842
+ if (!notResetPercentage) contextVideo._element.removeAttribute('data-percentage');
843
+
844
+ // save current size
845
+ this.plugins.resizing._module_saveCurrentSize.call(this, contextVideo);
846
+ },
847
+
848
+ /**
849
+ * @Override resizing
850
+ */
851
+ setAutoSize: function () {
852
+ this.plugins.video.setPercentSize.call(this, 100, this.context.video._defaultRatio);
853
+ },
854
+
855
+ /**
856
+ * @Override resizing
857
+ */
858
+ setOriginSize: function (dataSize) {
859
+ const contextVideo = this.context.video;
860
+ contextVideo._element.removeAttribute('data-percentage');
861
+
862
+ this.plugins.resizing.resetTransform.call(this, contextVideo._element);
863
+ this.plugins.video.cancelPercentAttr.call(this);
864
+
865
+ const originSize = ((dataSize ? contextVideo._element.getAttribute('data-size') : '') || contextVideo._element.getAttribute('data-origin') || '').split(',');
866
+
867
+ if (originSize) {
868
+ const w = originSize[0];
869
+ const h = originSize[1];
870
+
871
+ if (contextVideo._onlyPercentage || (/%$/.test(w) && (/%$/.test(h) || !/\d/.test(h)))) {
872
+ this.plugins.video.setPercentSize.call(this, w, h);
873
+ } else {
874
+ this.plugins.video.setSize.call(this, w, h);
875
+ }
876
+
877
+ // save current size
878
+ this.plugins.resizing._module_saveCurrentSize.call(this, contextVideo);
879
+ }
880
+ },
881
+
882
+ /**
883
+ * @Override resizing
884
+ */
885
+ setPercentSize: function (w, h) {
886
+ const contextVideo = this.context.video;
887
+ h = !!h && !/%$/.test(h) && !this.util.getNumber(h, 0) ? this.util.isNumber(h) ? h + '%' : h : this.util.isNumber(h) ? h + contextVideo.sizeUnit : (h || contextVideo._defaultRatio);
888
+
889
+ contextVideo._container.style.width = this.util.isNumber(w) ? w + '%' : w;
890
+ contextVideo._container.style.height = '';
891
+ contextVideo._cover.style.width = '100%';
892
+ contextVideo._cover.style.height = h;
893
+ contextVideo._cover.style.paddingBottom = h;
894
+ contextVideo._element.style.width = '100%';
895
+ contextVideo._element.style.height = '100%';
896
+ contextVideo._element.style.maxWidth = '';
897
+
898
+ if (contextVideo._align === 'center') this.plugins.video.setAlign.call(this, null, null, null, null);
899
+ contextVideo._element.setAttribute('data-percentage', w + ',' + h);
900
+
901
+ // save current size
902
+ this.plugins.resizing._module_saveCurrentSize.call(this, contextVideo);
903
+ },
904
+
905
+ /**
906
+ * @Override resizing
907
+ */
908
+ cancelPercentAttr: function () {
909
+ const contextVideo = this.context.video;
910
+
911
+ contextVideo._cover.style.width = '';
912
+ contextVideo._cover.style.height = '';
913
+ contextVideo._cover.style.paddingBottom = '';
914
+ contextVideo._container.style.width = '';
915
+ contextVideo._container.style.height = '';
916
+
917
+ this.util.removeClass(contextVideo._container, this.context.video._floatClassRegExp);
918
+ this.util.addClass(contextVideo._container, '__se__float-' + contextVideo._align);
919
+
920
+ if (contextVideo._align === 'center') this.plugins.video.setAlign.call(this, null, null, null, null);
921
+ },
922
+
923
+ /**
924
+ * @Override resizing
925
+ */
926
+ setAlign: function (align, element, cover, container) {
927
+ const contextVideo = this.context.video;
928
+
929
+ if (!align) align = contextVideo._align;
930
+ if (!element) element = contextVideo._element;
931
+ if (!cover) cover = contextVideo._cover;
932
+ if (!container) container = contextVideo._container;
933
+
934
+ if (align && align !== 'none') {
935
+ cover.style.margin = 'auto';
936
+ } else {
937
+ cover.style.margin = '0';
938
+ }
939
+
940
+ if (/%$/.test(element.style.width) && align === 'center') {
941
+ container.style.minWidth = '100%';
942
+ cover.style.width = container.style.width;
943
+ cover.style.height = cover.style.height;
944
+ cover.style.paddingBottom = !/%$/.test(cover.style.height) ? cover.style.height : this.util.getNumber((this.util.getNumber(cover.style.height, 2) / 100) * this.util.getNumber(cover.style.width, 2), 2) + '%';
945
+ } else {
946
+ container.style.minWidth = '';
947
+ cover.style.width = this.context.resizing._rotateVertical ? (element.style.height || element.offsetHeight) : (element.style.width || '100%');
948
+ cover.style.paddingBottom = cover.style.height;
949
+ }
950
+
951
+ if (!this.util.hasClass(container, '__se__float-' + align)) {
952
+ this.util.removeClass(container, contextVideo._floatClassRegExp);
953
+ this.util.addClass(container, '__se__float-' + align);
954
+ }
955
+
956
+ element.setAttribute('data-align', align);
957
+ },
958
+
959
+ /**
960
+ * @Override dialog
961
+ */
962
+ init: function () {
963
+ const contextVideo = this.context.video;
964
+ if (contextVideo.videoInputFile) contextVideo.videoInputFile.value = '';
965
+ if (contextVideo.videoUrlFile) contextVideo._linkValue = contextVideo.preview.textContent = contextVideo.videoUrlFile.value = '';
966
+ if (contextVideo.videoInputFile && contextVideo.videoUrlFile) {
967
+ contextVideo.videoUrlFile.removeAttribute('disabled');
968
+ contextVideo.preview.style.textDecoration = '';
969
+ }
970
+
971
+ contextVideo._origin_w = this.options.videoWidth;
972
+ contextVideo._origin_h = this.options.videoHeight;
973
+ contextVideo.modal.querySelector('input[name="suneditor_video_radio"][value="none"]').checked = true;
974
+
975
+ if (contextVideo._resizing) {
976
+ contextVideo.inputX.value = this.options.videoWidth === contextVideo._defaultSizeX ? '' : this.options.videoWidth;
977
+ contextVideo.inputY.value = this.options.videoHeight === contextVideo._defaultSizeY ? '' : this.options.videoHeight;
978
+ contextVideo.proportion.checked = true;
979
+ contextVideo.proportion.disabled = true;
980
+ this.plugins.video.setVideoRatioSelect.call(this, contextVideo._defaultRatio);
981
+ }
982
+ }
983
+ };