jodit 4.17.5 → 4.18.0

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 (105) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/es2015/jodit.css +87 -3
  3. package/es2015/jodit.fat.min.css +1 -1
  4. package/es2015/jodit.fat.min.js +9 -9
  5. package/es2015/jodit.js +873 -163
  6. package/es2015/jodit.min.css +1 -1
  7. package/es2015/jodit.min.js +16 -16
  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 +9 -9
  16. package/es2018/jodit.min.css +1 -1
  17. package/es2018/jodit.min.js +9 -9
  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 +87 -3
  21. package/es2021/jodit.fat.min.css +1 -1
  22. package/es2021/jodit.fat.min.js +16 -16
  23. package/es2021/jodit.js +851 -163
  24. package/es2021/jodit.min.css +1 -1
  25. package/es2021/jodit.min.js +16 -16
  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 +87 -3
  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 +851 -163
  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 +92 -6
  45. package/es5/jodit.fat.min.css +1 -1
  46. package/es5/jodit.fat.min.js +2 -2
  47. package/es5/jodit.js +1141 -223
  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 +65 -2
  60. package/esm/config.js +29 -2
  61. package/esm/core/constants.js +1 -1
  62. package/esm/core/helpers/utils/align.d.ts +11 -5
  63. package/esm/core/helpers/utils/align.js +28 -8
  64. package/esm/core/helpers/utils/index.d.ts +1 -0
  65. package/esm/core/helpers/utils/index.js +1 -0
  66. package/esm/core/helpers/utils/media-wrapper.d.ts +29 -0
  67. package/esm/core/helpers/utils/media-wrapper.js +63 -0
  68. package/esm/core/selection/selection.js +34 -8
  69. package/esm/modules/history/history.js +20 -12
  70. package/esm/modules/toolbar/button/button.d.ts +18 -0
  71. package/esm/modules/toolbar/button/button.js +75 -5
  72. package/esm/plugins/font/config.d.ts +12 -0
  73. package/esm/plugins/font/config.js +25 -10
  74. package/esm/plugins/font/font-picker.d.ts +51 -0
  75. package/esm/plugins/font/font-picker.js +238 -0
  76. package/esm/plugins/font/font.js +12 -0
  77. package/esm/plugins/font/google-fonts.d.ts +65 -0
  78. package/esm/plugins/font/google-fonts.js +143 -0
  79. package/esm/plugins/image-properties/readers/index.js +2 -1
  80. package/esm/plugins/image-properties/writers/index.js +2 -2
  81. package/esm/plugins/inline-popup/config/items/img.js +10 -8
  82. package/esm/plugins/inline-popup/inline-popup.js +13 -6
  83. package/esm/plugins/line-height/line-height.js +1 -1
  84. package/esm/plugins/media/config.d.ts +26 -1
  85. package/esm/plugins/media/config.js +1 -3
  86. package/esm/plugins/media/media.js +12 -4
  87. package/esm/plugins/placeholder/placeholder.js +1 -1
  88. package/esm/plugins/source/editor/engines/ace.d.ts +2 -0
  89. package/esm/plugins/source/editor/engines/ace.js +35 -3
  90. package/esm/plugins/video/config.js +24 -4
  91. package/esm/types/toolbar.d.ts +2 -0
  92. package/esm/types/types.d.ts +13 -1
  93. package/package.json +1 -1
  94. package/types/config.d.ts +65 -2
  95. package/types/core/helpers/utils/align.d.ts +11 -5
  96. package/types/core/helpers/utils/index.d.ts +1 -0
  97. package/types/core/helpers/utils/media-wrapper.d.ts +29 -0
  98. package/types/modules/toolbar/button/button.d.ts +18 -0
  99. package/types/plugins/font/config.d.ts +12 -0
  100. package/types/plugins/font/font-picker.d.ts +51 -0
  101. package/types/plugins/font/google-fonts.d.ts +65 -0
  102. package/types/plugins/media/config.d.ts +26 -1
  103. package/types/plugins/source/editor/engines/ace.d.ts +2 -0
  104. package/types/types/toolbar.d.ts +2 -0
  105. package/types/types/types.d.ts +13 -1
