jodit 4.11.15 → 4.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/es2015/jodit.css +1 -1
  3. package/es2015/jodit.fat.min.js +4 -4
  4. package/es2015/jodit.js +151 -14
  5. package/es2015/jodit.min.js +4 -4
  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 +4 -4
  13. package/es2018/jodit.min.js +24 -24
  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 +5 -5
  18. package/es2021/jodit.js +151 -14
  19. package/es2021/jodit.min.js +5 -5
  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 +5 -5
  28. package/es2021.en/jodit.js +151 -14
  29. package/es2021.en/jodit.min.js +6 -6
  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 +157 -14
  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 +48 -4
  51. package/esm/config.js +28 -3
  52. package/esm/core/constants.js +1 -1
  53. package/esm/core/helpers/utils/config-proto.d.ts +21 -0
  54. package/esm/core/helpers/utils/config-proto.js +32 -0
  55. package/esm/core/ui/icon.d.ts +10 -0
  56. package/esm/core/ui/icon.js +17 -1
  57. package/esm/jodit.d.ts +44 -0
  58. package/esm/jodit.js +47 -1
  59. package/esm/modules/widget/file-selector/file-selector.js +2 -1
  60. package/esm/plugins/link/link.js +2 -1
  61. package/esm/typings.d.ts +1 -0
  62. package/package.json +1 -1
  63. package/types/config.d.ts +48 -4
  64. package/types/core/helpers/utils/config-proto.d.ts +21 -0
  65. package/types/core/ui/icon.d.ts +10 -0
  66. package/types/jodit.d.ts +44 -0
  67. package/types/typings.d.ts +1 -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.11.15
