jodit 4.17.1 → 4.17.2

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.
Files changed (60) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/es2015/jodit.css +1 -1
  3. package/es2015/jodit.fat.min.js +2 -2
  4. package/es2015/jodit.js +150 -7
  5. package/es2015/jodit.min.js +2 -2
  6. package/es2015/plugins/debug/debug.css +1 -1
  7. package/es2015/plugins/debug/debug.js +1 -1
  8. package/es2015/plugins/debug/debug.min.js +1 -1
  9. package/es2015/plugins/speech-recognize/speech-recognize.css +1 -1
  10. package/es2015/plugins/speech-recognize/speech-recognize.js +1 -1
  11. package/es2015/plugins/speech-recognize/speech-recognize.min.js +1 -1
  12. package/es2018/jodit.fat.min.js +2 -2
  13. package/es2018/jodit.min.js +2 -2
  14. package/es2018/plugins/debug/debug.min.js +1 -1
  15. package/es2018/plugins/speech-recognize/speech-recognize.min.js +1 -1
  16. package/es2021/jodit.css +1 -1
  17. package/es2021/jodit.fat.min.js +3 -3
  18. package/es2021/jodit.js +150 -7
  19. package/es2021/jodit.min.js +3 -3
  20. package/es2021/plugins/debug/debug.css +1 -1
  21. package/es2021/plugins/debug/debug.js +1 -1
  22. package/es2021/plugins/debug/debug.min.js +1 -1
  23. package/es2021/plugins/speech-recognize/speech-recognize.css +1 -1
  24. package/es2021/plugins/speech-recognize/speech-recognize.js +1 -1
  25. package/es2021/plugins/speech-recognize/speech-recognize.min.js +1 -1
  26. package/es2021.en/jodit.css +1 -1
  27. package/es2021.en/jodit.fat.min.js +24 -24
  28. package/es2021.en/jodit.js +150 -7
  29. package/es2021.en/jodit.min.js +3 -3
  30. package/es2021.en/plugins/debug/debug.css +1 -1
  31. package/es2021.en/plugins/debug/debug.js +1 -1
  32. package/es2021.en/plugins/debug/debug.min.js +1 -1
  33. package/es2021.en/plugins/speech-recognize/speech-recognize.css +1 -1
  34. package/es2021.en/plugins/speech-recognize/speech-recognize.js +1 -1
  35. package/es2021.en/plugins/speech-recognize/speech-recognize.min.js +1 -1
  36. package/es5/jodit.css +2 -2
  37. package/es5/jodit.fat.min.js +2 -2
  38. package/es5/jodit.js +165 -7
  39. package/es5/jodit.min.css +2 -2
  40. package/es5/jodit.min.js +2 -2
  41. package/es5/plugins/debug/debug.css +1 -1
  42. package/es5/plugins/debug/debug.js +1 -1
  43. package/es5/plugins/debug/debug.min.js +1 -1
  44. package/es5/plugins/speech-recognize/speech-recognize.css +1 -1
  45. package/es5/plugins/speech-recognize/speech-recognize.js +1 -1
  46. package/es5/plugins/speech-recognize/speech-recognize.min.js +1 -1
  47. package/es5/polyfills.fat.min.js +1 -1
  48. package/es5/polyfills.js +1 -1
  49. package/es5/polyfills.min.js +1 -1
  50. package/esm/core/constants.js +1 -1
  51. package/esm/core/selection/helpers/composed-ranges.d.ts +21 -0
  52. package/esm/core/selection/helpers/composed-ranges.js +45 -0
  53. package/esm/core/selection/helpers/index.d.ts +1 -0
  54. package/esm/core/selection/helpers/index.js +1 -0
  55. package/esm/core/selection/selection.d.ts +23 -0
  56. package/esm/core/selection/selection.js +80 -4
  57. package/package.json +1 -1
  58. package/types/core/selection/helpers/composed-ranges.d.ts +21 -0
  59. package/types/core/selection/helpers/index.d.ts +1 -0
  60. package/types/core/selection/selection.d.ts +23 -0
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * jodit - Jodit is an awesome and useful wysiwyg editor with filebrowser
3
3
  * Author: Chupurnov <chupurnov@gmail.com> (https://xdsoft.net/jodit/)
4
- * Version: v4.17.1
4
+ * Version: v4.17.2
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * jodit - Jodit is an awesome and useful wysiwyg editor with filebrowser
3
3
  * Author: Chupurnov <chupurnov@gmail.com> (https://xdsoft.net/jodit/)
4
- * Version: v4.17.1
4
+ * Version: v4.17.2
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * jodit - Jodit is an awesome and useful wysiwyg editor with filebrowser
3
3
  * Author: Chupurnov <chupurnov@gmail.com> (https://xdsoft.net/jodit/)
4
- * Version: v4.17.1
4
+ * Version: v4.17.2
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * jodit - Jodit is an awesome and useful wysiwyg editor with filebrowser
3
3
  * Author: Chupurnov <chupurnov@gmail.com> (https://xdsoft.net/jodit/)
4
- * Version: v4.17.1
4
+ * Version: v4.17.2
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * jodit - Jodit is an awesome and useful wysiwyg editor with filebrowser
3
3
  * Author: Chupurnov <chupurnov@gmail.com> (https://xdsoft.net/jodit/)
4
- * Version: v4.17.1
4
+ * Version: v4.17.2
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * jodit - Jodit is an awesome and useful wysiwyg editor with filebrowser
3
3
  * Author: Chupurnov <chupurnov@gmail.com> (https://xdsoft.net/jodit/)
4
- * Version: v4.17.1
4
+ * Version: v4.17.2
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * jodit - Jodit is an awesome and useful wysiwyg editor with filebrowser
3
3
  * Author: Chupurnov <chupurnov@gmail.com> (https://xdsoft.net/jodit/)
4
- * Version: v4.17.1
4
+ * Version: v4.17.2
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
package/es5/polyfills.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * jodit - Jodit is an awesome and useful wysiwyg editor with filebrowser
3
3
  * Author: Chupurnov <chupurnov@gmail.com> (https://xdsoft.net/jodit/)
4
- * Version: v4.17.1
4
+ * Version: v4.17.2
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * jodit - Jodit is an awesome and useful wysiwyg editor with filebrowser
3
3
  * Author: Chupurnov <chupurnov@gmail.com> (https://xdsoft.net/jodit/)
4
- * Version: v4.17.1
4
+ * Version: v4.17.2
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
@@ -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.17.1";
6
+ export const APP_VERSION = "4.17.2";
7
7
  // prettier-ignore
8
8
  export const ES = "es2020";
9
9
  export const IS_ES_MODERN = true;
@@ -0,0 +1,21 @@
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 selection
8
+ */
9
+ import type { Nullable } from "../../../types/index";
10
+ /**
11
+ * Ranges of a selection that lies inside the given shadow tree.
12
+ *
13
+ * `window.getSelection()` stops at the shadow boundary and reports the host
14
+ * instead of the node the caret is really in. `getComposedRanges` is the
15
+ * standard way to ask past that boundary.
16
+ *
17
+ * @returns `null` when the browser has no `getComposedRanges`, or when neither
18
+ * call shape returned ranges that point inside the shadow tree — in which case
19
+ * there is nothing better to offer than the plain window selection.
20
+ */
21
+ export declare function composedRanges(selection: Selection, shadowRoot: ShadowRoot): Nullable<StaticRange[]>;
@@ -0,0 +1,45 @@
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 { isFunction } from "../../helpers/checker/index.js";
7
+ function isInside(node, shadowRoot) {
8
+ return node === shadowRoot || shadowRoot.contains(node);
9
+ }
10
+ function usable(ranges, shadowRoot) {
11
+ return (Array.isArray(ranges) &&
12
+ ranges.length > 0 &&
13
+ ranges.every(range => isInside(range.startContainer, shadowRoot)));
14
+ }
15
+ /**
16
+ * Ranges of a selection that lies inside the given shadow tree.
17
+ *
18
+ * `window.getSelection()` stops at the shadow boundary and reports the host
19
+ * instead of the node the caret is really in. `getComposedRanges` is the
20
+ * standard way to ask past that boundary.
21
+ *
22
+ * @returns `null` when the browser has no `getComposedRanges`, or when neither
23
+ * call shape returned ranges that point inside the shadow tree — in which case
24
+ * there is nothing better to offer than the plain window selection.
25
+ */
26
+ export function composedRanges(selection, shadowRoot) {
27
+ const getComposedRanges = selection.getComposedRanges;
28
+ if (!isFunction(getComposedRanges)) {
29
+ return null;
30
+ }
31
+ for (const args of [[{ shadowRoots: [shadowRoot] }], [shadowRoot]]) {
32
+ let ranges;
33
+ try {
34
+ ranges = getComposedRanges.apply(selection, args);
35
+ }
36
+ catch (_a) {
37
+ // A build that only knows the other shape throws on this one.
38
+ continue;
39
+ }
40
+ if (usable(ranges, shadowRoot)) {
41
+ return ranges;
42
+ }
43
+ }
44
+ return null;
45
+ }
@@ -3,6 +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 * from "./composed-ranges";
6
7
  export * from "./move-node-inside-start";
7
8
  export * from "./move-the-node-along-the-edge-outward";
8
9
  /**
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { INVISIBLE_SPACE_REG_EXP_END as INV_END, INVISIBLE_SPACE_REG_EXP_START as INV_START } from "../../constants.js";
10
10
  import { Dom } from "../../dom/dom.js";
11
+ export * from "./composed-ranges.js";
11
12
  export * from "./move-node-inside-start.js";
12
13
  export * from "./move-the-node-along-the-edge-outward.js";
13
14
  /**
@@ -37,6 +37,29 @@ export declare class Selection implements ISelect {
37
37
  * Return current selection object
38
38
  */
39
39
  get sel(): ISelect['sel'];
40
+ /**
41
+ * Selection of a shadow tree in a browser without `ShadowRoot.getSelection`.
42
+ *
43
+ * `ShadowRoot.getSelection()` is a non-standard method that WebKit never
44
+ * implemented, and `window.getSelection()` stops at the shadow boundary: for
45
+ * a caret inside the shadow tree it reports the host, or the document
46
+ * itself. Everything downstream then works on the wrong tree, which is why
47
+ * Backspace used to throw `HierarchyRequestError` and Enter did nothing when
48
+ * the editor lived in a shadow root in Safari.
49
+ *
50
+ * The standard replacement is `Selection.getComposedRanges()`, which does
51
+ * reach into a shadow tree it is given. It hands back `StaticRange`s, and
52
+ * the editor mutates ranges constantly, so they are turned into live ones.
53
+ *
54
+ * Reads are answered from those ranges; everything else, including the
55
+ * writes (`addRange`, `removeAllRanges`, `setBaseAndExtent`), goes to the
56
+ * real selection, which does accept nodes inside a shadow tree. Reading is
57
+ * the only side the browser blocks.
58
+ *
59
+ * @returns `null` when the browser offers neither API, so the caller falls
60
+ * back to the plain window selection.
61
+ */
62
+ private __composedSelection;
40
63
  /**
41
64
  * Return first selected range or create new
42
65
  */
@@ -24,7 +24,7 @@ import { assert } from "../helpers/utils/assert.js";
24
24
  import { moveTheNodeAlongTheEdgeOutward } from "./helpers/move-the-node-along-the-edge-outward.js";
25
25
  import "./interface.js";
26
26
  import { CommitStyle } from "./style/commit-style.js";
27
- import { cursorInTheEdgeOfString, findCorrectCurrentNode } from "./helpers/index.js";
27
+ import { composedRanges, cursorInTheEdgeOfString, findCorrectCurrentNode } from "./helpers/index.js";
28
28
  export class Selection {
29
29
  constructor(jodit) {
30
30
  this.jodit = jodit;
@@ -69,12 +69,88 @@ export class Selection {
69
69
  * Return current selection object
70
70
  */
71
71
  get sel() {
72
- if (this.j.o.shadowRoot &&
73
- isFunction(this.j.o.shadowRoot.getSelection)) {
74
- return this.j.o.shadowRoot.getSelection();
72
+ const { shadowRoot } = this.j.o;
73
+ if (shadowRoot) {
74
+ if (isFunction(shadowRoot.getSelection)) {
75
+ return shadowRoot.getSelection();
76
+ }
77
+ const composed = this.__composedSelection(shadowRoot);
78
+ if (composed) {
79
+ return composed;
80
+ }
75
81
  }
76
82
  return this.win.getSelection();
77
83
  }
84
+ /**
85
+ * Selection of a shadow tree in a browser without `ShadowRoot.getSelection`.
86
+ *
87
+ * `ShadowRoot.getSelection()` is a non-standard method that WebKit never
88
+ * implemented, and `window.getSelection()` stops at the shadow boundary: for
89
+ * a caret inside the shadow tree it reports the host, or the document
90
+ * itself. Everything downstream then works on the wrong tree, which is why
91
+ * Backspace used to throw `HierarchyRequestError` and Enter did nothing when
92
+ * the editor lived in a shadow root in Safari.
93
+ *
94
+ * The standard replacement is `Selection.getComposedRanges()`, which does
95
+ * reach into a shadow tree it is given. It hands back `StaticRange`s, and
96
+ * the editor mutates ranges constantly, so they are turned into live ones.
97
+ *
98
+ * Reads are answered from those ranges; everything else, including the
99
+ * writes (`addRange`, `removeAllRanges`, `setBaseAndExtent`), goes to the
100
+ * real selection, which does accept nodes inside a shadow tree. Reading is
101
+ * the only side the browser blocks.
102
+ *
103
+ * @returns `null` when the browser offers neither API, so the caller falls
104
+ * back to the plain window selection.
105
+ */
106
+ __composedSelection(shadowRoot) {
107
+ const selection = this.win.getSelection();
108
+ if (!selection) {
109
+ return null;
110
+ }
111
+ // Firefox has no `ShadowRoot.getSelection` either, but its window
112
+ // selection does see into a shadow tree. Where the plain selection
113
+ // already points at the right node there is nothing to work around, and
114
+ // taking it over would change behaviour that works today.
115
+ const { anchorNode } = selection;
116
+ if (anchorNode && Dom.isOrContains(shadowRoot, anchorNode)) {
117
+ return null;
118
+ }
119
+ const ranges = composedRanges(selection, shadowRoot);
120
+ if (!ranges) {
121
+ return null;
122
+ }
123
+ const live = ranges.map(staticRange => {
124
+ const range = this.createRange();
125
+ range.setStart(staticRange.startContainer, staticRange.startOffset);
126
+ range.setEnd(staticRange.endContainer, staticRange.endOffset);
127
+ return range;
128
+ });
129
+ const first = live[0];
130
+ const overrides = {
131
+ rangeCount: live.length,
132
+ getRangeAt: (index) => live[index],
133
+ isCollapsed: live.every(range => range.collapsed),
134
+ // Without these the editor asks the window selection where the
135
+ // caret is, is told "outside the editable area", and resets the
136
+ // caret to the start of it — which is the focus loss that shows up
137
+ // right after a successful Backspace.
138
+ anchorNode: first ? first.startContainer : null,
139
+ anchorOffset: first ? first.startOffset : 0,
140
+ focusNode: first ? first.endContainer : null,
141
+ focusOffset: first ? first.endOffset : 0,
142
+ toString: () => live.map(range => range.toString()).join('')
143
+ };
144
+ return new Proxy(selection, {
145
+ get(target, property) {
146
+ if (isString(property) && property in overrides) {
147
+ return overrides[property];
148
+ }
149
+ const value = Reflect.get(target, property, target);
150
+ return isFunction(value) ? value.bind(target) : value;
151
+ }
152
+ });
153
+ }
78
154
  /**
79
155
  * Return first selected range or create new
80
156
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jodit",
3
- "version": "4.17.1",
3
+ "version": "4.17.2",
4
4
  "description": "Jodit is an awesome and useful wysiwyg editor with filebrowser",
5
5
  "main": "esm/index.js",
6
6
  "types": "types/index.d.ts",
@@ -0,0 +1,21 @@
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 selection
8
+ */
9
+ import type { Nullable } from "../../../types/index";
10
+ /**
11
+ * Ranges of a selection that lies inside the given shadow tree.
12
+ *
13
+ * `window.getSelection()` stops at the shadow boundary and reports the host
14
+ * instead of the node the caret is really in. `getComposedRanges` is the
15
+ * standard way to ask past that boundary.
16
+ *
17
+ * @returns `null` when the browser has no `getComposedRanges`, or when neither
18
+ * call shape returned ranges that point inside the shadow tree — in which case
19
+ * there is nothing better to offer than the plain window selection.
20
+ */
21
+ export declare function composedRanges(selection: Selection, shadowRoot: ShadowRoot): Nullable<StaticRange[]>;
@@ -3,6 +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 * from "./composed-ranges";
6
7
  export * from "./move-node-inside-start";
7
8
  export * from "./move-the-node-along-the-edge-outward";
8
9
  /**
@@ -37,6 +37,29 @@ export declare class Selection implements ISelect {
37
37
  * Return current selection object
38
38
  */
39
39
  get sel(): ISelect['sel'];
40
+ /**
41
+ * Selection of a shadow tree in a browser without `ShadowRoot.getSelection`.
42
+ *
43
+ * `ShadowRoot.getSelection()` is a non-standard method that WebKit never
44
+ * implemented, and `window.getSelection()` stops at the shadow boundary: for
45
+ * a caret inside the shadow tree it reports the host, or the document
46
+ * itself. Everything downstream then works on the wrong tree, which is why
47
+ * Backspace used to throw `HierarchyRequestError` and Enter did nothing when
48
+ * the editor lived in a shadow root in Safari.
49
+ *
50
+ * The standard replacement is `Selection.getComposedRanges()`, which does
51
+ * reach into a shadow tree it is given. It hands back `StaticRange`s, and
52
+ * the editor mutates ranges constantly, so they are turned into live ones.
53
+ *
54
+ * Reads are answered from those ranges; everything else, including the
55
+ * writes (`addRange`, `removeAllRanges`, `setBaseAndExtent`), goes to the
56
+ * real selection, which does accept nodes inside a shadow tree. Reading is
57
+ * the only side the browser blocks.
58
+ *
59
+ * @returns `null` when the browser offers neither API, so the caller falls
60
+ * back to the plain window selection.
61
+ */
62
+ private __composedSelection;
40
63
  /**
41
64
  * Return first selected range or create new
42
65
  */