jodit 4.17.6 → 4.18.1
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 +87 -3
- package/es2015/jodit.fat.min.css +1 -1
- package/es2015/jodit.fat.min.js +8 -8
- package/es2015/jodit.js +808 -132
- 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 +8 -8
- package/es2018/jodit.min.css +1 -1
- package/es2018/jodit.min.js +8 -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 +87 -3
- package/es2021/jodit.fat.min.css +1 -1
- package/es2021/jodit.fat.min.js +14 -14
- package/es2021/jodit.js +788 -132
- package/es2021/jodit.min.css +1 -1
- package/es2021/jodit.min.js +14 -14
- 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 +26 -26
- package/es2021.en/jodit.js +788 -132
- package/es2021.en/jodit.min.css +1 -1
- package/es2021.en/jodit.min.js +12 -12
- 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 +1073 -192
- 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 +3 -3
- package/esm/modules/toolbar/button/button.d.ts +18 -0
- package/esm/modules/toolbar/button/button.js +75 -5
- package/esm/plugins/dtd/after-insert/remove-extra-br.js +6 -1
- package/esm/plugins/dtd/before-insert/check-block-nesting.js +16 -1
- package/esm/plugins/dtd/config.js +3 -0
- 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/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/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/types/toolbar.d.ts +2 -0
- package/types/types/types.d.ts +13 -1
package/es5/polyfills.fat.min.js
CHANGED
package/es5/polyfills.js
CHANGED
package/es5/polyfills.min.js
CHANGED
package/esm/config.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type { IControlType } from "./types/index";
|
|
|
15
15
|
import type { formTemplate } from "./plugins/link/template";
|
|
16
16
|
import type { IUIForm } from "./types/index";
|
|
17
17
|
import type { IUIOption } from "./types/index";
|
|
18
|
+
import type { MediaWrapper } from "./types/index";
|
|
18
19
|
import type { ButtonsOption } from "./types/index";
|
|
19
20
|
import type { InsertMode } from "./types/index";
|
|
20
21
|
import type { FuzzySearch } from "./types/index";
|
|
@@ -22,6 +23,7 @@ import type { ISourceEditor } from "./types/index";
|
|
|
22
23
|
import type { ISpeechRecognizeConstructor } from "./plugins/speech-recognize/interface";
|
|
23
24
|
import type { Attributes } from "./types/index";
|
|
24
25
|
import type { Controls } from "./types/index";
|
|
26
|
+
import type { HAlignClasses } from "./types/index";
|
|
25
27
|
import type { IAsyncStorageOptions } from "./types/index";
|
|
26
28
|
import type { IExtraPlugin } from "./types/index";
|
|
27
29
|
import type { ImageAlignClasses } from "./types/index";
|
|
@@ -642,19 +644,46 @@ declare class Config implements IViewOptions {
|
|
|
642
644
|
/**
|
|
643
645
|
* Class names to align images with, instead of an inline `float` or
|
|
644
646
|
* `margin`. Aligning an image removes every class listed here and adds the
|
|
645
|
-
* chosen alignment's, which can be several separated by spaces.
|
|
647
|
+
* chosen alignment's, which can be several separated by spaces. An
|
|
648
|
+
* alignment can also list several such sets, to recognise each of them;
|
|
649
|
+
* aligning writes the first. `alignClasses.img` takes its place when set.
|
|
646
650
|
*
|
|
647
651
|
* ```javascript
|
|
648
652
|
* Jodit.make('#editor', {
|
|
649
653
|
* imageAlignClasses: {
|
|
650
654
|
* left: 'align-left',
|
|
651
655
|
* right: 'align-right',
|
|
652
|
-
* center: 'align-center'
|
|
656
|
+
* center: ['align-center', 'centered']
|
|
653
657
|
* }
|
|
654
658
|
* });
|
|
655
659
|
* ```
|
|
656
660
|
*/
|
|
657
661
|
imageAlignClasses: Nullable<ImageAlignClasses>;
|
|
662
|
+
/**
|
|
663
|
+
* Class names to align elements with, by tag name, as `imageAlignClasses`
|
|
664
|
+
* does for images: `img`, and `video`, `audio` and `iframe` in the media
|
|
665
|
+
* popup. A media wrapper (see `mediaWrappers`), and Jodit's own `jodit` and
|
|
666
|
+
* `jodit-media` wrappers, are aligned with the classes for the media in
|
|
667
|
+
* them.
|
|
668
|
+
*
|
|
669
|
+
* ```javascript
|
|
670
|
+
* const media = {
|
|
671
|
+
* left: 'media-left',
|
|
672
|
+
* right: 'media-right',
|
|
673
|
+
* center: 'media-center'
|
|
674
|
+
* };
|
|
675
|
+
*
|
|
676
|
+
* Jodit.make('#editor', {
|
|
677
|
+
* alignClasses: { video: media, iframe: media }
|
|
678
|
+
* });
|
|
679
|
+
* ```
|
|
680
|
+
*/
|
|
681
|
+
alignClasses: IDictionary<HAlignClasses>;
|
|
682
|
+
/**
|
|
683
|
+
* Alignment given to the media the video popup inserts. `'normal'` leaves
|
|
684
|
+
* them unaligned
|
|
685
|
+
*/
|
|
686
|
+
mediaDefaultAlign: ImageHAlign;
|
|
658
687
|
/**
|
|
659
688
|
* Do not display these buttons that are on the list
|
|
660
689
|
*
|
|
@@ -1293,6 +1322,18 @@ interface Config {
|
|
|
1293
1322
|
}
|
|
1294
1323
|
interface Config {
|
|
1295
1324
|
defaultFontSizePoints: 'px' | 'pt';
|
|
1325
|
+
font: {
|
|
1326
|
+
googleFonts: {
|
|
1327
|
+
/**
|
|
1328
|
+
* Adds the Google Fonts library to the font family dropdown, which then
|
|
1329
|
+
* also lists the theme's fonts and the fonts the content uses. The
|
|
1330
|
+
* address answers with the JSON of the Google Fonts Developer API:
|
|
1331
|
+
* the API itself (`https://www.googleapis.com/webfonts/v1/webfonts?key=…`)
|
|
1332
|
+
* or a proxy of it. Empty leaves the dropdown as it is
|
|
1333
|
+
*/
|
|
1334
|
+
url: string;
|
|
1335
|
+
};
|
|
1336
|
+
};
|
|
1296
1337
|
}
|
|
1297
1338
|
interface Config {
|
|
1298
1339
|
/**
|
|
@@ -1549,6 +1590,28 @@ interface Config {
|
|
|
1549
1590
|
* Media tags
|
|
1550
1591
|
*/
|
|
1551
1592
|
mediaBlocks: string[];
|
|
1593
|
+
/**
|
|
1594
|
+
* Wrappers that media keep in the content, by the media's tag name, such as
|
|
1595
|
+
* Froala's `<span class="fr-video">` around video, audio and embeds. An
|
|
1596
|
+
* element with a wrapper for its tag:
|
|
1597
|
+
* - is wrapped in it instead of the editor-only `mediaFakeTag` when its tag
|
|
1598
|
+
* is one of `mediaBlocks`, or left in it when it's already in one
|
|
1599
|
+
* - goes in inside it when the video popup inserts it, as an upload, a
|
|
1600
|
+
* link's embed or embed code that is one element
|
|
1601
|
+
* - opens the media popup for its wrapper when clicked, whose Align and
|
|
1602
|
+
* Delete act on the wrapper
|
|
1603
|
+
*
|
|
1604
|
+
* A wrapper is given `contenteditable="false"` and `draggable="true"`,
|
|
1605
|
+
* which stay in the value.
|
|
1606
|
+
* ```javascript
|
|
1607
|
+
* const frVideo = { tag: 'span', className: 'fr-video' };
|
|
1608
|
+
*
|
|
1609
|
+
* Jodit.make('#editor', {
|
|
1610
|
+
* mediaWrappers: { video: frVideo, audio: frVideo, iframe: frVideo }
|
|
1611
|
+
* });
|
|
1612
|
+
* ```
|
|
1613
|
+
*/
|
|
1614
|
+
mediaWrappers: IDictionary<MediaWrapper>;
|
|
1552
1615
|
}
|
|
1553
1616
|
interface Config {
|
|
1554
1617
|
/**
|
package/esm/config.js
CHANGED
|
@@ -748,19 +748,46 @@ class Config {
|
|
|
748
748
|
/**
|
|
749
749
|
* Class names to align images with, instead of an inline `float` or
|
|
750
750
|
* `margin`. Aligning an image removes every class listed here and adds the
|
|
751
|
-
* chosen alignment's, which can be several separated by spaces.
|
|
751
|
+
* chosen alignment's, which can be several separated by spaces. An
|
|
752
|
+
* alignment can also list several such sets, to recognise each of them;
|
|
753
|
+
* aligning writes the first. `alignClasses.img` takes its place when set.
|
|
752
754
|
*
|
|
753
755
|
* ```javascript
|
|
754
756
|
* Jodit.make('#editor', {
|
|
755
757
|
* imageAlignClasses: {
|
|
756
758
|
* left: 'align-left',
|
|
757
759
|
* right: 'align-right',
|
|
758
|
-
* center: 'align-center'
|
|
760
|
+
* center: ['align-center', 'centered']
|
|
759
761
|
* }
|
|
760
762
|
* });
|
|
761
763
|
* ```
|
|
762
764
|
*/
|
|
763
765
|
this.imageAlignClasses = null;
|
|
766
|
+
/**
|
|
767
|
+
* Class names to align elements with, by tag name, as `imageAlignClasses`
|
|
768
|
+
* does for images: `img`, and `video`, `audio` and `iframe` in the media
|
|
769
|
+
* popup. A media wrapper (see `mediaWrappers`), and Jodit's own `jodit` and
|
|
770
|
+
* `jodit-media` wrappers, are aligned with the classes for the media in
|
|
771
|
+
* them.
|
|
772
|
+
*
|
|
773
|
+
* ```javascript
|
|
774
|
+
* const media = {
|
|
775
|
+
* left: 'media-left',
|
|
776
|
+
* right: 'media-right',
|
|
777
|
+
* center: 'media-center'
|
|
778
|
+
* };
|
|
779
|
+
*
|
|
780
|
+
* Jodit.make('#editor', {
|
|
781
|
+
* alignClasses: { video: media, iframe: media }
|
|
782
|
+
* });
|
|
783
|
+
* ```
|
|
784
|
+
*/
|
|
785
|
+
this.alignClasses = {};
|
|
786
|
+
/**
|
|
787
|
+
* Alignment given to the media the video popup inserts. `'normal'` leaves
|
|
788
|
+
* them unaligned
|
|
789
|
+
*/
|
|
790
|
+
this.mediaDefaultAlign = 'normal';
|
|
764
791
|
/**
|
|
765
792
|
* Do not display these buttons that are on the list
|
|
766
793
|
*
|
package/esm/core/constants.js
CHANGED
|
@@ -3,7 +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
|
-
export const APP_VERSION = "4.
|
|
6
|
+
export const APP_VERSION = "4.18.1";
|
|
7
7
|
// prettier-ignore
|
|
8
8
|
export const ES = "es2020";
|
|
9
9
|
export const IS_ES_MODERN = true;
|
|
@@ -6,16 +6,22 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* @module helpers/utils
|
|
8
8
|
*/
|
|
9
|
-
import type {
|
|
9
|
+
import type { HAlignClasses, IJodit, ImageHAlign, Nullable } from "../../../types/index";
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* The class names to align `elm` with: `alignClasses` for its tag, or for the
|
|
12
|
+
* media in it when it wraps media (a `mediaWrappers` wrapper, `jodit` or
|
|
13
|
+
* `jodit-media`), falling back to `imageAlignClasses` for an image
|
|
13
14
|
*/
|
|
14
|
-
export declare function
|
|
15
|
+
export declare function getAlignClasses(jodit: IJodit, elm: Nullable<HTMLElement>): Nullable<HAlignClasses>;
|
|
16
|
+
/**
|
|
17
|
+
* Align image, with `classes` (see `alignClasses`) instead of inline styles
|
|
18
|
+
* when they are given
|
|
19
|
+
*/
|
|
20
|
+
export declare function hAlignElement(image: HTMLElement, align: ImageHAlign, classes?: Nullable<HAlignClasses>): void;
|
|
15
21
|
/**
|
|
16
22
|
* The alignment `hAlignElement` has set on an element
|
|
17
23
|
*/
|
|
18
|
-
export declare function getHAlign(elm: HTMLElement, classes?: Nullable<
|
|
24
|
+
export declare function getHAlign(elm: HTMLElement, classes?: Nullable<HAlignClasses>): ImageHAlign;
|
|
19
25
|
/**
|
|
20
26
|
* Remove text-align style for all selected children
|
|
21
27
|
*/
|
|
@@ -4,18 +4,40 @@
|
|
|
4
4
|
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
|
|
5
5
|
*/
|
|
6
6
|
import { Dom } from "../../dom/index.js";
|
|
7
|
+
import { isArray } from "../checker/is-array.js";
|
|
7
8
|
import { attr } from "./attr.js";
|
|
8
9
|
import { clearCenterAlign, css, cssInline } from "./css.js";
|
|
9
|
-
|
|
10
|
+
import { getWrappedMedia } from "./media-wrapper.js";
|
|
11
|
+
// The sets of class names an alignment lists, the first being the one to write
|
|
12
|
+
const classSets = (value) => (isArray(value) ? value : [value !== null && value !== void 0 ? value : ''])
|
|
13
|
+
.map(set => set.split(/\s+/).filter(Boolean))
|
|
14
|
+
.filter(set => set.length);
|
|
10
15
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
16
|
+
* The class names to align `elm` with: `alignClasses` for its tag, or for the
|
|
17
|
+
* media in it when it wraps media (a `mediaWrappers` wrapper, `jodit` or
|
|
18
|
+
* `jodit-media`), falling back to `imageAlignClasses` for an image
|
|
19
|
+
*/
|
|
20
|
+
export function getAlignClasses(jodit, elm) {
|
|
21
|
+
var _a, _b;
|
|
22
|
+
if (!elm) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const media = Dom.isTag(elm, new Set(['jodit', 'jodit-media']))
|
|
26
|
+
? elm.firstElementChild
|
|
27
|
+
: getWrappedMedia(jodit, elm);
|
|
28
|
+
const tag = (media !== null && media !== void 0 ? media : elm).nodeName.toLowerCase();
|
|
29
|
+
return ((_b = (_a = jodit.o.alignClasses) === null || _a === void 0 ? void 0 : _a[tag]) !== null && _b !== void 0 ? _b : (tag === 'img' ? jodit.o.imageAlignClasses : null));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Align image, with `classes` (see `alignClasses`) instead of inline styles
|
|
33
|
+
* when they are given
|
|
13
34
|
*/
|
|
14
35
|
export function hAlignElement(image, align, classes) {
|
|
36
|
+
var _a;
|
|
15
37
|
if (classes) {
|
|
16
38
|
hAlignElement(image, 'normal');
|
|
17
|
-
Object.values(classes).forEach(value => image.classList.remove(...
|
|
18
|
-
image.classList.add(...
|
|
39
|
+
Object.values(classes).forEach(value => classSets(value).forEach(set => image.classList.remove(...set)));
|
|
40
|
+
image.classList.add(...((_a = classSets(classes[align || 'normal'])[0]) !== null && _a !== void 0 ? _a : []));
|
|
19
41
|
['class', 'style'].forEach(name => {
|
|
20
42
|
var _a;
|
|
21
43
|
if (!((_a = attr(image, name)) === null || _a === void 0 ? void 0 : _a.trim())) {
|
|
@@ -52,9 +74,7 @@ export function hAlignElement(image, align, classes) {
|
|
|
52
74
|
export function getHAlign(elm, classes) {
|
|
53
75
|
if (classes) {
|
|
54
76
|
for (const align of ['left', 'right', 'center', 'normal']) {
|
|
55
|
-
|
|
56
|
-
if (names.length &&
|
|
57
|
-
names.every(name => elm.classList.contains(name))) {
|
|
77
|
+
if (classSets(classes[align]).some(set => set.every(name => elm.classList.contains(name)))) {
|
|
58
78
|
return align;
|
|
59
79
|
}
|
|
60
80
|
}
|
|
@@ -26,6 +26,7 @@ export * from "./get-class-name";
|
|
|
26
26
|
export * from "./human-size-to-bytes";
|
|
27
27
|
export * from "./is-unsafe-proto-key";
|
|
28
28
|
export * from "./mark-deprecated";
|
|
29
|
+
export * from "./media-wrapper";
|
|
29
30
|
export * from "./parse-query";
|
|
30
31
|
export * from "./print";
|
|
31
32
|
export * from "./reset";
|
|
@@ -26,6 +26,7 @@ export * from "./get-class-name.js";
|
|
|
26
26
|
export * from "./human-size-to-bytes.js";
|
|
27
27
|
export * from "./is-unsafe-proto-key.js";
|
|
28
28
|
export * from "./mark-deprecated.js";
|
|
29
|
+
export * from "./media-wrapper.js";
|
|
29
30
|
export * from "./parse-query.js";
|
|
30
31
|
export * from "./print.js";
|
|
31
32
|
export * from "./reset.js";
|
|
@@ -0,0 +1,29 @@
|
|
|
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 helpers/utils
|
|
8
|
+
*/
|
|
9
|
+
import type { IJodit, Nullable } from "../../../types/index";
|
|
10
|
+
/**
|
|
11
|
+
* The media in `elm` when `elm` is a wrapper media keep in the content (see
|
|
12
|
+
* `mediaWrappers`): an element of a type `elm` is the wrapper for
|
|
13
|
+
*/
|
|
14
|
+
export declare function getWrappedMedia(jodit: IJodit, elm: Nullable<Node>): Nullable<HTMLElement>;
|
|
15
|
+
/**
|
|
16
|
+
* The wrapper media keep in the content (see `mediaWrappers`) that is or
|
|
17
|
+
* holds `node`
|
|
18
|
+
*/
|
|
19
|
+
export declare function getMediaWrapper(jodit: IJodit, node: Nullable<Node>): Nullable<HTMLElement>;
|
|
20
|
+
/**
|
|
21
|
+
* An empty wrapper for media of type `tag` to keep in the content (see
|
|
22
|
+
* `mediaWrappers`), or null when there is none for it
|
|
23
|
+
*/
|
|
24
|
+
export declare function createMediaWrapper(jodit: IJodit, tag: string): Nullable<HTMLElement>;
|
|
25
|
+
/**
|
|
26
|
+
* Puts `element` in the wrapper for its type (see `mediaWrappers`), and
|
|
27
|
+
* returns the wrapper, or `element` when there is none for it
|
|
28
|
+
*/
|
|
29
|
+
export declare function wrapMedia(jodit: IJodit, element: HTMLElement): HTMLElement;
|
|
@@ -0,0 +1,63 @@
|
|
|
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 "../../dom/dom.js";
|
|
7
|
+
const isWrapper = (node, wrapper) => Dom.isHTMLElement(node) &&
|
|
8
|
+
node.nodeName.toLowerCase() === wrapper.tag.toLowerCase() &&
|
|
9
|
+
node.classList.contains(wrapper.className);
|
|
10
|
+
/**
|
|
11
|
+
* The media in `elm` when `elm` is a wrapper media keep in the content (see
|
|
12
|
+
* `mediaWrappers`): an element of a type `elm` is the wrapper for
|
|
13
|
+
*/
|
|
14
|
+
export function getWrappedMedia(jodit, elm) {
|
|
15
|
+
var _a;
|
|
16
|
+
for (const [tag, wrapper] of Object.entries((_a = jodit.o.mediaWrappers) !== null && _a !== void 0 ? _a : {})) {
|
|
17
|
+
if (isWrapper(elm, wrapper)) {
|
|
18
|
+
const media = elm.querySelector(tag);
|
|
19
|
+
if (media) {
|
|
20
|
+
return media;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The wrapper media keep in the content (see `mediaWrappers`) that is or
|
|
28
|
+
* holds `node`
|
|
29
|
+
*/
|
|
30
|
+
export function getMediaWrapper(jodit, node) {
|
|
31
|
+
return Dom.closest(node, elm => Boolean(getWrappedMedia(jodit, elm)), jodit.editor);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* An empty wrapper for media of type `tag` to keep in the content (see
|
|
35
|
+
* `mediaWrappers`), or null when there is none for it
|
|
36
|
+
*/
|
|
37
|
+
export function createMediaWrapper(jodit, tag) {
|
|
38
|
+
var _a;
|
|
39
|
+
const wrapper = (_a = jodit.o.mediaWrappers) === null || _a === void 0 ? void 0 : _a[tag.toLowerCase()];
|
|
40
|
+
if (!wrapper) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return jodit.createInside.element(wrapper.tag, {
|
|
44
|
+
class: wrapper.className,
|
|
45
|
+
contenteditable: false,
|
|
46
|
+
draggable: true
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Puts `element` in the wrapper for its type (see `mediaWrappers`), and
|
|
51
|
+
* returns the wrapper, or `element` when there is none for it
|
|
52
|
+
*/
|
|
53
|
+
export function wrapMedia(jodit, element) {
|
|
54
|
+
const wrapper = createMediaWrapper(jodit, element.nodeName);
|
|
55
|
+
if (!wrapper) {
|
|
56
|
+
return element;
|
|
57
|
+
}
|
|
58
|
+
if (element.parentNode) {
|
|
59
|
+
Dom.before(element, wrapper);
|
|
60
|
+
}
|
|
61
|
+
Dom.append(wrapper, element);
|
|
62
|
+
return wrapper;
|
|
63
|
+
}
|
|
@@ -19,7 +19,7 @@ import { autobind } from "../decorators/index.js";
|
|
|
19
19
|
import { Dom } from "../dom/dom.js";
|
|
20
20
|
import { $$, attr, css, error, getScrollParent, scrollIntoViewIfNeeded, size, toArray } from "../helpers/index.js";
|
|
21
21
|
import { isFunction, isMarker, isString } from "../helpers/checker/index.js";
|
|
22
|
-
import { hAlignElement } from "../helpers/utils/align.js";
|
|
22
|
+
import { getAlignClasses, hAlignElement } from "../helpers/utils/align.js";
|
|
23
23
|
import { assert } from "../helpers/utils/assert.js";
|
|
24
24
|
import { moveTheNodeAlongTheEdgeOutward } from "./helpers/move-the-node-along-the-edge-outward.js";
|
|
25
25
|
import "./interface.js";
|
|
@@ -776,9 +776,9 @@ export class Selection {
|
|
|
776
776
|
if (styles && typeof styles === 'object') {
|
|
777
777
|
css(image, styles);
|
|
778
778
|
}
|
|
779
|
-
const { imageDefaultAlign
|
|
779
|
+
const { imageDefaultAlign } = this.j.o;
|
|
780
780
|
if (imageDefaultAlign && imageDefaultAlign !== 'normal') {
|
|
781
|
-
hAlignElement(image, imageDefaultAlign,
|
|
781
|
+
hAlignElement(image, imageDefaultAlign, getAlignClasses(this.j, image));
|
|
782
782
|
}
|
|
783
783
|
const onload = () => {
|
|
784
784
|
if (image.naturalHeight < image.offsetHeight ||
|
|
@@ -77,6 +77,23 @@ export declare class ToolbarButton<T extends IViewBased = IViewBased> extends UI
|
|
|
77
77
|
* `ArrowUp` on the first item close it and give the focus back to the button
|
|
78
78
|
*/
|
|
79
79
|
private __onPopupKeyDown;
|
|
80
|
+
/**
|
|
81
|
+
* The mouse opens a dropdown without taking the focus out of the editor.
|
|
82
|
+
* While a dropdown list is open, typing there searches the list, as in an
|
|
83
|
+
* open `<select>`, instead of writing into the content
|
|
84
|
+
*/
|
|
85
|
+
private __onEditorKeyDown;
|
|
86
|
+
private __typed;
|
|
87
|
+
private __typedAt;
|
|
88
|
+
private __typedTo;
|
|
89
|
+
/**
|
|
90
|
+
* Typing in a dropdown list focuses the next item whose label starts with
|
|
91
|
+
* the typed text, whatever their case and accents, and repeating one letter
|
|
92
|
+
* cycles through the items that start with it, like the WAI-ARIA menu
|
|
93
|
+
* pattern's type-ahead. The typed text starts again after a pause, or once
|
|
94
|
+
* the focus has moved on
|
|
95
|
+
*/
|
|
96
|
+
private __typeAhead;
|
|
80
97
|
/**
|
|
81
98
|
* Visible controls of the opened dropdown that can take the focus
|
|
82
99
|
*/
|
|
@@ -86,6 +103,7 @@ export declare class ToolbarButton<T extends IViewBased = IViewBased> extends UI
|
|
|
86
103
|
* Create an open popup list
|
|
87
104
|
*/
|
|
88
105
|
private __openControlList;
|
|
106
|
+
private __openMenu;
|
|
89
107
|
protected onOutsideClick(e: MouseEvent): void;
|
|
90
108
|
private openPopup;
|
|
91
109
|
private __closePopup;
|
|
@@ -36,6 +36,7 @@ import { Icon } from "../../../core/ui/icon.js";
|
|
|
36
36
|
import { Popup } from "../../../core/ui/popup/popup.js";
|
|
37
37
|
import { makeCollection } from "../factory.js";
|
|
38
38
|
const INPUT_TAGS = new Set(['input', 'textarea', 'select']);
|
|
39
|
+
const TYPE_AHEAD_TIMEOUT = 1000;
|
|
39
40
|
const FOCUSABLE_SELECTOR = [
|
|
40
41
|
'button:not([disabled])',
|
|
41
42
|
'a[href]',
|
|
@@ -177,6 +178,9 @@ let ToolbarButton = class ToolbarButton extends UIButton {
|
|
|
177
178
|
currentValue: '',
|
|
178
179
|
hasTrigger: false
|
|
179
180
|
};
|
|
181
|
+
this.__typed = '';
|
|
182
|
+
this.__typedAt = 0;
|
|
183
|
+
this.__typedTo = null;
|
|
180
184
|
this.openedPopup = null;
|
|
181
185
|
const button = this.getElm('button');
|
|
182
186
|
assert(button, 'Element button should exists');
|
|
@@ -366,11 +370,65 @@ let ToolbarButton = class ToolbarButton extends UIButton {
|
|
|
366
370
|
items[(index + 1) % items.length].focus();
|
|
367
371
|
break;
|
|
368
372
|
default:
|
|
369
|
-
|
|
373
|
+
if (!this.control.list ||
|
|
374
|
+
!this.__typeAhead(e.key, items, index)) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
370
377
|
}
|
|
371
378
|
e.preventDefault();
|
|
372
379
|
e.stopPropagation();
|
|
373
380
|
}
|
|
381
|
+
/**
|
|
382
|
+
* The mouse opens a dropdown without taking the focus out of the editor.
|
|
383
|
+
* While a dropdown list is open, typing there searches the list, as in an
|
|
384
|
+
* open `<select>`, instead of writing into the content
|
|
385
|
+
*/
|
|
386
|
+
__onEditorKeyDown(e) {
|
|
387
|
+
if (!this.openedPopup ||
|
|
388
|
+
!this.control.list ||
|
|
389
|
+
e.altKey ||
|
|
390
|
+
e.ctrlKey ||
|
|
391
|
+
e.metaKey ||
|
|
392
|
+
!this.__typeAhead(e.key, this.__popupFocusables(), -1)) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
e.preventDefault();
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Typing in a dropdown list focuses the next item whose label starts with
|
|
400
|
+
* the typed text, whatever their case and accents, and repeating one letter
|
|
401
|
+
* cycles through the items that start with it, like the WAI-ARIA menu
|
|
402
|
+
* pattern's type-ahead. The typed text starts again after a pause, or once
|
|
403
|
+
* the focus has moved on
|
|
404
|
+
*/
|
|
405
|
+
__typeAhead(key, items, index) {
|
|
406
|
+
var _a, _b;
|
|
407
|
+
const now = Date.now();
|
|
408
|
+
if (now - this.__typedAt > TYPE_AHEAD_TIMEOUT ||
|
|
409
|
+
items[index] !== this.__typedTo) {
|
|
410
|
+
this.__typed = '';
|
|
411
|
+
}
|
|
412
|
+
if (key.length !== 1 || (key === ' ' && !this.__typed)) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
this.__typedAt = now;
|
|
416
|
+
this.__typed += key;
|
|
417
|
+
this.__typedTo = items[index];
|
|
418
|
+
const { compare } = new Intl.Collator(undefined, {
|
|
419
|
+
usage: 'search',
|
|
420
|
+
sensitivity: 'base'
|
|
421
|
+
}), typed = this.__typed, cycle = Array.from(typed).every(char => compare(char, typed[0]) === 0), prefix = cycle ? typed[0] : typed, start = cycle ? index + 1 : Math.max(index, 0);
|
|
422
|
+
for (let i = 0; i < items.length; i += 1) {
|
|
423
|
+
const item = items[(start + i) % items.length], label = (_b = (_a = item.textContent) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
|
|
424
|
+
if (compare(label.slice(0, prefix.length), prefix) === 0) {
|
|
425
|
+
item.focus();
|
|
426
|
+
this.__typedTo = item;
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
374
432
|
/**
|
|
375
433
|
* Visible controls of the opened dropdown that can take the focus
|
|
376
434
|
*/
|
|
@@ -385,12 +443,16 @@ let ToolbarButton = class ToolbarButton extends UIButton {
|
|
|
385
443
|
* Create an open popup list
|
|
386
444
|
*/
|
|
387
445
|
__openControlList(control) {
|
|
388
|
-
var _a;
|
|
446
|
+
var _a, _b, _c, _d, _e;
|
|
389
447
|
const controls = (_a = this.jodit.options.controls) !== null && _a !== void 0 ? _a : {}, getControl = (key) => findControlType(key, controls);
|
|
390
448
|
const list = control.list;
|
|
391
449
|
const menu = this.openPopup();
|
|
392
|
-
const toolbar = makeCollection(this.j);
|
|
393
450
|
menu.parentElement = this;
|
|
451
|
+
const content = (_b = control.popup) === null || _b === void 0 ? void 0 : _b.call(control, this.j, (_e = (_d = (_c = this.toolbar) === null || _c === void 0 ? void 0 : _c.getTarget(this)) !== null && _d !== void 0 ? _d : this.target) !== null && _e !== void 0 ? _e : null, this.__closePopup, this);
|
|
452
|
+
if (content) {
|
|
453
|
+
return this.__openMenu(menu, content);
|
|
454
|
+
}
|
|
455
|
+
const toolbar = makeCollection(this.j);
|
|
394
456
|
toolbar.parentElement = menu;
|
|
395
457
|
toolbar.mode = 'vertical';
|
|
396
458
|
const isListItem = (key) => isPlainObject(key) && 'title' in key && 'value' in key;
|
|
@@ -442,7 +504,10 @@ let ToolbarButton = class ToolbarButton extends UIButton {
|
|
|
442
504
|
toolbar.build(isArray(list)
|
|
443
505
|
? list.map(getButton)
|
|
444
506
|
: keys(list, false).map(key => getButton(key, list[key])), this.target);
|
|
445
|
-
|
|
507
|
+
this.__openMenu(menu, toolbar);
|
|
508
|
+
}
|
|
509
|
+
__openMenu(menu, content) {
|
|
510
|
+
menu.setContent(content).open(() => position(this.container), false, this.j.o.allowTabNavigation ? this.container : undefined);
|
|
446
511
|
this.state.activated = true;
|
|
447
512
|
}
|
|
448
513
|
onOutsideClick(e) {
|
|
@@ -462,6 +527,7 @@ let ToolbarButton = class ToolbarButton extends UIButton {
|
|
|
462
527
|
this.j.e
|
|
463
528
|
.on(this.ow, 'mousedown touchstart', this.onOutsideClick)
|
|
464
529
|
.on('escape closeAllPopups', this.onOutsideClick)
|
|
530
|
+
.on('keydown', this.__onEditorKeyDown)
|
|
465
531
|
.on(this.openedPopup.container, 'keydown', this.__onPopupKeyDown);
|
|
466
532
|
return this.openedPopup;
|
|
467
533
|
}
|
|
@@ -479,7 +545,8 @@ let ToolbarButton = class ToolbarButton extends UIButton {
|
|
|
479
545
|
this.openedPopup = null;
|
|
480
546
|
this.j.e
|
|
481
547
|
.off(this.ow, 'mousedown touchstart', this.onOutsideClick)
|
|
482
|
-
.off('escape closeAllPopups', this.onOutsideClick)
|
|
548
|
+
.off('escape closeAllPopups', this.onOutsideClick)
|
|
549
|
+
.off('keydown', this.__onEditorKeyDown);
|
|
483
550
|
// The keyboard brought the focus into the dropdown: give it back to
|
|
484
551
|
// the button, otherwise it would fall out to the document
|
|
485
552
|
const { activeElement } = this.od, hadFocus = activeElement &&
|
|
@@ -558,6 +625,9 @@ __decorate([
|
|
|
558
625
|
__decorate([
|
|
559
626
|
autobind
|
|
560
627
|
], ToolbarButton.prototype, "__onPopupKeyDown", null);
|
|
628
|
+
__decorate([
|
|
629
|
+
autobind
|
|
630
|
+
], ToolbarButton.prototype, "__onEditorKeyDown", null);
|
|
561
631
|
__decorate([
|
|
562
632
|
autobind
|
|
563
633
|
], ToolbarButton.prototype, "onOutsideClick", null);
|