jodit 4.18.3 → 4.18.4
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.
- package/CHANGELOG.md +17 -0
- package/es2015/jodit.css +60 -1
- package/es2015/jodit.fat.min.css +1 -1
- package/es2015/jodit.fat.min.js +35 -10
- package/es2015/jodit.js +968 -152
- package/es2015/jodit.min.css +1 -1
- package/es2015/jodit.min.js +41 -16
- package/es2015/plugins/debug/debug.css +1 -1
- package/es2015/plugins/debug/debug.js +1 -1
- package/es2015/plugins/debug/debug.min.js +1 -1
- package/es2015/plugins/speech-recognize/speech-recognize.css +1 -1
- package/es2015/plugins/speech-recognize/speech-recognize.js +1 -1
- package/es2015/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es2018/jodit.fat.min.css +1 -1
- package/es2018/jodit.fat.min.js +33 -8
- package/es2018/jodit.min.css +1 -1
- package/es2018/jodit.min.js +33 -8
- package/es2018/plugins/debug/debug.min.js +1 -1
- package/es2018/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es2021/jodit.css +60 -1
- package/es2021/jodit.fat.min.css +1 -1
- package/es2021/jodit.fat.min.js +36 -11
- package/es2021/jodit.js +958 -150
- package/es2021/jodit.min.css +1 -1
- package/es2021/jodit.min.js +36 -11
- package/es2021/plugins/debug/debug.css +1 -1
- package/es2021/plugins/debug/debug.js +1 -1
- package/es2021/plugins/debug/debug.min.js +1 -1
- package/es2021/plugins/speech-recognize/speech-recognize.css +1 -1
- package/es2021/plugins/speech-recognize/speech-recognize.js +1 -1
- package/es2021/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es2021.en/jodit.css +60 -1
- package/es2021.en/jodit.fat.min.css +1 -1
- package/es2021.en/jodit.fat.min.js +36 -11
- package/es2021.en/jodit.js +932 -124
- package/es2021.en/jodit.min.css +1 -1
- package/es2021.en/jodit.min.js +51 -26
- package/es2021.en/plugins/debug/debug.css +1 -1
- package/es2021.en/plugins/debug/debug.js +1 -1
- package/es2021.en/plugins/debug/debug.min.js +1 -1
- package/es2021.en/plugins/speech-recognize/speech-recognize.css +1 -1
- package/es2021.en/plugins/speech-recognize/speech-recognize.js +1 -1
- package/es2021.en/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es5/jodit.css +76 -2
- package/es5/jodit.fat.min.css +1 -1
- package/es5/jodit.fat.min.js +2 -2
- package/es5/jodit.js +1103 -155
- package/es5/jodit.min.css +3 -3
- package/es5/jodit.min.js +2 -2
- package/es5/plugins/debug/debug.css +1 -1
- package/es5/plugins/debug/debug.js +1 -1
- package/es5/plugins/debug/debug.min.js +1 -1
- package/es5/plugins/speech-recognize/speech-recognize.css +1 -1
- package/es5/plugins/speech-recognize/speech-recognize.js +1 -1
- package/es5/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es5/polyfills.fat.min.js +1 -1
- package/es5/polyfills.js +1 -1
- package/es5/polyfills.min.js +1 -1
- package/esm/config.d.ts +25 -26
- package/esm/config.js +19 -21
- package/esm/core/constants.js +1 -1
- package/esm/core/helpers/utils/align.d.ts +1 -1
- package/esm/core/helpers/utils/align.js +2 -3
- package/esm/jodit.js +14 -4
- package/esm/langs/ar.d.ts +7 -0
- package/esm/langs/ar.js +8 -1
- package/esm/langs/az.d.ts +7 -0
- package/esm/langs/az.js +8 -1
- package/esm/langs/cs_cz.d.ts +7 -0
- package/esm/langs/cs_cz.js +9 -1
- package/esm/langs/de.d.ts +7 -0
- package/esm/langs/de.js +8 -1
- package/esm/langs/es.d.ts +7 -0
- package/esm/langs/es.js +8 -1
- package/esm/langs/fa.d.ts +7 -0
- package/esm/langs/fa.js +8 -1
- package/esm/langs/fi.d.ts +7 -0
- package/esm/langs/fi.js +8 -1
- package/esm/langs/fr.d.ts +7 -0
- package/esm/langs/fr.js +8 -1
- package/esm/langs/he.d.ts +7 -0
- package/esm/langs/he.js +8 -1
- package/esm/langs/hu.d.ts +7 -0
- package/esm/langs/hu.js +8 -1
- package/esm/langs/id.d.ts +7 -0
- package/esm/langs/id.js +8 -1
- package/esm/langs/it.d.ts +7 -0
- package/esm/langs/it.js +8 -1
- package/esm/langs/ja.d.ts +7 -0
- package/esm/langs/ja.js +8 -1
- package/esm/langs/ko.d.ts +7 -0
- package/esm/langs/ko.js +8 -1
- package/esm/langs/mn.d.ts +7 -0
- package/esm/langs/mn.js +8 -1
- package/esm/langs/nl.d.ts +7 -0
- package/esm/langs/nl.js +8 -1
- package/esm/langs/no.d.ts +7 -0
- package/esm/langs/no.js +8 -1
- package/esm/langs/pl.d.ts +7 -0
- package/esm/langs/pl.js +8 -1
- package/esm/langs/pt_br.d.ts +7 -0
- package/esm/langs/pt_br.js +8 -1
- package/esm/langs/ru.d.ts +7 -0
- package/esm/langs/ru.js +8 -1
- package/esm/langs/sk.d.ts +7 -0
- package/esm/langs/sk.js +8 -1
- package/esm/langs/sv.d.ts +7 -0
- package/esm/langs/sv.js +8 -1
- package/esm/langs/tr.d.ts +7 -0
- package/esm/langs/tr.js +8 -1
- package/esm/langs/ua.d.ts +7 -0
- package/esm/langs/ua.js +8 -1
- package/esm/langs/zh_cn.d.ts +7 -0
- package/esm/langs/zh_cn.js +8 -1
- package/esm/langs/zh_tw.d.ts +7 -0
- package/esm/langs/zh_tw.js +8 -1
- package/esm/plugins/all.d.ts +1 -0
- package/esm/plugins/all.js +1 -0
- package/esm/plugins/image-processor/image-processor.d.ts +4 -1
- package/esm/plugins/image-processor/image-processor.js +9 -3
- package/esm/plugins/image-properties/readers/align.d.ts +2 -2
- package/esm/plugins/index.d.ts +1 -0
- package/esm/plugins/inline-popup/config/items/iframe.d.ts +9 -1
- package/esm/plugins/inline-popup/config/items/iframe.js +15 -0
- package/esm/plugins/media/media.js +16 -7
- package/esm/plugins/resizer/config.d.ts +5 -3
- package/esm/plugins/resizer/config.js +8 -2
- package/esm/plugins/resizer/resizer.d.ts +10 -0
- package/esm/plugins/resizer/resizer.js +64 -15
- package/esm/plugins/video/config.d.ts +6 -0
- package/esm/plugins/video/config.js +77 -65
- package/esm/plugins/video-properties/interface.d.ts +31 -0
- package/esm/plugins/video-properties/interface.js +6 -0
- package/esm/plugins/video-properties/readers.d.ts +26 -0
- package/esm/plugins/video-properties/readers.js +65 -0
- package/esm/plugins/video-properties/replace.d.ts +15 -0
- package/esm/plugins/video-properties/replace.js +97 -0
- package/esm/plugins/video-properties/ui/ui-video-form.d.ts +41 -0
- package/esm/plugins/video-properties/ui/ui-video-form.js +228 -0
- package/esm/plugins/video-properties/video-properties.d.ts +37 -0
- package/esm/plugins/video-properties/video-properties.js +175 -0
- package/esm/plugins/video-properties/writers.d.ts +15 -0
- package/esm/plugins/video-properties/writers.js +73 -0
- package/esm/types/types.d.ts +1 -1
- package/package.json +1 -1
- package/types/config.d.ts +25 -26
- package/types/core/helpers/utils/align.d.ts +1 -1
- package/types/langs/ar.d.ts +7 -0
- package/types/langs/az.d.ts +7 -0
- package/types/langs/cs_cz.d.ts +7 -0
- package/types/langs/de.d.ts +7 -0
- package/types/langs/es.d.ts +7 -0
- package/types/langs/fa.d.ts +7 -0
- package/types/langs/fi.d.ts +7 -0
- package/types/langs/fr.d.ts +7 -0
- package/types/langs/he.d.ts +7 -0
- package/types/langs/hu.d.ts +7 -0
- package/types/langs/id.d.ts +7 -0
- package/types/langs/it.d.ts +7 -0
- package/types/langs/ja.d.ts +7 -0
- package/types/langs/ko.d.ts +7 -0
- package/types/langs/mn.d.ts +7 -0
- package/types/langs/nl.d.ts +7 -0
- package/types/langs/no.d.ts +7 -0
- package/types/langs/pl.d.ts +7 -0
- package/types/langs/pt_br.d.ts +7 -0
- package/types/langs/ru.d.ts +7 -0
- package/types/langs/sk.d.ts +7 -0
- package/types/langs/sv.d.ts +7 -0
- package/types/langs/tr.d.ts +7 -0
- package/types/langs/ua.d.ts +7 -0
- package/types/langs/zh_cn.d.ts +7 -0
- package/types/langs/zh_tw.d.ts +7 -0
- package/types/plugins/image-processor/image-processor.d.ts +4 -1
- package/types/plugins/image-properties/readers/align.d.ts +2 -2
- package/types/plugins/index.d.ts +1 -0
- package/types/plugins/inline-popup/config/items/iframe.d.ts +9 -1
- package/types/plugins/resizer/config.d.ts +5 -3
- package/types/plugins/resizer/resizer.d.ts +10 -0
- package/types/plugins/video/config.d.ts +6 -0
- package/types/plugins/video-properties/interface.d.ts +31 -0
- package/types/plugins/video-properties/readers.d.ts +26 -0
- package/types/plugins/video-properties/replace.d.ts +15 -0
- package/types/plugins/video-properties/ui/ui-video-form.d.ts +41 -0
- package/types/plugins/video-properties/video-properties.d.ts +37 -0
- package/types/plugins/video-properties/writers.d.ts +15 -0
- package/types/types/types.d.ts +1 -1
|
@@ -4,12 +4,18 @@
|
|
|
4
4
|
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
5
|
*/
|
|
6
6
|
import { Config } from "../../config.js";
|
|
7
|
-
Config.prototype.allowResizeTags = new Set([
|
|
7
|
+
Config.prototype.allowResizeTags = new Set([
|
|
8
|
+
'img',
|
|
9
|
+
'iframe',
|
|
10
|
+
'table',
|
|
11
|
+
'jodit',
|
|
12
|
+
'video'
|
|
13
|
+
]);
|
|
8
14
|
Config.prototype.resizer = {
|
|
9
15
|
showSize: true,
|
|
10
16
|
hideSizeTimeout: 1000,
|
|
11
17
|
forImageChangeAttributes: true,
|
|
12
18
|
min_width: 10,
|
|
13
19
|
min_height: 10,
|
|
14
|
-
useAspectRatio: new Set(['img'])
|
|
20
|
+
useAspectRatio: new Set(['img', 'video'])
|
|
15
21
|
};
|
|
@@ -26,6 +26,7 @@ export declare class resizer extends Plugin {
|
|
|
26
26
|
private height;
|
|
27
27
|
private ratio;
|
|
28
28
|
private rect;
|
|
29
|
+
private layer;
|
|
29
30
|
private sizeViewer;
|
|
30
31
|
/** @override */
|
|
31
32
|
protected afterInit(editor: IJodit): void;
|
|
@@ -33,6 +34,11 @@ export declare class resizer extends Plugin {
|
|
|
33
34
|
* Click in the editor area
|
|
34
35
|
*/
|
|
35
36
|
protected onEditorClick(e: MouseEvent): void;
|
|
37
|
+
/**
|
|
38
|
+
* What a click on `node` resizes: the media in it when it's a media
|
|
39
|
+
* wrapper (`jodit-media`, or one from `mediaWrappers`), or `node` itself
|
|
40
|
+
*/
|
|
41
|
+
private __resizable;
|
|
36
42
|
protected __afterInsertImage(image: HTMLImageElement): void;
|
|
37
43
|
private addEventListeners;
|
|
38
44
|
private onStartResizing;
|
|
@@ -46,6 +52,10 @@ export declare class resizer extends Plugin {
|
|
|
46
52
|
private onClickOutside;
|
|
47
53
|
private getWorkplacePosition;
|
|
48
54
|
private applySize;
|
|
55
|
+
/**
|
|
56
|
+
* The framed element can go with a wrapper round it, keeping its parent
|
|
57
|
+
*/
|
|
58
|
+
private get isElementInEditor();
|
|
49
59
|
private onDelete;
|
|
50
60
|
private __onChangeEditor;
|
|
51
61
|
/**
|
|
@@ -18,7 +18,7 @@ import { IS_ES_NEXT, IS_IE, KEY_ALT } from "../../core/constants.js";
|
|
|
18
18
|
import { autobind, watch } from "../../core/decorators/index.js";
|
|
19
19
|
import { Dom } from "../../core/dom/dom.js";
|
|
20
20
|
import { eventEmitter, pluginSystem } from "../../core/global.js";
|
|
21
|
-
import { $$, attr, css, dataBind, innerWidth, markOwner, offset } from "../../core/helpers/index.js";
|
|
21
|
+
import { $$, attr, css, dataBind, getWrappedMedia, innerWidth, markOwner, offset } from "../../core/helpers/index.js";
|
|
22
22
|
import { cssInline } from "../../core/helpers/utils/css.js";
|
|
23
23
|
import { Plugin } from "../../core/plugin/plugin.js";
|
|
24
24
|
import "./config.js";
|
|
@@ -36,6 +36,15 @@ function attrPixelSize(element, key) {
|
|
|
36
36
|
}
|
|
37
37
|
return parseFloat(value);
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* A video with no height of its own takes it from its width and its ratio,
|
|
41
|
+
* so resizing it keeps the ratio without setting a height.
|
|
42
|
+
*/
|
|
43
|
+
function heightFollowsWidth(element) {
|
|
44
|
+
return (Dom.isTag(element, 'video') &&
|
|
45
|
+
!attr(element, 'height') &&
|
|
46
|
+
!cssInline(element, 'height'));
|
|
47
|
+
}
|
|
39
48
|
/**
|
|
40
49
|
* The module creates a supporting frame for resizing of the elements img and table
|
|
41
50
|
*/
|
|
@@ -58,6 +67,8 @@ export class resizer extends Plugin {
|
|
|
58
67
|
<div class="jodit-resizer__bottom-left"></div>
|
|
59
68
|
<span>100x100</span>
|
|
60
69
|
</div>`);
|
|
70
|
+
// Clips the frame to the workplace, without the frame making it scroll
|
|
71
|
+
this.layer = this.j.c.div('jodit-resizer-layer');
|
|
61
72
|
this.sizeViewer = this.rect.getElementsByTagName('span')[0];
|
|
62
73
|
this.pointerX = 0;
|
|
63
74
|
this.pointerY = 0;
|
|
@@ -72,6 +83,10 @@ export class resizer extends Plugin {
|
|
|
72
83
|
if (Dom.isTag(this.element, 'img') && !this.element.complete) {
|
|
73
84
|
this.j.e.one(this.element, 'load', this.updateSize);
|
|
74
85
|
}
|
|
86
|
+
if (Dom.isTag(this.element, 'video') &&
|
|
87
|
+
this.element.readyState < HTMLMediaElement.HAVE_METADATA) {
|
|
88
|
+
this.j.e.one(this.element, 'loadedmetadata', this.updateSize);
|
|
89
|
+
}
|
|
75
90
|
}
|
|
76
91
|
};
|
|
77
92
|
this.updateSize = () => {
|
|
@@ -135,14 +150,33 @@ export class resizer extends Plugin {
|
|
|
135
150
|
let node = e.target;
|
|
136
151
|
const { editor, options: { allowResizeTags } } = this.j;
|
|
137
152
|
while (node && node !== editor) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
this.
|
|
153
|
+
const element = this.__resizable(node);
|
|
154
|
+
if (Dom.isTag(element, allowResizeTags)) {
|
|
155
|
+
this.__bind(element);
|
|
156
|
+
this.onClickElement(element);
|
|
141
157
|
return;
|
|
142
158
|
}
|
|
143
159
|
node = node.parentNode;
|
|
144
160
|
}
|
|
145
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* What a click on `node` resizes: the media in it when it's a media
|
|
164
|
+
* wrapper (`jodit-media`, or one from `mediaWrappers`), or `node` itself
|
|
165
|
+
*/
|
|
166
|
+
__resizable(node) {
|
|
167
|
+
var _a;
|
|
168
|
+
const media = (_a = getWrappedMedia(this.j, node)) !== null && _a !== void 0 ? _a : (Dom.isHTMLElement(node) && attr(node, 'data-jodit_fake_wrapper')
|
|
169
|
+
? node.firstElementChild
|
|
170
|
+
: null);
|
|
171
|
+
if (!media) {
|
|
172
|
+
return node;
|
|
173
|
+
}
|
|
174
|
+
// An embed is resized through its own wrapper
|
|
175
|
+
return Dom.isHTMLElement(media.parentNode) &&
|
|
176
|
+
attr(media.parentNode, 'data-jodit_iframe_wrapper')
|
|
177
|
+
? media.parentNode
|
|
178
|
+
: media;
|
|
179
|
+
}
|
|
146
180
|
__afterInsertImage(image) {
|
|
147
181
|
if (this.j.o.resizer.forImageChangeAttributes) {
|
|
148
182
|
return;
|
|
@@ -169,17 +203,15 @@ export class resizer extends Plugin {
|
|
|
169
203
|
})
|
|
170
204
|
.on(editor.ow, 'resize.resizer', this.updateSize)
|
|
171
205
|
.on('resize.resizer', this.updateSize)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
})
|
|
206
|
+
// The frame is outside the content, so it's moved to follow the
|
|
207
|
+
// element as the content scrolls
|
|
208
|
+
.on([editor.ow, editor.editor], 'scroll.resizer', this.updateSize)
|
|
177
209
|
.on(editor.ow, 'keydown.resizer', this.onKeyDown)
|
|
178
210
|
.on(editor.ow, 'keyup.resizer', this.onKeyUp)
|
|
179
211
|
.on(editor.ow, 'mouseup.resizer touchend.resizer', this.onClickOutside);
|
|
180
212
|
}
|
|
181
213
|
onStartResizing(resizeHandle, e) {
|
|
182
|
-
if (!this.element || !this.
|
|
214
|
+
if (!this.element || !this.isElementInEditor) {
|
|
183
215
|
this.hide();
|
|
184
216
|
return false;
|
|
185
217
|
}
|
|
@@ -233,8 +265,9 @@ export class resizer extends Plugin {
|
|
|
233
265
|
const className = this.handle.className;
|
|
234
266
|
let new_w, new_h;
|
|
235
267
|
const uar = this.j.o.resizer.useAspectRatio;
|
|
236
|
-
|
|
237
|
-
(uar === true || (uar && Dom.isTag(this.element, uar)))
|
|
268
|
+
const keepRatio = !this.isAltMode &&
|
|
269
|
+
(uar === true || Boolean(uar && Dom.isTag(this.element, uar)));
|
|
270
|
+
if (keepRatio) {
|
|
238
271
|
if (diff_x) {
|
|
239
272
|
new_w =
|
|
240
273
|
this.width +
|
|
@@ -266,7 +299,8 @@ export class resizer extends Plugin {
|
|
|
266
299
|
this.applySize(this.element, 'width', '100%');
|
|
267
300
|
}
|
|
268
301
|
}
|
|
269
|
-
if (new_h > this.j.o.resizer.min_height
|
|
302
|
+
if (new_h > this.j.o.resizer.min_height &&
|
|
303
|
+
!(keepRatio && heightFollowsWidth(this.element))) {
|
|
270
304
|
this.applySize(this.element, 'height', new_h);
|
|
271
305
|
}
|
|
272
306
|
this.updateSize();
|
|
@@ -312,10 +346,20 @@ export class resizer extends Plugin {
|
|
|
312
346
|
css(element, key, value);
|
|
313
347
|
}
|
|
314
348
|
}
|
|
349
|
+
/**
|
|
350
|
+
* The framed element can go with a wrapper round it, keeping its parent
|
|
351
|
+
*/
|
|
352
|
+
get isElementInEditor() {
|
|
353
|
+
return Boolean(this.element && this.j.editor.contains(this.element));
|
|
354
|
+
}
|
|
315
355
|
onDelete(e) {
|
|
316
356
|
if (!this.element) {
|
|
317
357
|
return;
|
|
318
358
|
}
|
|
359
|
+
if (!this.isElementInEditor) {
|
|
360
|
+
this.hide();
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
319
363
|
if (this.element.tagName !== 'JODIT') {
|
|
320
364
|
this.j.s.select(this.element);
|
|
321
365
|
}
|
|
@@ -327,7 +371,7 @@ export class resizer extends Plugin {
|
|
|
327
371
|
}
|
|
328
372
|
__onChangeEditor() {
|
|
329
373
|
if (this.isShown) {
|
|
330
|
-
if (!this.
|
|
374
|
+
if (!this.isElementInEditor) {
|
|
331
375
|
this.hide();
|
|
332
376
|
}
|
|
333
377
|
else {
|
|
@@ -439,9 +483,13 @@ export class resizer extends Plugin {
|
|
|
439
483
|
return;
|
|
440
484
|
}
|
|
441
485
|
this.isShown = true;
|
|
486
|
+
if (this.layer.parentNode !== this.j.workplace) {
|
|
487
|
+
markOwner(this.j, this.layer);
|
|
488
|
+
Dom.append(this.j.workplace, this.layer);
|
|
489
|
+
}
|
|
442
490
|
if (!this.rect.parentNode) {
|
|
443
491
|
markOwner(this.j, this.rect);
|
|
444
|
-
Dom.append(this.
|
|
492
|
+
Dom.append(this.layer, this.rect);
|
|
445
493
|
}
|
|
446
494
|
if (this.j.isFullSize) {
|
|
447
495
|
css(this.rect, 'zIndex', css(this.j.container, 'zIndex').toString());
|
|
@@ -462,6 +510,7 @@ export class resizer extends Plugin {
|
|
|
462
510
|
}
|
|
463
511
|
beforeDestruct(jodit) {
|
|
464
512
|
this.hide();
|
|
513
|
+
Dom.safeRemove(this.layer);
|
|
465
514
|
eventEmitter.off('hideHelpers', this.hide);
|
|
466
515
|
jodit.e.off(this.j.ow, '.resizer').off('.resizer');
|
|
467
516
|
}
|
|
@@ -53,3 +53,9 @@ declare module 'jodit/config' {
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* The video popup's tabs: Upload, when the uploader is configured, then Link
|
|
58
|
+
* and Code. `insert` runs what a tab inserts, which by default goes in at the
|
|
59
|
+
* selection the popup was opened with.
|
|
60
|
+
*/
|
|
61
|
+
export declare function videoTabs(jodit: IJodit, close: () => void, insert?: (run: () => void) => void): HTMLElement;
|
|
@@ -26,73 +26,85 @@ Config.prototype.video = {
|
|
|
26
26
|
defaultHandlerSuccess: insertVideos
|
|
27
27
|
};
|
|
28
28
|
Icon.set('video', videoIcon);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
new
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
29
|
+
/**
|
|
30
|
+
* The video popup's tabs: Upload, when the uploader is configured, then Link
|
|
31
|
+
* and Code. `insert` runs what a tab inserts, which by default goes in at the
|
|
32
|
+
* selection the popup was opened with.
|
|
33
|
+
*/
|
|
34
|
+
export function videoTabs(jodit, close, insert) {
|
|
35
|
+
const run = insert !== null && insert !== void 0 ? insert : ((fn) => {
|
|
36
|
+
jodit.s.restore();
|
|
37
|
+
fn();
|
|
38
|
+
});
|
|
39
|
+
const formLink = new UIForm(jodit, [
|
|
40
|
+
new UIBlock(jodit, [
|
|
41
|
+
new UIInput(jodit, {
|
|
42
|
+
name: 'url',
|
|
43
|
+
required: true,
|
|
44
|
+
label: 'URL',
|
|
45
|
+
placeholder: 'https://',
|
|
46
|
+
validators: ['url']
|
|
47
|
+
})
|
|
48
|
+
]),
|
|
49
|
+
new UIBlock(jodit, [
|
|
50
|
+
Button(jodit, '', 'Insert', 'primary').onAction(() => formLink.submit())
|
|
51
|
+
])
|
|
52
|
+
]), formCode = new UIForm(jodit, [
|
|
53
|
+
new UIBlock(jodit, [
|
|
54
|
+
new UITextArea(jodit, {
|
|
55
|
+
name: 'code',
|
|
56
|
+
required: true,
|
|
57
|
+
label: 'Embed code'
|
|
58
|
+
})
|
|
59
|
+
]),
|
|
60
|
+
new UIBlock(jodit, [
|
|
61
|
+
Button(jodit, '', 'Insert', 'primary').onAction(() => formCode.submit())
|
|
62
|
+
])
|
|
63
|
+
]), tabs = [], insertCode = (code) => {
|
|
64
|
+
run(() => jodit.s.insertHTML(prepareEmbedCode(jodit, code)));
|
|
65
|
+
close();
|
|
66
|
+
};
|
|
67
|
+
if (!insert) {
|
|
60
68
|
jodit.s.save();
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
69
|
+
}
|
|
70
|
+
const { uploader } = jodit.o;
|
|
71
|
+
// Base64 alone would put whole video files into the content
|
|
72
|
+
if (uploader.showTabInFileSelector !== false &&
|
|
73
|
+
(uploader.url || uploader.customUploadFunction)) {
|
|
74
|
+
tabs.push(UploadTab(jodit, data => {
|
|
75
|
+
run(() => {
|
|
66
76
|
var _a, _b;
|
|
67
|
-
jodit.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
77
|
+
return ((_b = (_a = jodit.o.video) === null || _a === void 0 ? void 0 : _a.defaultHandlerSuccess) !== null && _b !== void 0 ? _b : insertVideos).call(jodit, data);
|
|
78
|
+
});
|
|
79
|
+
}, { label: 'Drop file', accept: 'video/*' }));
|
|
80
|
+
}
|
|
81
|
+
tabs.push({
|
|
82
|
+
icon: 'link',
|
|
83
|
+
name: 'Link',
|
|
84
|
+
content: formLink.container
|
|
85
|
+
}, {
|
|
86
|
+
icon: 'source',
|
|
87
|
+
name: 'Code',
|
|
88
|
+
content: formCode.container
|
|
89
|
+
});
|
|
90
|
+
formLink.onSubmit(data => {
|
|
91
|
+
var _a, _b, _c, _d;
|
|
92
|
+
insertCode(call((_b = (_a = jodit.o.video) === null || _a === void 0 ? void 0 : _a.parseUrlToVideoEmbed) !== null && _b !== void 0 ? _b : convertMediaUrlToVideoEmbed, data.url, {
|
|
93
|
+
width: (_c = jodit.o.video) === null || _c === void 0 ? void 0 : _c.defaultWidth,
|
|
94
|
+
height: (_d = jodit.o.video) === null || _d === void 0 ? void 0 : _d.defaultHeight
|
|
95
|
+
}));
|
|
96
|
+
});
|
|
97
|
+
formCode.onSubmit(data => {
|
|
98
|
+
insertCode(data.code);
|
|
99
|
+
});
|
|
100
|
+
const box = TabsWidget(jodit, tabs);
|
|
101
|
+
// Its tabs are sized to their labels, as the file selector's are, since
|
|
102
|
+
// "Upload" doesn't fit a third of the popup
|
|
103
|
+
box.classList.add('jodit-file-selector');
|
|
104
|
+
return box;
|
|
105
|
+
}
|
|
106
|
+
Config.prototype.controls.video = {
|
|
107
|
+
popup: (jodit, current, close) => videoTabs(jodit, close),
|
|
96
108
|
tags: ['iframe'],
|
|
97
109
|
tooltip: 'Insert youtube/vimeo video'
|
|
98
110
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Jodit Editor (https://xdsoft.net/jodit/)
|
|
3
|
+
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
|
+
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* @module plugins/video-properties
|
|
8
|
+
*/
|
|
9
|
+
export type EditedMedia = HTMLVideoElement | HTMLAudioElement | HTMLIFrameElement;
|
|
10
|
+
export interface VideoPropertiesValues {
|
|
11
|
+
width: number | string;
|
|
12
|
+
height: number | string;
|
|
13
|
+
poster: string;
|
|
14
|
+
autoplay: boolean;
|
|
15
|
+
loop: boolean;
|
|
16
|
+
muted: boolean;
|
|
17
|
+
controls: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface VideoPropertiesState {
|
|
20
|
+
/**
|
|
21
|
+
* What the dialog was opened for: the media, or a wrapper round it
|
|
22
|
+
*/
|
|
23
|
+
target: HTMLElement;
|
|
24
|
+
media: EditedMedia;
|
|
25
|
+
ratio: number;
|
|
26
|
+
sizeIsLocked: boolean;
|
|
27
|
+
values: VideoPropertiesValues;
|
|
28
|
+
}
|
|
29
|
+
export interface VideoPropertiesAPI {
|
|
30
|
+
openPosterPopup: (target: HTMLElement) => void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Jodit Editor (https://xdsoft.net/jodit/)
|
|
3
|
+
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
|
+
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* @module plugins/video-properties
|
|
8
|
+
*/
|
|
9
|
+
import type { Nullable } from "../../types/index";
|
|
10
|
+
import type { EditedMedia, VideoPropertiesState } from "./interface";
|
|
11
|
+
/**
|
|
12
|
+
* The media `target` is, or holds as its wrapper (`jodit-media`, the resizer's
|
|
13
|
+
* `jodit` for an embed, or one from `mediaWrappers`)
|
|
14
|
+
* @private
|
|
15
|
+
*/
|
|
16
|
+
export declare function editedMedia(target: HTMLElement): Nullable<EditedMedia>;
|
|
17
|
+
/**
|
|
18
|
+
* The size `media` is given, inline or by its attribute
|
|
19
|
+
* @private
|
|
20
|
+
*/
|
|
21
|
+
export declare function ownSize(media: EditedMedia, key: 'width' | 'height'): string;
|
|
22
|
+
/**
|
|
23
|
+
* Read the media's values into the state
|
|
24
|
+
* @private
|
|
25
|
+
*/
|
|
26
|
+
export declare function readValues(state: VideoPropertiesState): void;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Jodit Editor (https://xdsoft.net/jodit/)
|
|
3
|
+
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
|
+
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
|
+
*/
|
|
6
|
+
import { Dom } from "../../core/dom/dom.js";
|
|
7
|
+
import { isNumeric } from "../../core/helpers/checker/is-numeric.js";
|
|
8
|
+
import { attr } from "../../core/helpers/utils/attr.js";
|
|
9
|
+
import { cssInline } from "../../core/helpers/utils/css.js";
|
|
10
|
+
import { normalSizeFromString } from "../image-properties/utils/utils.js";
|
|
11
|
+
const mediaSelector = 'video, audio, iframe';
|
|
12
|
+
/**
|
|
13
|
+
* The media `target` is, or holds as its wrapper (`jodit-media`, the resizer's
|
|
14
|
+
* `jodit` for an embed, or one from `mediaWrappers`)
|
|
15
|
+
* @private
|
|
16
|
+
*/
|
|
17
|
+
export function editedMedia(target) {
|
|
18
|
+
return (target.matches(mediaSelector)
|
|
19
|
+
? target
|
|
20
|
+
: target.querySelector(mediaSelector));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The size `media` is given, inline or by its attribute
|
|
24
|
+
* @private
|
|
25
|
+
*/
|
|
26
|
+
export function ownSize(media, key) {
|
|
27
|
+
return cssInline(media, key) || attr(media, key) || '';
|
|
28
|
+
}
|
|
29
|
+
const toNumber = (value) => parseFloat(value.toString());
|
|
30
|
+
/**
|
|
31
|
+
* Read the media's values into the state
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
export function readValues(state) {
|
|
35
|
+
const { media, values } = state;
|
|
36
|
+
const width = ownSize(media, 'width');
|
|
37
|
+
const height = ownSize(media, 'height');
|
|
38
|
+
values.width = width ? normalSizeFromString(width) : media.offsetWidth;
|
|
39
|
+
values.height = height ? normalSizeFromString(height) : '';
|
|
40
|
+
if (Dom.isTag(media, 'video') && media.videoWidth) {
|
|
41
|
+
state.ratio = media.videoWidth / media.videoHeight;
|
|
42
|
+
}
|
|
43
|
+
else if (isNumeric(values.width) && isNumeric(values.height)) {
|
|
44
|
+
state.ratio = toNumber(values.width) / toNumber(values.height);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
state.ratio = media.offsetWidth / media.offsetHeight || 1;
|
|
48
|
+
}
|
|
49
|
+
// One with no height takes it from its width
|
|
50
|
+
if (!height && isNumeric(values.width)) {
|
|
51
|
+
values.height = Math.round(toNumber(values.width) / state.ratio);
|
|
52
|
+
}
|
|
53
|
+
state.sizeIsLocked =
|
|
54
|
+
isNumeric(values.width) &&
|
|
55
|
+
isNumeric(values.height) &&
|
|
56
|
+
(!height ||
|
|
57
|
+
Math.abs(toNumber(values.width) - toNumber(values.height) * state.ratio) < 1);
|
|
58
|
+
values.poster = attr(media, 'poster') || '';
|
|
59
|
+
values.autoplay = media.hasAttribute('autoplay');
|
|
60
|
+
values.loop = media.hasAttribute('loop');
|
|
61
|
+
// Autoplay's own: turning it off gives the sound back
|
|
62
|
+
values.muted = media.hasAttribute('muted') && !values.autoplay;
|
|
63
|
+
// Without Autoplay it keeps its controls, or it could never be played
|
|
64
|
+
values.controls = media.hasAttribute('controls') || !values.autoplay;
|
|
65
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Jodit Editor (https://xdsoft.net/jodit/)
|
|
3
|
+
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
|
+
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* @module plugins/video-properties
|
|
8
|
+
*/
|
|
9
|
+
import type { IJodit } from "../../types/index";
|
|
10
|
+
/**
|
|
11
|
+
* Run `insert`, which inserts media at the cursor, with the cursor after
|
|
12
|
+
* `replaced`, and put what it inserts in place of `replaced`
|
|
13
|
+
* @private
|
|
14
|
+
*/
|
|
15
|
+
export declare function replaceMedia(j: IJodit, replaced: HTMLElement, insert: () => void): void;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Jodit Editor (https://xdsoft.net/jodit/)
|
|
3
|
+
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
|
+
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
|
+
*/
|
|
6
|
+
import { Dom } from "../../core/dom/dom.js";
|
|
7
|
+
import { attr } from "../../core/helpers/utils/attr.js";
|
|
8
|
+
import { css } from "../../core/helpers/utils/css.js";
|
|
9
|
+
import { editedMedia, ownSize, readValues } from "./readers.js";
|
|
10
|
+
import { applyValues } from "./writers.js";
|
|
11
|
+
/**
|
|
12
|
+
* Give `media`, in `outer`, the classes, inline styles, size and playback
|
|
13
|
+
* settings of `old`, in `oldOuter`. It keeps its own source and poster.
|
|
14
|
+
*/
|
|
15
|
+
function takeOver(j, oldOuter, old, outer, media) {
|
|
16
|
+
var _a;
|
|
17
|
+
const state = {
|
|
18
|
+
target: oldOuter,
|
|
19
|
+
media: old,
|
|
20
|
+
ratio: 1,
|
|
21
|
+
sizeIsLocked: true,
|
|
22
|
+
values: {
|
|
23
|
+
width: '',
|
|
24
|
+
height: '',
|
|
25
|
+
poster: '',
|
|
26
|
+
autoplay: false,
|
|
27
|
+
loop: false,
|
|
28
|
+
muted: false,
|
|
29
|
+
controls: true
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
readValues(state);
|
|
33
|
+
const pairs = [[old, media]];
|
|
34
|
+
// A wrapper kept in the content, which holds the alignment
|
|
35
|
+
if (oldOuter !== old &&
|
|
36
|
+
outer !== media &&
|
|
37
|
+
oldOuter.nodeName === outer.nodeName) {
|
|
38
|
+
pairs.push([oldOuter, outer]);
|
|
39
|
+
}
|
|
40
|
+
pairs.forEach(([from, to]) => {
|
|
41
|
+
['class', 'style'].forEach(name => attr(to, name, attr(from, name)));
|
|
42
|
+
});
|
|
43
|
+
// A video's size is CSS and an embed's its attributes, so the writer sizes
|
|
44
|
+
// one that's a different element
|
|
45
|
+
if (old.nodeName !== media.nodeName) {
|
|
46
|
+
css(media, { width: null, height: null });
|
|
47
|
+
if (!((_a = attr(media, 'style')) === null || _a === void 0 ? void 0 : _a.trim())) {
|
|
48
|
+
attr(media, 'style', null);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
state.media = media;
|
|
52
|
+
state.values.poster = attr(media, 'poster') || '';
|
|
53
|
+
applyValues(j, state, Boolean(ownSize(old, 'width') || ownSize(old, 'height')));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Run `insert`, which inserts media at the cursor, with the cursor after
|
|
57
|
+
* `replaced`, and put what it inserts in place of `replaced`
|
|
58
|
+
* @private
|
|
59
|
+
*/
|
|
60
|
+
export function replaceMedia(j, replaced, insert) {
|
|
61
|
+
const inserted = [];
|
|
62
|
+
const collect = (node) => {
|
|
63
|
+
inserted.push(node);
|
|
64
|
+
};
|
|
65
|
+
j.history.snapshot.transaction(() => {
|
|
66
|
+
j.s.focus();
|
|
67
|
+
j.s.setCursorAfter(replaced);
|
|
68
|
+
j.e.on('afterInsertNode', collect);
|
|
69
|
+
try {
|
|
70
|
+
insert();
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
j.e.off('afterInsertNode', collect);
|
|
74
|
+
}
|
|
75
|
+
const [outer] = inserted;
|
|
76
|
+
if (!Dom.isHTMLElement(outer)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const old = editedMedia(replaced);
|
|
80
|
+
const media = editedMedia(outer);
|
|
81
|
+
if (old && media) {
|
|
82
|
+
takeOver(j, replaced, old, outer, media);
|
|
83
|
+
}
|
|
84
|
+
// The resizer can have put an embed in its own wrapper already
|
|
85
|
+
const placed = Dom.isHTMLElement(outer.parentElement) &&
|
|
86
|
+
attr(outer.parentElement, 'data-jodit-temp')
|
|
87
|
+
? outer.parentElement
|
|
88
|
+
: outer;
|
|
89
|
+
// Where the old one was, since media Jodit counts as blocks can be put
|
|
90
|
+
// after the paragraph
|
|
91
|
+
Dom.before(replaced, placed);
|
|
92
|
+
Dom.safeRemove(replaced);
|
|
93
|
+
j.s.setCursorAfter(placed);
|
|
94
|
+
});
|
|
95
|
+
j.synchronizeValues();
|
|
96
|
+
j.e.fire('closeAllPopups');
|
|
97
|
+
}
|