jodit 4.18.4 → 4.18.6

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 (194) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/es2015/jodit.css +13 -34
  3. package/es2015/jodit.fat.min.css +1 -1
  4. package/es2015/jodit.fat.min.js +11 -11
  5. package/es2015/jodit.js +1029 -205
  6. package/es2015/jodit.min.css +1 -1
  7. package/es2015/jodit.min.js +11 -11
  8. package/es2015/plugins/debug/debug.css +1 -1
  9. package/es2015/plugins/debug/debug.js +1 -1
  10. package/es2015/plugins/debug/debug.min.js +1 -1
  11. package/es2015/plugins/speech-recognize/speech-recognize.css +1 -1
  12. package/es2015/plugins/speech-recognize/speech-recognize.js +1 -1
  13. package/es2015/plugins/speech-recognize/speech-recognize.min.js +1 -1
  14. package/es2018/jodit.fat.min.css +1 -1
  15. package/es2018/jodit.fat.min.js +11 -11
  16. package/es2018/jodit.min.css +1 -1
  17. package/es2018/jodit.min.js +11 -11
  18. package/es2018/plugins/debug/debug.min.js +1 -1
  19. package/es2018/plugins/speech-recognize/speech-recognize.min.js +1 -1
  20. package/es2021/jodit.css +13 -34
  21. package/es2021/jodit.fat.min.css +1 -1
  22. package/es2021/jodit.fat.min.js +15 -15
  23. package/es2021/jodit.js +1021 -203
  24. package/es2021/jodit.min.css +1 -1
  25. package/es2021/jodit.min.js +15 -15
  26. package/es2021/plugins/debug/debug.css +1 -1
  27. package/es2021/plugins/debug/debug.js +1 -1
  28. package/es2021/plugins/debug/debug.min.js +1 -1
  29. package/es2021/plugins/speech-recognize/speech-recognize.css +1 -1
  30. package/es2021/plugins/speech-recognize/speech-recognize.js +1 -1
  31. package/es2021/plugins/speech-recognize/speech-recognize.min.js +1 -1
  32. package/es2021.en/jodit.css +13 -34
  33. package/es2021.en/jodit.fat.min.css +1 -1
  34. package/es2021.en/jodit.fat.min.js +15 -15
  35. package/es2021.en/jodit.js +995 -177
  36. package/es2021.en/jodit.min.css +1 -1
  37. package/es2021.en/jodit.min.js +15 -15
  38. package/es2021.en/plugins/debug/debug.css +1 -1
  39. package/es2021.en/plugins/debug/debug.js +1 -1
  40. package/es2021.en/plugins/debug/debug.min.js +1 -1
  41. package/es2021.en/plugins/speech-recognize/speech-recognize.css +1 -1
  42. package/es2021.en/plugins/speech-recognize/speech-recognize.js +1 -1
  43. package/es2021.en/plugins/speech-recognize/speech-recognize.min.js +1 -1
  44. package/es5/jodit.css +14 -41
  45. package/es5/jodit.fat.min.css +1 -1
  46. package/es5/jodit.fat.min.js +2 -2
  47. package/es5/jodit.js +1152 -254
  48. package/es5/jodit.min.css +3 -3
  49. package/es5/jodit.min.js +2 -2
  50. package/es5/plugins/debug/debug.css +1 -1
  51. package/es5/plugins/debug/debug.js +1 -1
  52. package/es5/plugins/debug/debug.min.js +1 -1
  53. package/es5/plugins/speech-recognize/speech-recognize.css +1 -1
  54. package/es5/plugins/speech-recognize/speech-recognize.js +1 -1
  55. package/es5/plugins/speech-recognize/speech-recognize.min.js +1 -1
  56. package/es5/polyfills.fat.min.js +1 -1
  57. package/es5/polyfills.js +1 -1
  58. package/es5/polyfills.min.js +1 -1
  59. package/esm/config.d.ts +3 -0
  60. package/esm/core/constants.js +1 -1
  61. package/esm/core/helpers/html/apply-styles.js +3 -0
  62. package/esm/core/helpers/html/clean-from-word.js +3 -0
  63. package/esm/core/helpers/html/strip-tags.js +24 -6
  64. package/esm/core/helpers/html/word-lists.d.ts +29 -0
  65. package/esm/core/helpers/html/word-lists.js +158 -0
  66. package/esm/core/selection/helpers/index.d.ts +1 -0
  67. package/esm/core/selection/helpers/index.js +1 -0
  68. package/esm/core/selection/helpers/is-moved.d.ts +15 -0
  69. package/esm/core/selection/helpers/is-moved.js +22 -0
  70. package/esm/core/selection/style/api/list/toggle-ordered-list.js +40 -3
  71. package/esm/core/selection/style/transactions.js +5 -2
  72. package/esm/langs/ar.d.ts +3 -0
  73. package/esm/langs/ar.js +4 -1
  74. package/esm/langs/az.d.ts +3 -0
  75. package/esm/langs/az.js +4 -1
  76. package/esm/langs/cs_cz.d.ts +3 -0
  77. package/esm/langs/cs_cz.js +4 -1
  78. package/esm/langs/de.d.ts +3 -0
  79. package/esm/langs/de.js +4 -1
  80. package/esm/langs/es.d.ts +3 -0
  81. package/esm/langs/es.js +4 -1
  82. package/esm/langs/fa.d.ts +3 -0
  83. package/esm/langs/fa.js +4 -1
  84. package/esm/langs/fi.d.ts +3 -0
  85. package/esm/langs/fi.js +4 -1
  86. package/esm/langs/fr.d.ts +3 -0
  87. package/esm/langs/fr.js +4 -1
  88. package/esm/langs/he.d.ts +3 -0
  89. package/esm/langs/he.js +4 -1
  90. package/esm/langs/hu.d.ts +3 -0
  91. package/esm/langs/hu.js +4 -1
  92. package/esm/langs/id.d.ts +3 -0
  93. package/esm/langs/id.js +4 -1
  94. package/esm/langs/it.d.ts +3 -0
  95. package/esm/langs/it.js +4 -1
  96. package/esm/langs/ja.d.ts +3 -0
  97. package/esm/langs/ja.js +4 -1
  98. package/esm/langs/ko.d.ts +3 -0
  99. package/esm/langs/ko.js +4 -1
  100. package/esm/langs/mn.d.ts +3 -0
  101. package/esm/langs/mn.js +4 -1
  102. package/esm/langs/nl.d.ts +3 -0
  103. package/esm/langs/nl.js +4 -1
  104. package/esm/langs/no.d.ts +3 -0
  105. package/esm/langs/no.js +4 -1
  106. package/esm/langs/pl.d.ts +3 -0
  107. package/esm/langs/pl.js +4 -1
  108. package/esm/langs/pt_br.d.ts +3 -0
  109. package/esm/langs/pt_br.js +4 -1
  110. package/esm/langs/ru.d.ts +3 -0
  111. package/esm/langs/ru.js +4 -1
  112. package/esm/langs/sk.d.ts +3 -0
  113. package/esm/langs/sk.js +4 -1
  114. package/esm/langs/sv.d.ts +3 -0
  115. package/esm/langs/sv.js +4 -1
  116. package/esm/langs/tr.d.ts +3 -0
  117. package/esm/langs/tr.js +4 -1
  118. package/esm/langs/ua.d.ts +3 -0
  119. package/esm/langs/ua.js +4 -1
  120. package/esm/langs/zh_cn.d.ts +3 -0
  121. package/esm/langs/zh_cn.js +4 -1
  122. package/esm/langs/zh_tw.d.ts +3 -0
  123. package/esm/langs/zh_tw.js +4 -1
  124. package/esm/modules/dialog/dialog.d.ts +7 -0
  125. package/esm/modules/dialog/dialog.js +24 -6
  126. package/esm/modules/table/table.d.ts +6 -0
  127. package/esm/modules/table/table.js +18 -26
  128. package/esm/plugins/backspace/cases/check-join-neighbors.js +90 -3
  129. package/esm/plugins/clean-html/clean-html.d.ts +8 -0
  130. package/esm/plugins/clean-html/clean-html.js +21 -1
  131. package/esm/plugins/clean-html/helpers/index.d.ts +1 -0
  132. package/esm/plugins/clean-html/helpers/index.js +1 -0
  133. package/esm/plugins/clean-html/helpers/remove-placeholder-br.d.ts +16 -0
  134. package/esm/plugins/clean-html/helpers/remove-placeholder-br.js +51 -0
  135. package/esm/plugins/delete/delete.d.ts +11 -1
  136. package/esm/plugins/delete/delete.js +92 -16
  137. package/esm/plugins/focus/focus.js +12 -1
  138. package/esm/plugins/iframe/config.d.ts +3 -0
  139. package/esm/plugins/iframe/config.js +0 -27
  140. package/esm/plugins/iframe/iframe.js +8 -1
  141. package/esm/plugins/image-processor/image-processor.js +6 -1
  142. package/esm/plugins/inline-popup/inline-popup.d.ts +19 -0
  143. package/esm/plugins/inline-popup/inline-popup.js +70 -1
  144. package/esm/plugins/ordered-list/config.js +13 -2
  145. package/esm/plugins/ordered-list/start-dialog.d.ts +19 -0
  146. package/esm/plugins/ordered-list/start-dialog.js +58 -0
  147. package/esm/plugins/placeholder/placeholder.js +7 -1
  148. package/esm/plugins/resizer/resizer.d.ts +10 -0
  149. package/esm/plugins/resizer/resizer.js +76 -2
  150. package/esm/plugins/select-cells/select-cells.d.ts +23 -0
  151. package/esm/plugins/select-cells/select-cells.js +75 -1
  152. package/esm/plugins/table-keyboard-navigation/table-keyboard-navigation.js +3 -2
  153. package/package.json +1 -1
  154. package/types/config.d.ts +3 -0
  155. package/types/core/helpers/html/word-lists.d.ts +29 -0
  156. package/types/core/selection/helpers/index.d.ts +1 -0
  157. package/types/core/selection/helpers/is-moved.d.ts +15 -0
  158. package/types/langs/ar.d.ts +3 -0
  159. package/types/langs/az.d.ts +3 -0
  160. package/types/langs/cs_cz.d.ts +3 -0
  161. package/types/langs/de.d.ts +3 -0
  162. package/types/langs/es.d.ts +3 -0
  163. package/types/langs/fa.d.ts +3 -0
  164. package/types/langs/fi.d.ts +3 -0
  165. package/types/langs/fr.d.ts +3 -0
  166. package/types/langs/he.d.ts +3 -0
  167. package/types/langs/hu.d.ts +3 -0
  168. package/types/langs/id.d.ts +3 -0
  169. package/types/langs/it.d.ts +3 -0
  170. package/types/langs/ja.d.ts +3 -0
  171. package/types/langs/ko.d.ts +3 -0
  172. package/types/langs/mn.d.ts +3 -0
  173. package/types/langs/nl.d.ts +3 -0
  174. package/types/langs/no.d.ts +3 -0
  175. package/types/langs/pl.d.ts +3 -0
  176. package/types/langs/pt_br.d.ts +3 -0
  177. package/types/langs/ru.d.ts +3 -0
  178. package/types/langs/sk.d.ts +3 -0
  179. package/types/langs/sv.d.ts +3 -0
  180. package/types/langs/tr.d.ts +3 -0
  181. package/types/langs/ua.d.ts +3 -0
  182. package/types/langs/zh_cn.d.ts +3 -0
  183. package/types/langs/zh_tw.d.ts +3 -0
  184. package/types/modules/dialog/dialog.d.ts +7 -0
  185. package/types/modules/table/table.d.ts +6 -0
  186. package/types/plugins/clean-html/clean-html.d.ts +8 -0
  187. package/types/plugins/clean-html/helpers/index.d.ts +1 -0
  188. package/types/plugins/clean-html/helpers/remove-placeholder-br.d.ts +16 -0
  189. package/types/plugins/delete/delete.d.ts +11 -1
  190. package/types/plugins/iframe/config.d.ts +3 -0
  191. package/types/plugins/inline-popup/inline-popup.d.ts +19 -0
  192. package/types/plugins/ordered-list/start-dialog.d.ts +19 -0
  193. package/types/plugins/resizer/resizer.d.ts +10 -0
  194. package/types/plugins/select-cells/select-cells.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.18.4
