jodit 4.12.43 → 4.13.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 +15 -0
- package/es2015/jodit.css +6 -1
- package/es2015/jodit.fat.min.css +1 -1
- package/es2015/jodit.fat.min.js +14 -14
- package/es2015/jodit.js +198 -20
- package/es2015/jodit.min.css +1 -1
- package/es2015/jodit.min.js +5 -5
- package/es2015/plugins/debug/debug.css +1 -1
- package/es2015/plugins/debug/debug.js +1 -1
- package/es2015/plugins/debug/debug.min.js +1 -1
- package/es2015/plugins/speech-recognize/speech-recognize.css +1 -1
- package/es2015/plugins/speech-recognize/speech-recognize.js +1 -1
- package/es2015/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es2018/jodit.fat.min.css +1 -1
- package/es2018/jodit.fat.min.js +3 -3
- package/es2018/jodit.min.css +1 -1
- package/es2018/jodit.min.js +5 -5
- package/es2018/plugins/debug/debug.min.js +1 -1
- package/es2018/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es2021/jodit.css +6 -1
- package/es2021/jodit.fat.min.css +1 -1
- package/es2021/jodit.fat.min.js +5 -5
- package/es2021/jodit.js +189 -20
- package/es2021/jodit.min.css +1 -1
- package/es2021/jodit.min.js +7 -7
- 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 +6 -1
- package/es2021.en/jodit.fat.min.css +1 -1
- package/es2021.en/jodit.fat.min.js +6 -6
- package/es2021.en/jodit.js +189 -20
- package/es2021.en/jodit.min.css +1 -1
- package/es2021.en/jodit.min.js +6 -6
- 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 +7 -2
- package/es5/jodit.fat.min.css +1 -1
- package/es5/jodit.fat.min.js +2 -2
- package/es5/jodit.js +234 -20
- package/es5/jodit.min.css +3 -3
- package/es5/jodit.min.js +2 -2
- package/es5/plugins/debug/debug.css +1 -1
- package/es5/plugins/debug/debug.js +1 -1
- package/es5/plugins/debug/debug.min.js +1 -1
- package/es5/plugins/speech-recognize/speech-recognize.css +1 -1
- package/es5/plugins/speech-recognize/speech-recognize.js +1 -1
- package/es5/plugins/speech-recognize/speech-recognize.min.js +1 -1
- package/es5/polyfills.fat.min.js +1 -1
- package/es5/polyfills.js +1 -1
- package/es5/polyfills.min.js +1 -1
- package/esm/config.d.ts +22 -0
- package/esm/config.js +21 -0
- package/esm/core/constants.js +1 -1
- package/esm/core/selection/selection.d.ts +31 -0
- package/esm/core/selection/selection.js +143 -14
- package/esm/core/storage/async-storage.d.ts +2 -2
- package/esm/core/storage/async-storage.js +19 -1
- package/esm/core/storage/engines/local-storage-provider.js +6 -1
- package/esm/core/view/view-with-toolbar.d.ts +6 -0
- package/esm/core/view/view-with-toolbar.js +18 -2
- package/esm/core/view/view.js +1 -1
- package/esm/jodit.js +7 -0
- package/esm/types/jodit.d.ts +2 -0
- package/esm/types/storage.d.ts +15 -0
- package/esm/types/view.d.ts +8 -1
- package/package.json +1 -1
- package/types/config.d.ts +22 -0
- package/types/core/selection/selection.d.ts +31 -0
- package/types/core/storage/async-storage.d.ts +2 -2
- package/types/core/view/view-with-toolbar.d.ts +6 -0
- package/types/types/jodit.d.ts +2 -0
- package/types/types/storage.d.ts +15 -0
- package/types/types/view.d.ts +8 -1
package/es5/polyfills.fat.min.js
CHANGED
package/es5/polyfills.js
CHANGED
package/es5/polyfills.min.js
CHANGED
package/esm/config.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type { ISourceEditor } from "./types/index";
|
|
|
21
21
|
import type { ISpeechRecognizeConstructor } from "./plugins/speech-recognize/interface";
|
|
22
22
|
import type { Attributes } from "./types/index";
|
|
23
23
|
import type { Controls } from "./types/index";
|
|
24
|
+
import type { IAsyncStorageOptions } from "./types/index";
|
|
24
25
|
import type { IExtraPlugin } from "./types/index";
|
|
25
26
|
import type { IUIButtonState } from "./types/index";
|
|
26
27
|
import type { IViewOptions } from "./types/index";
|
|
@@ -345,6 +346,27 @@ declare class Config implements IViewOptions {
|
|
|
345
346
|
* if set true, then the current mode is saved in a cookie, and is restored after a reload of the page
|
|
346
347
|
*/
|
|
347
348
|
saveModeInStorage: boolean;
|
|
349
|
+
/**
|
|
350
|
+
* Configure the provider that backs {@link IViewBased.asyncStorage}.
|
|
351
|
+
*
|
|
352
|
+
* By default the editor's `asyncStorage` uses persistent `IndexedDB` (with an
|
|
353
|
+
* in-memory fallback when it is unavailable). Set `defaultProvider` to override it:
|
|
354
|
+
* - `'local'` — persist in `localStorage`;
|
|
355
|
+
* - `'memory'` — keep everything in memory (nothing survives a reload);
|
|
356
|
+
* - a custom {@link IAsyncStorage} implementation — plug in your own backend.
|
|
357
|
+
*
|
|
358
|
+
* ```javascript
|
|
359
|
+
* Jodit.make('#editor', {
|
|
360
|
+
* asyncStorage: { defaultProvider: 'local' }
|
|
361
|
+
* });
|
|
362
|
+
*
|
|
363
|
+
* // or a fully custom backend
|
|
364
|
+
* Jodit.make('#editor', {
|
|
365
|
+
* asyncStorage: { defaultProvider: myAsyncStorage }
|
|
366
|
+
* });
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
asyncStorage: IAsyncStorageOptions;
|
|
348
370
|
/**
|
|
349
371
|
* Class name that can be appended to the editable area
|
|
350
372
|
*
|
package/esm/config.js
CHANGED
|
@@ -363,6 +363,27 @@ class Config {
|
|
|
363
363
|
* if set true, then the current mode is saved in a cookie, and is restored after a reload of the page
|
|
364
364
|
*/
|
|
365
365
|
this.saveModeInStorage = false;
|
|
366
|
+
/**
|
|
367
|
+
* Configure the provider that backs {@link IViewBased.asyncStorage}.
|
|
368
|
+
*
|
|
369
|
+
* By default the editor's `asyncStorage` uses persistent `IndexedDB` (with an
|
|
370
|
+
* in-memory fallback when it is unavailable). Set `defaultProvider` to override it:
|
|
371
|
+
* - `'local'` — persist in `localStorage`;
|
|
372
|
+
* - `'memory'` — keep everything in memory (nothing survives a reload);
|
|
373
|
+
* - a custom {@link IAsyncStorage} implementation — plug in your own backend.
|
|
374
|
+
*
|
|
375
|
+
* ```javascript
|
|
376
|
+
* Jodit.make('#editor', {
|
|
377
|
+
* asyncStorage: { defaultProvider: 'local' }
|
|
378
|
+
* });
|
|
379
|
+
*
|
|
380
|
+
* // or a fully custom backend
|
|
381
|
+
* Jodit.make('#editor', {
|
|
382
|
+
* asyncStorage: { defaultProvider: myAsyncStorage }
|
|
383
|
+
* });
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
this.asyncStorage = {};
|
|
366
387
|
/**
|
|
367
388
|
* Class name that can be appended to the editable area
|
|
368
389
|
*
|
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.13.3";
|
|
7
7
|
// prettier-ignore
|
|
8
8
|
export const ES = "es2020";
|
|
9
9
|
export const IS_ES_MODERN = true;
|
|
@@ -224,6 +224,37 @@ export declare class Selection implements ISelect {
|
|
|
224
224
|
* ```
|
|
225
225
|
*/
|
|
226
226
|
get html(): string;
|
|
227
|
+
/**
|
|
228
|
+
* Splits the boundaries of the current selection and wraps every
|
|
229
|
+
* contiguous run of selected inline content (grouped by block) into a
|
|
230
|
+
* `<font>` element, returning those wrappers in document order.
|
|
231
|
+
*
|
|
232
|
+
* This is a pure-DOM replacement for the old
|
|
233
|
+
* `nativeExecCommand('fontsize', false, '7')` trick which relied on the
|
|
234
|
+
* browser to split the selection into `<font size="7">` fragments.
|
|
235
|
+
*/
|
|
236
|
+
private __wrapSelectionFragments;
|
|
237
|
+
/**
|
|
238
|
+
* Splits the text nodes at both ends of the range so that its boundaries
|
|
239
|
+
* always fall between nodes. Afterwards every node inside the range is
|
|
240
|
+
* fully (not partially) selected.
|
|
241
|
+
*/
|
|
242
|
+
private __splitSelectionBoundaries;
|
|
243
|
+
private __normalizeRangeBoundary;
|
|
244
|
+
/**
|
|
245
|
+
* Collects the highest-level nodes that are completely inside the range,
|
|
246
|
+
* descending into nodes that are only partially selected.
|
|
247
|
+
*/
|
|
248
|
+
private __collectContainedNodes;
|
|
249
|
+
private __isFullyContained;
|
|
250
|
+
/**
|
|
251
|
+
* Wraps every contiguous run of selected inline siblings into a `<font>`
|
|
252
|
+
* element. Block-level nodes are never wrapped themselves - their inline
|
|
253
|
+
* content is wrapped instead, keeping every `<font>` inside a single block.
|
|
254
|
+
*/
|
|
255
|
+
private __wrapSelectionRuns;
|
|
256
|
+
private __isOrContainsBlock;
|
|
257
|
+
private __wrapRunInFont;
|
|
227
258
|
/**
|
|
228
259
|
* Wrap all selected fragments inside Tag or apply some callback
|
|
229
260
|
*/
|
|
@@ -898,6 +898,148 @@ export class Selection {
|
|
|
898
898
|
}
|
|
899
899
|
return '';
|
|
900
900
|
}
|
|
901
|
+
/**
|
|
902
|
+
* Splits the boundaries of the current selection and wraps every
|
|
903
|
+
* contiguous run of selected inline content (grouped by block) into a
|
|
904
|
+
* `<font>` element, returning those wrappers in document order.
|
|
905
|
+
*
|
|
906
|
+
* This is a pure-DOM replacement for the old
|
|
907
|
+
* `nativeExecCommand('fontsize', false, '7')` trick which relied on the
|
|
908
|
+
* browser to split the selection into `<font size="7">` fragments.
|
|
909
|
+
*/
|
|
910
|
+
__wrapSelectionFragments() {
|
|
911
|
+
const range = this.range;
|
|
912
|
+
this.__splitSelectionBoundaries(range);
|
|
913
|
+
let root = range.commonAncestorContainer;
|
|
914
|
+
if (Dom.isText(root)) {
|
|
915
|
+
root = root.parentNode;
|
|
916
|
+
}
|
|
917
|
+
if (!root) {
|
|
918
|
+
return [];
|
|
919
|
+
}
|
|
920
|
+
return this.__wrapSelectionRuns(this.__collectContainedNodes(root, range));
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Splits the text nodes at both ends of the range so that its boundaries
|
|
924
|
+
* always fall between nodes. Afterwards every node inside the range is
|
|
925
|
+
* fully (not partially) selected.
|
|
926
|
+
*/
|
|
927
|
+
__splitSelectionBoundaries(range) {
|
|
928
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
929
|
+
let { startContainer, endContainer } = range;
|
|
930
|
+
let { startOffset, endOffset } = range;
|
|
931
|
+
if (Dom.isText(endContainer) &&
|
|
932
|
+
endOffset > 0 &&
|
|
933
|
+
endOffset < ((_b = (_a = endContainer.nodeValue) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0)) {
|
|
934
|
+
endContainer.splitText(endOffset);
|
|
935
|
+
endOffset = (_d = (_c = endContainer.nodeValue) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : endOffset;
|
|
936
|
+
}
|
|
937
|
+
if (Dom.isText(startContainer) &&
|
|
938
|
+
startOffset > 0 &&
|
|
939
|
+
startOffset < ((_f = (_e = startContainer.nodeValue) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : 0)) {
|
|
940
|
+
const middle = startContainer.splitText(startOffset);
|
|
941
|
+
// Selection located inside a single text node - the tail we just
|
|
942
|
+
// cut off is the actual selected fragment.
|
|
943
|
+
if (startContainer === endContainer) {
|
|
944
|
+
endContainer = middle;
|
|
945
|
+
endOffset = (_h = (_g = middle.nodeValue) === null || _g === void 0 ? void 0 : _g.length) !== null && _h !== void 0 ? _h : 0;
|
|
946
|
+
}
|
|
947
|
+
startContainer = middle;
|
|
948
|
+
startOffset = 0;
|
|
949
|
+
}
|
|
950
|
+
range.setStart(startContainer, startOffset);
|
|
951
|
+
range.setEnd(endContainer, endOffset);
|
|
952
|
+
// Normalize text-edge boundaries (e.g. `(text, 0)` or `(text, length)`)
|
|
953
|
+
// to the element level so that containment checks based on
|
|
954
|
+
// `selectNode()` treat a fully selected text node as contained.
|
|
955
|
+
this.__normalizeRangeBoundary(range, true);
|
|
956
|
+
this.__normalizeRangeBoundary(range, false);
|
|
957
|
+
}
|
|
958
|
+
__normalizeRangeBoundary(range, atStart) {
|
|
959
|
+
var _a, _b;
|
|
960
|
+
const container = atStart ? range.startContainer : range.endContainer;
|
|
961
|
+
if (!Dom.isText(container)) {
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
const offset = atStart ? range.startOffset : range.endOffset;
|
|
965
|
+
const length = (_b = (_a = container.nodeValue) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
|
|
966
|
+
if (offset === 0) {
|
|
967
|
+
atStart
|
|
968
|
+
? range.setStartBefore(container)
|
|
969
|
+
: range.setEndBefore(container);
|
|
970
|
+
}
|
|
971
|
+
else if (offset >= length) {
|
|
972
|
+
atStart
|
|
973
|
+
? range.setStartAfter(container)
|
|
974
|
+
: range.setEndAfter(container);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Collects the highest-level nodes that are completely inside the range,
|
|
979
|
+
* descending into nodes that are only partially selected.
|
|
980
|
+
*/
|
|
981
|
+
__collectContainedNodes(root, range) {
|
|
982
|
+
const result = [];
|
|
983
|
+
toArray(root.childNodes).forEach(child => {
|
|
984
|
+
if (this.__isFullyContained(range, child)) {
|
|
985
|
+
result.push(child);
|
|
986
|
+
}
|
|
987
|
+
else if (child.childNodes.length && range.intersectsNode(child)) {
|
|
988
|
+
result.push(...this.__collectContainedNodes(child, range));
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
return result;
|
|
992
|
+
}
|
|
993
|
+
__isFullyContained(range, node) {
|
|
994
|
+
const nodeRange = this.createRange();
|
|
995
|
+
nodeRange.selectNode(node);
|
|
996
|
+
return (range.compareBoundaryPoints(Range.START_TO_START, nodeRange) <= 0 &&
|
|
997
|
+
range.compareBoundaryPoints(Range.END_TO_END, nodeRange) >= 0);
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Wraps every contiguous run of selected inline siblings into a `<font>`
|
|
1001
|
+
* element. Block-level nodes are never wrapped themselves - their inline
|
|
1002
|
+
* content is wrapped instead, keeping every `<font>` inside a single block.
|
|
1003
|
+
*/
|
|
1004
|
+
__wrapSelectionRuns(nodes) {
|
|
1005
|
+
const fonts = [];
|
|
1006
|
+
let run = [];
|
|
1007
|
+
const flush = () => {
|
|
1008
|
+
if (run.length) {
|
|
1009
|
+
fonts.push(this.__wrapRunInFont(run));
|
|
1010
|
+
run = [];
|
|
1011
|
+
}
|
|
1012
|
+
};
|
|
1013
|
+
nodes.forEach(node => {
|
|
1014
|
+
if (Dom.isElement(node) && this.__isOrContainsBlock(node)) {
|
|
1015
|
+
flush();
|
|
1016
|
+
fonts.push(...this.__wrapSelectionRuns(toArray(node.childNodes)));
|
|
1017
|
+
}
|
|
1018
|
+
else {
|
|
1019
|
+
if (run.length &&
|
|
1020
|
+
run[run.length - 1].parentNode !== node.parentNode) {
|
|
1021
|
+
flush();
|
|
1022
|
+
}
|
|
1023
|
+
run.push(node);
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
flush();
|
|
1027
|
+
return fonts;
|
|
1028
|
+
}
|
|
1029
|
+
__isOrContainsBlock(node) {
|
|
1030
|
+
if (Dom.isBlock(node)) {
|
|
1031
|
+
return true;
|
|
1032
|
+
}
|
|
1033
|
+
return toArray(node.childNodes).some(child => this.__isOrContainsBlock(child));
|
|
1034
|
+
}
|
|
1035
|
+
__wrapRunInFont(run) {
|
|
1036
|
+
var _a;
|
|
1037
|
+
const font = this.j.createInside.element('font');
|
|
1038
|
+
const [first] = run;
|
|
1039
|
+
(_a = first.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(font, first);
|
|
1040
|
+
run.forEach(node => font.appendChild(node));
|
|
1041
|
+
return font;
|
|
1042
|
+
}
|
|
901
1043
|
/**
|
|
902
1044
|
* Wrap all selected fragments inside Tag or apply some callback
|
|
903
1045
|
*/
|
|
@@ -912,20 +1054,7 @@ export class Selection {
|
|
|
912
1054
|
Dom.unwrap(font);
|
|
913
1055
|
return;
|
|
914
1056
|
}
|
|
915
|
-
|
|
916
|
-
$$('*[style*=font-size]', this.area).forEach(elm => {
|
|
917
|
-
attr(elm, 'data-font-size', elm.style.fontSize.toString());
|
|
918
|
-
elm.style.removeProperty('font-size');
|
|
919
|
-
});
|
|
920
|
-
this.j.nativeExecCommand('fontsize', false, '7');
|
|
921
|
-
$$('*[data-font-size]', this.area).forEach(elm => {
|
|
922
|
-
const fontSize = attr(elm, 'data-font-size');
|
|
923
|
-
if (fontSize) {
|
|
924
|
-
elm.style.fontSize = fontSize;
|
|
925
|
-
attr(elm, 'data-font-size', null);
|
|
926
|
-
}
|
|
927
|
-
});
|
|
928
|
-
const elms = $$('font[size="7"]', this.area);
|
|
1057
|
+
const elms = this.__wrapSelectionFragments();
|
|
929
1058
|
for (const font of elms) {
|
|
930
1059
|
const { firstChild, lastChild } = font;
|
|
931
1060
|
if (firstChild &&
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* @module storage
|
|
8
8
|
*/
|
|
9
|
-
import type { IAsyncStorage, IStorage, StorageValueType } from "../../types/index";
|
|
9
|
+
import type { IAsyncStorage, IAsyncStorageOptions, IStorage, StorageValueType } from "../../types/index";
|
|
10
10
|
import { type WebStorageStrategy } from "./engines/local-storage-provider";
|
|
11
11
|
export declare class AsyncStorage<T = StorageValueType> implements IAsyncStorage<T> {
|
|
12
12
|
private provider;
|
|
@@ -18,5 +18,5 @@ export declare class AsyncStorage<T = StorageValueType> implements IAsyncStorage
|
|
|
18
18
|
exists(key: string): Promise<boolean>;
|
|
19
19
|
clear(): Promise<this>;
|
|
20
20
|
close(): Promise<void>;
|
|
21
|
-
static makeStorage(persistentOrStrategy?: boolean | WebStorageStrategy | 'memoryStorage' | 'indexedDB', suffix?: string): IAsyncStorage;
|
|
21
|
+
static makeStorage(persistentOrStrategy?: boolean | WebStorageStrategy | 'memoryStorage' | 'indexedDB', suffix?: string, options?: IAsyncStorageOptions): IAsyncStorage;
|
|
22
22
|
}
|
|
@@ -45,9 +45,27 @@ export class AsyncStorage {
|
|
|
45
45
|
await provider.close();
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
static makeStorage(persistentOrStrategy = false, suffix) {
|
|
48
|
+
static makeStorage(persistentOrStrategy = false, suffix, options) {
|
|
49
49
|
let provider = undefined;
|
|
50
50
|
let storage = null;
|
|
51
|
+
// An explicit `defaultProvider` overrides the strategy-based selection
|
|
52
|
+
// below and decides which provider backs the storage.
|
|
53
|
+
const defaultProvider = options === null || options === void 0 ? void 0 : options.defaultProvider;
|
|
54
|
+
if (defaultProvider != null) {
|
|
55
|
+
if (defaultProvider === 'local') {
|
|
56
|
+
provider = canUsePersistentStorage('localStorage')
|
|
57
|
+
? new LocalStorageProvider(StorageKey + (suffix || ''))
|
|
58
|
+
: new MemoryStorageProvider();
|
|
59
|
+
}
|
|
60
|
+
else if (defaultProvider === 'memory') {
|
|
61
|
+
provider = new MemoryStorageProvider();
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// A custom IAsyncStorage implementation, used as-is.
|
|
65
|
+
provider = defaultProvider;
|
|
66
|
+
}
|
|
67
|
+
return new AsyncStorage(Promise.resolve(provider), suffix);
|
|
68
|
+
}
|
|
51
69
|
if (persistentOrStrategy === 'localStorage' ||
|
|
52
70
|
persistentOrStrategy === 'sessionStorage') {
|
|
53
71
|
if (canUsePersistentStorage(persistentOrStrategy)) {
|
|
@@ -47,7 +47,12 @@ export class LocalStorageProvider {
|
|
|
47
47
|
}
|
|
48
48
|
delete(key) {
|
|
49
49
|
try {
|
|
50
|
-
this.storage.
|
|
50
|
+
const buffer = this.storage.getItem(this.rootKey);
|
|
51
|
+
if (buffer) {
|
|
52
|
+
const json = JSON.parse(buffer);
|
|
53
|
+
delete json[key];
|
|
54
|
+
this.storage.setItem(this.rootKey, JSON.stringify(json));
|
|
55
|
+
}
|
|
51
56
|
}
|
|
52
57
|
catch (_a) { }
|
|
53
58
|
return this;
|
|
@@ -16,6 +16,12 @@ export declare abstract class ViewWithToolbar extends View implements IViewWithT
|
|
|
16
16
|
* Container for toolbar
|
|
17
17
|
*/
|
|
18
18
|
get toolbarContainer(): HTMLElement;
|
|
19
|
+
/**
|
|
20
|
+
* Keep the toolbar box as the first child of the container, except that
|
|
21
|
+
* children flagged with `data-jodit-above-toolbar` (e.g. the `above`
|
|
22
|
+
* workplace slot used for presence bars and banners) stay above it.
|
|
23
|
+
*/
|
|
24
|
+
private __appendToolbarBox;
|
|
19
25
|
/**
|
|
20
26
|
* Change panel container
|
|
21
27
|
*/
|
|
@@ -33,10 +33,26 @@ export class ViewWithToolbar extends View {
|
|
|
33
33
|
(isString(this.o.toolbar) || Dom.isHTMLElement(this.o.toolbar))) {
|
|
34
34
|
return resolveElement(this.o.toolbar, this.o.shadowRoot || this.od);
|
|
35
35
|
}
|
|
36
|
-
this.o.toolbar &&
|
|
37
|
-
Dom.appendChildFirst(this.container, this.__defaultToolbarContainer);
|
|
36
|
+
this.o.toolbar && this.__appendToolbarBox();
|
|
38
37
|
return this.__defaultToolbarContainer;
|
|
39
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Keep the toolbar box as the first child of the container, except that
|
|
41
|
+
* children flagged with `data-jodit-above-toolbar` (e.g. the `above`
|
|
42
|
+
* workplace slot used for presence bars and banners) stay above it.
|
|
43
|
+
*/
|
|
44
|
+
__appendToolbarBox() {
|
|
45
|
+
const box = this.__defaultToolbarContainer;
|
|
46
|
+
let anchor = this.container.firstElementChild;
|
|
47
|
+
while (anchor &&
|
|
48
|
+
anchor !== box &&
|
|
49
|
+
anchor.hasAttribute('data-jodit-above-toolbar')) {
|
|
50
|
+
anchor = anchor.nextElementSibling;
|
|
51
|
+
}
|
|
52
|
+
if (anchor !== box) {
|
|
53
|
+
this.container.insertBefore(box, anchor);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
40
56
|
/**
|
|
41
57
|
* Change panel container
|
|
42
58
|
*/
|
package/esm/core/view/view.js
CHANGED
|
@@ -105,7 +105,7 @@ let View = View_1 = class View extends Component {
|
|
|
105
105
|
* Container for persistent set/get value
|
|
106
106
|
*/
|
|
107
107
|
get asyncStorage() {
|
|
108
|
-
return AsyncStorage.makeStorage(true, this.id);
|
|
108
|
+
return AsyncStorage.makeStorage(true, this.id, this.o.asyncStorage);
|
|
109
109
|
}
|
|
110
110
|
/**
|
|
111
111
|
* Short alias for `create`
|
package/esm/jodit.js
CHANGED
|
@@ -1016,6 +1016,12 @@ let Jodit = Jodit_1 = class Jodit extends ViewWithToolbar {
|
|
|
1016
1016
|
element.style.display = 'none';
|
|
1017
1017
|
}
|
|
1018
1018
|
const SLOT = 'workplace-slot';
|
|
1019
|
+
// A slot that always stays ABOVE the toolbar (presence bars, banners…).
|
|
1020
|
+
// The `data-jodit-above-toolbar` attribute tells ViewWithToolbar to keep
|
|
1021
|
+
// the toolbar box below it when (re)attaching the toolbar container.
|
|
1022
|
+
const aboveSlot = this.c.div(this.getFullElName(SLOT, 'above'), NOEDIT);
|
|
1023
|
+
aboveSlot.setAttribute('data-jodit-above-toolbar', '');
|
|
1024
|
+
Dom.appendChildFirst(container, aboveSlot);
|
|
1019
1025
|
const topSlot = this.c.div(this.getFullElName(SLOT, 'top'), NOEDIT);
|
|
1020
1026
|
container.appendChild(topSlot);
|
|
1021
1027
|
const centerSlot = this.c.div(this.getFullElName(SLOT, 'center'), NOEDIT);
|
|
@@ -1048,6 +1054,7 @@ let Jodit = Jodit_1 = class Jodit extends ViewWithToolbar {
|
|
|
1048
1054
|
container,
|
|
1049
1055
|
workplace,
|
|
1050
1056
|
slots: {
|
|
1057
|
+
above: aboveSlot,
|
|
1051
1058
|
top: topSlot,
|
|
1052
1059
|
bottom: bottomPanel,
|
|
1053
1060
|
center: centerSlot,
|
package/esm/types/jodit.d.ts
CHANGED
package/esm/types/storage.d.ts
CHANGED
|
@@ -22,3 +22,18 @@ export interface IAsyncStorage<T = StorageValueType> {
|
|
|
22
22
|
clear(): Promise<this>;
|
|
23
23
|
close(): Promise<void>;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Which provider should back an {@link IAsyncStorage} created by
|
|
27
|
+
* `AsyncStorage.makeStorage`:
|
|
28
|
+
* - `'local'` — use `localStorage` (falls back to memory if it is blocked);
|
|
29
|
+
* - `'memory'` — use an in-memory store (nothing survives a page reload);
|
|
30
|
+
* - a custom {@link IAsyncStorage} implementation — use it as-is.
|
|
31
|
+
*/
|
|
32
|
+
export type AsyncStorageDefaultProvider = 'local' | 'memory' | IAsyncStorage;
|
|
33
|
+
export interface IAsyncStorageOptions {
|
|
34
|
+
/**
|
|
35
|
+
* Overrides the provider that backs the storage. When omitted the storage
|
|
36
|
+
* behaves as before — persistent `IndexedDB` with an in-memory fallback.
|
|
37
|
+
*/
|
|
38
|
+
defaultProvider?: AsyncStorageDefaultProvider;
|
|
39
|
+
}
|
package/esm/types/view.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { Buttons, ButtonsGroup, ButtonsOption, Controls, IControlType, IProgressBar, IToolbarCollection } from "./toolbar";
|
|
10
10
|
import type { CanUndef, IComponent, IContainer, IDictionary, Nullable } from "./types";
|
|
11
11
|
import type { Attributes, ICreate } from "./create";
|
|
12
|
-
import type { IAsyncStorage, IStorage } from "./storage";
|
|
12
|
+
import type { IAsyncStorage, IAsyncStorageOptions, IStorage } from "./storage";
|
|
13
13
|
import type { IUIButtonState } from "./ui";
|
|
14
14
|
import type { IEventEmitter } from "./events";
|
|
15
15
|
import type { IPluginButton } from "./plugin";
|
|
@@ -40,6 +40,13 @@ interface IViewOptions extends ILanguageOptions, IToolbarOptions {
|
|
|
40
40
|
* Use cache for heavy methods
|
|
41
41
|
*/
|
|
42
42
|
cache?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Configure the provider that backs {@link IViewBased.asyncStorage}.
|
|
45
|
+
* By default it uses persistent `IndexedDB` (with an in-memory fallback);
|
|
46
|
+
* set `defaultProvider` to `'local'`, `'memory'` or a custom
|
|
47
|
+
* {@link IAsyncStorage} implementation to override it.
|
|
48
|
+
*/
|
|
49
|
+
asyncStorage?: IAsyncStorageOptions;
|
|
43
50
|
getIcon?: (name: string, clearName: string) => CanUndef<string>;
|
|
44
51
|
headerButtons?: string | Array<IControlType | string | ButtonsGroup>;
|
|
45
52
|
readonly basePath?: string;
|
package/package.json
CHANGED
package/types/config.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type { ISourceEditor } from "./types/index";
|
|
|
21
21
|
import type { ISpeechRecognizeConstructor } from "./plugins/speech-recognize/interface";
|
|
22
22
|
import type { Attributes } from "./types/index";
|
|
23
23
|
import type { Controls } from "./types/index";
|
|
24
|
+
import type { IAsyncStorageOptions } from "./types/index";
|
|
24
25
|
import type { IExtraPlugin } from "./types/index";
|
|
25
26
|
import type { IUIButtonState } from "./types/index";
|
|
26
27
|
import type { IViewOptions } from "./types/index";
|
|
@@ -345,6 +346,27 @@ declare class Config implements IViewOptions {
|
|
|
345
346
|
* if set true, then the current mode is saved in a cookie, and is restored after a reload of the page
|
|
346
347
|
*/
|
|
347
348
|
saveModeInStorage: boolean;
|
|
349
|
+
/**
|
|
350
|
+
* Configure the provider that backs {@link IViewBased.asyncStorage}.
|
|
351
|
+
*
|
|
352
|
+
* By default the editor's `asyncStorage` uses persistent `IndexedDB` (with an
|
|
353
|
+
* in-memory fallback when it is unavailable). Set `defaultProvider` to override it:
|
|
354
|
+
* - `'local'` — persist in `localStorage`;
|
|
355
|
+
* - `'memory'` — keep everything in memory (nothing survives a reload);
|
|
356
|
+
* - a custom {@link IAsyncStorage} implementation — plug in your own backend.
|
|
357
|
+
*
|
|
358
|
+
* ```javascript
|
|
359
|
+
* Jodit.make('#editor', {
|
|
360
|
+
* asyncStorage: { defaultProvider: 'local' }
|
|
361
|
+
* });
|
|
362
|
+
*
|
|
363
|
+
* // or a fully custom backend
|
|
364
|
+
* Jodit.make('#editor', {
|
|
365
|
+
* asyncStorage: { defaultProvider: myAsyncStorage }
|
|
366
|
+
* });
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
asyncStorage: IAsyncStorageOptions;
|
|
348
370
|
/**
|
|
349
371
|
* Class name that can be appended to the editable area
|
|
350
372
|
*
|
|
@@ -224,6 +224,37 @@ export declare class Selection implements ISelect {
|
|
|
224
224
|
* ```
|
|
225
225
|
*/
|
|
226
226
|
get html(): string;
|
|
227
|
+
/**
|
|
228
|
+
* Splits the boundaries of the current selection and wraps every
|
|
229
|
+
* contiguous run of selected inline content (grouped by block) into a
|
|
230
|
+
* `<font>` element, returning those wrappers in document order.
|
|
231
|
+
*
|
|
232
|
+
* This is a pure-DOM replacement for the old
|
|
233
|
+
* `nativeExecCommand('fontsize', false, '7')` trick which relied on the
|
|
234
|
+
* browser to split the selection into `<font size="7">` fragments.
|
|
235
|
+
*/
|
|
236
|
+
private __wrapSelectionFragments;
|
|
237
|
+
/**
|
|
238
|
+
* Splits the text nodes at both ends of the range so that its boundaries
|
|
239
|
+
* always fall between nodes. Afterwards every node inside the range is
|
|
240
|
+
* fully (not partially) selected.
|
|
241
|
+
*/
|
|
242
|
+
private __splitSelectionBoundaries;
|
|
243
|
+
private __normalizeRangeBoundary;
|
|
244
|
+
/**
|
|
245
|
+
* Collects the highest-level nodes that are completely inside the range,
|
|
246
|
+
* descending into nodes that are only partially selected.
|
|
247
|
+
*/
|
|
248
|
+
private __collectContainedNodes;
|
|
249
|
+
private __isFullyContained;
|
|
250
|
+
/**
|
|
251
|
+
* Wraps every contiguous run of selected inline siblings into a `<font>`
|
|
252
|
+
* element. Block-level nodes are never wrapped themselves - their inline
|
|
253
|
+
* content is wrapped instead, keeping every `<font>` inside a single block.
|
|
254
|
+
*/
|
|
255
|
+
private __wrapSelectionRuns;
|
|
256
|
+
private __isOrContainsBlock;
|
|
257
|
+
private __wrapRunInFont;
|
|
227
258
|
/**
|
|
228
259
|
* Wrap all selected fragments inside Tag or apply some callback
|
|
229
260
|
*/
|