@@ -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.5
4
+ * Version: v4.18.0
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.5
4
+ * Version: v4.18.0
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.5
4
+ * Version: v4.18.0
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.5
4
+ * Version: v4.18.0
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.5
4
+ * Version: v4.18.0
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.5
4
+ * Version: v4.18.0
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.5
4
+ * Version: v4.18.0
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.5
4
+ * Version: v4.18.0
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.5
4
+ * Version: v4.18.0
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
package/esm/config.d.ts CHANGED
@@ -15,6 +15,7 @@ import type { IControlType } from "./types/index";
15
15
  import type { formTemplate } from "./plugins/link/template";
16
16
  import type { IUIForm } from "./types/index";
17
17
  import type { IUIOption } from "./types/index";
18
+ import type { MediaWrapper } from "./types/index";
18
19
  import type { ButtonsOption } from "./types/index";
19
20
  import type { InsertMode } from "./types/index";
20
21
  import type { FuzzySearch } from "./types/index";
@@ -22,6 +23,7 @@ import type { ISourceEditor } from "./types/index";
22
23
  import type { ISpeechRecognizeConstructor } from "./plugins/speech-recognize/interface";
23
24
  import type { Attributes } from "./types/index";
24
25
  import type { Controls } from "./types/index";
26
+ import type { HAlignClasses } from "./types/index";
25
27
  import type { IAsyncStorageOptions } from "./types/index";
26
28
  import type { IExtraPlugin } from "./types/index";
27
29
  import type { ImageAlignClasses } from "./types/index";
@@ -642,19 +644,46 @@ declare class Config implements IViewOptions {
642
644
  /**
643
645
  * Class names to align images with, instead of an inline `float` or
644
646
  * `margin`. Aligning an image removes every class listed here and adds the
645
- * chosen alignment's, which can be several separated by spaces.
647
+ * chosen alignment's, which can be several separated by spaces. An
648
+ * alignment can also list several such sets, to recognise each of them;
649
+ * aligning writes the first. `alignClasses.img` takes its place when set.
646
650
  *
647
651
  * ```javascript
648
652
  * Jodit.make('#editor', {
649
653
  * imageAlignClasses: {
650
654
  * left: 'align-left',
651
655
  * right: 'align-right',
652
- * center: 'align-center'
656
+ * center: ['align-center', 'centered']
653
657
  * }
654
658
  * });
655
659
  * ```
656
660
  */
657
661
  imageAlignClasses: Nullable<ImageAlignClasses>;
662
+ /**
663
+ * Class names to align elements with, by tag name, as `imageAlignClasses`
664
+ * does for images: `img`, and `video`, `audio` and `iframe` in the media
665
+ * popup. A media wrapper (see `mediaWrappers`), and Jodit's own `jodit` and
666
+ * `jodit-media` wrappers, are aligned with the classes for the media in
667
+ * them.
668
+ *
669
+ * ```javascript
670
+ * const media = {
671
+ * left: 'media-left',
672
+ * right: 'media-right',
673
+ * center: 'media-center'
674
+ * };
675
+ *
676
+ * Jodit.make('#editor', {
677
+ * alignClasses: { video: media, iframe: media }
678
+ * });
679
+ * ```
680
+ */
681
+ alignClasses: IDictionary<HAlignClasses>;
682
+ /**
683
+ * Alignment given to the media the video popup inserts. `'normal'` leaves
684
+ * them unaligned
685
+ */
686
+ mediaDefaultAlign: ImageHAlign;
658
687
  /**
659
688
  * Do not display these buttons that are on the list
660
689
  *
@@ -1293,6 +1322,18 @@ interface Config {
1293
1322
  }
1294
1323
  interface Config {
1295
1324
  defaultFontSizePoints: 'px' | 'pt';
1325
+ font: {
1326
+ googleFonts: {
1327
+ /**
1328
+ * Adds the Google Fonts library to the font family dropdown, which then
1329
+ * also lists the theme's fonts and the fonts the content uses. The
1330
+ * address answers with the JSON of the Google Fonts Developer API:
1331
+ * the API itself (`https://www.googleapis.com/webfonts/v1/webfonts?key=…`)
1332
+ * or a proxy of it. Empty leaves the dropdown as it is
1333
+ */
1334
+ url: string;
1335
+ };
1336
+ };
1296
1337
  }
