jodit 4.12.35 → 4.12.37

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 (79) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/es2015/jodit.css +1 -1
  3. package/es2015/jodit.fat.min.js +3 -3
  4. package/es2015/jodit.js +110 -49
  5. package/es2015/jodit.min.js +3 -3
  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 +3 -3
  13. package/es2018/jodit.min.js +3 -3
  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 +4 -4
  18. package/es2021/jodit.js +109 -47
  19. package/es2021/jodit.min.js +4 -4
  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 +4 -4
  28. package/es2021.en/jodit.js +109 -47
  29. package/es2021.en/jodit.min.js +5 -5
  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 +259 -195
  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/config.d.ts +14 -6
  51. package/esm/core/constants.js +1 -1
  52. package/esm/core/dom/dom.d.ts +35 -12
  53. package/esm/core/dom/dom.js +42 -20
  54. package/esm/core/dom/lazy-walker.d.ts +18 -1
  55. package/esm/core/dom/lazy-walker.js +23 -3
  56. package/esm/core/selection/helpers/index.d.ts +3 -1
  57. package/esm/core/selection/helpers/index.js +3 -1
  58. package/esm/core/selection/selection.d.ts +14 -0
  59. package/esm/core/selection/selection.js +19 -8
  60. package/esm/core/selection/style/api/is-same-attributes.d.ts +4 -2
  61. package/esm/core/selection/style/api/is-same-attributes.js +3 -4
  62. package/esm/core/selection/style/api/is-suit-element.d.ts +2 -1
  63. package/esm/core/selection/style/api/is-suit-element.js +2 -1
  64. package/esm/core/selection/style/api/list/toggle-ordered-list.js +7 -0
  65. package/esm/core/selection/style/api/toggle-attributes.js +3 -1
  66. package/esm/core/selection/style/commit-style.d.ts +1 -1
  67. package/esm/core/selection/style/commit-style.js +2 -2
  68. package/esm/plugins/source/config.d.ts +14 -6
  69. package/esm/plugins/source/editor/engines/ace.js +5 -0
  70. package/package.json +1 -1
  71. package/types/config.d.ts +14 -6
  72. package/types/core/dom/dom.d.ts +35 -12
  73. package/types/core/dom/lazy-walker.d.ts +18 -1
  74. package/types/core/selection/helpers/index.d.ts +3 -1
  75. package/types/core/selection/selection.d.ts +14 -0
  76. package/types/core/selection/style/api/is-same-attributes.d.ts +4 -2
  77. package/types/core/selection/style/api/is-suit-element.d.ts +2 -1
  78. package/types/core/selection/style/commit-style.d.ts +1 -1
  79. package/types/plugins/source/config.d.ts +14 -6
