jodit 4.15.17 → 4.16.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 +18 -0
- package/es2015/jodit.css +1 -1
- package/es2015/jodit.fat.min.js +12 -11
- package/es2015/jodit.js +198 -80
- package/es2015/jodit.min.js +12 -11
- 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.js +11 -10
- package/es2018/jodit.min.js +12 -11
- package/es2018/plugins/debug/debug.min.js +1 -1
- package/es2018/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es2021/jodit.css +1 -1
- package/es2021/jodit.fat.min.js +15 -14
- package/es2021/jodit.js +196 -80
- package/es2021/jodit.min.js +17 -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 +1 -1
- package/es2021.en/jodit.fat.min.js +15 -14
- package/es2021.en/jodit.js +196 -80
- package/es2021.en/jodit.min.js +15 -14
- 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 +2 -2
- package/es5/jodit.fat.min.js +2 -2
- package/es5/jodit.js +258 -116
- package/es5/jodit.min.css +2 -2
- 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 +23 -0
- package/esm/config.js +21 -0
- package/esm/core/constants.js +1 -1
- package/esm/core/helpers/utils/align.d.ts +8 -3
- package/esm/core/helpers/utils/align.js +40 -2
- package/esm/core/selection/selection.js +5 -0
- package/esm/core/ui/button/button/button.js +10 -4
- package/esm/core/ui/form/inputs/file/file.js +1 -0
- package/esm/core/ui/group/group.d.ts +4 -0
- package/esm/core/ui/group/group.js +5 -1
- package/esm/core/ui/group/list.js +2 -2
- package/esm/modules/dialog/dialog.js +7 -0
- package/esm/modules/toolbar/button/content.d.ts +7 -0
- package/esm/modules/toolbar/button/content.js +9 -1
- package/esm/plugins/image-properties/readers/align.d.ts +5 -1
- package/esm/plugins/image-properties/readers/align.js +4 -18
- package/esm/plugins/image-properties/readers/index.js +1 -1
- package/esm/plugins/image-properties/writers/index.js +1 -1
- package/esm/plugins/inline-popup/config/items/img.js +17 -2
- package/esm/plugins/source/editor/engines/ace.js +13 -6
- package/esm/plugins/source/source.js +20 -1
- package/esm/types/types.d.ts +4 -0
- package/package.json +1 -1
- package/types/config.d.ts +23 -0
- package/types/core/helpers/utils/align.d.ts +8 -3
- package/types/core/ui/group/group.d.ts +4 -0
- package/types/modules/toolbar/button/content.d.ts +7 -0
- package/types/plugins/image-properties/readers/align.d.ts +5 -1
- package/types/types/types.d.ts +4 -0
package/es5/polyfills.fat.min.js
CHANGED
package/es5/polyfills.js
CHANGED
package/es5/polyfills.min.js
CHANGED
package/esm/config.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ import type { Attributes } from "./types/index";
|
|
|
23
23
|
import type { Controls } from "./types/index";
|
|
24
24
|
import type { IAsyncStorageOptions } from "./types/index";
|
|
25
25
|
import type { IExtraPlugin } from "./types/index";
|
|
26
|
+
import type { ImageAlignClasses } from "./types/index";
|
|
27
|
+
import type { ImageHAlign } from "./types/index";
|
|
26
28
|
import type { IUIButtonState } from "./types/index";
|
|
27
29
|
import type { IViewOptions } from "./types/index";
|
|
28
30
|
import type { NodeFunction } from "./types/index";
|
|
@@ -631,6 +633,27 @@ declare class Config implements IViewOptions {
|
|
|
631
633
|
* Default width (in pixels) applied to images inserted into the editor
|
|
632
634
|
*/
|
|
633
635
|
imageDefaultWidth: number;
|
|
636
|
+
/**
|
|
637
|
+
* Alignment given to images inserted into the editor. `'normal'` leaves
|
|
638
|
+
* them unaligned
|
|
639
|
+
*/
|
|
640
|
+
imageDefaultAlign: ImageHAlign;
|
|
641
|
+
/**
|
|
642
|
+
* Class names to align images with, instead of an inline `float` or
|
|
643
|
+
* `margin`. Aligning an image removes every class listed here and adds the
|
|
644
|
+
* chosen alignment's, which can be several separated by spaces.
|
|
645
|
+
*
|
|
646
|
+
* ```javascript
|
|
647
|
+
* Jodit.make('#editor', {
|
|
648
|
+
* imageAlignClasses: {
|
|
649
|
+
* left: 'align-left',
|
|
650
|
+
* right: 'align-right',
|
|
651
|
+
* center: 'align-center'
|
|
652
|
+
* }
|
|
653
|
+
* });
|
|
654
|
+
* ```
|
|
655
|
+
*/
|
|
656
|
+
imageAlignClasses: Nullable<ImageAlignClasses>;
|
|
634
657
|
/**
|
|
635
658
|
* Do not display these buttons that are on the list
|
|
636
659
|
*
|
package/esm/config.js
CHANGED
|
@@ -740,6 +740,27 @@ class Config {
|
|
|
740
740
|
* Default width (in pixels) applied to images inserted into the editor
|
|
741
741
|
*/
|
|
742
742
|
this.imageDefaultWidth = 300;
|
|
743
|
+
/**
|
|
744
|
+
* Alignment given to images inserted into the editor. `'normal'` leaves
|
|
745
|
+
* them unaligned
|
|
746
|
+
*/
|
|
747
|
+
this.imageDefaultAlign = 'normal';
|
|
748
|
+
/**
|
|
749
|
+
* Class names to align images with, instead of an inline `float` or
|
|
750
|
+
* `margin`. Aligning an image removes every class listed here and adds the
|
|
751
|
+
* chosen alignment's, which can be several separated by spaces.
|
|
752
|
+
*
|
|
753
|
+
* ```javascript
|
|
754
|
+
* Jodit.make('#editor', {
|
|
755
|
+
* imageAlignClasses: {
|
|
756
|
+
* left: 'align-left',
|
|
757
|
+
* right: 'align-right',
|
|
758
|
+
* center: 'align-center'
|
|
759
|
+
* }
|
|
760
|
+
* });
|
|
761
|
+
* ```
|
|
762
|
+
*/
|
|
763
|
+
this.imageAlignClasses = null;
|
|
743
764
|
/**
|
|
744
765
|
* Do not display these buttons that are on the list
|
|
745
766
|
*
|
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.16.0";
|
|
7
7
|
// prettier-ignore
|
|
8
8
|
export const ES = "es2020";
|
|
9
9
|
export const IS_ES_MODERN = true;
|
|
@@ -6,11 +6,16 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* @module helpers/utils
|
|
8
8
|
*/
|
|
9
|
-
import type { ImageHAlign } from "../../../types/index";
|
|
9
|
+
import type { ImageAlignClasses, ImageHAlign, Nullable } from "../../../types/index";
|
|
10
10
|
/**
|
|
11
|
-
* Align image
|
|
11
|
+
* Align image, with `classes` (see `imageAlignClasses`) instead of inline
|
|
12
|
+
* styles when they are given
|
|
12
13
|
*/
|
|
13
|
-
export declare function hAlignElement(image: HTMLElement, align: ImageHAlign): void;
|
|
14
|
+
export declare function hAlignElement(image: HTMLElement, align: ImageHAlign, classes?: Nullable<ImageAlignClasses>): void;
|
|
15
|
+
/**
|
|
16
|
+
* The alignment `hAlignElement` has set on an element
|
|
17
|
+
*/
|
|
18
|
+
export declare function getHAlign(elm: HTMLElement, classes?: Nullable<ImageAlignClasses>): ImageHAlign;
|
|
14
19
|
/**
|
|
15
20
|
* Remove text-align style for all selected children
|
|
16
21
|
*/
|
|
@@ -6,10 +6,24 @@
|
|
|
6
6
|
import { Dom } from "../../dom/index.js";
|
|
7
7
|
import { attr } from "./attr.js";
|
|
8
8
|
import { clearCenterAlign, css, cssInline } from "./css.js";
|
|
9
|
+
const classNames = (value) => (value !== null && value !== void 0 ? value : '').split(/\s+/).filter(Boolean);
|
|
9
10
|
/**
|
|
10
|
-
* Align image
|
|
11
|
+
* Align image, with `classes` (see `imageAlignClasses`) instead of inline
|
|
12
|
+
* styles when they are given
|
|
11
13
|
*/
|
|
12
|
-
export function hAlignElement(image, align) {
|
|
14
|
+
export function hAlignElement(image, align, classes) {
|
|
15
|
+
if (classes) {
|
|
16
|
+
hAlignElement(image, 'normal');
|
|
17
|
+
Object.values(classes).forEach(value => image.classList.remove(...classNames(value)));
|
|
18
|
+
image.classList.add(...classNames(classes[align || 'normal']));
|
|
19
|
+
['class', 'style'].forEach(name => {
|
|
20
|
+
var _a;
|
|
21
|
+
if (!((_a = attr(image, name)) === null || _a === void 0 ? void 0 : _a.trim())) {
|
|
22
|
+
attr(image, name, null);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
13
27
|
if (align && align !== 'normal') {
|
|
14
28
|
if (align !== 'center') {
|
|
15
29
|
css(image, 'float', align);
|
|
@@ -32,6 +46,30 @@ export function hAlignElement(image, align) {
|
|
|
32
46
|
clearCenterAlign(image);
|
|
33
47
|
}
|
|
34
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* The alignment `hAlignElement` has set on an element
|
|
51
|
+
*/
|
|
52
|
+
export function getHAlign(elm, classes) {
|
|
53
|
+
if (classes) {
|
|
54
|
+
for (const align of ['left', 'right', 'center', 'normal']) {
|
|
55
|
+
const names = classNames(classes[align]);
|
|
56
|
+
if (names.length &&
|
|
57
|
+
names.every(name => elm.classList.contains(name))) {
|
|
58
|
+
return align;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const float = cssInline(elm, 'float').toLowerCase();
|
|
63
|
+
if (float === 'left' || float === 'right') {
|
|
64
|
+
return float;
|
|
65
|
+
}
|
|
66
|
+
if (css(elm, 'display') === 'block' &&
|
|
67
|
+
cssInline(elm, 'margin-left') === 'auto' &&
|
|
68
|
+
cssInline(elm, 'margin-right') === 'auto') {
|
|
69
|
+
return 'center';
|
|
70
|
+
}
|
|
71
|
+
return 'normal';
|
|
72
|
+
}
|
|
35
73
|
/**
|
|
36
74
|
* Remove text-align style for all selected children
|
|
37
75
|
*/
|
|
@@ -19,6 +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
23
|
import { assert } from "../helpers/utils/assert.js";
|
|
23
24
|
import { moveTheNodeAlongTheEdgeOutward } from "./helpers/move-the-node-along-the-edge-outward.js";
|
|
24
25
|
import "./interface.js";
|
|
@@ -661,6 +662,10 @@ export class Selection {
|
|
|
661
662
|
if (styles && typeof styles === 'object') {
|
|
662
663
|
css(image, styles);
|
|
663
664
|
}
|
|
665
|
+
const { imageDefaultAlign, imageAlignClasses } = this.j.o;
|
|
666
|
+
if (imageDefaultAlign && imageDefaultAlign !== 'normal') {
|
|
667
|
+
hAlignElement(image, imageDefaultAlign, imageAlignClasses);
|
|
668
|
+
}
|
|
664
669
|
const onload = () => {
|
|
665
670
|
if (image.naturalHeight < image.offsetHeight ||
|
|
666
671
|
image.naturalWidth < image.offsetWidth) {
|
|
@@ -112,6 +112,7 @@ let UIButton = class UIButton extends UIElement {
|
|
|
112
112
|
this.name = this.state.name;
|
|
113
113
|
attr(this.container, 'data-ref', this.state.name);
|
|
114
114
|
attr(this.container, 'ref', this.state.name);
|
|
115
|
+
this.updateAriaLabel();
|
|
115
116
|
}
|
|
116
117
|
onChangeTooltip() {
|
|
117
118
|
const i8nTooltip = this.state.tooltip
|
|
@@ -125,11 +126,16 @@ let UIButton = class UIButton extends UIElement {
|
|
|
125
126
|
}
|
|
126
127
|
updateAriaLabel() {
|
|
127
128
|
const hasText = this.state.text.trim().length > 0;
|
|
128
|
-
|
|
129
|
+
// An icon-only button without a tooltip would have no accessible
|
|
130
|
+
// name at all — fall back to its raw name (not translated: control
|
|
131
|
+
// names are identifiers, not i18n keys).
|
|
132
|
+
const label = this.state.tooltip
|
|
129
133
|
? this.jodit.i18n(this.state.tooltip)
|
|
130
|
-
:
|
|
131
|
-
|
|
132
|
-
|
|
134
|
+
: !hasText && this.state.name
|
|
135
|
+
? this.state.name
|
|
136
|
+
: null;
|
|
137
|
+
attr(this.container, 'aria-label', label);
|
|
138
|
+
attr(this.button, 'aria-label', !hasText ? label : null);
|
|
133
139
|
}
|
|
134
140
|
onChangeTabIndex() {
|
|
135
141
|
attr(this.container, 'tabindex', this.state.tabIndex);
|
|
@@ -43,6 +43,7 @@ let UIFileInput = class UIFileInput extends UIInput {
|
|
|
43
43
|
return this.j.create.fromHTML(`<input
|
|
44
44
|
type="file"
|
|
45
45
|
accept="${options.onlyImages ? 'image/*' : '*'}"
|
|
46
|
+
aria-label="${this.j.i18n(options.tooltip || 'Upload')}"
|
|
46
47
|
tabindex="-1"
|
|
47
48
|
dir="auto"
|
|
48
49
|
multiple=""
|
|
@@ -13,6 +13,10 @@ import { UIElement } from "../element";
|
|
|
13
13
|
export declare class UIGroup<T extends IViewBased = IViewBased> extends UIElement<T> implements IUIGroup {
|
|
14
14
|
readonly options?: IDictionary | undefined;
|
|
15
15
|
className(): string;
|
|
16
|
+
/**
|
|
17
|
+
* A generic container: `group` allows any children. Toolbar button groups
|
|
18
|
+
* are lists of `listitem`s and get `role="list"` explicitly from `UIList`.
|
|
19
|
+
*/
|
|
16
20
|
getRole(): string;
|
|
17
21
|
/**
|
|
18
22
|
* Synchronize mods to all children
|
|
@@ -23,9 +23,13 @@ let UIGroup = UIGroup_1 = class UIGroup extends UIElement {
|
|
|
23
23
|
className() {
|
|
24
24
|
return 'UIGroup';
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* A generic container: `group` allows any children. Toolbar button groups
|
|
28
|
+
* are lists of `listitem`s and get `role="list"` explicitly from `UIList`.
|
|
29
|
+
*/
|
|
26
30
|
getRole() {
|
|
27
31
|
var _a;
|
|
28
|
-
return ((_a = this.options) === null || _a === void 0 ? void 0 : _a.role) || '
|
|
32
|
+
return ((_a = this.options) === null || _a === void 0 ? void 0 : _a.role) || 'group';
|
|
29
33
|
}
|
|
30
34
|
/**
|
|
31
35
|
* All group children
|
|
@@ -43,8 +43,8 @@ let UIList = class UIList extends UIGroup {
|
|
|
43
43
|
/**
|
|
44
44
|
* Make new group and append it in list of elements
|
|
45
45
|
*/
|
|
46
|
-
makeGroup(role) {
|
|
47
|
-
return new UIGroup(this.jodit, [],
|
|
46
|
+
makeGroup(role = 'list') {
|
|
47
|
+
return new UIGroup(this.jodit, [], { role });
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
50
|
* All buttons from list
|
|
@@ -21,6 +21,7 @@ import { Dom } from "../../core/dom/dom.js";
|
|
|
21
21
|
import { eventEmitter, pluginSystem } from "../../core/global.js";
|
|
22
22
|
import { asArray, splitArray, toArray } from "../../core/helpers/array/index.js";
|
|
23
23
|
import { hasContainer, isArray, isBoolean, isFunction, isNumber, isString, isVoid } from "../../core/helpers/checker/index.js";
|
|
24
|
+
import { stripTags } from "../../core/helpers/html/strip-tags.js";
|
|
24
25
|
import { attr, ConfigProto, css } from "../../core/helpers/utils/index.js";
|
|
25
26
|
import { assert } from "../../core/helpers/utils/assert.js";
|
|
26
27
|
import { Icon } from "../../core/ui/index.js";
|
|
@@ -292,6 +293,12 @@ let Dialog = Dialog_1 = class Dialog extends ViewWithToolbar {
|
|
|
292
293
|
*/
|
|
293
294
|
setHeader(content) {
|
|
294
295
|
this.setElements(this.dialogbox_header, content);
|
|
296
|
+
// `role="dialog"` needs an accessible name; a text title is the
|
|
297
|
+
// natural one (element titles are left to the caller).
|
|
298
|
+
if (isString(content)) {
|
|
299
|
+
const name = stripTags(content, this.od).trim();
|
|
300
|
+
attr(this.container, 'aria-label', name || null);
|
|
301
|
+
}
|
|
295
302
|
return this;
|
|
296
303
|
}
|
|
297
304
|
/**
|
|
@@ -13,6 +13,13 @@ export declare class ToolbarContent<T extends IViewBased = IViewBased> extends U
|
|
|
13
13
|
readonly target: Nullable<HTMLElement>;
|
|
14
14
|
/** @override */
|
|
15
15
|
className(): string;
|
|
16
|
+
/** A toolbar item like `ToolbarButton`: the parent group is a `list`. */
|
|
17
|
+
getRole(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Arbitrary content is not a toggle button — `aria-pressed` is not
|
|
20
|
+
* allowed on a `listitem`.
|
|
21
|
+
*/
|
|
22
|
+
protected onChangeActivated(): void;
|
|
16
23
|
/** @override */
|
|
17
24
|
update(): void;
|
|
18
25
|
/**
|
|
@@ -28,6 +28,15 @@ let ToolbarContent = class ToolbarContent extends UIButton {
|
|
|
28
28
|
className() {
|
|
29
29
|
return 'ToolbarContent';
|
|
30
30
|
}
|
|
31
|
+
/** A toolbar item like `ToolbarButton`: the parent group is a `list`. */
|
|
32
|
+
getRole() {
|
|
33
|
+
return 'listitem';
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Arbitrary content is not a toggle button — `aria-pressed` is not
|
|
37
|
+
* allowed on a `listitem`.
|
|
38
|
+
*/
|
|
39
|
+
onChangeActivated() { }
|
|
31
40
|
/** @override */
|
|
32
41
|
update() {
|
|
33
42
|
var _a, _b, _c;
|
|
@@ -73,7 +82,6 @@ let ToolbarContent = class ToolbarContent extends UIButton {
|
|
|
73
82
|
this.control = control;
|
|
74
83
|
this.target = target;
|
|
75
84
|
this.container.classList.add(`${this.componentName}_${this.clearName(control.name)}`);
|
|
76
|
-
attr(this.container, 'role', 'content');
|
|
77
85
|
}
|
|
78
86
|
};
|
|
79
87
|
ToolbarContent = __decorate([
|
|
@@ -3,8 +3,12 @@
|
|
|
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/image-properties
|
|
8
|
+
*/
|
|
9
|
+
import type { ImageAlignClasses, Nullable } from "../../../types/index";
|
|
6
10
|
import type { EditValues } from "../interface";
|
|
7
11
|
/**
|
|
8
12
|
* @private
|
|
9
13
|
*/
|
|
10
|
-
export declare function readAlign(image: HTMLImageElement, values: EditValues): void;
|
|
14
|
+
export declare function readAlign(image: HTMLImageElement, values: EditValues, classes?: Nullable<ImageAlignClasses>): void;
|
|
@@ -3,25 +3,11 @@
|
|
|
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 {
|
|
6
|
+
import { getHAlign } from "../../../core/helpers/utils/align.js";
|
|
7
7
|
/**
|
|
8
8
|
* @private
|
|
9
9
|
*/
|
|
10
|
-
export function readAlign(image, values) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
['left', 'right'].indexOf(cssInline(image, 'float').toLowerCase()) !==
|
|
14
|
-
-1) {
|
|
15
|
-
values.align = css(image, 'float');
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
if (css(image, 'display') === 'block' &&
|
|
19
|
-
cssInline(image, 'margin-left') === 'auto' &&
|
|
20
|
-
cssInline(image, 'margin-right') === 'auto') {
|
|
21
|
-
values.align = 'center';
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
values.align = '';
|
|
25
|
-
}
|
|
26
|
-
}
|
|
10
|
+
export function readAlign(image, values, classes) {
|
|
11
|
+
const align = getHAlign(image, classes);
|
|
12
|
+
values.align = align === 'normal' ? '' : align;
|
|
27
13
|
}
|
|
@@ -15,7 +15,7 @@ import { readSizes } from "./size.js";
|
|
|
15
15
|
*/
|
|
16
16
|
export async function readValuesFromImage(j, state) {
|
|
17
17
|
const { sourceImage: image, values } = state;
|
|
18
|
-
readAlign(image, values);
|
|
18
|
+
readAlign(image, values, j.o.imageAlignClasses);
|
|
19
19
|
// Border radius
|
|
20
20
|
values.borderRadius =
|
|
21
21
|
parseInt(cssInline(image, 'border-radius') || '0', 10) || 0;
|
|
@@ -5,12 +5,27 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { Dom } from "../../../../core/dom/index.js";
|
|
7
7
|
import { isString } from "../../../../core/helpers/checker/is-string.js";
|
|
8
|
-
import { hAlignElement } from "../../../../core/helpers/utils/align.js";
|
|
8
|
+
import { getHAlign, hAlignElement } from "../../../../core/helpers/utils/align.js";
|
|
9
9
|
import { css } from "../../../../core/helpers/utils/css.js";
|
|
10
|
+
// Images align with `imageAlignClasses`; the wrappers of iframes and media
|
|
11
|
+
// keep inline styles
|
|
12
|
+
const alignClasses = (editor, elm) => Dom.isTag(elm, 'img') ? editor.o.imageAlignClasses : null;
|
|
13
|
+
const currentAlign = (editor, elm) => (elm ? getHAlign(elm, alignClasses(editor, elm)) : 'normal');
|
|
10
14
|
export const align = {
|
|
11
15
|
name: 'left',
|
|
12
16
|
childTemplate: (_, __, value) => value,
|
|
13
17
|
list: ['Left', 'Right', 'Center', 'Normal'],
|
|
18
|
+
update: (editor, button) => {
|
|
19
|
+
const current = currentAlign(editor, button.target);
|
|
20
|
+
button.state.icon.name = current === 'normal' ? 'left' : current;
|
|
21
|
+
},
|
|
22
|
+
isActive: (editor, button) => currentAlign(editor, button.target) !== 'normal',
|
|
23
|
+
isChildActive: (editor, button) => {
|
|
24
|
+
var _a;
|
|
25
|
+
const [value] = (_a = button.control.args) !== null && _a !== void 0 ? _a : [];
|
|
26
|
+
return (isString(value) &&
|
|
27
|
+
currentAlign(editor, button.target) === value.toLowerCase());
|
|
28
|
+
},
|
|
14
29
|
exec: (editor, elm, { control }) => {
|
|
15
30
|
if (!Dom.isTag(elm, new Set(['img', 'jodit', 'jodit-media']))) {
|
|
16
31
|
return;
|
|
@@ -21,7 +36,7 @@ export const align = {
|
|
|
21
36
|
if (!command) {
|
|
22
37
|
return false;
|
|
23
38
|
}
|
|
24
|
-
hAlignElement(elm, command);
|
|
39
|
+
hAlignElement(elm, command, alignClasses(editor, elm));
|
|
25
40
|
if (Dom.isTag(elm, new Set(['jodit', 'jodit-media'])) &&
|
|
26
41
|
elm.firstElementChild) {
|
|
27
42
|
hAlignElement(elm.firstElementChild, command);
|
|
@@ -127,23 +127,30 @@ export class AceEditor extends SourceEditor {
|
|
|
127
127
|
onResize();
|
|
128
128
|
this.onReady();
|
|
129
129
|
};
|
|
130
|
+
let loading = false;
|
|
130
131
|
const initAceEditor = () => {
|
|
131
132
|
if (editor.isInDestruct ||
|
|
132
133
|
(editor.getRealMode() !== constants.MODE_SOURCE &&
|
|
133
134
|
editor.getMode() !== constants.MODE_SPLIT)) {
|
|
134
135
|
return;
|
|
135
136
|
}
|
|
137
|
+
// ACE (~400 KiB from the CDN) is only needed once the source view
|
|
138
|
+
// is actually shown — a page that stays in WYSIWYG must not
|
|
139
|
+
// download it.
|
|
140
|
+
if (!this.aceExists()) {
|
|
141
|
+
if (!loading) {
|
|
142
|
+
loading = true;
|
|
143
|
+
loadNext(editor, editor.o.sourceEditorCDNUrlsJS)
|
|
144
|
+
.then(initAceEditor)
|
|
145
|
+
.catch(() => null);
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
136
149
|
this.fromWYSIWYG();
|
|
137
150
|
tryInitAceEditor();
|
|
138
151
|
};
|
|
139
152
|
editor.e.on('afterSetMode', initAceEditor);
|
|
140
153
|
initAceEditor();
|
|
141
|
-
// global add ace editor in browser
|
|
142
|
-
if (!this.aceExists()) {
|
|
143
|
-
loadNext(editor, editor.o.sourceEditorCDNUrlsJS)
|
|
144
|
-
.then(initAceEditor)
|
|
145
|
-
.catch(() => null);
|
|
146
|
-
}
|
|
147
154
|
}
|
|
148
155
|
destruct() {
|
|
149
156
|
var _a, _b;
|
|
@@ -261,7 +261,26 @@ export class source extends Plugin {
|
|
|
261
261
|
return false;
|
|
262
262
|
};
|
|
263
263
|
if (!addEventListener()) {
|
|
264
|
-
|
|
264
|
+
// js-beautify is only needed once the source view is shown —
|
|
265
|
+
// do not download it for a page that stays in WYSIWYG.
|
|
266
|
+
let loading = false;
|
|
267
|
+
const loadBeautify = () => {
|
|
268
|
+
if (loading ||
|
|
269
|
+
(editor.getMode() !== MODE_SOURCE &&
|
|
270
|
+
editor.getMode() !== MODE_SPLIT)) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
loading = true;
|
|
274
|
+
loadNext(editor, editor.o.beautifyHTMLCDNUrlsJS).then(() => {
|
|
275
|
+
// The source view was filled before the
|
|
276
|
+
// beautifier arrived — format it now.
|
|
277
|
+
if (addEventListener()) {
|
|
278
|
+
this.syncValueFromWYSIWYG(true);
|
|
279
|
+
}
|
|
280
|
+
}, () => null);
|
|
281
|
+
};
|
|
282
|
+
editor.e.on('afterSetMode.source', loadBeautify);
|
|
283
|
+
loadBeautify();
|
|
265
284
|
}
|
|
266
285
|
}
|
|
267
286
|
this.syncValueFromWYSIWYG(true);
|
package/esm/types/types.d.ts
CHANGED
|
@@ -187,6 +187,10 @@ 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 image alignment, see `imageAlignClasses`
|
|
192
|
+
*/
|
|
193
|
+
export type ImageAlignClasses = Partial<Record<Exclude<ImageHAlign, ''>, string>>;
|
|
190
194
|
export interface ImageBox {
|
|
191
195
|
w: number;
|
|
192
196
|
h: number;
|
package/package.json
CHANGED
package/types/config.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ import type { Attributes } from "./types/index";
|
|
|
23
23
|
import type { Controls } from "./types/index";
|
|
24
24
|
import type { IAsyncStorageOptions } from "./types/index";
|
|
25
25
|
import type { IExtraPlugin } from "./types/index";
|
|
26
|
+
import type { ImageAlignClasses } from "./types/index";
|
|
27
|
+
import type { ImageHAlign } from "./types/index";
|
|
26
28
|
import type { IUIButtonState } from "./types/index";
|
|
27
29
|
import type { IViewOptions } from "./types/index";
|
|
28
30
|
import type { NodeFunction } from "./types/index";
|
|
@@ -631,6 +633,27 @@ declare class Config implements IViewOptions {
|
|
|
631
633
|
* Default width (in pixels) applied to images inserted into the editor
|
|
632
634
|
*/
|
|
633
635
|
imageDefaultWidth: number;
|
|
636
|
+
/**
|
|
637
|
+
* Alignment given to images inserted into the editor. `'normal'` leaves
|
|
638
|
+
* them unaligned
|
|
639
|
+
*/
|
|
640
|
+
imageDefaultAlign: ImageHAlign;
|
|
641
|
+
/**
|
|
642
|
+
* Class names to align images with, instead of an inline `float` or
|
|
643
|
+
* `margin`. Aligning an image removes every class listed here and adds the
|
|
644
|
+
* chosen alignment's, which can be several separated by spaces.
|
|
645
|
+
*
|
|
646
|
+
* ```javascript
|
|
647
|
+
* Jodit.make('#editor', {
|
|
648
|
+
* imageAlignClasses: {
|
|
649
|
+
* left: 'align-left',
|
|
650
|
+
* right: 'align-right',
|
|
651
|
+
* center: 'align-center'
|
|
652
|
+
* }
|
|
653
|
+
* });
|
|
654
|
+
* ```
|
|
655
|
+
*/
|
|
656
|
+
imageAlignClasses: Nullable<ImageAlignClasses>;
|
|
634
657
|
/**
|
|
635
658
|
* Do not display these buttons that are on the list
|
|
636
659
|
*
|