jodit 4.18.1 → 4.18.3
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 +30 -0
- package/es2015/jodit.css +7 -6
- package/es2015/jodit.fat.min.js +10 -10
- package/es2015/jodit.js +492 -189
- package/es2015/jodit.min.js +10 -10
- 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 +9 -9
- 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 +7 -6
- package/es2021/jodit.fat.min.js +16 -16
- package/es2021/jodit.js +432 -135
- 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 +7 -6
- package/es2021.en/jodit.fat.min.js +16 -16
- package/es2021.en/jodit.js +432 -135
- package/es2021.en/jodit.min.js +16 -16
- 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 +8 -7
- package/es5/jodit.fat.min.js +2 -2
- package/es5/jodit.js +555 -215
- 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 +4 -2
- package/esm/core/constants.js +1 -1
- package/esm/core/helpers/utils/index.d.ts +1 -0
- package/esm/core/helpers/utils/index.js +1 -0
- package/esm/core/helpers/utils/prepare-media.d.ts +19 -0
- package/esm/core/helpers/utils/prepare-media.js +31 -0
- package/esm/core/selection/style/api/wrap-unwrapped-text.js +3 -2
- package/esm/modules/history/history.d.ts +17 -2
- package/esm/modules/history/history.js +41 -19
- package/esm/modules/table/table.js +7 -2
- package/esm/modules/widget/file-selector/file-selector.js +2 -2
- package/esm/plugins/backspace/backspace.js +4 -0
- package/esm/plugins/clean-html/clean-html.d.ts +9 -1
- package/esm/plugins/clean-html/clean-html.js +41 -7
- package/esm/plugins/clean-html/helpers/visitor/filters/replace-old-tags.js +26 -1
- package/esm/plugins/dtd/before-insert/check-block-nesting.d.ts +2 -1
- package/esm/plugins/dtd/before-insert/check-block-nesting.js +26 -5
- package/esm/plugins/enter/helpers/move-cursor-out-from-specal-tags.js +8 -0
- package/esm/plugins/focus/focus.js +3 -0
- package/esm/plugins/hr/hr.js +21 -5
- package/esm/plugins/iframe/config.js +8 -3
- package/esm/plugins/inline-popup/inline-popup.d.ts +5 -0
- package/esm/plugins/inline-popup/inline-popup.js +26 -2
- package/esm/plugins/link/link.js +5 -2
- package/esm/plugins/media/config.d.ts +2 -1
- package/esm/plugins/media/media.js +21 -2
- package/esm/plugins/paste/helpers.d.ts +8 -0
- package/esm/plugins/paste/helpers.js +18 -1
- package/esm/plugins/paste/paste.js +2 -2
- package/esm/plugins/resizer/resizer.js +8 -3
- package/esm/plugins/select-cells/select-cells.d.ts +10 -0
- package/esm/plugins/select-cells/select-cells.js +54 -6
- package/esm/plugins/table/config.js +51 -1
- package/esm/plugins/table-keyboard-navigation/table-keyboard-navigation.js +1 -1
- package/esm/plugins/video/config.js +6 -22
- package/package.json +1 -1
- package/types/config.d.ts +4 -2
- package/types/core/helpers/utils/index.d.ts +1 -0
- package/types/core/helpers/utils/prepare-media.d.ts +19 -0
- package/types/modules/history/history.d.ts +17 -2
- package/types/plugins/clean-html/clean-html.d.ts +9 -1
- package/types/plugins/dtd/before-insert/check-block-nesting.d.ts +2 -1
- package/types/plugins/inline-popup/inline-popup.d.ts +5 -0
- package/types/plugins/media/config.d.ts +2 -1
- package/types/plugins/paste/helpers.d.ts +8 -0
- package/types/plugins/select-cells/select-cells.d.ts +10 -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
|
@@ -989,7 +989,8 @@ interface Config {
|
|
|
989
989
|
*/
|
|
990
990
|
maxHistoryLength: number;
|
|
991
991
|
/**
|
|
992
|
-
*
|
|
992
|
+
* How long a step stays open after its first change, in
|
|
993
|
+
* milliseconds. The changes made in that time are undone together.
|
|
993
994
|
*/
|
|
994
995
|
timeout: number;
|
|
995
996
|
};
|
|
@@ -1597,7 +1598,8 @@ interface Config {
|
|
|
1597
1598
|
* - is wrapped in it instead of the editor-only `mediaFakeTag` when its tag
|
|
1598
1599
|
* is one of `mediaBlocks`, or left in it when it's already in one
|
|
1599
1600
|
* - 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
|
+
* link's embed or embed code that is one element, and when a pasted
|
|
1602
|
+
* video link is turned into its embed
|
|
1601
1603
|
* - opens the media popup for its wrapper when clicked, whose Align and
|
|
1602
1604
|
* Delete act on the wrapper
|
|
1603
1605
|
*
|
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.18.
|
|
6
|
+
export const APP_VERSION = "4.18.3";
|
|
7
7
|
// prettier-ignore
|
|
8
8
|
export const ES = "es2020";
|
|
9
9
|
export const IS_ES_MODERN = true;
|
|
@@ -28,6 +28,7 @@ export * from "./is-unsafe-proto-key";
|
|
|
28
28
|
export * from "./mark-deprecated";
|
|
29
29
|
export * from "./media-wrapper";
|
|
30
30
|
export * from "./parse-query";
|
|
31
|
+
export * from "./prepare-media";
|
|
31
32
|
export * from "./print";
|
|
32
33
|
export * from "./reset";
|
|
33
34
|
export * from "./scroll-into-view";
|
|
@@ -28,6 +28,7 @@ export * from "./is-unsafe-proto-key.js";
|
|
|
28
28
|
export * from "./mark-deprecated.js";
|
|
29
29
|
export * from "./media-wrapper.js";
|
|
30
30
|
export * from "./parse-query.js";
|
|
31
|
+
export * from "./prepare-media.js";
|
|
31
32
|
export * from "./print.js";
|
|
32
33
|
export * from "./reset.js";
|
|
33
34
|
export * from "./scroll-into-view.js";
|
|
@@ -0,0 +1,19 @@
|
|
|
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 } from "../../../types/index";
|
|
10
|
+
/**
|
|
11
|
+
* Puts new media in its wrapper (see `mediaWrappers`) and aligns it with
|
|
12
|
+
* `mediaDefaultAlign`. Returns the wrapper, or `element` when there is none
|
|
13
|
+
*/
|
|
14
|
+
export declare function prepareMedia(jodit: IJodit, element: HTMLElement): HTMLElement;
|
|
15
|
+
/**
|
|
16
|
+
* Embed code that is one element is prepared as media (see `prepareMedia`);
|
|
17
|
+
* other code is returned as it is
|
|
18
|
+
*/
|
|
19
|
+
export declare function prepareEmbedCode(jodit: IJodit, code: string): string | HTMLElement;
|
|
@@ -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
|
+
import { getAlignClasses, hAlignElement } from "./align.js";
|
|
7
|
+
import { wrapMedia } from "./media-wrapper.js";
|
|
8
|
+
/**
|
|
9
|
+
* Puts new media in its wrapper (see `mediaWrappers`) and aligns it with
|
|
10
|
+
* `mediaDefaultAlign`. Returns the wrapper, or `element` when there is none
|
|
11
|
+
*/
|
|
12
|
+
export function prepareMedia(jodit, element) {
|
|
13
|
+
const media = wrapMedia(jodit, element);
|
|
14
|
+
const { mediaDefaultAlign } = jodit.o;
|
|
15
|
+
if (mediaDefaultAlign && mediaDefaultAlign !== 'normal') {
|
|
16
|
+
hAlignElement(media, mediaDefaultAlign, getAlignClasses(jodit, media));
|
|
17
|
+
}
|
|
18
|
+
return media;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Embed code that is one element is prepared as media (see `prepareMedia`);
|
|
22
|
+
* other code is returned as it is
|
|
23
|
+
*/
|
|
24
|
+
export function prepareEmbedCode(jodit, code) {
|
|
25
|
+
const box = jodit.createInside.div();
|
|
26
|
+
box.innerHTML = code.trim();
|
|
27
|
+
const element = box.firstElementChild;
|
|
28
|
+
return element && box.childNodes.length === 1
|
|
29
|
+
? prepareMedia(jodit, element)
|
|
30
|
+
: code;
|
|
31
|
+
}
|
|
@@ -43,8 +43,9 @@ export function wrapUnwrappedText(style, elm, jodit) {
|
|
|
43
43
|
Dom.append(wrapper, fragment);
|
|
44
44
|
Dom.safeInsertNode(range, wrapper);
|
|
45
45
|
if (style.elementIsBlock) {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
// The wrapped content can bring its own `<br>` (an empty table
|
|
47
|
+
// cell's), after or inside the temporary `<font>` around the selection
|
|
48
|
+
if (Dom.isEmpty(wrapper) && !wrapper.querySelector('br')) {
|
|
48
49
|
Dom.append(wrapper, ci.element('br'));
|
|
49
50
|
}
|
|
50
51
|
}
|
|
@@ -21,7 +21,8 @@ declare module 'jodit/config' {
|
|
|
21
21
|
*/
|
|
22
22
|
maxHistoryLength: number;
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* How long a step stays open after its first change, in
|
|
25
|
+
* milliseconds. The changes made in that time are undone together.
|
|
25
26
|
*/
|
|
26
27
|
timeout: number;
|
|
27
28
|
};
|
|
@@ -52,15 +53,29 @@ export declare class History extends ViewComponent<IJodit> implements IHistory {
|
|
|
52
53
|
snapshot: ISnapshot & IDestructible;
|
|
53
54
|
constructor(editor: IJodit, stack?: Stack, snapshot?: Snapshot);
|
|
54
55
|
private updateTick;
|
|
56
|
+
/**
|
|
57
|
+
* A step is open: changes have come in that are not in the stack yet
|
|
58
|
+
*/
|
|
59
|
+
private __hasPendingChanges;
|
|
60
|
+
/**
|
|
61
|
+
* Records the open step `history.timeout` after its first change
|
|
62
|
+
*/
|
|
63
|
+
private __timer;
|
|
55
64
|
/**
|
|
56
65
|
* Update change counter
|
|
57
66
|
* @internal
|
|
58
67
|
*/
|
|
59
68
|
protected __upTick(): void;
|
|
60
69
|
/**
|
|
61
|
-
* Push new command in stack on some changes
|
|
70
|
+
* Push new command in stack on some changes. The first change opens a
|
|
71
|
+
* step, and later ones join it until `history.timeout` has passed,
|
|
72
|
+
* however close together they come.
|
|
62
73
|
*/
|
|
63
74
|
private onChange;
|
|
75
|
+
/**
|
|
76
|
+
* Push the open step's changes now
|
|
77
|
+
*/
|
|
78
|
+
private __processPendingChanges;
|
|
64
79
|
/**
|
|
65
80
|
* @internal
|
|
66
81
|
*/
|
|
@@ -3,18 +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
|
-
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
|
-
};
|
|
16
6
|
import { ViewComponent } from "../../core/component/view-component.js";
|
|
17
|
-
import { debounce } from "../../core/decorators/debounce/debounce.js";
|
|
18
7
|
import { Config } from "../../config.js";
|
|
19
8
|
import { Command } from "./command.js";
|
|
20
9
|
import { Snapshot } from "./snapshot.js";
|
|
@@ -36,28 +25,32 @@ export class History extends ViewComponent {
|
|
|
36
25
|
* Return state of the WYSIWYG editor to step back
|
|
37
26
|
*/
|
|
38
27
|
redo() {
|
|
28
|
+
this.__processPendingChanges();
|
|
39
29
|
if (this.__stack.redo()) {
|
|
40
30
|
this.startValue = this.snapshot.make();
|
|
41
31
|
this.fireChangeStack();
|
|
42
32
|
}
|
|
43
33
|
}
|
|
44
34
|
canRedo() {
|
|
45
|
-
return this.__stack.canRedo();
|
|
35
|
+
return !this.__hasPendingChanges && this.__stack.canRedo();
|
|
46
36
|
}
|
|
47
37
|
/**
|
|
48
38
|
* Return the state of the WYSIWYG editor to step forward
|
|
49
39
|
*/
|
|
50
40
|
undo() {
|
|
41
|
+
this.__processPendingChanges();
|
|
51
42
|
if (this.__stack.undo()) {
|
|
52
43
|
this.startValue = this.snapshot.make();
|
|
53
44
|
this.fireChangeStack();
|
|
54
45
|
}
|
|
55
46
|
}
|
|
56
47
|
canUndo() {
|
|
57
|
-
return this.__stack.canUndo();
|
|
48
|
+
return this.__hasPendingChanges || this.__stack.canUndo();
|
|
58
49
|
}
|
|
59
50
|
clear() {
|
|
60
51
|
this.startValue = this.snapshot.make();
|
|
52
|
+
this.async.clearTimeout(this.__timer);
|
|
53
|
+
this.__hasPendingChanges = false;
|
|
61
54
|
this.__stack.clear();
|
|
62
55
|
this.fireChangeStack();
|
|
63
56
|
}
|
|
@@ -73,6 +66,14 @@ export class History extends ViewComponent {
|
|
|
73
66
|
constructor(editor, stack = new Stack(editor.o.history.maxHistoryLength), snapshot = new Snapshot(editor)) {
|
|
74
67
|
super(editor);
|
|
75
68
|
this.updateTick = 0;
|
|
69
|
+
/**
|
|
70
|
+
* A step is open: changes have come in that are not in the stack yet
|
|
71
|
+
*/
|
|
72
|
+
this.__hasPendingChanges = false;
|
|
73
|
+
/**
|
|
74
|
+
* Records the open step `history.timeout` after its first change
|
|
75
|
+
*/
|
|
76
|
+
this.__timer = 0;
|
|
76
77
|
this.__stack = stack;
|
|
77
78
|
this.snapshot = snapshot;
|
|
78
79
|
if (editor.o.history.enable) {
|
|
@@ -84,6 +85,7 @@ export class History extends ViewComponent {
|
|
|
84
85
|
editor.events
|
|
85
86
|
// save selection
|
|
86
87
|
.on('internalChange internalUpdate', () => {
|
|
88
|
+
this.__processPendingChanges();
|
|
87
89
|
this.startValue = this.snapshot.make();
|
|
88
90
|
})
|
|
89
91
|
.on(editor.editor, [
|
|
@@ -102,7 +104,7 @@ export class History extends ViewComponent {
|
|
|
102
104
|
this.startValue = this.snapshot.make();
|
|
103
105
|
}
|
|
104
106
|
})
|
|
105
|
-
.on(this, 'change.history', this.onChange);
|
|
107
|
+
.on(this, 'change.history', () => this.onChange());
|
|
106
108
|
});
|
|
107
109
|
}
|
|
108
110
|
}
|
|
@@ -114,10 +116,31 @@ export class History extends ViewComponent {
|
|
|
114
116
|
this.updateTick += 1;
|
|
115
117
|
}
|
|
116
118
|
/**
|
|
117
|
-
* Push new command in stack on some changes
|
|
119
|
+
* Push new command in stack on some changes. The first change opens a
|
|
120
|
+
* step, and later ones join it until `history.timeout` has passed,
|
|
121
|
+
* however close together they come.
|
|
118
122
|
*/
|
|
119
123
|
onChange() {
|
|
120
|
-
|
|
124
|
+
// A transaction is over by the time the step is recorded, so it is
|
|
125
|
+
// checked now
|
|
126
|
+
if (this.snapshot.isBlocked || this.__hasPendingChanges) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
this.__hasPendingChanges = true;
|
|
130
|
+
this.__timer = this.async.setTimeout(() => this.__processPendingChanges(), this.j.o.history.timeout);
|
|
131
|
+
// With a timeout of 0 the step is already recorded. Otherwise
|
|
132
|
+
// canUndo and canRedo count it from now
|
|
133
|
+
if (this.__hasPendingChanges) {
|
|
134
|
+
this.fireChangeStack();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Push the open step's changes now
|
|
139
|
+
*/
|
|
140
|
+
__processPendingChanges() {
|
|
141
|
+
if (this.__hasPendingChanges) {
|
|
142
|
+
this.__processChanges();
|
|
143
|
+
}
|
|
121
144
|
}
|
|
122
145
|
/**
|
|
123
146
|
* @internal
|
|
@@ -126,6 +149,8 @@ export class History extends ViewComponent {
|
|
|
126
149
|
if (this.snapshot.isBlocked || !this.j.o.history.enable) {
|
|
127
150
|
return;
|
|
128
151
|
}
|
|
152
|
+
this.async.clearTimeout(this.__timer);
|
|
153
|
+
this.__hasPendingChanges = false;
|
|
129
154
|
this.updateStack();
|
|
130
155
|
}
|
|
131
156
|
/**
|
|
@@ -171,6 +196,3 @@ export class History extends ViewComponent {
|
|
|
171
196
|
super.destruct();
|
|
172
197
|
}
|
|
173
198
|
}
|
|
174
|
-
__decorate([
|
|
175
|
-
debounce()
|
|
176
|
-
], History.prototype, "onChange", null);
|
|
@@ -172,7 +172,9 @@ export class Table extends ViewComponent {
|
|
|
172
172
|
const columnsCount = Table.__getColumnsCount(table);
|
|
173
173
|
row = create.element('tr');
|
|
174
174
|
for (let j = 0; j < columnsCount; j += 1) {
|
|
175
|
-
|
|
175
|
+
const cell = create.element('td');
|
|
176
|
+
Dom.append(cell, create.element('br'));
|
|
177
|
+
Dom.append(row, cell);
|
|
176
178
|
}
|
|
177
179
|
}
|
|
178
180
|
else {
|
|
@@ -190,6 +192,7 @@ export class Table extends ViewComponent {
|
|
|
190
192
|
Dom.each(row, cell => {
|
|
191
193
|
if (Dom.isCell(cell)) {
|
|
192
194
|
Dom.detach(cell);
|
|
195
|
+
Dom.append(cell, create.element('br'));
|
|
193
196
|
}
|
|
194
197
|
});
|
|
195
198
|
}
|
|
@@ -288,6 +291,7 @@ export class Table extends ViewComponent {
|
|
|
288
291
|
if (cells[columnIndex] !== cells[newColumnIndex] ||
|
|
289
292
|
columnIndex === newColumnIndex) {
|
|
290
293
|
const cell = this.j.createInside.element('td');
|
|
294
|
+
Dom.append(cell, this.j.createInside.element('br'));
|
|
291
295
|
if (insertAfter) {
|
|
292
296
|
Dom.after(cells[columnIndex], cell);
|
|
293
297
|
}
|
|
@@ -516,7 +520,8 @@ export class Table extends ViewComponent {
|
|
|
516
520
|
Table.__setColumnWidthByDelta(table, first_j, 0, true, __marked);
|
|
517
521
|
}
|
|
518
522
|
}
|
|
519
|
-
first.innerHTML =
|
|
523
|
+
first.innerHTML =
|
|
524
|
+
html.join('<br/>') || '<br/>';
|
|
520
525
|
instance(jodit).addSelection(first);
|
|
521
526
|
alreadyMerged.delete(first);
|
|
522
527
|
Table.__unmark(__marked);
|
|
@@ -99,8 +99,8 @@ export const FileSelectorWidget = (editor, callbacks, elm, close, isImage = true
|
|
|
99
99
|
const box = TabsWidget(editor, tabs);
|
|
100
100
|
// Marks this popup so its tabs auto-size to their labels (see
|
|
101
101
|
// `.jodit-file-selector` in tabs.less) — long localized tab captions are no
|
|
102
|
-
// longer clipped — while leaving other tab popups (link
|
|
103
|
-
//
|
|
102
|
+
// longer clipped — while leaving other tab popups (link …) untouched. The
|
|
103
|
+
// video popup, which has an Upload tab too, uses it as well.
|
|
104
104
|
box.classList.add('jodit-file-selector');
|
|
105
105
|
return box;
|
|
106
106
|
};
|
|
@@ -56,6 +56,10 @@ export class backspace extends Plugin {
|
|
|
56
56
|
if (!sel.isFocused()) {
|
|
57
57
|
sel.focus();
|
|
58
58
|
}
|
|
59
|
+
// Focused with no range, as WebKit leaves it after a click on an embed
|
|
60
|
+
if (!sel.isInsideArea) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
59
63
|
if (checkNotCollapsed(jodit)) {
|
|
60
64
|
jodit.e.fire('backSpaceAfterDelete', backspace);
|
|
61
65
|
return false;
|
|
@@ -14,8 +14,9 @@ import "./config";
|
|
|
14
14
|
export declare class cleanHtml extends Plugin {
|
|
15
15
|
/** @override */
|
|
16
16
|
buttons: Plugin['buttons'];
|
|
17
|
+
constructor(jodit: IJodit);
|
|
17
18
|
/** @override */
|
|
18
|
-
protected afterInit(
|
|
19
|
+
protected afterInit(): void;
|
|
19
20
|
private get isEditMode();
|
|
20
21
|
/**
|
|
21
22
|
* Clean HTML code on every change
|
|
@@ -39,6 +40,13 @@ export declare class cleanHtml extends Plugin {
|
|
|
39
40
|
protected onBeforeSetNativeEditorValue(data: {
|
|
40
41
|
value: string;
|
|
41
42
|
}): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Replace old tags (`<b>` with `<strong>`, `<i>` with `<em>`) in an
|
|
45
|
+
* assigned value straight away. The walker would get to them only after
|
|
46
|
+
* every node before them, seconds later on a long document, and until
|
|
47
|
+
* then Bold does not treat `<b>` as bold, and the swap can move the caret.
|
|
48
|
+
*/
|
|
49
|
+
private __replaceOldTags;
|
|
42
50
|
/**
|
|
43
51
|
* Collapse a value that holds only a single empty block (e.g.
|
|
44
52
|
* `<p><br></p>` left after deleting all content) to an empty string —
|
|
@@ -13,21 +13,22 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
13
13
|
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
14
14
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
15
15
|
};
|
|
16
|
-
import { hook, watch } from "../../core/decorators/index.js";
|
|
16
|
+
import { autobind, hook, watch } from "../../core/decorators/index.js";
|
|
17
17
|
import { Dom } from "../../core/dom/dom.js";
|
|
18
18
|
import { LazyWalker } from "../../core/dom/lazy-walker.js";
|
|
19
19
|
import { pluginSystem } from "../../core/global.js";
|
|
20
20
|
import { safeHTML } from "../../core/helpers/html/safe-html.js";
|
|
21
21
|
import { Plugin } from "../../core/plugin/plugin.js";
|
|
22
22
|
import "./config.js";
|
|
23
|
+
import { replaceOldTags } from "./helpers/visitor/filters/replace-old-tags.js";
|
|
23
24
|
import { getHash, removeFormatForCollapsedSelection, removeFormatForSelection, visitNodeWalker } from "./helpers/index.js";
|
|
24
25
|
/**
|
|
25
26
|
* Clean HTML after removeFormat and insertHorizontalRule command
|
|
26
27
|
*/
|
|
27
28
|
const TRAILING_BR_BLOCKS = 'p, div, li, td, th, h1, h2, h3, h4, h5, h6, blockquote';
|
|
28
29
|
export class cleanHtml extends Plugin {
|
|
29
|
-
constructor() {
|
|
30
|
-
super(
|
|
30
|
+
constructor(jodit) {
|
|
31
|
+
super(jodit);
|
|
31
32
|
/** @override */
|
|
32
33
|
this.buttons = [
|
|
33
34
|
{
|
|
@@ -39,9 +40,22 @@ export class cleanHtml extends Plugin {
|
|
|
39
40
|
this.walker = new LazyWalker(this.j.async, {
|
|
40
41
|
timeout: this.j.o.cleanHTML.timeout
|
|
41
42
|
});
|
|
43
|
+
// `@watch` binds once the plugin is ready, which is after the initial
|
|
44
|
+
// value has already gone into the editor. The handlers that keep
|
|
45
|
+
// markup out of the live tree have to be there from the start, or
|
|
46
|
+
// event handlers in a textarea's content run before anything
|
|
47
|
+
// sanitises them.
|
|
48
|
+
jodit.e
|
|
49
|
+
.on('safeHTML', this.onSafeHTML)
|
|
50
|
+
.on('beforeSetNativeEditorValue', this.onBeforeSetNativeEditorValue)
|
|
51
|
+
.on('afterSetNativeEditorValue', this.onAfterSetNativeEditorValue);
|
|
42
52
|
}
|
|
43
53
|
/** @override */
|
|
44
|
-
afterInit(
|
|
54
|
+
afterInit() {
|
|
55
|
+
// The walker's own `:afterInit` watcher is bound on that very event,
|
|
56
|
+
// so without this the first pass waited for the first key or click
|
|
57
|
+
this.onChangeCleanHTML();
|
|
58
|
+
}
|
|
45
59
|
get isEditMode() {
|
|
46
60
|
return !(this.j.isInDestruct ||
|
|
47
61
|
!this.j.isEditorMode() ||
|
|
@@ -106,11 +120,27 @@ export class cleanHtml extends Plugin {
|
|
|
106
120
|
];
|
|
107
121
|
sandBox.innerHTML = data.value;
|
|
108
122
|
this.j.e.fire('safeHTML', sandBox);
|
|
123
|
+
this.__replaceOldTags(sandBox);
|
|
109
124
|
data.value = sandBox.innerHTML;
|
|
110
125
|
safeHTML(sandBox, { safeJavaScriptLink: true, removeOnError: true });
|
|
111
126
|
Dom.safeRemove(iframe);
|
|
112
127
|
return false;
|
|
113
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Replace old tags (`<b>` with `<strong>`, `<i>` with `<em>`) in an
|
|
131
|
+
* assigned value straight away. The walker would get to them only after
|
|
132
|
+
* every node before them, seconds later on a long document, and until
|
|
133
|
+
* then Bold does not treat `<b>` as bold, and the swap can move the caret.
|
|
134
|
+
*/
|
|
135
|
+
__replaceOldTags(box) {
|
|
136
|
+
const { replaceOldTags: list, disableCleanFilter } = this.j.o.cleanHTML;
|
|
137
|
+
if (!list || (disableCleanFilter === null || disableCleanFilter === void 0 ? void 0 : disableCleanFilter.has('replaceOldTags'))) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
Dom.each(box, node => {
|
|
141
|
+
replaceOldTags(this.j, node, false);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
114
144
|
/**
|
|
115
145
|
* Collapse a value that holds only a single empty block (e.g.
|
|
116
146
|
* `<p><br></p>` left after deleting all content) to an empty string —
|
|
@@ -199,6 +229,10 @@ export class cleanHtml extends Plugin {
|
|
|
199
229
|
}
|
|
200
230
|
/** @override */
|
|
201
231
|
beforeDestruct() {
|
|
232
|
+
this.j.e
|
|
233
|
+
.off('safeHTML', this.onSafeHTML)
|
|
234
|
+
.off('beforeSetNativeEditorValue', this.onBeforeSetNativeEditorValue)
|
|
235
|
+
.off('afterSetNativeEditorValue', this.onAfterSetNativeEditorValue);
|
|
202
236
|
this.walker.destruct();
|
|
203
237
|
}
|
|
204
238
|
}
|
|
@@ -212,15 +246,15 @@ __decorate([
|
|
|
212
246
|
watch(':beforeCommand')
|
|
213
247
|
], cleanHtml.prototype, "beforeCommand", null);
|
|
214
248
|
__decorate([
|
|
215
|
-
|
|
249
|
+
autobind
|
|
216
250
|
], cleanHtml.prototype, "onAfterSetNativeEditorValue", null);
|
|
217
251
|
__decorate([
|
|
218
|
-
|
|
252
|
+
autobind
|
|
219
253
|
], cleanHtml.prototype, "onBeforeSetNativeEditorValue", null);
|
|
220
254
|
__decorate([
|
|
221
255
|
watch(':afterGetValueFromEditor')
|
|
222
256
|
], cleanHtml.prototype, "onAfterGetValueFromEditor", null);
|
|
223
257
|
__decorate([
|
|
224
|
-
|
|
258
|
+
autobind
|
|
225
259
|
], cleanHtml.prototype, "onSafeHTML", null);
|
|
226
260
|
pluginSystem.add('cleanHtml', cleanHtml);
|
|
@@ -24,7 +24,32 @@ function replaceIfMatched(jodit, oldParent, list) {
|
|
|
24
24
|
}
|
|
25
25
|
const tagName = list[oldParent.nodeName.toLowerCase()] || list[oldParent.nodeName];
|
|
26
26
|
if (tagName) {
|
|
27
|
-
|
|
27
|
+
const restoreSelection = keepSelection(jodit, oldParent);
|
|
28
|
+
const newParent = Dom.replace(oldParent, tagName, jodit.createInside, true, false);
|
|
29
|
+
restoreSelection === null || restoreSelection === void 0 ? void 0 : restoreSelection(newParent);
|
|
30
|
+
return newParent;
|
|
28
31
|
}
|
|
29
32
|
return oldParent;
|
|
30
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Moving the children out of an element collapses a selection inside them to
|
|
36
|
+
* where the element was, so the caret jumps to the start of the text being
|
|
37
|
+
* typed. Returns a callback that puts the selection back on the same nodes.
|
|
38
|
+
*
|
|
39
|
+
* Like `removeEmptyTextNode`, it leaves the selection alone once focus has
|
|
40
|
+
* left the editor, because selecting would take focus back.
|
|
41
|
+
*/
|
|
42
|
+
function keepSelection(jodit, elm) {
|
|
43
|
+
const { sel } = jodit.s;
|
|
44
|
+
if (!(sel === null || sel === void 0 ? void 0 : sel.anchorNode) || !sel.focusNode || !jodit.s.isFocused()) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const { anchorNode, anchorOffset, focusNode, focusOffset } = sel;
|
|
48
|
+
if (!Dom.isOrContains(elm, anchorNode) &&
|
|
49
|
+
!Dom.isOrContains(elm, focusNode)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return (newElm) => {
|
|
53
|
+
sel.setBaseAndExtent(anchorNode === elm ? newElm : anchorNode, anchorOffset, focusNode === elm ? newElm : focusNode, focusOffset);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
import type { IJodit, Nullable } from "../../../types/index";
|
|
11
11
|
/**
|
|
12
12
|
* Checks whether the insertion of an element at the current location is allowed,
|
|
13
|
-
* if it is not allowed, it deletes an empty block element
|
|
13
|
+
* if it is not allowed, it deletes an empty block element, moves the cursor
|
|
14
|
+
* before or after it, or splits it at the cursor
|
|
14
15
|
* @internal
|
|
15
16
|
*/
|
|
16
17
|
export declare function checkBlockNesting(jodit: IJodit, node: Nullable<Node>): void;
|