@@ -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.12.35
4
+ * Version: v4.12.37
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.12.35
4
+ * Version: v4.12.37
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.12.35
4
+ * Version: v4.12.37
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.12.35
4
+ * Version: v4.12.37
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.12.35
4
+ * Version: v4.12.37
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.12.35
4
+ * Version: v4.12.37
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.12.35
4
+ * Version: v4.12.37
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.12.35
4
+ * Version: v4.12.37
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.12.35
4
+ * Version: v4.12.37
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
package/esm/config.d.ts CHANGED
@@ -1678,15 +1678,22 @@ interface Config {
1678
1678
  */
1679
1679
  sourceEditor: 'area' | 'ace' | ((jodit: IJodit) => ISourceEditor);
1680
1680
  /**
1681
- * Options for [ace](https://ace.c9.io/#config) editor
1681
+ * Options for [ace](https://ace.c9.io/#config) editor.
1682
+ *
1683
+ * Besides the named keys below, any other native ACE option is
1684
+ * forwarded to `editor.setOptions()` as is, so e.g. `fontSize`,
1685
+ * `tabSize` or `useSoftTabs` work too.
1682
1686
  *
1683
1687
  * ```js
1684
1688
  * Jodit.make('#editor', {
1685
- * showGutter: true,
1686
- * theme: 'ace/theme/idle_fingers',
1687
- * mode: 'ace/mode/html',
1688
- * wrap: true,
1689
- * highlightActiveLine: true
1689
+ * sourceEditorNativeOptions: {
1690
+ * showGutter: true,
1691
+ * theme: 'ace/theme/idle_fingers',
1692
+ * mode: 'ace/mode/html',
1693
+ * wrap: true,
1694
+ * highlightActiveLine: true,
1695
+ * fontSize: '16px'
1696
+ * }
1690
1697
  * })
1691
1698
  * ```
1692
1699
  */
@@ -1696,6 +1703,7 @@ interface Config {
1696
1703
  mode: string;
1697
1704
  wrap: string | boolean | number;
1698
1705
  highlightActiveLine: boolean;
1706
+ [option: string]: string | boolean | number | undefined;
1699
1707
  };
1700
1708
  /**
1701
1709
  * Beautify HTML then it possible
@@ -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.35";
6
+ export const APP_VERSION = "4.12.37";
7
7
  // prettier-ignore
8
8
  export const ES = "es2020";
9
9
  export const IS_ES_MODERN = true;
@@ -34,7 +34,11 @@ export declare class Dom {
34
34
  */
35
35
  static unwrap(node: Node): void;
36
36
  /**
37
- * Call functions for all nodes between `start` and `end`
37
+ * Call callback for all nodes between `start` and `end` in document order
38
+ * (`start` and `end` are not included). Iteration stops when the callback returns `true`.
39
+ *
40
+ * `end` must be positioned after `start` in the document (e.g. selection markers),
41
+ * otherwise iteration will stop only at the end of the tree.
38
42
  */
39
43
  static between(start: Node, end: Node, callback: (node: Node) => void | boolean): void;
40
44
  /**
@@ -46,14 +50,18 @@ export declare class Dom {
46
50
  * @param notMoveContent - false - Move content from elm to newTagName
47
51
  * @example
48
52
  * ```javascript
49
- * Jodit.modules.Dom.replace(parent.editor.getElementsByTagName('span')[0], 'p');
50
- * // Replace the first <span> element to the < p >
53
+ * Jodit.modules.Dom.replace(
54
+ * parent.editor.getElementsByTagName('span')[0],
55
+ * 'p',
56
+ * parent.createInside
57
+ * );
58
+ * // Replace the first <span> element to the <p>
51
59
  * ```
52
60
  */
53
- static replace<T extends HTMLElement>(elm: Node, newTagName: HTMLTagNames): T;
54
61
  static replace<T extends HTMLElement>(elm: Node, newTagName: HTMLTagNames, create: ICreate, withAttributes?: boolean, notMoveContent?: boolean): T;
55
- static replace<T extends Node>(elm: Node, newTagName: T): T;
56
- static replace<T extends Node>(elm: Node, newTagName: T | string, create?: ICreate, withAttributes?: boolean, notMoveContent?: boolean): T;
62
+ static replace<T extends Node>(elm: Node, newElement: T, create?: ICreate, withAttributes?: boolean, notMoveContent?: boolean): T;
63
+ static replace<T extends Node>(elm: Node, html: string, create: ICreate, withAttributes?: boolean, notMoveContent?: boolean): T;
64
+ static replace<T extends Node>(elm: Node, newTagNameOrElement: HTMLTagNames | T, create: ICreate, withAttributes?: boolean, notMoveContent?: boolean): T;
57
65
  /**
58
66
  * Checks whether the Node text and blank (in this case it may contain invisible auxiliary characters ,
59
67
  * it is also empty )
@@ -80,9 +88,9 @@ export declare class Dom {
80
88
  */
81
89
  static isCell(elm: unknown): elm is HTMLTableCellElement;
82
90
  /**
83
- * Check if element is a list element UL or OL
91
+ * Check if element is a list element UL or OL
84
92
  */
85
- static isList(elm: Nullable<Node>): elm is HTMLOListElement;
93
+ static isList(elm: Nullable<Node>): elm is HTMLUListElement | HTMLOListElement;
86
94
  /**
87
95
  * Check if element is a part of list element LI
88
96
  */
@@ -103,7 +111,7 @@ export declare class Dom {
103
111
  /**
104
112
  * Check if element is comment node
105
113
  */
106
- static isComment(node: Node): node is Comment;
114
+ static isComment(node: Nullable<Node> | false): node is Comment;
107
115
  /**
108
116
  * Check if element is element node
109
117
  */
@@ -147,7 +155,9 @@ export declare class Dom {
147
155
  */
148
156
  static find<T extends Node = Node>(node: Node, condition: NodeCondition, root: HTMLElement, leftToRight?: boolean, withChild?: boolean): Nullable<T>;
149
157
  /**
150
- * Find next/prev node what `condition(next) === true`
158
+ * Lazily iterate over all nodes what follow after `start` (in document order
159
+ * for `leftToRight = true`, in reverse order otherwise) inside `root`.
160
+ * Ancestors of `start` are not yielded.
151
161
  */
152
162
  static nextGen(start: Node, root: HTMLElement, leftToRight?: boolean, withChild?: boolean): Generator<Node>;
153
163
  /**
@@ -159,7 +169,7 @@ export declare class Dom {
159
169
  * ```javascript
160
170
  * Jodit.modules.Dom.each(editor.s.current(), function (node) {
161
171
  * if (node.nodeType === Node.TEXT_NODE) {
162
- * node.nodeValue = node.nodeValue.replace(Jodit.INVISIBLE_SPACE_REG_EX, '') // remove all of the text element codes invisible character
172
+ * node.nodeValue = node.nodeValue.replace(Jodit.INVISIBLE_SPACE_REG_EXP(), '') // remove all of the text element codes invisible character
163
173
  * }
164
174
  * });
165
175
  * ```
@@ -183,9 +193,16 @@ export declare class Dom {
183
193
  * Returns the nearest non-empty neighbor
184
194
  */
185
195
  static findNotEmptyNeighbor(node: Node, left: boolean, root: HTMLElement): Nullable<Node>;
196
+ /**
197
+ * Returns the previous (`left = true`) or next sibling of the node
198
+ */
186
199
  static sibling(node: Node, left?: boolean): Nullable<Node>;
187
200
  /**
188
201
  * It goes through all the elements in ascending order, and checks to see if they meet the predetermined condition
202
+ *
203
+ * The condition is checked for the `node` itself too. The `root` reached
204
+ * while ascending is checked only when `checkRoot = true`
205
+ * (but if `node === root` it is checked in any case).
189
206
  */
190
207
  static up<T extends HTMLElement>(node: Nullable<Node>, condition: NodeCondition, root?: Node, checkRoot?: boolean): Nullable<T>;
191
208
  /**
@@ -232,6 +249,12 @@ export declare class Dom {
232
249
  * Safe remove element from DOM
233
250
  */
234
251
  static safeRemove(...nodes: unknown[]): void;
252
+ /**
253
+ * Insert a node into the range and collapse the range to the start of
254
+ * the inserted content. Unlike the native `Range.insertNode` it does not
255
+ * split inseparable elements (BR, HR, IMG etc.) and removes empty text
256
+ * nodes produced by the split of a text container.
257
+ */
235
258
  static safeInsertNode(range: Range, node: Node): void;
236
259
  /**
237
260
  * Hide element
@@ -259,7 +282,7 @@ export declare class Dom {
259
282
  */
260
283
  static isMarker(elm: Nullable<Node>): elm is HTMLElement;
261
284
  /**
262
- * Replace temporary elements from string
285
+ * Unwrap temporary elements inside a HTML string (keeps their content)
263
286
  */
264
287
  static replaceTemporaryFromString(value: string): string;
265
288
  /**
@@ -19,6 +19,7 @@ import { css } from "../helpers/utils/css.js";
19
19
  import { dataBind } from "../helpers/utils/data-bind.js";
20
20
  import { error } from "../helpers/utils/error/index.js";
21
21
  import { call } from "../helpers/utils/utils.js";
22
+ const TEMP_ELEMENT_REG_EXP = new RegExp(`<([a-z]+)[^>]+${TEMP_ATTR}[^>]*>(.+?)</\\1>`, 'gi');
22
23
  /**
23
24
  * Module for working with DOM
24
25
  */
@@ -129,7 +130,11 @@ export class Dom {
129
130
  }
130
131
  }
131
132
  /**
132
- * Call functions for all nodes between `start` and `end`
133
+ * Call callback for all nodes between `start` and `end` in document order
134
+ * (`start` and `end` are not included). Iteration stops when the callback returns `true`.
135
+ *
136
+ * `end` must be positioned after `start` in the document (e.g. selection markers),
137
+ * otherwise iteration will stop only at the end of the tree.
133
138
  */
134
139
  static between(start, end, callback) {
135
140
  let next = start;
@@ -141,6 +146,10 @@ export class Dom {
141
146
  if (!step) {
142
147
  while (next && !next.nextSibling) {
143
148
  next = next.parentNode;
149
+ // `end` was reached while ascending - all nodes between were visited
150
+ if (next === end) {
151
+ return;
152
+ }
144
153
  }
145
154
  step = next === null || next === void 0 ? void 0 : next.nextSibling;
146
155
  }
@@ -240,7 +249,7 @@ export class Dom {
240
249
  return (Dom.isNode(elm) && (elm.nodeName === 'TD' || elm.nodeName === 'TH'));
241
250
  }
242
251
  /**
243
- * Check if element is a list element UL or OL
252
+ * Check if element is a list element UL or OL
244
253
  */
245
254
  static isList(elm) {
246
255
  return Dom.isTag(elm, LIST_TAGS);
@@ -294,12 +303,13 @@ export class Dom {
294
303
  * Check if element is document fragment
295
304
  */
296
305
  static isFragment(node) {
297
- var _a;
298
306
  if (!Dom.isNode(node)) {
299
307
  return false;
300
308
  }
301
- const win = (_a = node.ownerDocument) === null || _a === void 0 ? void 0 : _a.defaultView;
302
- return Boolean(win && node.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
309
+ // no `defaultView` requirement fragments of an inert document
310
+ // (`template.content`, `DOMParser`, `implementation.createHTMLDocument`)
311
+ // are still fragments
312
+ return node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
303
313
  }
304
314
  /**
305
315
  * Check if element is HTMLElement node
@@ -424,7 +434,9 @@ export class Dom {
424
434
  return null;
425
435
  }
426
436
  /**
427
- * Find next/prev node what `condition(next) === true`
437
+ * Lazily iterate over all nodes what follow after `start` (in document order
438
+ * for `leftToRight = true`, in reverse order otherwise) inside `root`.
439
+ * Ancestors of `start` are not yielded.
428
440
  */
429
441
  static *nextGen(start, root, leftToRight = true, withChild = true) {
430
442
  const stack = [];
@@ -434,9 +446,10 @@ export class Dom {
434
446
  ? currentNode.nextSibling
435
447
  : currentNode.previousSibling;
436
448
  while (next) {
437
- stack.unshift(next);
449
+ stack.push(next);
438
450
  next = leftToRight ? next.nextSibling : next.previousSibling;
439
451
  }
452
+ stack.reverse();
440
453
  yield* this.runInStack(start, stack, leftToRight, withChild);
441
454
  currentNode = currentNode.parentNode;
442
455
  } while (currentNode && currentNode !== root);
@@ -451,7 +464,7 @@ export class Dom {
451
464
  * ```javascript
452
465
  * Jodit.modules.Dom.each(editor.s.current(), function (node) {
453
466
  * if (node.nodeType === Node.TEXT_NODE) {
454
- * node.nodeValue = node.nodeValue.replace(Jodit.INVISIBLE_SPACE_REG_EX, '') // remove all of the text element codes invisible character
467
+ * node.nodeValue = node.nodeValue.replace(Jodit.INVISIBLE_SPACE_REG_EXP(), '') // remove all of the text element codes invisible character
455
468
  * }
456
469
  * });
457
470
  * ```
@@ -539,11 +552,18 @@ export class Dom {
539
552
  (!(Dom.isText(n) || Dom.isComment(n)) ||
540
553
  trim((n === null || n === void 0 ? void 0 : n.nodeValue) || '').length)), root);
541
554
  }
555
+ /**
556
+ * Returns the previous (`left = true`) or next sibling of the node
557
+ */
542
558
  static sibling(node, left) {
543
559
  return left ? node.previousSibling : node.nextSibling;
544
560
  }
545
561
  /**
546
562
  * It goes through all the elements in ascending order, and checks to see if they meet the predetermined condition
563
+ *
564
+ * The condition is checked for the `node` itself too. The `root` reached
565
+ * while ascending is checked only when `checkRoot = true`
566
+ * (but if `node === root` it is checked in any case).
547
567
  */
548
568
  static up(node, condition, root, checkRoot = false) {
549
569
  let start = node;
@@ -689,6 +709,12 @@ export class Dom {
689
709
  node.parentNode &&
690
710
  node.parentNode.removeChild(node));
691
711
  }
712
+ /**
713
+ * Insert a node into the range and collapse the range to the start of
714
+ * the inserted content. Unlike the native `Range.insertNode` it does not
715
+ * split inseparable elements (BR, HR, IMG etc.) and removes empty text
716
+ * nodes produced by the split of a text container.
717
+ */
692
718
  static safeInsertNode(range, node) {
693
719
  range.collapsed || range.deleteContents();
694
720
  const child = Dom.isFragment(node) ? node.lastChild : node;
@@ -730,22 +756,18 @@ export class Dom {
730
756
  }
731
757
  }
732
758
  static isTag(node, tagNames) {
759
+ if (Array.isArray(tagNames)) {
760
+ throw new TypeError('Dom.isTag does not support array');
761
+ }
733
762
  if (!this.isElement(node)) {
734
763
  return false;
735
764
  }
736
765
  const nameL = node.tagName.toLowerCase();
737
- const nameU = node.tagName.toUpperCase();
738
766
  if (tagNames instanceof Set) {
739
- return tagNames.has(nameL) || tagNames.has(nameU);
740
- }
741
- if (Array.isArray(tagNames)) {
742
- throw new TypeError('Dom.isTag does not support array');
743
- }
744
- const tags = tagNames;
745
- if (nameL === tags || nameU === tags) {
746
- return true;
767
+ return (tagNames.has(nameL) ||
768
+ tagNames.has(node.tagName.toUpperCase()));
747
769
  }
748
- return false;
770
+ return (nameL === tagNames || node.tagName.toUpperCase() === tagNames);
749
771
  }
750
772
  /**
751
773
  * Marks an item as temporary
@@ -773,10 +795,10 @@ export class Dom {
773
795
  elm.hasAttribute('data-' + MARKER_CLASS));
774
796
  }
775
797
  /**
776
- * Replace temporary elements from string
798
+ * Unwrap temporary elements inside a HTML string (keeps their content)
777
799
  */
778
800
  static replaceTemporaryFromString(value) {
779
- return value.replace(/<([a-z]+)[^>]+data-jodit-temp[^>]+>(.+?)<\/\1>/gi, '$2');
801
+ return value.replace(TEMP_ELEMENT_REG_EXP, '$2');
780
802
  }
781
803
  /**
782
804
  * Get temporary list
@@ -8,6 +8,16 @@
8
8
  */
9
9
  import type { IAsync, IDestructible } from "../../types/index";
10
10
  import { Eventify } from "../event-emitter/eventify";
11
+ /**
12
+ * Walks the DOM tree in small chunks between browser tasks so that even huge
13
+ * documents do not block the main thread.
14
+ *
15
+ * Events:
16
+ * - `visit` - is called for every node; return `true` to mark the pass as
17
+ * "had effect" (it will be passed into the `end` event)
18
+ * - `break` - the pass was interrupted via [[LazyWalker.break]]
19
+ * - `end` - the pass is finished, receives `true` if some `visit` handler returned `true`
20
+ */
11
21
  export declare class LazyWalker extends Eventify<{
12
22
  visit: (node: Node) => boolean;
13
23
  break: (reason?: string) => void;
@@ -16,17 +26,24 @@ export declare class LazyWalker extends Eventify<{
16
26
  private readonly async;
17
27
  private readonly options;
18
28
  private workNodes;
29
+ /**
30
+ * Starts a new pass over the `root` tree.
31
+ * If a previous pass is still running it will be stopped first.
32
+ */
19
33
  setWork(root: Node): this;
20
34
  private hadAffect;
21
35
  private isWorked;
22
36
  private isFinished;
23
37
  constructor(async: IAsync, options?: {
38
+ /** Visit only nodes with this `nodeType` (e.g. `Node.ELEMENT_NODE`) */
24
39
  readonly whatToShow?: number;
40
+ /** Walk the tree from the last node to the first */
25
41
  readonly reverse?: boolean;
42
+ /** How many nodes are processed per one chunk. Default: 50 */
26
43
  readonly timeoutChunkSize?: number;
44
+ /** Delay between chunks in ms */
27
45
  readonly timeout?: number;
28
46
  });
29
- private idleId;
30
47
  private __schedulerController;
31
48
  private _requestStarting;
32
49
  break(reason?: string): void;
@@ -16,13 +16,32 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
16
16
  import { autobind } from "../decorators/autobind/autobind.js";
17
17
  import { Dom } from "./dom.js";
18
18
  import { Eventify } from "../event-emitter/eventify.js";
19
+ /**
20
+ * Walks the DOM tree in small chunks between browser tasks so that even huge
21
+ * documents do not block the main thread.
22
+ *
23
+ * Events:
24
+ * - `visit` - is called for every node; return `true` to mark the pass as
25
+ * "had effect" (it will be passed into the `end` event)
26
+ * - `break` - the pass was interrupted via [[LazyWalker.break]]
27
+ * - `end` - the pass is finished, receives `true` if some `visit` handler returned `true`
28
+ */
19
29
  export class LazyWalker extends Eventify {
30
+ /**
31
+ * Starts a new pass over the `root` tree.
32
+ * If a previous pass is still running it will be stopped first.
33
+ */
20
34
  setWork(root) {
21
35
  if (this.isWorked) {
22
36
  this.break();
23
37
  }
38
+ else {
39
+ // cancel a not yet started pass scheduled by the previous `setWork` call
40
+ this.stop();
41
+ }
24
42
  this.workNodes = Dom.eachGen(root, !this.options.reverse);
25
43
  this.isFinished = false;
44
+ this.hadAffect = false;
26
45
  this._requestStarting();
27
46
  return this;
28
47
  }
@@ -34,7 +53,6 @@ export class LazyWalker extends Eventify {
34
53
  this.hadAffect = false;
35
54
  this.isWorked = false;
36
55
  this.isFinished = false;
37
- this.idleId = 0;
38
56
  this.__schedulerController = null;
39
57
  }
40
58
  _requestStarting() {
@@ -60,10 +78,12 @@ export class LazyWalker extends Eventify {
60
78
  }
61
79
  }
62
80
  stop() {
81
+ var _a;
63
82
  this.isWorked = false;
64
83
  this.isFinished = true;
65
84
  this.workNodes = null;
66
- this.async.cancelIdleCallback(this.idleId);
85
+ (_a = this.__schedulerController) === null || _a === void 0 ? void 0 : _a.abort();
86
+ this.__schedulerController = null;
67
87
  }
68
88
  destruct() {
69
89
  super.destruct();
@@ -75,7 +95,7 @@ export class LazyWalker extends Eventify {
75
95
  this.isWorked = true;
76
96
  let count = 0;
77
97
  const chunkSize = (_a = this.options.timeoutChunkSize) !== null && _a !== void 0 ? _a : 50;
78
- while (!this.isFinished && count <= chunkSize) {
98
+ while (!this.isFinished && count < chunkSize) {
79
99
  const item = this.workNodes.next();
80
100
  count += 1;
81
101
  if (this.visitNode(item.value)) {
@@ -6,7 +6,9 @@
6
6
  export * from "./move-node-inside-start";
7
7
  export * from "./move-the-node-along-the-edge-outward";
8
8
  /**
9
- * Check if the cursor is at the edge of the string
9
+ * Despite the name, returns `true` when the cursor is NOT at the checked edge
10
+ * of the text node: there is significant (non invisible-space) text between
11
+ * the offset and the start (`start = true`) or the end (`end = true`) of the string.
10
12
  * @private
11
13
  */
12
14
  export declare function cursorInTheEdgeOfString(container: Node, offset: number, start: boolean, end: boolean): boolean;
@@ -11,7 +11,9 @@ import { Dom } from "../../dom/dom.js";
11
11
  export * from "./move-node-inside-start.js";
12
12
  export * from "./move-the-node-along-the-edge-outward.js";
13
13
  /**
14
- * Check if the cursor is at the edge of the string
14
+ * Despite the name, returns `true` when the cursor is NOT at the checked edge
15
+ * of the text node: there is significant (non invisible-space) text between
16
+ * the offset and the start (`start = true`) or the end (`end = true`) of the string.
15
17
  * @private
16
18
  */
17
19
  export function cursorInTheEdgeOfString(container, offset, start, end) {
@@ -90,6 +90,11 @@ export declare class Selection implements ISelect {
90
90
  * Restores user selections using marker invisible elements in the DOM.
91
91
  */
92
92
  restore(): void;
93
+ /**
94
+ * Inserts invisible fake nodes on the boundaries of the current selection
95
+ * and returns them. Unlike [[Select.save]] the selection stays valid while
96
+ * the DOM around it is being modified. Restore it later with [[Select.restoreFakes]].
97
+ */
93
98
  fakes(): [
94
99
  ] | [
95
100
  Node
@@ -97,6 +102,10 @@ export declare class Selection implements ISelect {
97
102
  Node,
98
103
  Node
99
104
  ];
105
+ /**
106
+ * Restores the selection previously saved with [[Select.fakes]]
107
+ * and removes the fake nodes (disconnected fakes are ignored).
108
+ */
100
109
  restoreFakes(fakes: [
101
110
  ] | [
102
111
  Node
@@ -244,5 +253,10 @@ export declare class Selection implements ISelect {
244
253
  * Split selection on two parts: left and right
245
254
  */
246
255
  splitSelection(currentBox: HTMLElement, edge?: Node): Nullable<Element>;
256
+ /**
257
+ * Expands the non-collapsed selection outward: boundaries positioned on the
258
+ * edge of their parents are moved out of them (e.g. `<p><b>|test|</b></p>`
259
+ * becomes `<p>|<b>test</b>|</p>`)
260
+ */
247
261
  expandSelection(): this;
248
262
  }
@@ -178,7 +178,7 @@ export class Selection {
178
178
  * Remove all markers
179
179
  */
180
180
  removeMarkers() {
181
- Dom.safeRemove.apply(null, this.markers);
181
+ Dom.safeRemove(...this.markers);
182
182
  }
183
183
  /**
184
184
  * Create marker element
@@ -244,6 +244,11 @@ export class Selection {
244
244
  this.selectRange(range);
245
245
  }
246
246
  }
247
+ /**
248
+ * Inserts invisible fake nodes on the boundaries of the current selection
249
+ * and returns them. Unlike [[Select.save]] the selection stays valid while
250
+ * the DOM around it is being modified. Restore it later with [[Select.restoreFakes]].
251
+ */
247
252
  fakes() {
248
253
  const sel = this.sel;
249
254
  if (!sel || !sel.rangeCount) {
@@ -268,6 +273,10 @@ export class Selection {
268
273
  this.selectRange(range);
269
274
  return result;
270
275
  }
276
+ /**
277
+ * Restores the selection previously saved with [[Select.fakes]]
278
+ * and removes the fake nodes (disconnected fakes are ignored).
279
+ */
271
280
  restoreFakes(fakes) {
272
281
  var _a, _b, _c, _d;
273
282
  const nodes = fakes.filter(n => n.isConnected);
@@ -523,7 +532,6 @@ export class Selection {
523
532
  }
524
533
  const node = this.j.createInside.div();
525
534
  const fragment = this.j.createInside.fragment();
526
- let lastChild;
527
535
  if (!this.isFocused() && this.j.isEditorMode()) {
528
536
  this.focus();
529
537
  this.restore();
@@ -538,12 +546,10 @@ export class Selection {
538
546
  this.j.e.fire('insertHTML', node.innerHTML) === false) {
539
547
  return;
540
548
  }
541
- lastChild = node.lastChild;
542
- if (!lastChild) {
549
+ if (!node.lastChild) {
543
550
  return;
544
551
  }
545
552
  while (node.firstChild) {
546
- lastChild = node.firstChild;
547
553
  fragment.appendChild(node.firstChild);
548
554
  }
549
555
  this.insertNode(fragment, insertCursorAfter, false);
@@ -678,7 +684,8 @@ export class Selection {
678
684
  if (Dom.isEmptyTextNode(start)) {
679
685
  nodes.push(start);
680
686
  }
681
- if (start.firstChild) {
687
+ // `start` can be undefined for an empty root
688
+ if (start === null || start === void 0 ? void 0 : start.firstChild) {
682
689
  nodes.push(start.firstChild);
683
690
  }
684
691
  }
@@ -694,8 +701,7 @@ export class Selection {
694
701
  * @returns true - the cursor is at the end(start) block, null - cursor somewhere outside
695
702
  */
696
703
  cursorInTheEdge(start, parentBlock, fake = null) {
697
- var _a;
698
- const end = !start, range = (_a = this.sel) === null || _a === void 0 ? void 0 : _a.getRangeAt(0);
704
+ const end = !start, sel = this.sel, range = (sel === null || sel === void 0 ? void 0 : sel.rangeCount) ? sel.getRangeAt(0) : null;
699
705
  fake !== null && fake !== void 0 ? fake : (fake = this.current(false));
700
706
  if (!range || !fake || !Dom.isOrContains(parentBlock, fake, true)) {
701
707
  return null;
@@ -1076,6 +1082,11 @@ export class Selection {
1076
1082
  }
1077
1083
  return currentBox.previousElementSibling;
1078
1084
  }
1085
+ /**
1086
+ * Expands the non-collapsed selection outward: boundaries positioned on the
1087
+ * edge of their parents are moved out of them (e.g. `<p><b>|test|</b></p>`
1088
+ * becomes `<p>|<b>test</b>|</p>`)
1089
+ */
1079
1090
  expandSelection() {
1080
1091
  if (this.isCollapsed()) {
1081
1092
  return this;