jodit 4.12.17 → 4.12.18
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 +16 -0
- package/es2015/jodit.css +1 -1
- package/es2015/jodit.fat.min.js +4 -4
- package/es2015/jodit.js +145 -37
- package/es2015/jodit.min.js +4 -4
- 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 +4 -4
- package/es2018/jodit.min.js +4 -4
- 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 +5 -5
- package/es2021/jodit.js +143 -37
- package/es2021/jodit.min.js +5 -5
- 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 +6 -6
- package/es2021.en/jodit.js +143 -37
- package/es2021.en/jodit.min.js +5 -5
- 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 +155 -37
- 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/core/constants.js +1 -1
- package/esm/core/helpers/utils/config-proto.js +15 -0
- package/esm/plugins/clean-html/helpers/visitor/filters/try-remove-node.js +8 -1
- package/esm/plugins/drag-and-drop-element/drag-and-drop-element.d.ts +21 -0
- package/esm/plugins/drag-and-drop-element/drag-and-drop-element.js +48 -3
- package/esm/plugins/enter/enter.js +11 -6
- package/esm/plugins/select/select.d.ts +8 -0
- package/esm/plugins/select/select.js +37 -0
- package/package.json +1 -1
- package/types/plugins/drag-and-drop-element/drag-and-drop-element.d.ts +21 -0
- package/types/plugins/select/select.d.ts +8 -0
package/es5/polyfills.fat.min.js
CHANGED
package/es5/polyfills.js
CHANGED
package/es5/polyfills.min.js
CHANGED
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.12.
|
|
6
|
+
export const APP_VERSION = "4.12.18";
|
|
7
7
|
// prettier-ignore
|
|
8
8
|
export const ES = "es2020";
|
|
9
9
|
export const IS_ES_MODERN = true;
|
|
@@ -10,6 +10,15 @@ import { isVoid } from "../checker/is-void.js";
|
|
|
10
10
|
import { Config } from "../../../config.js";
|
|
11
11
|
import { isAtom } from "./extend.js";
|
|
12
12
|
import { keys } from "./utils.js";
|
|
13
|
+
/**
|
|
14
|
+
* Keys that must never be copied from a (potentially untrusted) config object —
|
|
15
|
+
* assigning them during a recursive merge can reach and mutate
|
|
16
|
+
* `Object.prototype` (prototype pollution, CWE-1321).
|
|
17
|
+
*/
|
|
18
|
+
const UNSAFE_PROTO_KEYS = ['__proto__', 'constructor', 'prototype'];
|
|
19
|
+
function isUnsafeProtoKey(key) {
|
|
20
|
+
return UNSAFE_PROTO_KEYS.indexOf(key) !== -1;
|
|
21
|
+
}
|
|
13
22
|
/**
|
|
14
23
|
* @example
|
|
15
24
|
* ```js
|
|
@@ -59,6 +68,9 @@ export function ConfigProto(options, proto, deep = 0) {
|
|
|
59
68
|
}
|
|
60
69
|
const newOpt = {};
|
|
61
70
|
Object.keys(options).forEach(key => {
|
|
71
|
+
if (isUnsafeProtoKey(key)) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
62
74
|
const opt = options[key], protoKey = proto ? proto[key] : null;
|
|
63
75
|
if (isPlainObject(opt) && isPlainObject(protoKey) && !isAtom(opt)) {
|
|
64
76
|
newOpt[key] = ConfigProto(opt, protoKey, deep + 1);
|
|
@@ -119,6 +131,9 @@ export function ConfigFlatten(obj) {
|
|
|
119
131
|
*/
|
|
120
132
|
export function ConfigMerge(target, source) {
|
|
121
133
|
Object.keys(source).forEach(key => {
|
|
134
|
+
if (isUnsafeProtoKey(key)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
122
137
|
const srcVal = source[key];
|
|
123
138
|
const tgtVal = target[key];
|
|
124
139
|
if (isPlainObject(srcVal) && isPlainObject(tgtVal) && !isAtom(srcVal)) {
|
|
@@ -31,9 +31,16 @@ function isRemovableNode(jodit, node, current, allow, deny) {
|
|
|
31
31
|
if (!jodit.o.cleanHTML.removeEmptyElements) {
|
|
32
32
|
return false;
|
|
33
33
|
}
|
|
34
|
+
// Never drop an empty inline element that currently holds the caret — it is
|
|
35
|
+
// a pending-format marker the user is about to type into (#1291). `current`
|
|
36
|
+
// is captured before a click moves the caret, so also check the live caret.
|
|
37
|
+
const liveCaret = jodit.s.isCollapsed()
|
|
38
|
+
? jodit.s.range.startContainer
|
|
39
|
+
: null;
|
|
34
40
|
return (Dom.isElement(node) &&
|
|
35
41
|
node.nodeName.match(IS_INLINE) != null &&
|
|
36
42
|
!Dom.isTemporary(node) &&
|
|
37
43
|
trimInv(node.innerHTML).length === 0 &&
|
|
38
|
-
(current == null || !Dom.isOrContains(node, current))
|
|
44
|
+
(current == null || !Dom.isOrContains(node, current)) &&
|
|
45
|
+
(liveCaret == null || !Dom.isOrContains(node, liveCaret)));
|
|
39
46
|
}
|
|
@@ -21,10 +21,31 @@ export declare class dragAndDropElement extends Plugin {
|
|
|
21
21
|
private state;
|
|
22
22
|
/** @override */
|
|
23
23
|
protected afterInit(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Start dragging a specific element programmatically.
|
|
26
|
+
*
|
|
27
|
+
* Allows a separate UI element (for example a drag handle/anchor shown next
|
|
28
|
+
* to a block) to initiate the drag without the user pressing directly on the
|
|
29
|
+
* draggable element itself.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```js
|
|
33
|
+
* handle.addEventListener('mousedown', e => {
|
|
34
|
+
* editor.e.fire('startDragElement', preBlock, e);
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
private onStartDragElement;
|
|
24
39
|
/**
|
|
25
40
|
* Drag start handler
|
|
26
41
|
*/
|
|
27
42
|
private onDragStart;
|
|
43
|
+
/**
|
|
44
|
+
* Prepare the ghost element and switch to the waiting state.
|
|
45
|
+
* Shared by the native mousedown handler and the programmatic
|
|
46
|
+
* `startDragElement` event handler.
|
|
47
|
+
*/
|
|
48
|
+
private startDragging;
|
|
28
49
|
/**
|
|
29
50
|
* Mouse move handler handler
|
|
30
51
|
*/
|
|
@@ -49,11 +49,34 @@ export class dragAndDropElement extends Plugin {
|
|
|
49
49
|
.filter(Boolean)
|
|
50
50
|
.map(item => item.toLowerCase())
|
|
51
51
|
: [];
|
|
52
|
+
// Allow another plugin (e.g. a drag handle/anchor) to start dragging
|
|
53
|
+
// an element programmatically, regardless of the `draggableTags` list.
|
|
54
|
+
this.j.e.on('startDragElement', this.onStartDragElement);
|
|
52
55
|
if (!this.dragList.length) {
|
|
53
56
|
return;
|
|
54
57
|
}
|
|
55
58
|
this.j.e.on('mousedown dragstart', this.onDragStart);
|
|
56
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Start dragging a specific element programmatically.
|
|
62
|
+
*
|
|
63
|
+
* Allows a separate UI element (for example a drag handle/anchor shown next
|
|
64
|
+
* to a block) to initiate the drag without the user pressing directly on the
|
|
65
|
+
* draggable element itself.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```js
|
|
69
|
+
* handle.addEventListener('mousedown', e => {
|
|
70
|
+
* editor.e.fire('startDragElement', preBlock, e);
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
onStartDragElement(element, event) {
|
|
75
|
+
if (this.isInDestruct || this.state > DragState.IDLE || !element) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this.startDragging(element, event);
|
|
79
|
+
}
|
|
57
80
|
/**
|
|
58
81
|
* Drag start handler
|
|
59
82
|
*/
|
|
@@ -79,11 +102,19 @@ export class dragAndDropElement extends Plugin {
|
|
|
79
102
|
lastTarget.parentElement.lastChild === lastTarget) {
|
|
80
103
|
lastTarget = lastTarget.parentElement;
|
|
81
104
|
}
|
|
105
|
+
this.startDragging(lastTarget, event);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Prepare the ghost element and switch to the waiting state.
|
|
109
|
+
* Shared by the native mousedown handler and the programmatic
|
|
110
|
+
* `startDragElement` event handler.
|
|
111
|
+
*/
|
|
112
|
+
startDragging(target, event) {
|
|
82
113
|
this.startX = event.clientX;
|
|
83
114
|
this.startY = event.clientY;
|
|
84
115
|
this.isCopyMode = ctrlKey(event); // we can move only element from editor
|
|
85
|
-
this.draggable =
|
|
86
|
-
dataBind(this.draggable, 'target',
|
|
116
|
+
this.draggable = target.cloneNode(true);
|
|
117
|
+
dataBind(this.draggable, 'target', target);
|
|
87
118
|
this.state = DragState.WAIT_DRAGGING;
|
|
88
119
|
this.addDragListeners();
|
|
89
120
|
}
|
|
@@ -157,6 +188,15 @@ export class dragAndDropElement extends Plugin {
|
|
|
157
188
|
}
|
|
158
189
|
const { parentElement } = fragment;
|
|
159
190
|
this.j.s.insertNode(fragment, true, false);
|
|
191
|
+
// Dropping a non-editable block (e.g. a `<pre>` code sample) can leave an
|
|
192
|
+
// invisible filler text node beside it. Remove it so the drop does not
|
|
193
|
+
// introduce a stray empty line (which clean-html would otherwise strip
|
|
194
|
+
// later, causing a flash).
|
|
195
|
+
[fragment.previousSibling, fragment.nextSibling].forEach(node => {
|
|
196
|
+
if (Dom.isEmptyTextNode(node)) {
|
|
197
|
+
Dom.safeRemove(node);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
160
200
|
if (parentElement &&
|
|
161
201
|
Dom.isEmpty(parentElement) &&
|
|
162
202
|
!Dom.isCell(parentElement)) {
|
|
@@ -188,10 +228,15 @@ export class dragAndDropElement extends Plugin {
|
|
|
188
228
|
/** @override */
|
|
189
229
|
beforeDestruct() {
|
|
190
230
|
this.onDragEnd();
|
|
191
|
-
this.j.e
|
|
231
|
+
this.j.e
|
|
232
|
+
.off('mousedown dragstart', this.onDragStart)
|
|
233
|
+
.off('startDragElement', this.onStartDragElement);
|
|
192
234
|
this.removeDragListeners();
|
|
193
235
|
}
|
|
194
236
|
}
|
|
237
|
+
__decorate([
|
|
238
|
+
autobind
|
|
239
|
+
], dragAndDropElement.prototype, "onStartDragElement", null);
|
|
195
240
|
__decorate([
|
|
196
241
|
autobind
|
|
197
242
|
], dragAndDropElement.prototype, "onDragStart", null);
|
|
@@ -45,12 +45,17 @@ export class enter extends Plugin {
|
|
|
45
45
|
if (beforeEnter !== undefined) {
|
|
46
46
|
return beforeEnter;
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
editor.
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
// Delete-of-selection + new block must be a single history step,
|
|
49
|
+
// otherwise pressing Enter over a selection needs two Ctrl+Z to undo
|
|
50
|
+
// (the first only reverts to the intermediate empty state). #1292
|
|
51
|
+
editor.history.snapshot.transaction(() => {
|
|
52
|
+
if (!editor.s.isCollapsed()) {
|
|
53
|
+
editor.execCommand('Delete');
|
|
54
|
+
}
|
|
55
|
+
editor.s.focus();
|
|
56
|
+
this.onEnter(event);
|
|
57
|
+
editor.e.fire('afterEnter', event);
|
|
58
|
+
});
|
|
54
59
|
editor.synchronizeValues(); // fire change
|
|
55
60
|
return false;
|
|
56
61
|
}
|
|
@@ -39,6 +39,14 @@ export declare class select extends Plugin {
|
|
|
39
39
|
* of the end (#1296); move it to the end of the item's own text.
|
|
40
40
|
*/
|
|
41
41
|
protected onClickRightOfNestedListItem(e: MouseEvent): void;
|
|
42
|
+
/**
|
|
43
|
+
* Keep pending inline formatting after a click. Toggling Bold/Italic/etc. on
|
|
44
|
+
* a collapsed cursor leaves empty marker elements with the caret inside; a
|
|
45
|
+
* click puts the caret right before them (outside), so they get cleaned up
|
|
46
|
+
* and the formatting is lost. Move the caret back into the innermost marker
|
|
47
|
+
* so the next typed character keeps every pending format (#1291).
|
|
48
|
+
*/
|
|
49
|
+
protected onClickKeepPendingFormat(): void;
|
|
42
50
|
/**
|
|
43
51
|
* Normalize selection after triple click
|
|
44
52
|
*/
|
|
@@ -13,6 +13,7 @@ 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 { IS_INLINE } from "../../core/constants.js";
|
|
16
17
|
import { autobind, watch } from "../../core/decorators/index.js";
|
|
17
18
|
import { Dom } from "../../core/dom/dom.js";
|
|
18
19
|
import { pluginSystem } from "../../core/global.js";
|
|
@@ -121,6 +122,39 @@ export class select extends Plugin {
|
|
|
121
122
|
s.setCursorAfter(text);
|
|
122
123
|
}
|
|
123
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Keep pending inline formatting after a click. Toggling Bold/Italic/etc. on
|
|
127
|
+
* a collapsed cursor leaves empty marker elements with the caret inside; a
|
|
128
|
+
* click puts the caret right before them (outside), so they get cleaned up
|
|
129
|
+
* and the formatting is lost. Move the caret back into the innermost marker
|
|
130
|
+
* so the next typed character keeps every pending format (#1291).
|
|
131
|
+
*/
|
|
132
|
+
onClickKeepPendingFormat() {
|
|
133
|
+
var _a, _b;
|
|
134
|
+
const { s } = this.j;
|
|
135
|
+
const range = s.range;
|
|
136
|
+
if (!range.collapsed || !Dom.isText(range.startContainer)) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const text = range.startContainer;
|
|
140
|
+
// Caret must sit at the very end of the text, right before the markers.
|
|
141
|
+
if (range.startOffset !== ((_b = (_a = text.nodeValue) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const marker = text.nextSibling;
|
|
145
|
+
if (!Dom.isElement(marker) ||
|
|
146
|
+
!marker.nodeName.match(IS_INLINE) ||
|
|
147
|
+
!Dom.isEmpty(marker)) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
let inner = marker;
|
|
151
|
+
// Descend into the innermost empty formatting marker.
|
|
152
|
+
while (Dom.isElement(inner.firstElementChild) &&
|
|
153
|
+
inner.firstElementChild.nodeName.match(IS_INLINE)) {
|
|
154
|
+
inner = inner.firstElementChild;
|
|
155
|
+
}
|
|
156
|
+
s.setCursorIn(inner);
|
|
157
|
+
}
|
|
124
158
|
/**
|
|
125
159
|
* Normalize selection after triple click
|
|
126
160
|
*/
|
|
@@ -164,6 +198,9 @@ __decorate([
|
|
|
164
198
|
__decorate([
|
|
165
199
|
watch([':click'])
|
|
166
200
|
], select.prototype, "onClickRightOfNestedListItem", null);
|
|
201
|
+
__decorate([
|
|
202
|
+
watch([':click'])
|
|
203
|
+
], select.prototype, "onClickKeepPendingFormat", null);
|
|
167
204
|
__decorate([
|
|
168
205
|
watch([':click'])
|
|
169
206
|
], select.prototype, "onTripleClickNormalizeSelection", null);
|
package/package.json
CHANGED
|
@@ -21,10 +21,31 @@ export declare class dragAndDropElement extends Plugin {
|
|
|
21
21
|
private state;
|
|
22
22
|
/** @override */
|
|
23
23
|
protected afterInit(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Start dragging a specific element programmatically.
|
|
26
|
+
*
|
|
27
|
+
* Allows a separate UI element (for example a drag handle/anchor shown next
|
|
28
|
+
* to a block) to initiate the drag without the user pressing directly on the
|
|
29
|
+
* draggable element itself.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```js
|
|
33
|
+
* handle.addEventListener('mousedown', e => {
|
|
34
|
+
* editor.e.fire('startDragElement', preBlock, e);
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
private onStartDragElement;
|
|
24
39
|
/**
|
|
25
40
|
* Drag start handler
|
|
26
41
|
*/
|
|
27
42
|
private onDragStart;
|
|
43
|
+
/**
|
|
44
|
+
* Prepare the ghost element and switch to the waiting state.
|
|
45
|
+
* Shared by the native mousedown handler and the programmatic
|
|
46
|
+
* `startDragElement` event handler.
|
|
47
|
+
*/
|
|
48
|
+
private startDragging;
|
|
28
49
|
/**
|
|
29
50
|
* Mouse move handler handler
|
|
30
51
|
*/
|
|
@@ -39,6 +39,14 @@ export declare class select extends Plugin {
|
|
|
39
39
|
* of the end (#1296); move it to the end of the item's own text.
|
|
40
40
|
*/
|
|
41
41
|
protected onClickRightOfNestedListItem(e: MouseEvent): void;
|
|
42
|
+
/**
|
|
43
|
+
* Keep pending inline formatting after a click. Toggling Bold/Italic/etc. on
|
|
44
|
+
* a collapsed cursor leaves empty marker elements with the caret inside; a
|
|
45
|
+
* click puts the caret right before them (outside), so they get cleaned up
|
|
46
|
+
* and the formatting is lost. Move the caret back into the innermost marker
|
|
47
|
+
* so the next typed character keeps every pending format (#1291).
|
|
48
|
+
*/
|
|
49
|
+
protected onClickKeepPendingFormat(): void;
|
|
42
50
|
/**
|
|
43
51
|
* Normalize selection after triple click
|
|
44
52
|
*/
|