4
+ * Version: v4.12.3
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.11.15
4
+ * Version: v4.12.3
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.11.15
4
+ * Version: v4.12.3
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.11.15
4
+ * Version: v4.12.3
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.11.15
4
+ * Version: v4.12.3
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.11.15
4
+ * Version: v4.12.3
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.11.15
4
+ * Version: v4.12.3
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.11.15
4
+ * Version: v4.12.3
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.11.15
4
+ * Version: v4.12.3
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
package/esm/config.d.ts CHANGED
@@ -619,18 +619,62 @@ declare class Config implements IViewOptions {
619
619
  */
620
620
  disablePlugins: string[] | string;
621
621
  /**
622
- * Init and download extra plugins
622
+ * Init and download extra plugins that are **not** already bundled/registered.
623
+ *
624
+ * For every name in this list that is not found in the plugin registry, Jodit
625
+ * loads it **at runtime over the network** from:
626
+ *
627
+ * ```text
628
+ * <basePath>plugins/<name>/<name>(.min).js
629
+ * ```
630
+ *
631
+ * (see {@link Config.basePath} and {@link Config.minified}). If the plugin is
632
+ * already registered — e.g. you imported it statically, or you use a bundle
633
+ * that ships it (such as the `jodit-pro` / `jodit-pro-react` "all plugins"
634
+ * build) — it is **skipped** and no request is made; in that case you don't
635
+ * need `extraPlugins` at all, just add the plugin's button.
623
636
  *
624
637
  * ```typescript
625
- * var editor = Jodit.make('.editor', {
638
+ * // Dynamic loading: fetches <basePath>plugins/emoji/emoji.js
639
+ * const editor = Jodit.make('.editor', {
626
640
  * extraPlugins: ['emoji']
627
641
  * });
628
642
  * ```
629
- * It will try load %SCRIPT_PATH%/plugins/emoji/emoji.js and after load will try init it
643
+ *
644
+ * You can also pass an explicit URL to bypass the `basePath` convention:
645
+ *
646
+ * ```typescript
647
+ * const editor = Jodit.make('.editor', {
648
+ * extraPlugins: [{ name: 'emoji', url: 'https://cdn.example.com/emoji.js' }]
649
+ * });
650
+ * ```
651
+ *
652
+ * Note: if you see a request to a malformed URL (e.g. `.../src/main.tsx?t=...plugins/emoji/emoji.js`),
653
+ * it means `basePath` was auto-detected incorrectly under your bundler — set
654
+ * {@link Config.basePath} explicitly. See the Plugin System docs for details.
630
655
  */
631
656
  extraPlugins: Array<string | IExtraPlugin>;
632
657
  /**
633
- * Base path for download extra plugins
658
+ * Base path used to build the URL for dynamically loaded {@link Config.extraPlugins}
659
+ * (and their styles): `<basePath>plugins/<name>/<name>(.min).js`.
660
+ *
661
+ * When not set, Jodit auto-detects it from `document.currentScript`, then the
662
+ * last `<script src>` on the page, then `location.href`. That detection works
663
+ * for classic `<script>` includes, but **fails under ESM bundlers / dev
664
+ * servers** (Vite, Webpack dev, etc.) where there is no script tag for the
665
+ * bundle — it falls back to the entry module URL (e.g. `main.tsx`) and produces
666
+ * a broken plugin URL.
667
+ *
668
+ * Fix: host the plugin files at a public location and point `basePath` there
669
+ * (note the trailing slash):
670
+ *
671
+ * ```typescript
672
+ * const editor = Jodit.make('.editor', {
673
+ * basePath: 'https://your-site.com/jodit-assets/',
674
+ * extraPlugins: ['emoji']
675
+ * // → loads https://your-site.com/jodit-assets/plugins/emoji/emoji.js
676
+ * });
677
+ * ```
634
678
  */
635
679
  basePath?: string;
636
680
  /**
package/esm/config.js CHANGED
@@ -729,14 +729,39 @@ class Config {
729
729
  */
730
730
  this.disablePlugins = [];
731
731
  /**
732
- * Init and download extra plugins
732
+ * Init and download extra plugins that are **not** already bundled/registered.
733
+ *
734
+ * For every name in this list that is not found in the plugin registry, Jodit
735
+ * loads it **at runtime over the network** from:
736
+ *
737
+ * ```text
738
+ * <basePath>plugins/<name>/<name>(.min).js
739
+ * ```
740
+ *
741
+ * (see {@link Config.basePath} and {@link Config.minified}). If the plugin is
742
+ * already registered — e.g. you imported it statically, or you use a bundle
743
+ * that ships it (such as the `jodit-pro` / `jodit-pro-react` "all plugins"
744
+ * build) — it is **skipped** and no request is made; in that case you don't
745
+ * need `extraPlugins` at all, just add the plugin's button.
733
746
  *
734
747
  * ```typescript
735
- * var editor = Jodit.make('.editor', {
748
+ * // Dynamic loading: fetches <basePath>plugins/emoji/emoji.js
749
+ * const editor = Jodit.make('.editor', {
736
750
  * extraPlugins: ['emoji']
737
751
  * });
738
752
  * ```
739
- * It will try load %SCRIPT_PATH%/plugins/emoji/emoji.js and after load will try init it
753
+ *
754
+ * You can also pass an explicit URL to bypass the `basePath` convention:
755
+ *
756
+ * ```typescript
757
+ * const editor = Jodit.make('.editor', {
758
+ * extraPlugins: [{ name: 'emoji', url: 'https://cdn.example.com/emoji.js' }]
759
+ * });
760
+ * ```
761
+ *
762
+ * Note: if you see a request to a malformed URL (e.g. `.../src/main.tsx?t=...plugins/emoji/emoji.js`),
763
+ * it means `basePath` was auto-detected incorrectly under your bundler — set
764
+ * {@link Config.basePath} explicitly. See the Plugin System docs for details.
740
765
  */
741
766
  this.extraPlugins = [];
742
767
  /**
@@ -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.11.15";
6
+ export const APP_VERSION = "4.12.3";
7
7
  // prettier-ignore
8
8
  export const ES = "es2020";
9
9
  export const IS_ES_MODERN = true;
@@ -56,4 +56,25 @@ export declare function ConfigFlatten(obj: IDictionary): IDictionary;
56
56
  * console.log(JSON.stringify(plain)); // {"dialogWidth":500, "openOnDblClick": true, "editSrc": true, ...}
57
57
  * ```
58
58
  */
59
+ /**
60
+ * Deep-merges `source` into `target` in-place.
61
+ * Uses the same merge semantics as {@link ConfigProto}:
62
+ * - Nested plain objects are merged recursively
63
+ * - {@link isAtom | Atomic} values replace the target entirely
64
+ * - Everything else (primitives, arrays, class instances) replaces the target value
65
+ *
66
+ * Designed for patching `Config.defaultOptions` without losing existing keys:
67
+ *
68
+ * ```js
69
+ * Jodit.configure({
70
+ * controls: {
71
+ * someButton: { group: 'custom' }
72
+ * }
73
+ * });
74
+ * // Only `controls.someButton` is touched — all other controls remain intact.
75
+ * ```
76
+ *
77
+ * @see {@link ConfigProto} for the prototype-chain variant used at editor creation time
78
+ */
79
+ export declare function ConfigMerge(target: IDictionary, source: IDictionary): void;
59
80
  export declare function ConfigDeepFlatten(obj: IDictionary): IDictionary;
@@ -97,6 +97,38 @@ export function ConfigFlatten(obj) {
97
97
  * console.log(JSON.stringify(plain)); // {"dialogWidth":500, "openOnDblClick": true, "editSrc": true, ...}
98
98
  * ```
99
99
  */
100
+ /**
101
+ * Deep-merges `source` into `target` in-place.
102
+ * Uses the same merge semantics as {@link ConfigProto}:
103
+ * - Nested plain objects are merged recursively
104
+ * - {@link isAtom | Atomic} values replace the target entirely
105
+ * - Everything else (primitives, arrays, class instances) replaces the target value
106
+ *
107
+ * Designed for patching `Config.defaultOptions` without losing existing keys:
108
+ *
109
+ * ```js
110
+ * Jodit.configure({
111
+ * controls: {
112
+ * someButton: { group: 'custom' }
113
+ * }
114
+ * });
115
+ * // Only `controls.someButton` is touched — all other controls remain intact.
116
+ * ```
117
+ *
118
+ * @see {@link ConfigProto} for the prototype-chain variant used at editor creation time
119
+ */
120
+ export function ConfigMerge(target, source) {
121
+ Object.keys(source).forEach(key => {
122
+ const srcVal = source[key];
123
+ const tgtVal = target[key];
124
+ if (isPlainObject(srcVal) && isPlainObject(tgtVal) && !isAtom(srcVal)) {
125
+ ConfigMerge(tgtVal, srcVal);
126
+ }
127
+ else {
128
+ target[key] = srcVal;
129
+ }
130
+ });
131
+ }
100
132
  export function ConfigDeepFlatten(obj) {
101
133
  return keys(obj, false).reduce((app, key) => {
102
134
  app[key] = isPlainObject(obj[key])
@@ -27,4 +27,14 @@ export declare class Icon {
27
27
  * Make icon html element
28
28
  */
29
29
  static makeIcon(jodit: IViewBased, icon: IUIIconState): CanUndef<Node>;
30
+ /**
31
+ * Turn a raw icon string into an element with a `classList`.
32
+ *
33
+ * A plain-text icon (e.g. an emoji/text glyph) makes `fromHTML` return a
34
+ * Text node, which has no `classList`; wrap it in a span so classes/styles
35
+ * can be applied and `makeIcon` never crashes on `iconElement.classList`.
36
+ * Note: SVG icons are `SVGElement` (not `HTMLElement`) but are still Element
37
+ * nodes with a `classList`, so we check `isElement`, not `isHTMLElement`.
38
+ */
39
+ private static toIconElement;
30
40
  }
@@ -4,6 +4,7 @@
4
4
  * Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
6
  import { IS_PROD } from "../constants.js";
7
+ import { Dom } from "../dom/dom.js";
7
8
  import { camelCase, kebabCase } from "../helpers/index.js";
8
9
  import { css } from "../helpers/utils/css.js";
9
10
  export class Icon {
@@ -71,7 +72,7 @@ export class Icon {
71
72
  Icon.get(name, '') ||
72
73
  ((_d = jodit.o.extraIcons) === null || _d === void 0 ? void 0 : _d[name]);
73
74
  if (svg) {
74
- iconElement = jodit.c.fromHTML(svg.trim());
75
+ iconElement = Icon.toIconElement(jodit, svg.trim());
75
76
  if (!/^<svg/i.test(name)) {
76
77
  iconElement.classList.add('jodit-icon_' + clearName);
77
78
  }
@@ -88,6 +89,21 @@ export class Icon {
88
89
  }
89
90
  return iconElement;
90
91
  }
92
+ /**
93
+ * Turn a raw icon string into an element with a `classList`.
94
+ *
95
+ * A plain-text icon (e.g. an emoji/text glyph) makes `fromHTML` return a
96
+ * Text node, which has no `classList`; wrap it in a span so classes/styles
97
+ * can be applied and `makeIcon` never crashes on `iconElement.classList`.
98
+ * Note: SVG icons are `SVGElement` (not `HTMLElement`) but are still Element
99
+ * nodes with a `classList`, so we check `isElement`, not `isHTMLElement`.
100
+ */
101
+ static toIconElement(jodit, svg) {
102
+ const node = jodit.c.fromHTML(svg);
103
+ return Dom.isElement(node)
104
+ ? node
105
+ : jodit.c.span('jodit-icon_text', node);
106
+ }
91
107
  }
92
108
  Icon.icons = {};
93
109
  Icon.__cache = new Map();
package/esm/jodit.d.ts CHANGED
@@ -79,6 +79,50 @@ export declare class Jodit extends ViewWithToolbar implements IJodit, Dlgs {
79
79
  * Default settings
80
80
  */
81
81
  static get defaultOptions(): Config;
82
+ /**
83
+ * Deep-merges partial options into the global defaults without replacing
84
+ * top-level objects. This lets you patch nested settings (e.g. a single
85
+ * button inside `controls`) without losing the rest:
86
+ *
87
+ * ```js
88
+ * // Add a custom button — all existing controls remain untouched
89
+ * Jodit.configure({
90
+ * controls: {
91
+ * myButton: {
92
+ * icon: 'pencil',
93
+ * command: 'selectall'
94
+ * }
95
+ * }
96
+ * });
97
+ *
98
+ * // Override only the `group` of an existing button
99
+ * Jodit.configure({
100
+ * controls: {
101
+ * someButton: { group: 'custom' }
102
+ * }
103
+ * });
104
+ *
105
+ * // Works with any nested option
106
+ * Jodit.configure({
107
+ * createAttributes: {
108
+ * div: { class: 'my-class' }
109
+ * }
110
+ * });
111
+ *
112
+ * // Use Jodit.atom() to replace a nested value entirely instead of merging
113
+ * Jodit.configure({
114
+ * controls: {
115
+ * fontsize: {
116
+ * list: Jodit.atom([8, 9, 10])
117
+ * }
118
+ * }
119
+ * });
120
+ * ```
121
+ *
122
+ * @see {@link ConfigMerge} for the merge algorithm
123
+ * @see {@link ConfigProto} for per-instance prototype-based merge used at editor creation time
124
+ */
125
+ static configure(options: IDictionary): void;
82
126
  static fatMode: boolean;
83
127
  static readonly plugins: IPluginSystem;
84
128
  static modules: typeof Modules;
package/esm/jodit.js CHANGED
@@ -18,7 +18,7 @@ import * as constants from "./core/constants.js";
18
18
  import { FAT_MODE, IS_PROD, lang } from "./core/constants.js";
19
19
  import { autobind, cache, cached, derive, throttle, watch } from "./core/decorators/index.js";
20
20
  import { eventEmitter, instances, modules, pluginSystem } from "./core/global.js";
21
- import { asArray, attr, callPromise, ConfigProto, css, error, isFunction, isJoditObject, isNumber, isPromise, isString, isVoid, kebabCase, markAsAtomic, normalizeKeyAliases, resolveElement, toArray, ucfirst } from "./core/helpers/index.js";
21
+ import { asArray, attr, callPromise, ConfigMerge, ConfigProto, css, error, isFunction, isJoditObject, isNumber, isPromise, isString, isVoid, kebabCase, markAsAtomic, normalizeKeyAliases, resolveElement, toArray, ucfirst } from "./core/helpers/index.js";
22
22
  import { Ajax } from "./core/request/index.js";
23
23
  import { Dlgs } from "./core/traits/dlgs.js";
24
24
  import { Config } from "./config.js";
@@ -116,6 +116,52 @@ let Jodit = Jodit_1 = class Jodit extends ViewWithToolbar {
116
116
  static get defaultOptions() {
117
117
  return Config.defaultOptions;
118
118
  }
119
+ /**
120
+ * Deep-merges partial options into the global defaults without replacing
121
+ * top-level objects. This lets you patch nested settings (e.g. a single
122
+ * button inside `controls`) without losing the rest:
123
+ *
124
+ * ```js
125
+ * // Add a custom button — all existing controls remain untouched
126
+ * Jodit.configure({
127
+ * controls: {
128
+ * myButton: {
129
+ * icon: 'pencil',
130
+ * command: 'selectall'
131
+ * }
132
+ * }
133
+ * });
134
+ *
135
+ * // Override only the `group` of an existing button
136
+ * Jodit.configure({
137
+ * controls: {
138
+ * someButton: { group: 'custom' }
139
+ * }
140
+ * });
141
+ *
142
+ * // Works with any nested option
143
+ * Jodit.configure({
144
+ * createAttributes: {
145
+ * div: { class: 'my-class' }
146
+ * }
147
+ * });
148
+ *
149
+ * // Use Jodit.atom() to replace a nested value entirely instead of merging
150
+ * Jodit.configure({
151
+ * controls: {
152
+ * fontsize: {
153
+ * list: Jodit.atom([8, 9, 10])
154
+ * }
155
+ * }
156
+ * });
157
+ * ```
158
+ *
159
+ * @see {@link ConfigMerge} for the merge algorithm
160
+ * @see {@link ConfigProto} for per-instance prototype-based merge used at editor creation time
161
+ */
162
+ static configure(options) {
163
+ ConfigMerge(Jodit_1.defaultOptions, options);
164
+ }
119
165
  get createInside() {
120
166
  return new Create(() => this.ed, this.o.createAttributes);
121
167
  }
@@ -66,7 +66,8 @@ export const FileSelectorWidget = (editor, callbacks, elm, close, isImage = true
66
66
  type: 'submit',
67
67
  variant: 'primary',
68
68
  text: 'Insert'
69
- }), form = new UIForm(editor, [
69
+ });
70
+ const form = new UIForm(editor, [
70
71
  new UIInput(editor, {
71
72
  required: true,
72
73
  label: 'URL',
@@ -105,7 +105,8 @@ export class link extends Plugin {
105
105
  }
106
106
  __generateForm(current, close) {
107
107
  const { jodit } = this;
108
- const i18n = jodit.i18n.bind(jodit), { openInNewTabCheckbox, openInNewTabCheckboxDefaultChecked, noFollowCheckbox, formTemplate, formClassName, modeClassName } = jodit.o.link;
108
+ const i18n = jodit.i18n.bind(jodit);
109
+ const { openInNewTabCheckbox, openInNewTabCheckboxDefaultChecked, noFollowCheckbox, formTemplate, formClassName, modeClassName } = jodit.o.link;
109
110
  const html = formTemplate(jodit);
110
111
  const form = isString(html)
111
112
  ? jodit.c.fromHTML(html, {
package/esm/typings.d.ts CHANGED
@@ -10,3 +10,4 @@ declare module '*.svg' {
10
10
  const content: string;
11
11
  export default content;
12
12
  }
13
+ declare module '*.less';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jodit",
3
- "version": "4.11.15",
3
+ "version": "4.12.3",
4
4
  "description": "Jodit is an awesome and useful wysiwyg editor with filebrowser",
5
5
  "main": "esm/index.js",
6
6
  "types": "types/index.d.ts",
package/types/config.d.ts CHANGED
@@ -619,18 +619,62 @@ declare class Config implements IViewOptions {
619
619
  */
620
620
  disablePlugins: string[] | string;
621
621
  /**
622
- * Init and download extra plugins
622
+ * Init and download extra plugins that are **not** already bundled/registered.
623
+ *
624
+ * For every name in this list that is not found in the plugin registry, Jodit
625
+ * loads it **at runtime over the network** from:
626
+ *
627
+ * ```text
628
+ * <basePath>plugins/<name>/<name>(.min).js
629
+ * ```
630
+ *
631
+ * (see {@link Config.basePath} and {@link Config.minified}). If the plugin is
632
+ * already registered — e.g. you imported it statically, or you use a bundle
633
+ * that ships it (such as the `jodit-pro` / `jodit-pro-react` "all plugins"
634
+ * build) — it is **skipped** and no request is made; in that case you don't
635
+ * need `extraPlugins` at all, just add the plugin's button.
623
636
  *
624
637
  * ```typescript
625
- * var editor = Jodit.make('.editor', {
638
+ * // Dynamic loading: fetches <basePath>plugins/emoji/emoji.js
639
+ * const editor = Jodit.make('.editor', {
626
640
  * extraPlugins: ['emoji']
627
641
  * });
628
642
  * ```
629
- * It will try load %SCRIPT_PATH%/plugins/emoji/emoji.js and after load will try init it
643
+ *
644
+ * You can also pass an explicit URL to bypass the `basePath` convention:
645
+ *
646
+ * ```typescript
647
+ * const editor = Jodit.make('.editor', {
648
+ * extraPlugins: [{ name: 'emoji', url: 'https://cdn.example.com/emoji.js' }]
649
+ * });
650
+ * ```
651
+ *
652
+ * Note: if you see a request to a malformed URL (e.g. `.../src/main.tsx?t=...plugins/emoji/emoji.js`),
653
+ * it means `basePath` was auto-detected incorrectly under your bundler — set
654
+ * {@link Config.basePath} explicitly. See the Plugin System docs for details.
630
655
  */
631
656
  extraPlugins: Array<string | IExtraPlugin>;
632
657
  /**
633
- * Base path for download extra plugins
658
+ * Base path used to build the URL for dynamically loaded {@link Config.extraPlugins}
659
+ * (and their styles): `<basePath>plugins/<name>/<name>(.min).js`.
660
+ *
661
+ * When not set, Jodit auto-detects it from `document.currentScript`, then the
662
+ * last `<script src>` on the page, then `location.href`. That detection works
663
+ * for classic `<script>` includes, but **fails under ESM bundlers / dev
664
+ * servers** (Vite, Webpack dev, etc.) where there is no script tag for the
665
+ * bundle — it falls back to the entry module URL (e.g. `main.tsx`) and produces
666
+ * a broken plugin URL.
667
+ *
668
+ * Fix: host the plugin files at a public location and point `basePath` there
669
+ * (note the trailing slash):
670
+ *
671
+ * ```typescript
672
+ * const editor = Jodit.make('.editor', {
673
+ * basePath: 'https://your-site.com/jodit-assets/',
674
+ * extraPlugins: ['emoji']
675
+ * // → loads https://your-site.com/jodit-assets/plugins/emoji/emoji.js
676
+ * });
677
+ * ```
634
678
  */
635
679
  basePath?: string;
636
680
  /**
@@ -56,4 +56,25 @@ export declare function ConfigFlatten(obj: IDictionary): IDictionary;
56
56
  * console.log(JSON.stringify(plain)); // {"dialogWidth":500, "openOnDblClick": true, "editSrc": true, ...}
57
57
  * ```
58
58
  */
59
+ /**
60
+ * Deep-merges `source` into `target` in-place.
61
+ * Uses the same merge semantics as {@link ConfigProto}:
62
+ * - Nested plain objects are merged recursively
63
+ * - {@link isAtom | Atomic} values replace the target entirely
64
+ * - Everything else (primitives, arrays, class instances) replaces the target value
65
+ *
66
+ * Designed for patching `Config.defaultOptions` without losing existing keys:
67
+ *
68
+ * ```js
69
+ * Jodit.configure({
70
+ * controls: {
71
+ * someButton: { group: 'custom' }
72
+ * }
73
+ * });
74
+ * // Only `controls.someButton` is touched — all other controls remain intact.
75
+ * ```
76
+ *
77
+ * @see {@link ConfigProto} for the prototype-chain variant used at editor creation time
78
+ */
79
+ export declare function ConfigMerge(target: IDictionary, source: IDictionary): void;
59
80
  export declare function ConfigDeepFlatten(obj: IDictionary): IDictionary;
@@ -27,4 +27,14 @@ export declare class Icon {
27
27
  * Make icon html element
28
28
  */
29
29
  static makeIcon(jodit: IViewBased, icon: IUIIconState): CanUndef<Node>;
30
+ /**
31
+ * Turn a raw icon string into an element with a `classList`.
32
+ *
33
+ * A plain-text icon (e.g. an emoji/text glyph) makes `fromHTML` return a
34
+ * Text node, which has no `classList`; wrap it in a span so classes/styles
35
+ * can be applied and `makeIcon` never crashes on `iconElement.classList`.
36
+ * Note: SVG icons are `SVGElement` (not `HTMLElement`) but are still Element
37
+ * nodes with a `classList`, so we check `isElement`, not `isHTMLElement`.
38
+ */
39
+ private static toIconElement;
30
40
  }
package/types/jodit.d.ts CHANGED
@@ -79,6 +79,50 @@ export declare class Jodit extends ViewWithToolbar implements IJodit, Dlgs {
79
79
  * Default settings
80
80
  */
81
81
  static get defaultOptions(): Config;
82
+ /**
83
+ * Deep-merges partial options into the global defaults without replacing
84
+ * top-level objects. This lets you patch nested settings (e.g. a single
85
+ * button inside `controls`) without losing the rest:
86
+ *
87
+ * ```js
88
+ * // Add a custom button — all existing controls remain untouched
89
+ * Jodit.configure({
90
+ * controls: {
91
+ * myButton: {
92
+ * icon: 'pencil',
93
+ * command: 'selectall'
94
+ * }
95
+ * }
96
+ * });
97
+ *
98
+ * // Override only the `group` of an existing button
99
+ * Jodit.configure({
100
+ * controls: {
101
+ * someButton: { group: 'custom' }
102
+ * }
103
+ * });
104
+ *
105
+ * // Works with any nested option
106
+ * Jodit.configure({
107
+ * createAttributes: {
108
+ * div: { class: 'my-class' }
109
+ * }
110
+ * });
111
+ *
112
+ * // Use Jodit.atom() to replace a nested value entirely instead of merging
113
+ * Jodit.configure({
114
+ * controls: {
115
+ * fontsize: {
116
+ * list: Jodit.atom([8, 9, 10])
117
+ * }
118
+ * }
119
+ * });
120
+ * ```
121
+ *
122
+ * @see {@link ConfigMerge} for the merge algorithm
123
+ * @see {@link ConfigProto} for per-instance prototype-based merge used at editor creation time
124
+ */
125
+ static configure(options: IDictionary): void;
82
126
  static fatMode: boolean;
83
127
  static readonly plugins: IPluginSystem;
84
128
  static modules: typeof Modules;
@@ -10,3 +10,4 @@ declare module '*.svg' {
10
10
  const content: string;
11
11
  export default content;
12
12
  }
13
+ declare module '*.less';