4
+ * Version: v4.18.6
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.18.4
4
+ * Version: v4.18.6
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.18.4
4
+ * Version: v4.18.6
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.18.4
4
+ * Version: v4.18.6
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.18.4
4
+ * Version: v4.18.6
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.18.4
4
+ * Version: v4.18.6
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.18.4
4
+ * Version: v4.18.6
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.18.4
4
+ * Version: v4.18.6
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.18.4
4
+ * Version: v4.18.6
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
package/esm/config.d.ts CHANGED
@@ -1404,6 +1404,9 @@ interface Config {
1404
1404
  iframeDoctype: string;
1405
1405
  /**
1406
1406
  * Custom style to be used inside the iframe to display content.
1407
+ * It goes in after `iframeCSSLinks`. The rules Jodit's own plugins need
1408
+ * in the editing document are added apart from it, so a value of your
1409
+ * own doesn't have to repeat them.
1407
1410
  *
1408
1411
  * ```javascript
1409
1412
  * Jodit.make('#editor', {
@@ -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.18.4";
6
+ export const APP_VERSION = "4.18.6";
7
7
  // prettier-ignore
8
8
  export const ES = "es2020";
9
9
  export const IS_ES_MODERN = true;
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { globalDocument, IS_PROD } from "../../constants.js";
10
10
  import { Dom } from "../../dom/dom.js";
11
+ import { readWordListItems, wrapWordListItems } from "./word-lists.js";
11
12
  import { trim } from "../string/trim.js";
12
13
  import { attr } from "../utils/attr.js";
13
14
  import { css } from "../utils/css.js";
@@ -60,6 +61,7 @@ export function applyStyles(html) {
60
61
  iframeDoc.open();
61
62
  iframeDoc.write(html);
62
63
  iframeDoc.close();
64
+ const listItems = readWordListItems(iframeDoc.body);
63
65
  // Word marks its auto-generated list markers (the literal
64
66
  // bullet/number, e.g. `1.` or `·`) with `mso-list:Ignore`.
65
67
  // They are display-only and must not be imported, otherwise
@@ -95,6 +97,7 @@ export function applyStyles(html) {
95
97
  throw e;
96
98
  }
97
99
  }
100
+ wrapWordListItems(listItems);
98
101
  Dom.each(iframeDoc.body, node => {
99
102
  if (Dom.isElement(node)) {
100
103
  const cssText = attr(node, 'style');
@@ -9,6 +9,7 @@
9
9
  import { globalDocument } from "../../constants.js";
10
10
  import { Dom } from "../../dom/dom.js";
11
11
  import { toArray } from "../array/to-array.js";
12
+ import { readWordListItems, wrapWordListItems } from "./word-lists.js";
12
13
  import { trim } from "../string/trim.js";
13
14
  import { attr, attrRaw } from "../utils/attr.js";
14
15
  /**
@@ -25,6 +26,7 @@ export function cleanFromWord(html) {
25
26
  const div = globalDocument.createElement('div');
26
27
  div.innerHTML = html;
27
28
  const marks = [];
29
+ const listItems = readWordListItems(div);
28
30
  if (div.firstChild) {
29
31
  Dom.each(div, node => {
30
32
  if (!node) {
@@ -76,6 +78,7 @@ export function cleanFromWord(html) {
76
78
  });
77
79
  }
78
80
  Dom.safeRemove.apply(null, marks);
81
+ wrapWordListItems(listItems);
79
82
  convertedString = div.innerHTML;
80
83
  }
81
84
  catch (e) { }
@@ -16,8 +16,12 @@ const NEW_LINE_TAGS = new Set([
16
16
  'h4',
17
17
  'h5',
18
18
  'h6',
19
- 'hr'
19
+ 'hr',
20
+ 'ul',
21
+ 'ol',
22
+ 'li'
20
23
  ]);
24
+ const LIST_TAGS = new Set(['ul', 'ol']);
21
25
  const INVISIBLE_TAGS = new Set(['script', 'style']);
22
26
  const ALONE_TAGS = new Set(['br', 'hr', 'input']);
23
27
  /**
@@ -37,6 +41,16 @@ export function stripTags(html, doc = document, exclude = null, blockBr = false)
37
41
  Dom.each(tmp, node => {
38
42
  Dom.isElement(node) && all.push(node);
39
43
  });
44
+ // By default blocks are joined with a single space (single-line
45
+ // plain text). When `blockBr` is set, separate them with a line
46
+ // break instead, so paragraph structure survives — e.g. the
47
+ // "Insert only Text" paste option. See #1232
48
+ const separators = new Set();
49
+ const separator = () => {
50
+ const text = doc.createTextNode(blockBr ? '%%%jodit-single-br%%%' : ' ');
51
+ separators.add(text);
52
+ return text;
53
+ };
40
54
  all.forEach(p => {
41
55
  const pr = p.parentNode;
42
56
  if (!pr) {
@@ -58,16 +72,20 @@ export function stripTags(html, doc = document, exclude = null, blockBr = false)
58
72
  if (!Dom.isTag(p, NEW_LINE_TAGS)) {
59
73
  return;
60
74
  }
75
+ const pv = p.previousSibling;
76
+ // A nested list starts on a new line after its item's own text
77
+ if (Dom.isTag(p, LIST_TAGS) &&
78
+ pv &&
79
+ !separators.has(pv) &&
80
+ !(Dom.isText(pv) && /\s$/.test(pv.nodeValue || ''))) {
81
+ Dom.before(p, separator());
82
+ }
61
83
  const nx = p.nextSibling;
62
84
  if (Dom.isText(nx) && /^\s/.test(nx.nodeValue || '')) {
63
85
  return;
64
86
  }
65
87
  if (nx) {
66
- // By default blocks are joined with a single space (single-line
67
- // plain text). When `blockBr` is set, separate them with a line
68
- // break instead, so paragraph structure survives — e.g. the
69
- // "Insert only Text" paste option. See #1232
70
- Dom.before(nx, doc.createTextNode(blockBr ? '%%%jodit-single-br%%%' : ' '));
88
+ Dom.before(nx, separator());
71
89
  }
72
90
  });
73
91
  return restoreTags(trim(tmp.innerText));
@@ -0,0 +1,29 @@
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
+ * A Word list paragraph, read before its `mso-list` style is lost
8
+ */
9
+ export interface WordListItem {
10
+ paragraph: HTMLElement;
11
+ list: string;
12
+ level: number;
13
+ tag: 'ul' | 'ol';
14
+ listStyleType: string | null;
15
+ start: number;
16
+ }
17
+ /**
18
+ * Finds Word's list paragraphs (`<p style="mso-list:l0 level1 lfo1">`), reads
19
+ * their list, level and type from the `mso-list:Ignore` marker, and removes the
20
+ * marker. Call it before anything strips the `mso-list` styles, then pass the
21
+ * result to {@link wrapWordListItems}
22
+ */
23
+ export declare function readWordListItems(root: HTMLElement): WordListItem[];
24
+ /**
25
+ * Turns the paragraphs found by {@link readWordListItems} into `<ul>`/`<ol>`
26
+ * lists, nesting them by level. Each paragraph becomes an `<li>` without Word's
27
+ * hanging indent
28
+ */
29
+ export declare function wrapWordListItems(items: WordListItem[]): void;
@@ -0,0 +1,158 @@
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 helpers/html
8
+ */
9
+ import { Dom } from "../../dom/dom.js";
10
+ import { toArray } from "../array/to-array.js";
11
+ import { attr } from "../utils/attr.js";
12
+ const ROMAN = {
13
+ i: 1,
14
+ v: 5,
15
+ x: 10,
16
+ l: 50,
17
+ c: 100,
18
+ d: 500,
19
+ m: 1000
20
+ };
21
+ function romanValue(roman) {
22
+ let value = 0;
23
+ for (let i = 0; i < roman.length; i += 1) {
24
+ const current = ROMAN[roman[i]], next = ROMAN[roman[i + 1]] || 0;
25
+ value += current < next ? -current : current;
26
+ }
27
+ return value;
28
+ }
29
+ /**
30
+ * Word's marker text (`1.`, `b)`, `iv.`, `·`, `o`, `§`) decides the list type
31
+ */
32
+ function parseMarker(marker) {
33
+ const text = marker.replace(/\s+/g, '');
34
+ const decimal = /^\(?(?:\d+\.)*(\d+)[.)]?$/.exec(text);
35
+ if (decimal) {
36
+ return { tag: 'ol', listStyleType: null, start: Number(decimal[1]) };
37
+ }
38
+ const letters = /^\(?([a-z]+|[A-Z]+)[.)]$/.exec(text);
39
+ if (letters) {
40
+ const value = letters[1], lower = value.toLowerCase(), upper = value !== lower;
41
+ if (/^[ivxlcdm]+$/.test(lower) && (lower.length > 1 || lower === 'i')) {
42
+ return {
43
+ tag: 'ol',
44
+ listStyleType: upper ? 'upper-roman' : 'lower-roman',
45
+ start: romanValue(lower)
46
+ };
47
+ }
48
+ // Word continues a lettered list with aa, bb, ...
49
+ if (/^([a-z])\1*$/.test(lower)) {
50
+ return {
51
+ tag: 'ol',
52
+ listStyleType: upper ? 'upper-alpha' : 'lower-alpha',
53
+ start: (lower.length - 1) * 26 + lower.charCodeAt(0) - 96
54
+ };
55
+ }
56
+ }
57
+ return { tag: 'ul', listStyleType: null, start: 1 };
58
+ }
59
+ /**
60
+ * Removes the marker and the wrappers it leaves empty
61
+ */
62
+ function removeMarker(marker, paragraph) {
63
+ let parent = marker.parentNode;
64
+ Dom.safeRemove(marker);
65
+ while (parent &&
66
+ parent !== paragraph &&
67
+ Dom.isElement(parent) &&
68
+ Dom.isEmpty(parent)) {
69
+ const next = parent.parentNode;
70
+ Dom.safeRemove(parent);
71
+ parent = next;
72
+ }
73
+ }
74
+ /**
75
+ * Finds Word's list paragraphs (`<p style="mso-list:l0 level1 lfo1">`), reads
76
+ * their list, level and type from the `mso-list:Ignore` marker, and removes the
77
+ * marker. Call it before anything strips the `mso-list` styles, then pass the
78
+ * result to {@link wrapWordListItems}
79
+ */
80
+ export function readWordListItems(root) {
81
+ const items = [];
82
+ toArray(root.querySelectorAll('p')).forEach(paragraph => {
83
+ const match = /mso-list:\s*l(\d+)\s+level(\d+)(?:\s+lfo(\d+))?/i.exec(attr(paragraph, 'style') || '');
84
+ if (!match) {
85
+ return;
86
+ }
87
+ const marker = toArray(paragraph.querySelectorAll('[style]')).find(elm => /mso-list:\s*ignore/i.test(attr(elm, 'style') || ''));
88
+ items.push({
89
+ paragraph,
90
+ list: `${match[1]}:${match[3] || ''}`,
91
+ level: Number(match[2]),
92
+ ...parseMarker((marker === null || marker === void 0 ? void 0 : marker.textContent) || '')
93
+ });
94
+ if (marker) {
95
+ removeMarker(marker, paragraph);
96
+ }
97
+ });
98
+ return items;
99
+ }
100
+ function isAdjacent(prev, item) {
101
+ return (Dom.findSibling(item.paragraph, true, n => !Dom.isEmptyTextNode(n) && !Dom.isComment(n)) === prev.paragraph);
102
+ }
103
+ /**
104
+ * Turns the paragraphs found by {@link readWordListItems} into `<ul>`/`<ol>`
105
+ * lists, nesting them by level. Each paragraph becomes an `<li>` without Word's
106
+ * hanging indent
107
+ */
108
+ export function wrapWordListItems(items) {
109
+ let stack = [];
110
+ const continues = items.map((item, index) => index > 0 && isAdjacent(items[index - 1], item));
111
+ items.forEach((item, index) => {
112
+ const { paragraph } = item;
113
+ if (!continues[index]) {
114
+ stack = [];
115
+ }
116
+ while (stack.length && stack[stack.length - 1].level > item.level) {
117
+ stack.pop();
118
+ }
119
+ let top = stack[stack.length - 1];
120
+ if (top && top.level === item.level && top.list !== item.list) {
121
+ stack.pop();
122
+ top = stack[stack.length - 1];
123
+ }
124
+ const doc = paragraph.ownerDocument;
125
+ if (!top || top.level < item.level) {
126
+ const element = doc.createElement(item.tag);
127
+ if (item.listStyleType) {
128
+ element.style.listStyleType = item.listStyleType;
129
+ }
130
+ if (item.tag === 'ol' && item.start !== 1) {
131
+ attr(element, 'start', item.start);
132
+ }
133
+ if (top && top.li) {
134
+ top.li.appendChild(element);
135
+ }
136
+ else {
137
+ Dom.before(paragraph, element);
138
+ }
139
+ top = { level: item.level, list: item.list, element };
140
+ stack.push(top);
141
+ }
142
+ const li = doc.createElement('li');
143
+ toArray(paragraph.attributes).forEach(attribute => {
144
+ if (attribute.name !== 'class') {
145
+ li.setAttribute(attribute.name, attribute.value);
146
+ }
147
+ });
148
+ li.style.removeProperty('margin-left');
149
+ li.style.removeProperty('text-indent');
150
+ if (!attr(li, 'style')) {
151
+ attr(li, 'style', null);
152
+ }
153
+ Dom.moveContent(paragraph, li);
154
+ Dom.safeRemove(paragraph);
155
+ top.element.appendChild(li);
156
+ top.li = li;
157
+ });
158
+ }
@@ -4,6 +4,7 @@
4
4
  * Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
6
  export * from "./composed-ranges";
7
+ export * from "./is-moved";
7
8
  export * from "./move-node-inside-start";
8
9
  export * from "./move-the-node-along-the-edge-outward";
9
10
  /**
@@ -9,6 +9,7 @@
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
11
  export * from "./composed-ranges.js";
12
+ export * from "./is-moved.js";
12
13
  export * from "./move-node-inside-start.js";
13
14
  export * from "./move-the-node-along-the-edge-outward.js";
14
15
  /**
@@ -0,0 +1,15 @@
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 { IJodit, Nullable } from "../../../types/index";
10
+ /**
11
+ * Whether the selection, `before` as it was when a key went down, has since
12
+ * moved to somewhere else in the editor
13
+ * @private
14
+ */
15
+ export declare function isMoved(jodit: IJodit, before: Nullable<Range>): boolean;
@@ -0,0 +1,22 @@
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
+ * Whether the selection, `before` as it was when a key went down, has since
8
+ * moved to somewhere else in the editor
9
+ * @private
10
+ */
11
+ export function isMoved(jodit, before) {
12
+ const { s } = jodit;
13
+ // Selecting table cells, for one, removes the selection altogether
14
+ if (!before || !s.isInsideArea) {
15
+ return false;
16
+ }
17
+ const { range } = s;
18
+ return (range.startContainer !== before.startContainer ||
19
+ range.startOffset !== before.startOffset ||
20
+ range.endContainer !== before.endContainer ||
21
+ range.endOffset !== before.endOffset);
22
+ }
@@ -4,8 +4,10 @@
4
4
  * Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
6
  import { Dom } from "../../../../dom/dom.js";
7
+ import { size } from "../../../../helpers/size/object-size.js";
7
8
  import { assert } from "../../../../helpers/utils/assert.js";
8
9
  import { extractSelectedPart } from "../extract.js";
10
+ import { elementsEqualAttributes } from "../is-same-attributes.js";
9
11
  import { toggleAttributes } from "../toggle-attributes.js";
10
12
  import { _PREFIX, CHANGE, INITIAL, REPLACE, UNWRAP } from "../../constants.js";
11
13
  import { wrapList } from "./wrap-list.js";
@@ -28,10 +30,23 @@ export function toggleOrderedList(commitStyle, li, jodit, mode) {
28
30
  const hook = jodit.e.fire.bind(jodit.e, `${_PREFIX}AfterToggleList`);
29
31
  if (mode !== UNWRAP) {
30
32
  const isChangeMode = toggleAttributes(commitStyle, li.parentElement, jodit, INITIAL, true) === CHANGE;
33
+ const isSameList = Dom.isTag(list, commitStyle.element);
34
+ // ol => ol[style="list-style-type: lower-roman"]: a list's style
35
+ // belongs to the whole list, so it is changed without splitting it
36
+ if (isSameList &&
37
+ isChangeMode &&
38
+ mode !== REPLACE &&
39
+ changesStyleOnly(commitStyle)) {
40
+ toggleAttributes(commitStyle, list, jodit, CHANGE);
41
+ hook(CHANGE, joinPrevious(commitStyle, list, mode), commitStyle);
42
+ return CHANGE;
43
+ }
44
+ // Restyled already, for an earlier part of the selection
45
+ if (isSameList && mode === CHANGE) {
46
+ return CHANGE;
47
+ }
31
48
  // ul => ol, ol => ul or ul => ul.class1
32
- if (mode === REPLACE ||
33
- isChangeMode ||
34
- list.tagName.toLowerCase() !== commitStyle.element) {
49
+ if (mode === REPLACE || isChangeMode || !isSameList) {
35
50
  const wrapper = unwrapList(REPLACE, list, li, jodit, commitStyle);
36
51
  const newList = wrapList(commitStyle, wrapper, jodit);
37
52
  hook(REPLACE, newList, commitStyle);
@@ -42,6 +57,28 @@ export function toggleOrderedList(commitStyle, li, jodit, mode) {
42
57
  hook(UNWRAP, wrapper, commitStyle);
43
58
  return UNWRAP;
44
59
  }
60
+ /**
61
+ * Other attributes, such as a class, still move the items into a new list,
62
+ * where the `beforeWrapList` hook can add markup to each of them
63
+ */
64
+ function changesStyleOnly({ options: { attributes } }) {
65
+ return Boolean(attributes === null || attributes === void 0 ? void 0 : attributes.style) && size(attributes) === 1;
66
+ }
67
+ /**
68
+ * Joins a restyled list to the list before it, when the selection runs on
69
+ * from that list and the two now match
70
+ */
71
+ function joinPrevious(commitStyle, list, mode) {
72
+ const prev = Dom.findSibling(list, true);
73
+ if (mode !== CHANGE ||
74
+ !Dom.isTag(prev, commitStyle.element) ||
75
+ !elementsEqualAttributes(prev, list)) {
76
+ return list;
77
+ }
78
+ Dom.append(prev, Array.from(list.childNodes));
79
+ Dom.safeRemove(list);
80
+ return prev;
81
+ }
45
82
  function unwrapList(mode, list, li, jodit, cs) {
46
83
  const result = jodit.e.fire(`${_PREFIX}BeforeUnwrapList`, mode, list, cs);
47
84
  if (result) {
@@ -7,7 +7,7 @@ import { LIST_TAGS } from "../../constants.js";
7
7
  import { Dom } from "../../dom/dom.js";
8
8
  import { assert } from "../../helpers/utils/assert.js";
9
9
  import { extractSelectedPart, getSuitChild, getSuitParent, isInsideInvisibleElement, suitableClosest, toggleAttributes, toggleOrderedList, unwrapChildren, wrap, WRAP_NODES } from "./api/index.js";
10
- import { INITIAL, REPLACE, UNSET, UNWRAP, WRAP } from "./constants.js";
10
+ import { CHANGE, INITIAL, REPLACE, UNSET, UNWRAP, WRAP } from "./constants.js";
11
11
  export const states = {
12
12
  START: 'START',
13
13
  ELEMENT: 'ELEMENT',
@@ -54,7 +54,10 @@ export const transactions = {
54
54
  [states.LIST]: {
55
55
  exec(value) {
56
56
  const { element, jodit, mode } = value;
57
- if (mode !== INITIAL && mode !== UNWRAP && mode !== REPLACE) {
57
+ if (mode !== INITIAL &&
58
+ mode !== UNWRAP &&
59
+ mode !== REPLACE &&
60
+ mode !== CHANGE) {
58
61
  return { ...value, next: states.END };
59
62
  }
60
63
  const li = Dom.closest(element, 'li', jodit.editor);
package/esm/langs/ar.d.ts CHANGED
@@ -220,5 +220,8 @@ declare const _exports: {
220
220
  Loop: string;
221
221
  Muted: string;
222
222
  Controls: string;
223
+ 'Start at...': string;
224
+ 'Start numbering at': string;
225
+ 'Number for the first item': string;
223
226
  };
224
227
  export = _exports;
package/esm/langs/ar.js CHANGED
@@ -234,5 +234,8 @@ export default {
234
234
  Autoplay: 'تشغيل تلقائي',
235
235
  Loop: 'تكرار',
236
236
  Muted: 'مكتوم الصوت',
237
- Controls: 'عناصر التحكم'
237
+ Controls: 'عناصر التحكم',
238
+ 'Start at...': 'البدء من...',
239
+ 'Start numbering at': 'بدء الترقيم من',
240
+ 'Number for the first item': 'رقم العنصر الأول'
238
241
  };
package/esm/langs/az.d.ts CHANGED
@@ -226,5 +226,8 @@ declare const _exports: {
226
226
  Loop: string;
227
227
  Muted: string;
228
228
  Controls: string;
229
+ 'Start at...': string;
230
+ 'Start numbering at': string;
231
+ 'Number for the first item': string;
229
232
  };
230
233
  export = _exports;
package/esm/langs/az.js CHANGED
@@ -243,5 +243,8 @@ export default {
243
243
  Autoplay: 'Avtomatik oynatma',
244
244
  Loop: 'Təkrarla',
245
245
  Muted: 'Səssiz',
246
- Controls: 'İdarəetmə elementləri'
246
+ Controls: 'İdarəetmə elementləri',
247
+ 'Start at...': 'Başlanğıc nömrəsi...',
248
+ 'Start numbering at': 'Nömrələmənin başlanğıcı',
249
+ 'Number for the first item': 'Birinci bəndin nömrəsi'
247
250
  };
@@ -218,5 +218,8 @@ declare const _exports: {
218
218
  Loop: string;
219
219
  Muted: string;
220
220
  Controls: string;
221
+ 'Start at...': string;
222
+ 'Start numbering at': string;
223
+ 'Number for the first item': string;
221
224
  };
222
225
  export = _exports;
@@ -233,5 +233,8 @@ export default {
233
233
  Autoplay: 'Automatické přehrávání',
234
234
  Loop: 'Opakovat',
235
235
  Muted: 'Ztlumeno',
236
- Controls: 'Ovládací prvky'
236
+ Controls: 'Ovládací prvky',
237
+ 'Start at...': 'Začít od...',
238
+ 'Start numbering at': 'Začít číslování od',
239
+ 'Number for the first item': 'Číslo první položky'
237
240
  };
package/esm/langs/de.d.ts CHANGED
@@ -222,5 +222,8 @@ declare const _exports: {
222
222
  Loop: string;
223
223
  Muted: string;
224
224
  Controls: string;
225
+ 'Start at...': string;
226
+ 'Start numbering at': string;
227
+ 'Number for the first item': string;
225
228
  };
226
229
  export = _exports;
package/esm/langs/de.js CHANGED
@@ -238,5 +238,8 @@ export default {
238
238
  Autoplay: 'Automatische Wiedergabe',
239
239
  Loop: 'Wiederholen',
240
240
  Muted: 'Stumm',
241
- Controls: 'Steuerelemente'
241
+ Controls: 'Steuerelemente',
242
+ 'Start at...': 'Beginnen bei...',
243
+ 'Start numbering at': 'Nummerierung beginnen bei',
244
+ 'Number for the first item': 'Nummer des ersten Eintrags'
242
245
  };
package/esm/langs/es.d.ts CHANGED
@@ -222,5 +222,8 @@ declare const _exports: {
222
222
  Loop: string;
223
223
  Muted: string;
224
224
  Controls: string;
225
+ 'Start at...': string;
226
+ 'Start numbering at': string;
227
+ 'Number for the first item': string;
225
228
  };
226
229
  export = _exports;
package/esm/langs/es.js CHANGED
@@ -237,5 +237,8 @@ export default {
237
237
  Autoplay: 'Reproducción automática',
238
238
  Loop: 'Repetir',
239
239
  Muted: 'Silenciado',
240
- Controls: 'Controles'
240
+ Controls: 'Controles',
241
+ 'Start at...': 'Empezar en...',
242
+ 'Start numbering at': 'Empezar la numeración en',
243
+ 'Number for the first item': 'Número del primer elemento'
241
244
  };