1297
1338
  interface Config {
1298
1339
  /**
@@ -1549,6 +1590,28 @@ interface Config {
1549
1590
  * Media tags
1550
1591
  */
1551
1592
  mediaBlocks: string[];
1593
+ /**
1594
+ * Wrappers that media keep in the content, by the media's tag name, such as
1595
+ * Froala's `<span class="fr-video">` around video, audio and embeds. An
1596
+ * element with a wrapper for its tag:
1597
+ * - is wrapped in it instead of the editor-only `mediaFakeTag` when its tag
1598
+ * is one of `mediaBlocks`, or left in it when it's already in one
1599
+ * - goes in inside it when the video popup inserts it, as an upload, a
1600
+ * link's embed or embed code that is one element
1601
+ * - opens the media popup for its wrapper when clicked, whose Align and
1602
+ * Delete act on the wrapper
1603
+ *
1604
+ * A wrapper is given `contenteditable="false"` and `draggable="true"`,
1605
+ * which stay in the value.
1606
+ * ```javascript
1607
+ * const frVideo = { tag: 'span', className: 'fr-video' };
1608
+ *
1609
+ * Jodit.make('#editor', {
1610
+ * mediaWrappers: { video: frVideo, audio: frVideo, iframe: frVideo }
1611
+ * });
1612
+ * ```
1613
+ */
1614
+ mediaWrappers: IDictionary<MediaWrapper>;
1552
1615
  }
