jodit 4.12.22 → 4.12.24
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 +33 -0
- package/es2015/jodit.css +1 -1
- package/es2015/jodit.fat.min.js +11 -11
- package/es2015/jodit.js +353 -86
- package/es2015/jodit.min.js +11 -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 -11
- package/es2018/jodit.min.js +15 -15
- 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 +13 -13
- package/es2021/jodit.js +343 -85
- package/es2021/jodit.min.js +13 -13
- 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 +14 -14
- package/es2021.en/jodit.js +321 -63
- package/es2021.en/jodit.min.js +13 -13
- 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 +443 -153
- 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 +30 -0
- package/esm/core/component/view-component.js +8 -0
- package/esm/core/constants.js +1 -1
- package/esm/core/helpers/checker/is-html-from-word.d.ts +1 -1
- package/esm/core/helpers/checker/is-html-from-word.js +12 -1
- package/esm/core/selection/style/api/wrap.js +18 -3
- package/esm/core/ui/popup/popup.js +4 -1
- package/esm/jodit.js +12 -0
- package/esm/modules/image-editor/image-editor.js +5 -0
- package/esm/modules/toolbar/button/content.d.ts +6 -0
- package/esm/modules/toolbar/button/content.js +26 -1
- package/esm/modules/uploader/helpers/send-files.js +9 -0
- package/esm/plugins/add-new-line/add-new-line.js +4 -1
- package/esm/plugins/ai-assistant/ai-assistant.js +7 -1
- package/esm/plugins/backspace/backspace.js +7 -3
- package/esm/plugins/backspace/cases/check-join-neighbors.js +15 -1
- package/esm/plugins/backspace/cases/index.d.ts +19 -3
- package/esm/plugins/backspace/cases/index.js +18 -13
- package/esm/plugins/backspace/config.d.ts +15 -0
- package/esm/plugins/clean-html/clean-html.d.ts +8 -0
- package/esm/plugins/clean-html/clean-html.js +16 -0
- package/esm/plugins/clean-html/config.d.ts +9 -0
- package/esm/plugins/clean-html/config.js +1 -0
- package/esm/plugins/color/color.js +21 -21
- package/esm/plugins/font/config.js +19 -0
- package/esm/plugins/inline-popup/inline-popup.d.ts +7 -0
- package/esm/plugins/inline-popup/inline-popup.js +18 -2
- package/esm/plugins/link/config.d.ts +6 -0
- package/esm/plugins/link/config.js +1 -0
- package/esm/plugins/link/link.js +8 -1
- package/esm/plugins/link/template.js +10 -1
- package/esm/plugins/paste/config.js +11 -2
- package/esm/plugins/search/ui/search.js +22 -12
- package/esm/plugins/select-cells/select-cells.d.ts +1 -1
- package/esm/plugins/select-cells/select-cells.js +39 -8
- package/esm/types/uploader.d.ts +23 -0
- package/package.json +1 -1
- package/types/config.d.ts +30 -0
- package/types/core/helpers/checker/is-html-from-word.d.ts +1 -1
- package/types/modules/toolbar/button/content.d.ts +6 -0
- package/types/plugins/backspace/cases/index.d.ts +19 -3
- package/types/plugins/backspace/config.d.ts +15 -0
- package/types/plugins/clean-html/clean-html.d.ts +8 -0
- package/types/plugins/clean-html/config.d.ts +9 -0
- package/types/plugins/inline-popup/inline-popup.d.ts +7 -0
- package/types/plugins/link/config.d.ts +6 -0
- package/types/plugins/select-cells/select-cells.d.ts +1 -1
- package/types/types/uploader.d.ts +23 -0
|
@@ -15,28 +15,28 @@ export function color(editor) {
|
|
|
15
15
|
group: 'color'
|
|
16
16
|
});
|
|
17
17
|
const callback = (command, second, third) => {
|
|
18
|
+
var _a;
|
|
18
19
|
const colorHEX = normalizeColor(third);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
editor.s.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
break;
|
|
20
|
+
const value = !colorHEX ? '' : colorHEX;
|
|
21
|
+
const style = command === 'background'
|
|
22
|
+
? { backgroundColor: value }
|
|
23
|
+
: { color: value };
|
|
24
|
+
// Cells selected with the `select-cells` plugin drop or collapse the
|
|
25
|
+
// native range, so `commitStyle` would paint a pending caret format
|
|
26
|
+
// outside the table instead of the selection the user sees. Apply the
|
|
27
|
+
// style to the content of every selected cell instead. See #1250
|
|
28
|
+
const selectedCells = editor
|
|
29
|
+
.getInstance('Table', editor.o)
|
|
30
|
+
.getAllSelectedCells();
|
|
31
|
+
if (selectedCells.length && editor.s.isCollapsed()) {
|
|
32
|
+
selectedCells.forEach(cell => {
|
|
33
|
+
editor.s.select(cell, true);
|
|
34
|
+
editor.s.commitStyle({ attributes: { style } });
|
|
35
|
+
});
|
|
36
|
+
(_a = editor.s.sel) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
editor.s.commitStyle({ attributes: { style } });
|
|
40
40
|
}
|
|
41
41
|
editor.synchronizeValues();
|
|
42
42
|
return false;
|
|
@@ -117,5 +117,24 @@ Config.prototype.controls.font = {
|
|
|
117
117
|
.replace(/[^a-z0-9-]+/g, ',');
|
|
118
118
|
}
|
|
119
119
|
},
|
|
120
|
+
// When no font is explicitly set, the computed font-family equals the
|
|
121
|
+
// editor's own default (e.g. `-apple-system, …`). Return '' so the
|
|
122
|
+
// button shows the `Default` list entry instead of that raw stack.
|
|
123
|
+
// See #1370
|
|
124
|
+
value: (editor) => {
|
|
125
|
+
const current = editor.s.current();
|
|
126
|
+
if (!current) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const box = Dom.closest(current, Dom.isElement, editor.editor);
|
|
130
|
+
if (!box) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const value = css(box, 'font-family').toString();
|
|
134
|
+
if (value === css(editor.editor, 'font-family').toString()) {
|
|
135
|
+
return '';
|
|
136
|
+
}
|
|
137
|
+
return value;
|
|
138
|
+
},
|
|
120
139
|
tooltip: 'Font family'
|
|
121
140
|
};
|
|
@@ -50,6 +50,13 @@ export declare class inlinePopup extends Plugin {
|
|
|
50
50
|
private __reopenSelectionPopup;
|
|
51
51
|
private __onDocumentMouseDown;
|
|
52
52
|
private __onCloseAllPopups;
|
|
53
|
+
/**
|
|
54
|
+
* The selection rect comes from the editor document — in iframe mode its
|
|
55
|
+
* coordinates are iframe-local, while the popup lives in the host
|
|
56
|
+
* document, so the iframe offset must be added. See
|
|
57
|
+
* https://github.com/xdan/jodit/issues/1058
|
|
58
|
+
*/
|
|
59
|
+
private __selectionBound;
|
|
53
60
|
private snapRange;
|
|
54
61
|
private onSelectionStart;
|
|
55
62
|
private onSelectionEnd;
|
|
@@ -176,10 +176,26 @@ export class inlinePopup extends Plugin {
|
|
|
176
176
|
this.j.async.setTimeout(() => {
|
|
177
177
|
const sel = this.j.s.sel;
|
|
178
178
|
if (sel && !sel.isCollapsed) {
|
|
179
|
-
this.showPopup(() => this.
|
|
179
|
+
this.showPopup(() => this.__selectionBound(), 'selection');
|
|
180
180
|
}
|
|
181
181
|
}, 1);
|
|
182
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* The selection rect comes from the editor document — in iframe mode its
|
|
185
|
+
* coordinates are iframe-local, while the popup lives in the host
|
|
186
|
+
* document, so the iframe offset must be added. See
|
|
187
|
+
* https://github.com/xdan/jodit/issues/1058
|
|
188
|
+
*/
|
|
189
|
+
__selectionBound() {
|
|
190
|
+
const rect = this.j.s.range.getBoundingClientRect();
|
|
191
|
+
let { left, top } = rect;
|
|
192
|
+
if (this.j.iframe) {
|
|
193
|
+
const offset = position(this.j.iframe, this.j, true);
|
|
194
|
+
left += offset.left;
|
|
195
|
+
top += offset.top;
|
|
196
|
+
}
|
|
197
|
+
return { left, top, width: rect.width, height: rect.height };
|
|
198
|
+
}
|
|
183
199
|
onSelectionStart() {
|
|
184
200
|
this.snapRange = this.j.s.range.cloneRange();
|
|
185
201
|
}
|
|
@@ -219,7 +235,7 @@ export class inlinePopup extends Plugin {
|
|
|
219
235
|
if (!node) {
|
|
220
236
|
return;
|
|
221
237
|
}
|
|
222
|
-
this.showPopup(() =>
|
|
238
|
+
this.showPopup(() => this.__selectionBound(), type);
|
|
223
239
|
}
|
|
224
240
|
/**
|
|
225
241
|
* In not collapsed selection - only one image
|
|
@@ -39,6 +39,12 @@ declare module 'jodit/config' {
|
|
|
39
39
|
* Default value for the `Open in new tab` checkbox when inserting a new link.
|
|
40
40
|
*/
|
|
41
41
|
openInNewTabCheckboxDefaultChecked: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Show an `aria-label` text input in the link dialog so an
|
|
44
|
+
* accessible name can be set on the `<a>` (useful when several
|
|
45
|
+
* links share the same visible text, e.g. "here"). Default: false.
|
|
46
|
+
*/
|
|
47
|
+
ariaLabelInput: boolean;
|
|
42
48
|
/**
|
|
43
49
|
* Use an input text to ask the classname or a select or not ask
|
|
44
50
|
*/
|
package/esm/plugins/link/link.js
CHANGED
|
@@ -121,7 +121,7 @@ export class link extends Plugin {
|
|
|
121
121
|
const currentElement = current;
|
|
122
122
|
const isImageContent = Dom.isImage(currentElement);
|
|
123
123
|
let { content_input } = elements;
|
|
124
|
-
const { className_input } = elements, { className_select } = elements;
|
|
124
|
+
const { className_input } = elements, { className_select } = elements, { aria_label_input } = elements;
|
|
125
125
|
if (!content_input) {
|
|
126
126
|
content_input = jodit.c.element('input', {
|
|
127
127
|
type: 'hidden',
|
|
@@ -147,6 +147,9 @@ export class link extends Plugin {
|
|
|
147
147
|
if (!isImageContent && current) {
|
|
148
148
|
content_input.value = getSelectionText();
|
|
149
149
|
}
|
|
150
|
+
if (aria_label_input) {
|
|
151
|
+
aria_label_input.value = link ? attr(link, 'aria-label') || '' : '';
|
|
152
|
+
}
|
|
150
153
|
if (link) {
|
|
151
154
|
url_input.value = attr(link, 'href') || '';
|
|
152
155
|
if (modeClassName) {
|
|
@@ -237,6 +240,10 @@ export class link extends Plugin {
|
|
|
237
240
|
}
|
|
238
241
|
attr(a, 'rel', relParts.length ? relParts.join(' ') : null);
|
|
239
242
|
}
|
|
243
|
+
if (aria_label_input) {
|
|
244
|
+
const ariaLabel = aria_label_input.value.trim();
|
|
245
|
+
attr(a, 'aria-label', ariaLabel || null);
|
|
246
|
+
}
|
|
240
247
|
jodit.e.fire('applyLink', jodit, a, form);
|
|
241
248
|
});
|
|
242
249
|
jodit.synchronizeValues();
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { UIButton } from "../../core/ui/button/index.js";
|
|
7
7
|
import { UIBlock, UICheckbox, UIForm, UIInput, UISelect } from "../../core/ui/form/index.js";
|
|
8
8
|
export const formTemplate = (editor) => {
|
|
9
|
-
const { openInNewTabCheckbox, noFollowCheckbox, modeClassName, selectSizeClassName, selectMultipleClassName, selectOptionsClassName } = editor.o.link;
|
|
9
|
+
const { openInNewTabCheckbox, noFollowCheckbox, ariaLabelInput, modeClassName, selectSizeClassName, selectMultipleClassName, selectOptionsClassName } = editor.o.link;
|
|
10
10
|
return new UIForm(editor, [
|
|
11
11
|
new UIBlock(editor, [
|
|
12
12
|
new UIInput(editor, {
|
|
@@ -18,6 +18,15 @@ export const formTemplate = (editor) => {
|
|
|
18
18
|
required: true
|
|
19
19
|
})
|
|
20
20
|
]),
|
|
21
|
+
ariaLabelInput
|
|
22
|
+
? new UIBlock(editor, [
|
|
23
|
+
new UIInput(editor, {
|
|
24
|
+
name: 'ariaLabel',
|
|
25
|
+
ref: 'aria_label_input',
|
|
26
|
+
label: 'Aria label'
|
|
27
|
+
})
|
|
28
|
+
])
|
|
29
|
+
: null,
|
|
21
30
|
new UIBlock(editor, [
|
|
22
31
|
new UIInput(editor, {
|
|
23
32
|
name: 'content',
|
|
@@ -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
|
-
import { CLIPBOARD_ID, INSERT_AS_HTML, INSERT_AS_TEXT, INSERT_ONLY_TEXT, IS_PROD, TEXT_PLAIN } from "../../core/constants.js";
|
|
6
|
+
import { CLIPBOARD_ID, INSERT_AS_HTML, INSERT_AS_TEXT, INSERT_ONLY_TEXT, IS_PROD, TEXT_HTML, TEXT_PLAIN } from "../../core/constants.js";
|
|
7
7
|
import { Config } from "../../config.js";
|
|
8
8
|
import { pasteInsertHtml } from "./helpers.js";
|
|
9
9
|
Config.prototype.askBeforePasteHTML = true;
|
|
@@ -21,6 +21,7 @@ const psKey = 'pasteStorage';
|
|
|
21
21
|
Config.prototype.controls.paste = {
|
|
22
22
|
tooltip: 'Paste from clipboard',
|
|
23
23
|
async exec(editor, _, { control }) {
|
|
24
|
+
var _a;
|
|
24
25
|
if (control.name === psKey) {
|
|
25
26
|
editor.execCommand('showPasteStorage');
|
|
26
27
|
return;
|
|
@@ -31,7 +32,15 @@ Config.prototype.controls.paste = {
|
|
|
31
32
|
try {
|
|
32
33
|
const items = await navigator.clipboard.read();
|
|
33
34
|
if (items && items.length) {
|
|
34
|
-
const
|
|
35
|
+
const item = items[0];
|
|
36
|
+
// Prefer the HTML flavor so the button behaves like the
|
|
37
|
+
// Ctrl+V shortcut (which receives text/html from the
|
|
38
|
+
// native paste event). See
|
|
39
|
+
// https://github.com/xdan/jodit/issues/1061
|
|
40
|
+
const type = ((_a = item.types) === null || _a === void 0 ? void 0 : _a.includes(TEXT_HTML))
|
|
41
|
+
? TEXT_HTML
|
|
42
|
+
: TEXT_PLAIN;
|
|
43
|
+
const textBlob = await item.getType(type);
|
|
35
44
|
text = await new Response(textBlob).text();
|
|
36
45
|
}
|
|
37
46
|
error = false;
|
|
@@ -101,20 +101,30 @@ let UISearch = class UISearch extends UIElement {
|
|
|
101
101
|
.on(this.queryInput, 'input', () => {
|
|
102
102
|
this.setMod('empty-query', !trim(this.queryInput.value).length);
|
|
103
103
|
})
|
|
104
|
-
.on(this.queryInput, 'keydown',
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
.on(this.queryInput, 'keydown', (() => {
|
|
105
|
+
const debounced = this.j.async.debounce(async (e) => {
|
|
106
|
+
switch (e.key) {
|
|
107
|
+
case consts.KEY_ENTER:
|
|
108
|
+
if (await jodit.e.fire('searchNext')) {
|
|
109
|
+
this.close();
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
default:
|
|
113
|
+
jodit.e.fire(this, 'needUpdateCounters');
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}, this.j.defaultTimeout);
|
|
117
|
+
return (e) => {
|
|
118
|
+
// Must be canceled synchronously — inside the debounced
|
|
119
|
+
// handler the browser has already submitted the parent
|
|
120
|
+
// form. See https://github.com/xdan/jodit/issues/918
|
|
121
|
+
if (e.key === consts.KEY_ENTER) {
|
|
107
122
|
e.preventDefault();
|
|
108
123
|
e.stopImmediatePropagation();
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
default:
|
|
114
|
-
jodit.e.fire(this, 'needUpdateCounters');
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
}, this.j.defaultTimeout));
|
|
124
|
+
}
|
|
125
|
+
debounced(e);
|
|
126
|
+
};
|
|
127
|
+
})());
|
|
118
128
|
}
|
|
119
129
|
onEditorKeyDown(e) {
|
|
120
130
|
if (!this.isOpened) {
|
|
@@ -29,7 +29,7 @@ export declare class selectCells extends Plugin {
|
|
|
29
29
|
/**
|
|
30
30
|
* Mouse click inside the table
|
|
31
31
|
*/
|
|
32
|
-
protected onStartSelection(cell: HTMLTableCellElement): void | false;
|
|
32
|
+
protected onStartSelection(cell: HTMLTableCellElement, event?: MouseEvent): void | false;
|
|
33
33
|
protected onOutsideClick(): void;
|
|
34
34
|
protected onChange(): void;
|
|
35
35
|
/**
|
|
@@ -79,15 +79,42 @@ export class selectCells extends Plugin {
|
|
|
79
79
|
/**
|
|
80
80
|
* Mouse click inside the table
|
|
81
81
|
*/
|
|
82
|
-
onStartSelection(cell) {
|
|
82
|
+
onStartSelection(cell, event) {
|
|
83
|
+
var _a;
|
|
83
84
|
if (this.j.o.readonly) {
|
|
84
85
|
return;
|
|
85
86
|
}
|
|
87
|
+
const table = Dom.closest(cell, 'table', this.j.editor);
|
|
88
|
+
// Ctrl/Cmd + click toggles a single cell into the existing selection
|
|
89
|
+
// instead of resetting it — non-contiguous multi-cell selection.
|
|
90
|
+
// See https://github.com/xdan/jodit/issues/1163
|
|
91
|
+
if (((event === null || event === void 0 ? void 0 : event.ctrlKey) || (event === null || event === void 0 ? void 0 : event.metaKey)) &&
|
|
92
|
+
cell !== this.j.editor &&
|
|
93
|
+
table &&
|
|
94
|
+
Dom.isCell(cell)) {
|
|
95
|
+
if (this.__tableModule.getAllSelectedCells().includes(cell)) {
|
|
96
|
+
this.__tableModule.removeSelection(cell);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
if (!cell.firstChild) {
|
|
100
|
+
cell.appendChild(this.j.createInside.element('br'));
|
|
101
|
+
}
|
|
102
|
+
this.__tableModule.addSelection(cell);
|
|
103
|
+
}
|
|
104
|
+
this.__selectedCell = cell;
|
|
105
|
+
(_a = this.j.s.sel) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
|
|
106
|
+
if (this.__tableModule.getAllSelectedCells().length) {
|
|
107
|
+
this.j.e.fire('showPopup', table, () => position(cell, this.j), 'cells');
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
this.j.e.fire('hidePopup', 'cells');
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
86
114
|
this.unselectCells();
|
|
87
115
|
if (cell === this.j.editor) {
|
|
88
116
|
return;
|
|
89
117
|
}
|
|
90
|
-
const table = Dom.closest(cell, 'table', this.j.editor);
|
|
91
118
|
if (!cell || !table) {
|
|
92
119
|
return;
|
|
93
120
|
}
|
|
@@ -120,14 +147,16 @@ export class selectCells extends Plugin {
|
|
|
120
147
|
* Mouse move inside the table
|
|
121
148
|
*/
|
|
122
149
|
__onMove(table, e) {
|
|
123
|
-
var _a;
|
|
150
|
+
var _a, _b;
|
|
124
151
|
if (this.j.o.readonly && !this.j.isLocked) {
|
|
125
152
|
return;
|
|
126
153
|
}
|
|
127
154
|
if (this.j.isLockedNotBy(key)) {
|
|
128
155
|
return;
|
|
129
156
|
}
|
|
130
|
-
|
|
157
|
+
// Inside Shadow DOM `document.elementFromPoint` returns the shadow
|
|
158
|
+
// host, so the lookup must start from the shadow root
|
|
159
|
+
const node = ((_a = this.j.o.shadowRoot) !== null && _a !== void 0 ? _a : this.j.ed).elementFromPoint(e.clientX, e.clientY);
|
|
131
160
|
if (!node) {
|
|
132
161
|
return;
|
|
133
162
|
}
|
|
@@ -150,7 +179,7 @@ export class selectCells extends Plugin {
|
|
|
150
179
|
}
|
|
151
180
|
const cellsCount = this.__tableModule.getAllSelectedCells().length;
|
|
152
181
|
if (cellsCount > 1) {
|
|
153
|
-
(
|
|
182
|
+
(_b = this.j.s.sel) === null || _b === void 0 ? void 0 : _b.removeAllRanges();
|
|
154
183
|
}
|
|
155
184
|
this.j.e.fire('hidePopup');
|
|
156
185
|
e.stopPropagation();
|
|
@@ -184,13 +213,15 @@ export class selectCells extends Plugin {
|
|
|
184
213
|
* Stop a selection process
|
|
185
214
|
*/
|
|
186
215
|
__onStopSelection(table, e) {
|
|
187
|
-
var _a, _b;
|
|
216
|
+
var _a, _b, _c;
|
|
188
217
|
if (!this.__selectedCell) {
|
|
189
218
|
return;
|
|
190
219
|
}
|
|
191
220
|
this.__isSelectionMode = false;
|
|
192
221
|
this.j.unlock();
|
|
193
|
-
|
|
222
|
+
// Inside Shadow DOM `document.elementFromPoint` returns the shadow
|
|
223
|
+
// host, so the lookup must start from the shadow root
|
|
224
|
+
const node = ((_a = this.j.o.shadowRoot) !== null && _a !== void 0 ? _a : this.j.ed).elementFromPoint(e.clientX, e.clientY);
|
|
194
225
|
if (!node) {
|
|
195
226
|
return;
|
|
196
227
|
}
|
|
@@ -206,7 +237,7 @@ export class selectCells extends Plugin {
|
|
|
206
237
|
cell,
|
|
207
238
|
this.__selectedCell
|
|
208
239
|
]), box = this.__tableModule.formalMatrix(table);
|
|
209
|
-
const max = (
|
|
240
|
+
const max = (_b = box[bound[1][0]]) === null || _b === void 0 ? void 0 : _b[bound[1][1]], min = (_c = box[bound[0][0]]) === null || _c === void 0 ? void 0 : _c[bound[0][1]];
|
|
210
241
|
// `getSelectedBound` keeps its `Infinity` sentinel when none of the
|
|
211
242
|
// selected cells belong to this table's matrix — e.g. after a
|
|
212
243
|
// drag-and-drop that moved/removed the cells, leaving a stale anchor and
|
package/esm/types/uploader.d.ts
CHANGED
|
@@ -80,6 +80,29 @@ export interface IUploaderOptions<T> {
|
|
|
80
80
|
getDisplayName(this: T, baseurl: string, filename: string): string;
|
|
81
81
|
pathVariableName: string;
|
|
82
82
|
withCredentials: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Called with the list of files right before they are uploaded (or read as
|
|
85
|
+
* base64). Return `false` to abort the whole upload — useful for client
|
|
86
|
+
* side validation (size, type, count). Throwing an Error also aborts and
|
|
87
|
+
* routes the message through the uploader error handler.
|
|
88
|
+
*
|
|
89
|
+
* ```javascript
|
|
90
|
+
* Jodit.make('#editor', {
|
|
91
|
+
* uploader: {
|
|
92
|
+
* url: '...',
|
|
93
|
+
* beforeUpload(files) {
|
|
94
|
+
* for (const file of files) {
|
|
95
|
+
* if (file.size > 2 * 1024 * 1024) {
|
|
96
|
+
* this.jodit.message.error('Max 2MB');
|
|
97
|
+
* return false;
|
|
98
|
+
* }
|
|
99
|
+
* }
|
|
100
|
+
* }
|
|
101
|
+
* }
|
|
102
|
+
* });
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
beforeUpload?: (this: T, files: File[]) => boolean | void;
|
|
83
106
|
prepareData: (this: T, formData: FormData) => any;
|
|
84
107
|
buildData?: (this: T, formData: any) => BuildDataResult;
|
|
85
108
|
queryBuild?: (obj: string | IDictionary<string | object> | FormData, prefix?: string) => string | FormData;
|
package/package.json
CHANGED
package/types/config.d.ts
CHANGED
|
@@ -956,6 +956,21 @@ interface Config {
|
|
|
956
956
|
backspaceWord: string[];
|
|
957
957
|
backspaceSentence: string[];
|
|
958
958
|
};
|
|
959
|
+
/**
|
|
960
|
+
* Disable specific Backspace/Delete cleanup cases by their stable
|
|
961
|
+
* key, so the plugin no longer applies that particular behavior.
|
|
962
|
+
* Available keys: `remove-unbreakable`, `remove-not-editable`,
|
|
963
|
+
* `remove-char`, `table-cell`, `remove-empty-parent`,
|
|
964
|
+
* `remove-empty-neighbor`, `join-two-lists`, `join-neighbors`,
|
|
965
|
+
* `unwrap-first-list-item`.
|
|
966
|
+
*
|
|
967
|
+
* ```javascript
|
|
968
|
+
* Jodit.make('#editor', {
|
|
969
|
+
* delete: { disableCases: new Set(['remove-empty-parent']) }
|
|
970
|
+
* });
|
|
971
|
+
* ```
|
|
972
|
+
*/
|
|
973
|
+
disableCases?: Set<string>;
|
|
959
974
|
};
|
|
960
975
|
}
|
|
961
976
|
interface Config {
|
|
@@ -973,6 +988,15 @@ interface Config {
|
|
|
973
988
|
* Remove empty elements
|
|
974
989
|
*/
|
|
975
990
|
removeEmptyElements: boolean;
|
|
991
|
+
/**
|
|
992
|
+
* Return an empty string from `editor.value` (and the synced source
|
|
993
|
+
* element) when the editor holds only a single empty block — e.g.
|
|
994
|
+
* `<p><br></p>` left after the user deletes all the content.
|
|
995
|
+
* `contenteditable` keeps that caret container in the DOM, so by
|
|
996
|
+
* default the value getter returns it as-is; enable this to collapse
|
|
997
|
+
* it to `''` for form submission.
|
|
998
|
+
*/
|
|
999
|
+
collapseEmptyValueToEmptyString: boolean;
|
|
976
1000
|
/**
|
|
977
1001
|
* Replace old tags to new eg. <i> to <em>, <b> to <strong>
|
|
978
1002
|
*/
|
|
@@ -1371,6 +1395,12 @@ interface Config {
|
|
|
1371
1395
|
* Default value for the `Open in new tab` checkbox when inserting a new link.
|
|
1372
1396
|
*/
|
|
1373
1397
|
openInNewTabCheckboxDefaultChecked: boolean;
|
|
1398
|
+
/**
|
|
1399
|
+
* Show an `aria-label` text input in the link dialog so an
|
|
1400
|
+
* accessible name can be set on the `<a>` (useful when several
|
|
1401
|
+
* links share the same visible text, e.g. "here"). Default: false.
|
|
1402
|
+
*/
|
|
1403
|
+
ariaLabelInput: boolean;
|
|
1374
1404
|
/**
|
|
1375
1405
|
* Use an input text to ask the classname or a select or not ask
|
|
1376
1406
|
*/
|
|
@@ -15,6 +15,12 @@ export declare class ToolbarContent<T extends IViewBased = IViewBased> extends U
|
|
|
15
15
|
className(): string;
|
|
16
16
|
/** @override */
|
|
17
17
|
update(): void;
|
|
18
|
+
/**
|
|
19
|
+
* The content is arbitrary HTML — propagate the disabled state to the
|
|
20
|
+
* nested form controls (e.g. the file input of the Upload button),
|
|
21
|
+
* otherwise they stay interactive.
|
|
22
|
+
*/
|
|
23
|
+
protected onChangeDisabled(): void;
|
|
18
24
|
/** @override */
|
|
19
25
|
protected createContainer(): HTMLElement;
|
|
20
26
|
constructor(jodit: T, control: IControlTypeContent, target?: Nullable<HTMLElement>);
|
|
@@ -3,9 +3,25 @@
|
|
|
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
|
-
|
|
6
|
+
/**
|
|
7
|
+
* @module plugins/backspace
|
|
8
|
+
*/
|
|
9
|
+
import type { IJodit } from "../../../types/index";
|
|
10
|
+
import type { DeleteMode } from "../interface";
|
|
11
|
+
type CaseFn = (jodit: IJodit, fakeNode: any, backspace: boolean, mode: DeleteMode) => void | boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Ordered delete/backspace cases with stable keys. The first one returning
|
|
14
|
+
* `true` wins. The keys are stable across minified builds (function names are
|
|
15
|
+
* mangled by terser) so they can be referenced by `delete.disableCases`.
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
export declare const casesMap: ReadonlyArray<readonly [
|
|
19
|
+
string,
|
|
20
|
+
CaseFn
|
|
21
|
+
]>;
|
|
8
22
|
/**
|
|
9
23
|
* @private
|
|
24
|
+
* @deprecated Use `casesMap` to also get the stable case keys.
|
|
10
25
|
*/
|
|
11
|
-
export declare const cases:
|
|
26
|
+
export declare const cases: CaseFn[];
|
|
27
|
+
export {};
|
|
@@ -17,6 +17,21 @@ declare module 'jodit/config' {
|
|
|
17
17
|
backspaceWord: string[];
|
|
18
18
|
backspaceSentence: string[];
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* Disable specific Backspace/Delete cleanup cases by their stable
|
|
22
|
+
* key, so the plugin no longer applies that particular behavior.
|
|
23
|
+
* Available keys: `remove-unbreakable`, `remove-not-editable`,
|
|
24
|
+
* `remove-char`, `table-cell`, `remove-empty-parent`,
|
|
25
|
+
* `remove-empty-neighbor`, `join-two-lists`, `join-neighbors`,
|
|
26
|
+
* `unwrap-first-list-item`.
|
|
27
|
+
*
|
|
28
|
+
* ```javascript
|
|
29
|
+
* Jodit.make('#editor', {
|
|
30
|
+
* delete: { disableCases: new Set(['remove-empty-parent']) }
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
disableCases?: Set<string>;
|
|
20
35
|
};
|
|
21
36
|
}
|
|
22
37
|
}
|
|
@@ -34,6 +34,14 @@ export declare class cleanHtml extends Plugin {
|
|
|
34
34
|
protected onBeforeSetNativeEditorValue(data: {
|
|
35
35
|
value: string;
|
|
36
36
|
}): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Collapse a value that holds only a single empty block (e.g.
|
|
39
|
+
* `<p><br></p>` left after deleting all content) to an empty string —
|
|
40
|
+
* opt-in via `cleanHTML.collapseEmptyValueToEmptyString`. See #1149
|
|
41
|
+
*/
|
|
42
|
+
protected onAfterGetValueFromEditor(data: {
|
|
43
|
+
value: string;
|
|
44
|
+
}): void;
|
|
37
45
|
protected onSafeHTML(sandBox: HTMLElement): void;
|
|
38
46
|
/** @override */
|
|
39
47
|
protected beforeDestruct(): void;
|
|
@@ -23,6 +23,15 @@ declare module 'jodit/config' {
|
|
|
23
23
|
* Remove empty elements
|
|
24
24
|
*/
|
|
25
25
|
removeEmptyElements: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Return an empty string from `editor.value` (and the synced source
|
|
28
|
+
* element) when the editor holds only a single empty block — e.g.
|
|
29
|
+
* `<p><br></p>` left after the user deletes all the content.
|
|
30
|
+
* `contenteditable` keeps that caret container in the DOM, so by
|
|
31
|
+
* default the value getter returns it as-is; enable this to collapse
|
|
32
|
+
* it to `''` for form submission.
|
|
33
|
+
*/
|
|
34
|
+
collapseEmptyValueToEmptyString: boolean;
|
|
26
35
|
/**
|
|
27
36
|
* Replace old tags to new eg. <i> to <em>, <b> to <strong>
|
|
28
37
|
*/
|
|
@@ -50,6 +50,13 @@ export declare class inlinePopup extends Plugin {
|
|
|
50
50
|
private __reopenSelectionPopup;
|
|
51
51
|
private __onDocumentMouseDown;
|
|
52
52
|
private __onCloseAllPopups;
|
|
53
|
+
/**
|
|
54
|
+
* The selection rect comes from the editor document — in iframe mode its
|
|
55
|
+
* coordinates are iframe-local, while the popup lives in the host
|
|
56
|
+
* document, so the iframe offset must be added. See
|
|
57
|
+
* https://github.com/xdan/jodit/issues/1058
|
|
58
|
+
*/
|
|
59
|
+
private __selectionBound;
|
|
53
60
|
private snapRange;
|
|
54
61
|
private onSelectionStart;
|
|
55
62
|
private onSelectionEnd;
|
|
@@ -39,6 +39,12 @@ declare module 'jodit/config' {
|
|
|
39
39
|
* Default value for the `Open in new tab` checkbox when inserting a new link.
|
|
40
40
|
*/
|
|
41
41
|
openInNewTabCheckboxDefaultChecked: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Show an `aria-label` text input in the link dialog so an
|
|
44
|
+
* accessible name can be set on the `<a>` (useful when several
|
|
45
|
+
* links share the same visible text, e.g. "here"). Default: false.
|
|
46
|
+
*/
|
|
47
|
+
ariaLabelInput: boolean;
|
|
42
48
|
/**
|
|
43
49
|
* Use an input text to ask the classname or a select or not ask
|
|
44
50
|
*/
|
|
@@ -29,7 +29,7 @@ export declare class selectCells extends Plugin {
|
|
|
29
29
|
/**
|
|
30
30
|
* Mouse click inside the table
|
|
31
31
|
*/
|
|
32
|
-
protected onStartSelection(cell: HTMLTableCellElement): void | false;
|
|
32
|
+
protected onStartSelection(cell: HTMLTableCellElement, event?: MouseEvent): void | false;
|
|
33
33
|
protected onOutsideClick(): void;
|
|
34
34
|
protected onChange(): void;
|
|
35
35
|
/**
|