jodit 4.17.5 → 4.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/es2015/jodit.css +87 -3
- package/es2015/jodit.fat.min.css +1 -1
- package/es2015/jodit.fat.min.js +9 -9
- package/es2015/jodit.js +873 -163
- package/es2015/jodit.min.css +1 -1
- package/es2015/jodit.min.js +16 -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 +9 -9
- package/es2018/jodit.min.css +1 -1
- package/es2018/jodit.min.js +9 -9
- package/es2018/plugins/debug/debug.min.js +1 -1
- package/es2018/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es2021/jodit.css +87 -3
- package/es2021/jodit.fat.min.css +1 -1
- package/es2021/jodit.fat.min.js +16 -16
- package/es2021/jodit.js +851 -163
- package/es2021/jodit.min.css +1 -1
- package/es2021/jodit.min.js +16 -16
- 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 +87 -3
- package/es2021.en/jodit.fat.min.css +1 -1
- package/es2021.en/jodit.fat.min.js +15 -15
- package/es2021.en/jodit.js +851 -163
- package/es2021.en/jodit.min.css +1 -1
- package/es2021.en/jodit.min.js +15 -15
- 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 +92 -6
- package/es5/jodit.fat.min.css +1 -1
- package/es5/jodit.fat.min.js +2 -2
- package/es5/jodit.js +1141 -223
- 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 +65 -2
- package/esm/config.js +29 -2
- package/esm/core/constants.js +1 -1
- package/esm/core/helpers/utils/align.d.ts +11 -5
- package/esm/core/helpers/utils/align.js +28 -8
- package/esm/core/helpers/utils/index.d.ts +1 -0
- package/esm/core/helpers/utils/index.js +1 -0
- package/esm/core/helpers/utils/media-wrapper.d.ts +29 -0
- package/esm/core/helpers/utils/media-wrapper.js +63 -0
- package/esm/core/selection/selection.js +34 -8
- package/esm/modules/history/history.js +20 -12
- package/esm/modules/toolbar/button/button.d.ts +18 -0
- package/esm/modules/toolbar/button/button.js +75 -5
- package/esm/plugins/font/config.d.ts +12 -0
- package/esm/plugins/font/config.js +25 -10
- package/esm/plugins/font/font-picker.d.ts +51 -0
- package/esm/plugins/font/font-picker.js +238 -0
- package/esm/plugins/font/font.js +12 -0
- package/esm/plugins/font/google-fonts.d.ts +65 -0
- package/esm/plugins/font/google-fonts.js +143 -0
- package/esm/plugins/image-properties/readers/index.js +2 -1
- package/esm/plugins/image-properties/writers/index.js +2 -2
- package/esm/plugins/inline-popup/config/items/img.js +10 -8
- package/esm/plugins/inline-popup/inline-popup.js +13 -6
- package/esm/plugins/line-height/line-height.js +1 -1
- package/esm/plugins/media/config.d.ts +26 -1
- package/esm/plugins/media/config.js +1 -3
- package/esm/plugins/media/media.js +12 -4
- package/esm/plugins/placeholder/placeholder.js +1 -1
- package/esm/plugins/source/editor/engines/ace.d.ts +2 -0
- package/esm/plugins/source/editor/engines/ace.js +35 -3
- package/esm/plugins/video/config.js +24 -4
- package/esm/types/toolbar.d.ts +2 -0
- package/esm/types/types.d.ts +13 -1
- package/package.json +1 -1
- package/types/config.d.ts +65 -2
- package/types/core/helpers/utils/align.d.ts +11 -5
- package/types/core/helpers/utils/index.d.ts +1 -0
- package/types/core/helpers/utils/media-wrapper.d.ts +29 -0
- package/types/modules/toolbar/button/button.d.ts +18 -0
- package/types/plugins/font/config.d.ts +12 -0
- package/types/plugins/font/font-picker.d.ts +51 -0
- package/types/plugins/font/google-fonts.d.ts +65 -0
- package/types/plugins/media/config.d.ts +26 -1
- package/types/plugins/source/editor/engines/ace.d.ts +2 -0
- package/types/types/toolbar.d.ts +2 -0
- package/types/types/types.d.ts +13 -1
|
@@ -0,0 +1,143 @@
|
|
|
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 { ViewComponent } from "../../core/component/view-component.js";
|
|
7
|
+
import { Dom } from "../../core/dom/dom.js";
|
|
8
|
+
import { Ajax } from "../../core/request/ajax.js";
|
|
9
|
+
const CSS_URL = 'https://fonts.googleapis.com/css2';
|
|
10
|
+
// `cursive` and `fantasy` stand in badly for a handwriting or display font
|
|
11
|
+
const GENERIC_FAMILIES = {
|
|
12
|
+
serif: 'serif',
|
|
13
|
+
monospace: 'monospace'
|
|
14
|
+
};
|
|
15
|
+
const libraries = new Map();
|
|
16
|
+
/**
|
|
17
|
+
* The fonts of the library at `url`, fetched once for every editor on the page
|
|
18
|
+
*/
|
|
19
|
+
function library(jodit, url) {
|
|
20
|
+
let fonts = libraries.get(url);
|
|
21
|
+
if (!fonts) {
|
|
22
|
+
const ajax = new Ajax({ url, method: 'GET' }, jodit.o.defaultAjaxOptions);
|
|
23
|
+
fonts = ajax
|
|
24
|
+
.send()
|
|
25
|
+
.then(response => response.json())
|
|
26
|
+
// A font without a regular style can't be asked for by its name alone
|
|
27
|
+
.then(({ items }) => items.filter(font => font.variants.includes('regular')))
|
|
28
|
+
.catch(() => {
|
|
29
|
+
libraries.delete(url);
|
|
30
|
+
return [];
|
|
31
|
+
})
|
|
32
|
+
.finally(() => ajax.destruct());
|
|
33
|
+
libraries.set(url, fonts);
|
|
34
|
+
}
|
|
35
|
+
return fonts;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The first family of a `font-family` value, unquoted and in lower case for
|
|
39
|
+
* comparing
|
|
40
|
+
*/
|
|
41
|
+
export function firstFamily(fontFamily) {
|
|
42
|
+
return fontFamily.split(',')[0].replace(/["']/g, '').trim().toLowerCase();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Loads fonts from Google Fonts: the whole of each font the content uses into
|
|
46
|
+
* the editor's document, and just the letters of its name for the font list
|
|
47
|
+
*/
|
|
48
|
+
export class GoogleFonts extends ViewComponent {
|
|
49
|
+
className() {
|
|
50
|
+
return 'GoogleFonts';
|
|
51
|
+
}
|
|
52
|
+
constructor(jodit) {
|
|
53
|
+
super(jodit);
|
|
54
|
+
/**
|
|
55
|
+
* The library by lower-case family, empty until it arrives
|
|
56
|
+
*/
|
|
57
|
+
this.fonts = new Map();
|
|
58
|
+
this.__links = [];
|
|
59
|
+
this.__loaded = new WeakMap();
|
|
60
|
+
this.__previewed = new Set();
|
|
61
|
+
this.ready = library(jodit, jodit.o.font.googleFonts.url).then(fonts => {
|
|
62
|
+
fonts.forEach(font => this.fonts.set(font.family.toLowerCase(), font));
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The library's font named first in a `font-family` value
|
|
67
|
+
*/
|
|
68
|
+
find(fontFamily) {
|
|
69
|
+
return this.fonts.get(firstFamily(fontFamily));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The `font-family` value that applies a font
|
|
73
|
+
*/
|
|
74
|
+
value(font) {
|
|
75
|
+
var _a;
|
|
76
|
+
return `'${font.family}', ${(_a = GENERIC_FAMILIES[font.category]) !== null && _a !== void 0 ? _a : 'sans-serif'}`;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Loads the regular, italic, bold and bold italic styles that a font has
|
|
80
|
+
* into the editor's document
|
|
81
|
+
*/
|
|
82
|
+
load(font) {
|
|
83
|
+
const loaded = this.__loadedInto(this.j.ed);
|
|
84
|
+
if (loaded.has(font.family)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
loaded.add(font.family);
|
|
88
|
+
const styles = [
|
|
89
|
+
['regular', '0,400'],
|
|
90
|
+
['700', '0,700'],
|
|
91
|
+
['italic', '1,400'],
|
|
92
|
+
['700italic', '1,700']
|
|
93
|
+
]
|
|
94
|
+
.filter(([variant]) => font.variants.includes(variant))
|
|
95
|
+
.map(([, style]) => style);
|
|
96
|
+
this.__append(this.j.ed, `family=${this.__family(font)}:ital,wght@${styles.join(';')}`);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Loads every font of the library that the content uses
|
|
100
|
+
*/
|
|
101
|
+
loadUsed() {
|
|
102
|
+
this.j.editor
|
|
103
|
+
.querySelectorAll('[style*="font-family"]')
|
|
104
|
+
.forEach(element => {
|
|
105
|
+
const font = this.find(element.style.fontFamily);
|
|
106
|
+
font && this.load(font);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Loads into the page just the letters of each font's name, so the font
|
|
111
|
+
* list can show the name in its font
|
|
112
|
+
*/
|
|
113
|
+
preview(fonts) {
|
|
114
|
+
const loaded = this.__loadedInto(this.j.od);
|
|
115
|
+
fonts = fonts.filter(font => !this.__previewed.has(font.family) && !loaded.has(font.family));
|
|
116
|
+
if (!fonts.length) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
fonts.forEach(font => this.__previewed.add(font.family));
|
|
120
|
+
const families = fonts.map(font => `family=${this.__family(font)}`), letters = new Set(fonts.map(font => font.family).join(''));
|
|
121
|
+
this.__append(this.j.od, `${families.join('&')}&text=${encodeURIComponent([...letters].join(''))}`);
|
|
122
|
+
}
|
|
123
|
+
__loadedInto(doc) {
|
|
124
|
+
var _a;
|
|
125
|
+
const loaded = (_a = this.__loaded.get(doc)) !== null && _a !== void 0 ? _a : new Set();
|
|
126
|
+
this.__loaded.set(doc, loaded);
|
|
127
|
+
return loaded;
|
|
128
|
+
}
|
|
129
|
+
__family(font) {
|
|
130
|
+
return encodeURIComponent(font.family).replace(/%20/g, '+');
|
|
131
|
+
}
|
|
132
|
+
__append(doc, query) {
|
|
133
|
+
const link = doc.createElement('link');
|
|
134
|
+
link.rel = 'stylesheet';
|
|
135
|
+
link.href = `${CSS_URL}?${query}&display=swap`;
|
|
136
|
+
doc.head.appendChild(link);
|
|
137
|
+
this.__links.push(link);
|
|
138
|
+
}
|
|
139
|
+
destruct() {
|
|
140
|
+
this.__links.forEach(link => Dom.safeRemove(link));
|
|
141
|
+
super.destruct();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
4
|
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
5
|
*/
|
|
6
|
+
import { getAlignClasses } from "../../../core/helpers/utils/align.js";
|
|
6
7
|
import { attr } from "../../../core/helpers/utils/attr.js";
|
|
7
8
|
import { cssInline } from "../../../core/helpers/utils/css.js";
|
|
8
9
|
import { readAlign } from "./align.js";
|
|
@@ -15,7 +16,7 @@ import { readSizes } from "./size.js";
|
|
|
15
16
|
*/
|
|
16
17
|
export async function readValuesFromImage(j, state) {
|
|
17
18
|
const { sourceImage: image, values } = state;
|
|
18
|
-
readAlign(image, values, j
|
|
19
|
+
readAlign(image, values, getAlignClasses(j, image));
|
|
19
20
|
// Border radius
|
|
20
21
|
values.borderRadius =
|
|
21
22
|
parseInt(cssInline(image, 'border-radius') || '0', 10) || 0;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
5
|
*/
|
|
6
6
|
import { Dom } from "../../../core/dom/dom.js";
|
|
7
|
-
import { attr, hAlignElement } from "../../../core/helpers/utils/index.js";
|
|
7
|
+
import { attr, getAlignClasses, hAlignElement } from "../../../core/helpers/utils/index.js";
|
|
8
8
|
import { css } from "../../../core/helpers/utils/css.js";
|
|
9
9
|
import { applyLink } from "./link.js";
|
|
10
10
|
import { applyMargin } from "./margin.js";
|
|
@@ -49,6 +49,6 @@ export function applyValuesToImage(j, state, image) {
|
|
|
49
49
|
attr(image, 'id', id || null);
|
|
50
50
|
}
|
|
51
51
|
if (opt.image.editAlign) {
|
|
52
|
-
hAlignElement(image, align, j
|
|
52
|
+
hAlignElement(image, align, getAlignClasses(j, image));
|
|
53
53
|
}
|
|
54
54
|
}
|
|
@@ -5,11 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { Dom } from "../../../../core/dom/index.js";
|
|
7
7
|
import { isString } from "../../../../core/helpers/checker/is-string.js";
|
|
8
|
-
import { getHAlign, hAlignElement } from "../../../../core/helpers/utils/align.js";
|
|
8
|
+
import { getAlignClasses, getHAlign, hAlignElement } from "../../../../core/helpers/utils/align.js";
|
|
9
9
|
import { css } from "../../../../core/helpers/utils/css.js";
|
|
10
|
-
|
|
11
|
-
// keep inline styles
|
|
12
|
-
const alignClasses = (editor, elm) => Dom.isTag(elm, 'img') ? editor.o.imageAlignClasses : null;
|
|
10
|
+
import { getMediaWrapper } from "../../../../core/helpers/utils/media-wrapper.js";
|
|
13
11
|
// A Normal image sits where the text of its line is aligned (#1531)
|
|
14
12
|
const lineAlign = (editor, elm) => {
|
|
15
13
|
const block = (elm && Dom.closest(elm, Dom.isBlock, editor.editor)) || editor.editor;
|
|
@@ -30,7 +28,7 @@ const lineAlign = (editor, elm) => {
|
|
|
30
28
|
return 'left';
|
|
31
29
|
}
|
|
32
30
|
};
|
|
33
|
-
const currentAlign = (editor, elm) =>
|
|
31
|
+
const currentAlign = (editor, elm) => elm ? getHAlign(elm, getAlignClasses(editor, elm)) : 'normal';
|
|
34
32
|
export const align = {
|
|
35
33
|
name: 'left',
|
|
36
34
|
childTemplate: (_, __, value) => value,
|
|
@@ -48,7 +46,10 @@ export const align = {
|
|
|
48
46
|
currentAlign(editor, button.target) === value.toLowerCase());
|
|
49
47
|
},
|
|
50
48
|
exec: (editor, elm, { control }) => {
|
|
51
|
-
|
|
49
|
+
// A media wrapper kept in the content (`mediaWrappers`) is aligned itself
|
|
50
|
+
if (!Dom.isHTMLElement(elm) ||
|
|
51
|
+
(!Dom.isTag(elm, new Set(['img', 'jodit', 'jodit-media'])) &&
|
|
52
|
+
getMediaWrapper(editor, elm) !== elm)) {
|
|
52
53
|
return;
|
|
53
54
|
}
|
|
54
55
|
const command = control.args && isString(control.args[0])
|
|
@@ -57,10 +58,11 @@ export const align = {
|
|
|
57
58
|
if (!command) {
|
|
58
59
|
return false;
|
|
59
60
|
}
|
|
60
|
-
hAlignElement(elm, command,
|
|
61
|
+
hAlignElement(elm, command, getAlignClasses(editor, elm));
|
|
61
62
|
if (Dom.isTag(elm, new Set(['jodit', 'jodit-media'])) &&
|
|
62
63
|
elm.firstElementChild) {
|
|
63
|
-
|
|
64
|
+
const media = elm.firstElementChild;
|
|
65
|
+
hAlignElement(media, command, getAlignClasses(editor, media));
|
|
64
66
|
}
|
|
65
67
|
editor.synchronizeValues();
|
|
66
68
|
editor.e.fire('recalcPositionPopup');
|
|
@@ -16,7 +16,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
16
16
|
import { autobind, cache, debounce, wait, watch } from "../../core/decorators/index.js";
|
|
17
17
|
import { Dom } from "../../core/dom/index.js";
|
|
18
18
|
import { pluginSystem } from "../../core/global.js";
|
|
19
|
-
import { camelCase, isArray, isFunction, isString, keys, position, splitArray, toArray } from "../../core/helpers/index.js";
|
|
19
|
+
import { camelCase, getMediaWrapper, isArray, isFunction, isString, keys, position, splitArray, toArray } from "../../core/helpers/index.js";
|
|
20
20
|
import { Plugin } from "../../core/plugin/index.js";
|
|
21
21
|
import { UIElement } from "../../core/ui/index.js";
|
|
22
22
|
import { Popup } from "../../core/ui/popup/index.js";
|
|
@@ -45,11 +45,15 @@ export class inlinePopup extends Plugin {
|
|
|
45
45
|
return makeCollection(this.jodit, this.popup);
|
|
46
46
|
}
|
|
47
47
|
onClick(node) {
|
|
48
|
-
const elements = this.elmsList, target = Dom.isTag(node, 'img')
|
|
48
|
+
const elements = this.elmsList, mediaWrapper = getMediaWrapper(this.j, node), target = mediaWrapper !== null && mediaWrapper !== void 0 ? mediaWrapper : (Dom.isTag(node, 'img')
|
|
49
49
|
? node
|
|
50
|
-
: Dom.closest(node, elements, this.j.editor);
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
: Dom.closest(node, elements, this.j.editor));
|
|
51
|
+
// A media wrapper kept in the content gets the media popup
|
|
52
|
+
const type = mediaWrapper
|
|
53
|
+
? 'jodit-media'
|
|
54
|
+
: target === null || target === void 0 ? void 0 : target.nodeName.toLowerCase();
|
|
55
|
+
if (target && type && this.canShowPopupForType(type)) {
|
|
56
|
+
this.showPopup(() => position(target, this.j), type, target);
|
|
53
57
|
return false;
|
|
54
58
|
}
|
|
55
59
|
}
|
|
@@ -263,7 +267,10 @@ export class inlinePopup extends Plugin {
|
|
|
263
267
|
this.removeListenersForElements();
|
|
264
268
|
}
|
|
265
269
|
_eventsList() {
|
|
266
|
-
|
|
270
|
+
var _a;
|
|
271
|
+
// Clicks on a media wrapper kept in the content (`mediaWrappers`)
|
|
272
|
+
const wrapperTags = Object.values((_a = this.j.o.mediaWrappers) !== null && _a !== void 0 ? _a : {}).map(({ tag }) => tag.toLowerCase());
|
|
273
|
+
const el = [...new Set(this.elmsList.concat(wrapperTags))];
|
|
267
274
|
return el
|
|
268
275
|
.map(e => camelCase(`click_${e}`))
|
|
269
276
|
.concat(el.map(e => camelCase(`touchstart_${e}`)))
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
4
|
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
5
|
*/
|
|
6
|
+
/**
|
|
7
|
+
* @module plugins/media
|
|
8
|
+
*/
|
|
9
|
+
import type { IDictionary, MediaWrapper } from "../../types/index";
|
|
6
10
|
declare module 'jodit/config' {
|
|
7
11
|
interface Config {
|
|
8
12
|
/**
|
|
@@ -17,6 +21,27 @@ declare module 'jodit/config' {
|
|
|
17
21
|
* Media tags
|
|
18
22
|
*/
|
|
19
23
|
mediaBlocks: string[];
|
|
24
|
+
/**
|
|
25
|
+
* Wrappers that media keep in the content, by the media's tag name, such as
|
|
26
|
+
* Froala's `<span class="fr-video">` around video, audio and embeds. An
|
|
27
|
+
* element with a wrapper for its tag:
|
|
28
|
+
* - is wrapped in it instead of the editor-only `mediaFakeTag` when its tag
|
|
29
|
+
* is one of `mediaBlocks`, or left in it when it's already in one
|
|
30
|
+
* - goes in inside it when the video popup inserts it, as an upload, a
|
|
31
|
+
* link's embed or embed code that is one element
|
|
32
|
+
* - opens the media popup for its wrapper when clicked, whose Align and
|
|
33
|
+
* Delete act on the wrapper
|
|
34
|
+
*
|
|
35
|
+
* A wrapper is given `contenteditable="false"` and `draggable="true"`,
|
|
36
|
+
* which stay in the value.
|
|
37
|
+
* ```javascript
|
|
38
|
+
* const frVideo = { tag: 'span', className: 'fr-video' };
|
|
39
|
+
*
|
|
40
|
+
* Jodit.make('#editor', {
|
|
41
|
+
* mediaWrappers: { video: frVideo, audio: frVideo, iframe: frVideo }
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
mediaWrappers: IDictionary<MediaWrapper>;
|
|
20
46
|
}
|
|
21
47
|
}
|
|
22
|
-
export {};
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
4
|
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
5
|
*/
|
|
6
|
-
/**
|
|
7
|
-
* @module plugins/media
|
|
8
|
-
*/
|
|
9
6
|
import { Config } from "../../config.js";
|
|
10
7
|
Config.prototype.mediaFakeTag = 'jodit-media';
|
|
11
8
|
Config.prototype.mediaInFakeBlock = true;
|
|
12
9
|
Config.prototype.mediaBlocks = ['video', 'audio'];
|
|
10
|
+
Config.prototype.mediaWrappers = {};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import * as consts from "../../core/constants.js";
|
|
7
7
|
import { Dom } from "../../core/dom/dom.js";
|
|
8
8
|
import { pluginSystem } from "../../core/global.js";
|
|
9
|
-
import { $$, attr, dataBind } from "../../core/helpers/utils/index.js";
|
|
9
|
+
import { $$, attr, dataBind, getMediaWrapper, wrapMedia } from "../../core/helpers/utils/index.js";
|
|
10
10
|
import { css, cssInline } from "../../core/helpers/utils/css.js";
|
|
11
11
|
import "./config.js";
|
|
12
12
|
/**
|
|
@@ -14,13 +14,18 @@ import "./config.js";
|
|
|
14
14
|
*/
|
|
15
15
|
export function media(editor) {
|
|
16
16
|
const keyFake = 'jodit_fake_wrapper';
|
|
17
|
-
const { mediaFakeTag, mediaBlocks, mediaInFakeBlock } = editor.options;
|
|
17
|
+
const { mediaFakeTag, mediaBlocks, mediaInFakeBlock, mediaWrappers } = editor.options;
|
|
18
18
|
const wrap = (element) => {
|
|
19
|
+
var _a;
|
|
19
20
|
if (element.parentNode &&
|
|
20
21
|
attr(element.parentNode, 'data-jodit_iframe_wrapper')) {
|
|
21
22
|
element = element.parentNode;
|
|
22
23
|
}
|
|
23
|
-
else {
|
|
24
|
+
else if (mediaWrappers[element.nodeName.toLowerCase()]) {
|
|
25
|
+
element =
|
|
26
|
+
(_a = getMediaWrapper(editor, element)) !== null && _a !== void 0 ? _a : wrapMedia(editor, element);
|
|
27
|
+
}
|
|
28
|
+
else if (mediaInFakeBlock) {
|
|
24
29
|
const wrapper = editor.createInside.element(mediaFakeTag, {
|
|
25
30
|
'data-jodit-temp': 1,
|
|
26
31
|
contenteditable: false,
|
|
@@ -41,13 +46,16 @@ export function media(editor) {
|
|
|
41
46
|
Dom.append(wrapper, element);
|
|
42
47
|
element = wrapper;
|
|
43
48
|
}
|
|
49
|
+
else {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
44
52
|
editor.e
|
|
45
53
|
.off(element, 'mousedown.select touchstart.select')
|
|
46
54
|
.on(element, 'mousedown.select touchstart.select', () => {
|
|
47
55
|
editor.s.setCursorAfter(element);
|
|
48
56
|
});
|
|
49
57
|
};
|
|
50
|
-
if (mediaInFakeBlock) {
|
|
58
|
+
if (mediaInFakeBlock || Object.keys(mediaWrappers).length) {
|
|
51
59
|
editor.e
|
|
52
60
|
.on('afterGetValueFromEditor', (data) => {
|
|
53
61
|
const rxp = new RegExp(`<${mediaFakeTag}[^>]+data-${keyFake}[^>]+>([^]+?)</${mediaFakeTag}>`, 'ig');
|
|
@@ -69,7 +69,7 @@ export class placeholder extends Plugin {
|
|
|
69
69
|
.on('changePlace.placeholder', this.addNativeListeners)
|
|
70
70
|
.on('change.placeholder focus.placeholder keyup.placeholder mouseup.placeholder keydown.placeholder ' +
|
|
71
71
|
'mousedown.placeholder afterSetMode.placeholder changePlace.placeholder ' +
|
|
72
|
-
'internalChange.placeholder', this.toggle)
|
|
72
|
+
'internalChange.placeholder afterCommand.placeholder', this.toggle)
|
|
73
73
|
.on(window, 'load', this.toggle);
|
|
74
74
|
this.addNativeListeners();
|
|
75
75
|
this.toggle();
|
|
@@ -25,6 +25,8 @@ export declare class AceEditor extends SourceEditor<AceAjax.Editor> implements I
|
|
|
25
25
|
init(editor: IJodit): any;
|
|
26
26
|
destruct(): any;
|
|
27
27
|
setValue(value: string): void;
|
|
28
|
+
private __settingValue;
|
|
29
|
+
private __onChange;
|
|
28
30
|
getValue(): string;
|
|
29
31
|
setReadOnly(isReadOnly: boolean): void;
|
|
30
32
|
get isFocused(): boolean;
|
|
@@ -3,7 +3,18 @@
|
|
|
3
3
|
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
4
|
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
5
|
*/
|
|
6
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
7
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
9
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
10
|
+
else
|
|
11
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
12
|
+
if (d = decorators[i])
|
|
13
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
14
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
15
|
+
};
|
|
6
16
|
import * as constants from "../../../../core/constants.js";
|
|
17
|
+
import { autobind } from "../../../../core/decorators/autobind/autobind.js";
|
|
7
18
|
import { Dom } from "../../../../core/dom/dom.js";
|
|
8
19
|
import { isString, loadNext } from "../../../../core/helpers/index.js";
|
|
9
20
|
import { SourceEditor } from "../sourceEditor.js";
|
|
@@ -23,6 +34,7 @@ export class AceEditor extends SourceEditor {
|
|
|
23
34
|
this.proxyOnMouseDown = (e) => {
|
|
24
35
|
this.j.e.fire('mousedown', e);
|
|
25
36
|
};
|
|
37
|
+
this.__settingValue = false;
|
|
26
38
|
}
|
|
27
39
|
aceExists() {
|
|
28
40
|
return this.j.ow.ace !== undefined;
|
|
@@ -98,7 +110,7 @@ export class AceEditor extends SourceEditor {
|
|
|
98
110
|
this.instance.setOption('wrap', editor.o.sourceEditorNativeOptions.wrap);
|
|
99
111
|
this.instance.getSession().setUseWorker(false);
|
|
100
112
|
this.instance.$blockScrolling = Infinity;
|
|
101
|
-
this.instance.on('change', this.
|
|
113
|
+
this.instance.on('change', this.__onChange);
|
|
102
114
|
this.instance.on('focus', this.proxyOnFocus);
|
|
103
115
|
this.instance.on('mousedown', this.proxyOnMouseDown);
|
|
104
116
|
this.instance.on('blur', this.proxyOnBlur);
|
|
@@ -155,7 +167,7 @@ export class AceEditor extends SourceEditor {
|
|
|
155
167
|
}
|
|
156
168
|
destruct() {
|
|
157
169
|
var _a, _b;
|
|
158
|
-
this.instance.off('change', this.
|
|
170
|
+
this.instance.off('change', this.__onChange);
|
|
159
171
|
this.instance.off('focus', this.proxyOnFocus);
|
|
160
172
|
this.instance.off('mousedown', this.proxyOnMouseDown);
|
|
161
173
|
this.instance.destroy();
|
|
@@ -168,9 +180,26 @@ export class AceEditor extends SourceEditor {
|
|
|
168
180
|
value = html;
|
|
169
181
|
}
|
|
170
182
|
}
|
|
171
|
-
|
|
183
|
+
// ACE reports its own `setValue` as two changes, the removal of the
|
|
184
|
+
// old text and the insertion of the new one. Fed back into the
|
|
185
|
+
// editor they became two history entries, `content -> ''` and
|
|
186
|
+
// `'' -> content`, so the first undo after a switch to the source
|
|
187
|
+
// view and back emptied the editor. The text came from the editor
|
|
188
|
+
// in the first place, so there is nothing to send back.
|
|
189
|
+
this.__settingValue = true;
|
|
190
|
+
try {
|
|
191
|
+
this.instance.setValue(value);
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
this.__settingValue = false;
|
|
195
|
+
}
|
|
172
196
|
this.instance.clearSelection();
|
|
173
197
|
}
|
|
198
|
+
__onChange() {
|
|
199
|
+
if (!this.__settingValue) {
|
|
200
|
+
this.toWYSIWYG();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
174
203
|
getValue() {
|
|
175
204
|
return this.instance.getValue();
|
|
176
205
|
}
|
|
@@ -230,3 +259,6 @@ export class AceEditor extends SourceEditor {
|
|
|
230
259
|
});
|
|
231
260
|
}
|
|
232
261
|
}
|
|
262
|
+
__decorate([
|
|
263
|
+
autobind
|
|
264
|
+
], AceEditor.prototype, "__onChange", null);
|
|
@@ -3,22 +3,42 @@
|
|
|
3
3
|
* Released under MIT see LICENSE.txt in the project root for license information.
|
|
4
4
|
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
5
|
*/
|
|
6
|
-
import { call, convertMediaUrlToVideoEmbed } from "../../core/helpers/index.js";
|
|
6
|
+
import { call, convertMediaUrlToVideoEmbed, getAlignClasses, hAlignElement, wrapMedia } from "../../core/helpers/index.js";
|
|
7
7
|
import { Button } from "../../core/ui/button/index.js";
|
|
8
8
|
import { UIBlock, UIForm, UIInput, UITextArea } from "../../core/ui/form/index.js";
|
|
9
9
|
import { Icon } from "../../core/ui/icon.js";
|
|
10
10
|
import { Config } from "../../config.js";
|
|
11
11
|
import { TabsWidget, UploadTab } from "../../modules/widget/index.js";
|
|
12
12
|
import videoIcon from "./video.svg.js";
|
|
13
|
+
// Media the popup inserts go in their wrapper (see `mediaWrappers`), aligned
|
|
14
|
+
// with `mediaDefaultAlign`
|
|
15
|
+
function prepareMedia(jodit, element) {
|
|
16
|
+
const media = wrapMedia(jodit, element);
|
|
17
|
+
const { mediaDefaultAlign } = jodit.o;
|
|
18
|
+
if (mediaDefaultAlign && mediaDefaultAlign !== 'normal') {
|
|
19
|
+
hAlignElement(media, mediaDefaultAlign, getAlignClasses(jodit, media));
|
|
20
|
+
}
|
|
21
|
+
return media;
|
|
22
|
+
}
|
|
13
23
|
function insertVideos(data) {
|
|
14
24
|
var _a;
|
|
15
25
|
(_a = data.files) === null || _a === void 0 ? void 0 : _a.forEach(file => {
|
|
16
|
-
this.s.insertNode(this.createInside.element('video', {
|
|
26
|
+
this.s.insertNode(prepareMedia(this, this.createInside.element('video', {
|
|
17
27
|
controls: '',
|
|
18
28
|
src: data.baseurl + file
|
|
19
|
-
}));
|
|
29
|
+
})));
|
|
20
30
|
});
|
|
21
31
|
}
|
|
32
|
+
// Embed code that is one element is prepared as media; other code goes in as
|
|
33
|
+
// it is
|
|
34
|
+
function prepareEmbedCode(jodit, code) {
|
|
35
|
+
const box = jodit.createInside.div();
|
|
36
|
+
box.innerHTML = code.trim();
|
|
37
|
+
const element = box.firstElementChild;
|
|
38
|
+
return element && box.childNodes.length === 1
|
|
39
|
+
? prepareMedia(jodit, element)
|
|
40
|
+
: code;
|
|
41
|
+
}
|
|
22
42
|
Config.prototype.video = {
|
|
23
43
|
parseUrlToVideoEmbed: convertMediaUrlToVideoEmbed,
|
|
24
44
|
defaultWidth: 400,
|
|
@@ -54,7 +74,7 @@ Config.prototype.controls.video = {
|
|
|
54
74
|
])
|
|
55
75
|
]), tabs = [], insertCode = (code) => {
|
|
56
76
|
jodit.s.restore();
|
|
57
|
-
jodit.s.insertHTML(code);
|
|
77
|
+
jodit.s.insertHTML(prepareEmbedCode(jodit, code));
|
|
58
78
|
close();
|
|
59
79
|
};
|
|
60
80
|
jodit.s.save();
|
package/esm/types/toolbar.d.ts
CHANGED
|
@@ -193,6 +193,8 @@ interface IControlType<T extends IViewBased = IJodit | IViewBased | IFileBrowser
|
|
|
193
193
|
childTemplate?: (jodit: T, key: string, value: string, button: IToolbarButton) => string;
|
|
194
194
|
/**
|
|
195
195
|
* After click on the button it will show popup element which consist value that this function returned
|
|
196
|
+
* For a control with a `list`, the returned content replaces the list's buttons in its dropdown,
|
|
197
|
+
* and `false` keeps them
|
|
196
198
|
* @example
|
|
197
199
|
* ```javascript
|
|
198
200
|
* var editor = Jodit.make('.editor', {
|
package/esm/types/types.d.ts
CHANGED
|
@@ -187,10 +187,22 @@ export interface ImageEditorOptions {
|
|
|
187
187
|
export type InsertMode = 'insert_as_html' | 'insert_as_text' | 'insert_clear_html' | 'insert_only_text';
|
|
188
188
|
export type ImageAction = 'resize' | 'crop';
|
|
189
189
|
export type ImageHAlign = 'normal' | 'left' | 'right' | 'center' | '';
|
|
190
|
+
/**
|
|
191
|
+
* Class names for each alignment, see `alignClasses`. An alignment can list
|
|
192
|
+
* several sets of class names to recognise; aligning writes the first
|
|
193
|
+
*/
|
|
194
|
+
export type HAlignClasses = Partial<Record<Exclude<ImageHAlign, ''>, string | string[]>>;
|
|
190
195
|
/**
|
|
191
196
|
* Class names for each image alignment, see `imageAlignClasses`
|
|
192
197
|
*/
|
|
193
|
-
export type ImageAlignClasses =
|
|
198
|
+
export type ImageAlignClasses = HAlignClasses;
|
|
199
|
+
/**
|
|
200
|
+
* A wrapper that media keep in the content, see `mediaWrappers`
|
|
201
|
+
*/
|
|
202
|
+
export interface MediaWrapper {
|
|
203
|
+
tag: string;
|
|
204
|
+
className: string;
|
|
205
|
+
}
|
|
194
206
|
export interface ImageBox {
|
|
195
207
|
w: number;
|
|
196
208
|
h: number;
|