1553
1616
  interface Config {
1554
1617
  /**
package/esm/config.js CHANGED
@@ -748,19 +748,46 @@ class Config {
748
748
  /**
749
749
  * Class names to align images with, instead of an inline `float` or
750
750
  * `margin`. Aligning an image removes every class listed here and adds the
751
- * chosen alignment's, which can be several separated by spaces.
751
+ * chosen alignment's, which can be several separated by spaces. An
752
+ * alignment can also list several such sets, to recognise each of them;
753
+ * aligning writes the first. `alignClasses.img` takes its place when set.
752
754
  *
753
755
  * ```javascript
754
756
  * Jodit.make('#editor', {
755
757
  * imageAlignClasses: {
756
758
  * left: 'align-left',
757
759
  * right: 'align-right',
758
- * center: 'align-center'
760
+ * center: ['align-center', 'centered']
759
761
  * }
760
762
  * });
761
763
  * ```
762
764
  */
763
765
  this.imageAlignClasses = null;
766
+ /**
767
+ * Class names to align elements with, by tag name, as `imageAlignClasses`
768
+ * does for images: `img`, and `video`, `audio` and `iframe` in the media
769
+ * popup. A media wrapper (see `mediaWrappers`), and Jodit's own `jodit` and
770
+ * `jodit-media` wrappers, are aligned with the classes for the media in
771
+ * them.
772
+ *
773
+ * ```javascript
774
+ * const media = {
775
+ * left: 'media-left',
776
+ * right: 'media-right',
777
+ * center: 'media-center'
778
+ * };
779
+ *
780
+ * Jodit.make('#editor', {
781
+ * alignClasses: { video: media, iframe: media }
782
+ * });
783
+ * ```
784
+ */
785
+ this.alignClasses = {};
786
+ /**
787
+ * Alignment given to the media the video popup inserts. `'normal'` leaves
788
+ * them unaligned
789
+ */
790
+ this.mediaDefaultAlign = 'normal';
764
791
  /**
765
792
  * Do not display these buttons that are on the list
766
793
  *
@@ -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.5";
6
+ export const APP_VERSION = "4.18.0";
7
7
  // prettier-ignore
8
8
  export const ES = "es2020";
9
9
  export const IS_ES_MODERN = true;
@@ -6,16 +6,22 @@
6
6
  /**
7
7
  * @module helpers/utils
8
8
  */
9
- import type { ImageAlignClasses, ImageHAlign, Nullable } from "../../../types/index";
9
+ import type { HAlignClasses, IJodit, ImageHAlign, Nullable } from "../../../types/index";
10
10
  /**
11
- * Align image, with `classes` (see `imageAlignClasses`) instead of inline
12
- * styles when they are given
11
+ * The class names to align `elm` with: `alignClasses` for its tag, or for the
12
+ * media in it when it wraps media (a `mediaWrappers` wrapper, `jodit` or
13
+ * `jodit-media`), falling back to `imageAlignClasses` for an image
13
14
  */
14
- export declare function hAlignElement(image: HTMLElement, align: ImageHAlign, classes?: Nullable<ImageAlignClasses>): void;
15
+ export declare function getAlignClasses(jodit: IJodit, elm: Nullable<HTMLElement>): Nullable<HAlignClasses>;
16
+ /**
17
+ * Align image, with `classes` (see `alignClasses`) instead of inline styles
18
+ * when they are given
19
+ */
20
+ export declare function hAlignElement(image: HTMLElement, align: ImageHAlign, classes?: Nullable<HAlignClasses>): void;
15
21
  /**
16
22
  * The alignment `hAlignElement` has set on an element
17
23
  */
18
- export declare function getHAlign(elm: HTMLElement, classes?: Nullable<ImageAlignClasses>): ImageHAlign;
24
+ export declare function getHAlign(elm: HTMLElement, classes?: Nullable<HAlignClasses>): ImageHAlign;
19
25
  /**
20
26
  * Remove text-align style for all selected children
21
27
  */
@@ -4,18 +4,40 @@
4
4
  * Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
6
  import { Dom } from "../../dom/index.js";
7
+ import { isArray } from "../checker/is-array.js";
7
8
  import { attr } from "./attr.js";
8
9
  import { clearCenterAlign, css, cssInline } from "./css.js";
9
- const classNames = (value) => (value !== null && value !== void 0 ? value : '').split(/\s+/).filter(Boolean);
10
+ import { getWrappedMedia } from "./media-wrapper.js";
11
+ // The sets of class names an alignment lists, the first being the one to write
12
+ const classSets = (value) => (isArray(value) ? value : [value !== null && value !== void 0 ? value : ''])
13
+ .map(set => set.split(/\s+/).filter(Boolean))
14
+ .filter(set => set.length);
10
15
  /**
11
- * Align image, with `classes` (see `imageAlignClasses`) instead of inline
12
- * styles when they are given
16
+ * The class names to align `elm` with: `alignClasses` for its tag, or for the
17
+ * media in it when it wraps media (a `mediaWrappers` wrapper, `jodit` or
18
+ * `jodit-media`), falling back to `imageAlignClasses` for an image
19
+ */
20
+ export function getAlignClasses(jodit, elm) {
21
+ var _a, _b;
22
+ if (!elm) {
23
+ return null;
24
+ }
25
+ const media = Dom.isTag(elm, new Set(['jodit', 'jodit-media']))
26
+ ? elm.firstElementChild
27
+ : getWrappedMedia(jodit, elm);
28
+ const tag = (media !== null && media !== void 0 ? media : elm).nodeName.toLowerCase();
29
+ return ((_b = (_a = jodit.o.alignClasses) === null || _a === void 0 ? void 0 : _a[tag]) !== null && _b !== void 0 ? _b : (tag === 'img' ? jodit.o.imageAlignClasses : null));
30
+ }
31
+ /**
32
+ * Align image, with `classes` (see `alignClasses`) instead of inline styles
33
+ * when they are given
13
34
  */
14
35
  export function hAlignElement(image, align, classes) {
36
+ var _a;
15
37
  if (classes) {
16
38
  hAlignElement(image, 'normal');
17
- Object.values(classes).forEach(value => image.classList.remove(...classNames(value)));
18
- image.classList.add(...classNames(classes[align || 'normal']));
39
+ Object.values(classes).forEach(value => classSets(value).forEach(set => image.classList.remove(...set)));
40
+ image.classList.add(...((_a = classSets(classes[align || 'normal'])[0]) !== null && _a !== void 0 ? _a : []));
19
41
  ['class', 'style'].forEach(name => {
20
42
  var _a;
21
43
  if (!((_a = attr(image, name)) === null || _a === void 0 ? void 0 : _a.trim())) {
@@ -52,9 +74,7 @@ export function hAlignElement(image, align, classes) {
52
74
  export function getHAlign(elm, classes) {
53
75
  if (classes) {
54
76
  for (const align of ['left', 'right', 'center', 'normal']) {
55
- const names = classNames(classes[align]);
56
- if (names.length &&
57
- names.every(name => elm.classList.contains(name))) {
77
+ if (classSets(classes[align]).some(set => set.every(name => elm.classList.contains(name)))) {
58
78
  return align;
59
79
  }
60
80
  }
@@ -26,6 +26,7 @@ export * from "./get-class-name";
26
26
  export * from "./human-size-to-bytes";
27
27
  export * from "./is-unsafe-proto-key";
28
28
  export * from "./mark-deprecated";
29
+ export * from "./media-wrapper";
29
30
  export * from "./parse-query";
30
31
  export * from "./print";
31
32
  export * from "./reset";
@@ -26,6 +26,7 @@ export * from "./get-class-name.js";
26
26
  export * from "./human-size-to-bytes.js";
27
27
  export * from "./is-unsafe-proto-key.js";
28
28
  export * from "./mark-deprecated.js";
29
+ export * from "./media-wrapper.js";
29
30
  export * from "./parse-query.js";
30
31
  export * from "./print.js";
31
32
  export * from "./reset.js";
@@ -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
+ * @module helpers/utils
8
+ */
9
+ import type { IJodit, Nullable } from "../../../types/index";
10
+ /**
11
+ * The media in `elm` when `elm` is a wrapper media keep in the content (see
12
+ * `mediaWrappers`): an element of a type `elm` is the wrapper for
13
+ */
14
+ export declare function getWrappedMedia(jodit: IJodit, elm: Nullable<Node>): Nullable<HTMLElement>;
15
+ /**
16
+ * The wrapper media keep in the content (see `mediaWrappers`) that is or
17
+ * holds `node`
18
+ */
19
+ export declare function getMediaWrapper(jodit: IJodit, node: Nullable<Node>): Nullable<HTMLElement>;
20
+ /**
21
+ * An empty wrapper for media of type `tag` to keep in the content (see
22
+ * `mediaWrappers`), or null when there is none for it
23
+ */
24
+ export declare function createMediaWrapper(jodit: IJodit, tag: string): Nullable<HTMLElement>;
25
+ /**
26
+ * Puts `element` in the wrapper for its type (see `mediaWrappers`), and
27
+ * returns the wrapper, or `element` when there is none for it
28
+ */
29
+ export declare function wrapMedia(jodit: IJodit, element: HTMLElement): HTMLElement;
@@ -0,0 +1,63 @@
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 { Dom } from "../../dom/dom.js";
7
+ const isWrapper = (node, wrapper) => Dom.isHTMLElement(node) &&
8
+ node.nodeName.toLowerCase() === wrapper.tag.toLowerCase() &&
9
+ node.classList.contains(wrapper.className);
10
+ /**
11
+ * The media in `elm` when `elm` is a wrapper media keep in the content (see
12
+ * `mediaWrappers`): an element of a type `elm` is the wrapper for
13
+ */
14
+ export function getWrappedMedia(jodit, elm) {
15
+ var _a;
16
+ for (const [tag, wrapper] of Object.entries((_a = jodit.o.mediaWrappers) !== null && _a !== void 0 ? _a : {})) {
17
+ if (isWrapper(elm, wrapper)) {
18
+ const media = elm.querySelector(tag);
19
+ if (media) {
20
+ return media;
21
+ }
22
+ }
23
+ }
24
+ return null;
25
+ }
26
+ /**
27
+ * The wrapper media keep in the content (see `mediaWrappers`) that is or
28
+ * holds `node`
29
+ */
30
+ export function getMediaWrapper(jodit, node) {
31
+ return Dom.closest(node, elm => Boolean(getWrappedMedia(jodit, elm)), jodit.editor);
32
+ }
33
+ /**
34
+ * An empty wrapper for media of type `tag` to keep in the content (see
35
+ * `mediaWrappers`), or null when there is none for it
36
+ */
37
+ export function createMediaWrapper(jodit, tag) {
38
+ var _a;
39
+ const wrapper = (_a = jodit.o.mediaWrappers) === null || _a === void 0 ? void 0 : _a[tag.toLowerCase()];
40
+ if (!wrapper) {
41
+ return null;
42
+ }
43
+ return jodit.createInside.element(wrapper.tag, {
44
+ class: wrapper.className,
45
+ contenteditable: false,
46
+ draggable: true
47
+ });
48
+ }
49
+ /**
50
+ * Puts `element` in the wrapper for its type (see `mediaWrappers`), and
51
+ * returns the wrapper, or `element` when there is none for it
52
+ */
53
+ export function wrapMedia(jodit, element) {
54
+ const wrapper = createMediaWrapper(jodit, element.nodeName);
55
+ if (!wrapper) {
56
+ return element;
57
+ }
58
+ if (element.parentNode) {
59
+ Dom.before(element, wrapper);
60
+ }
61
+ Dom.append(wrapper, element);
62
+ return wrapper;
63
+ }
@@ -19,12 +19,21 @@ import { autobind } from "../decorators/index.js";
19
19
  import { Dom } from "../dom/dom.js";
20
20
  import { $$, attr, css, error, getScrollParent, scrollIntoViewIfNeeded, size, toArray } from "../helpers/index.js";
21
21
  import { isFunction, isMarker, isString } from "../helpers/checker/index.js";
22
- import { hAlignElement } from "../helpers/utils/align.js";
22
+ import { getAlignClasses, hAlignElement } from "../helpers/utils/align.js";
23
23
  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
27
  import { composedRanges, cursorInTheEdgeOfString, findCorrectCurrentNode } from "./helpers/index.js";
28
+ /**
29
+ * A text node that is empty or holds only invisible caret placeholders.
30
+ */
31
+ function isBlankTextNode(node) {
32
+ var _a;
33
+ return (Dom.isText(node) &&
34
+ ((_a = node.nodeValue) !== null && _a !== void 0 ? _a : '').replace(consts.INVISIBLE_SPACE_REG_EXP(), '')
35
+ .length === 0);
36
+ }
28
37
  export class Selection {
29
38
  constructor(jodit) {
30
39
  this.jodit = jodit;
@@ -287,6 +296,7 @@ export class Selection {
287
296
  * Create marker element
288
297
  */
289
298
  marker(atStart = false, range) {
299
+ var _a;
290
300
  let newRange = null;
291
301
  if (range) {
292
302
  newRange = range.cloneRange();
@@ -308,10 +318,24 @@ export class Selection {
308
318
  Dom.append(marker, this.j.createInside.text(consts.INVISIBLE_SPACE));
309
319
  if (newRange) {
310
320
  if (Dom.isOrContains(this.area, atStart ? newRange.startContainer : newRange.endContainer)) {
311
- // Here need do unsafe inserting
312
- // Deny Dom.safeInsertNode(newRange, marker);
313
- // Apply style -> Test Style module -> Base apply -> For selection <p><strong>|test|</strong></p> apply style {"element":"em","style":{"fontStyle":"italic"}}
314
- newRange.insertNode(marker);
321
+ const { startContainer: node, startOffset: offset } = newRange;
322
+ // At either end of a text node the marker goes beside it. Splitting
323
+ // the node there would leave the selection's boundary in an empty
324
+ // text node, and WebKit neither repaints a selection that leaves one
325
+ // nor moves a selection that follows one when it's removed.
326
+ if (Dom.isText(node) && offset === 0) {
327
+ Dom.before(node, marker);
328
+ }
329
+ else if (Dom.isText(node) &&
330
+ offset === ((_a = node.nodeValue) === null || _a === void 0 ? void 0 : _a.length)) {
331
+ Dom.after(node, marker);
332
+ }
333
+ else {
334
+ // Here need do unsafe inserting
335
+ // Deny Dom.safeInsertNode(newRange, marker);
336
+ // Apply style -> Test Style module -> Base apply -> For selection <p><strong>|test|</strong></p> apply style {"element":"em","style":{"fontStyle":"italic"}}
337
+ newRange.insertNode(marker);
338
+ }
315
339
  }
316
340
  }
317
341
  return marker;
@@ -651,7 +675,9 @@ export class Selection {
651
675
  () => { var _a; return (_a = firstChild === null || firstChild === void 0 ? void 0 : firstChild.previousSibling) === null || _a === void 0 ? void 0 : _a.lastChild; }
652
676
  ].forEach(getChildNode => {
653
677
  const childNode = getChildNode();
654
- if (childNode && Dom.isEmptyTextNode(childNode)) {
678
+ // Only a text node left with nothing in it by the split
679
+ // goes; one holding a space is content (`<b>a</b> |`).
680
+ if (childNode && isBlankTextNode(childNode)) {
655
681
  Dom.safeRemove(childNode);
656
682
  }
657
683
  });
@@ -750,9 +776,9 @@ export class Selection {
750
776
  if (styles && typeof styles === 'object') {
751
777
  css(image, styles);
752
778
  }
753
- const { imageDefaultAlign, imageAlignClasses } = this.j.o;
779
+ const { imageDefaultAlign } = this.j.o;
754
780
  if (imageDefaultAlign && imageDefaultAlign !== 'normal') {
755
- hAlignElement(image, imageDefaultAlign, imageAlignClasses);
781
+ hAlignElement(image, imageDefaultAlign, getAlignClasses(this.j, image));
756
782
  }
757
783
  const onload = () => {
758
784
  if (image.naturalHeight < image.offsetHeight ||
@@ -133,20 +133,28 @@ export class History extends ViewComponent {
133
133
  */
134
134
  updateStack(replace = false) {
135
135
  const newValue = this.snapshot.make();
136
- if (!Snapshot.equal(newValue, this.startValue)) {
137
- const newCommand = new Command(this.startValue, newValue, this, this.updateTick);
138
- if (replace) {
139
- const command = this.__stack.current();
140
- if (command && this.updateTick === command.tick) {
141
- this.__stack.replace(newCommand);
142
- }
143
- }
144
- else {
145
- this.__stack.push(newCommand);
146
- }
136
+ if (Snapshot.equal(newValue, this.startValue)) {
137
+ return;
138
+ }
139
+ // Only the selection moved, for example when the selection markers
140
+ // went in before a switch to the source view. There is nothing to
141
+ // undo, but the next change has to start from this selection.
142
+ if (newValue.html === this.startValue.html) {
147
143
  this.startValue = newValue;
148
- this.fireChangeStack();
144
+ return;
145
+ }
146
+ const newCommand = new Command(this.startValue, newValue, this, this.updateTick);
147
+ if (replace) {
148
+ const command = this.__stack.current();
149
+ if (command && this.updateTick === command.tick) {
150
+ this.__stack.replace(newCommand);
151
+ }
149
152
  }
153
+ else {
154
+ this.__stack.push(newCommand);
155
+ }
156
+ this.startValue = newValue;
157
+ this.fireChangeStack();
150
158
  }
151
159
  fireChangeStack() {
152
160
  var _a;
@@ -77,6 +77,23 @@ export declare class ToolbarButton<T extends IViewBased = IViewBased> extends UI
77
77
  * `ArrowUp` on the first item close it and give the focus back to the button
78
78
  */
79
79
  private __onPopupKeyDown;
80
+ /**
81
+ * The mouse opens a dropdown without taking the focus out of the editor.
82
+ * While a dropdown list is open, typing there searches the list, as in an
83
+ * open `<select>`, instead of writing into the content
84
+ */
85
+ private __onEditorKeyDown;
86
+ private __typed;
87
+ private __typedAt;
88
+ private __typedTo;
89
+ /**
90
+ * Typing in a dropdown list focuses the next item whose label starts with
91
+ * the typed text, whatever their case and accents, and repeating one letter
92
+ * cycles through the items that start with it, like the WAI-ARIA menu
93
+ * pattern's type-ahead. The typed text starts again after a pause, or once
94
+ * the focus has moved on
95
+ */
96
+ private __typeAhead;
80
97
  /**
81
98
  * Visible controls of the opened dropdown that can take the focus
82
99
  */
@@ -86,6 +103,7 @@ export declare class ToolbarButton<T extends IViewBased = IViewBased> extends UI
86
103
  * Create an open popup list
87
104
  */
88
105
  private __openControlList;
106
+ private __openMenu;
89
107
  protected onOutsideClick(e: MouseEvent): void;
90
108
  private openPopup;
91
109
  private __closePopup;