jodit 4.16.0 → 4.17.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 (80) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/es2015/jodit.css +11 -1
  3. package/es2015/jodit.fat.min.css +1 -1
  4. package/es2015/jodit.fat.min.js +7 -7
  5. package/es2015/jodit.js +176 -33
  6. package/es2015/jodit.min.css +1 -1
  7. package/es2015/jodit.min.js +7 -7
  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 +7 -7
  16. package/es2018/jodit.min.css +1 -1
  17. package/es2018/jodit.min.js +7 -7
  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 +11 -1
  21. package/es2021/jodit.fat.min.css +1 -1
  22. package/es2021/jodit.fat.min.js +7 -7
  23. package/es2021/jodit.js +168 -32
  24. package/es2021/jodit.min.css +1 -1
  25. package/es2021/jodit.min.js +7 -7
  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 +11 -1
  33. package/es2021.en/jodit.fat.min.css +1 -1
  34. package/es2021.en/jodit.fat.min.js +7 -7
  35. package/es2021.en/jodit.js +168 -32
  36. package/es2021.en/jodit.min.css +1 -1
  37. package/es2021.en/jodit.min.js +7 -7
  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 +15 -2
  45. package/es5/jodit.fat.min.css +1 -1
  46. package/es5/jodit.fat.min.js +2 -2
  47. package/es5/jodit.js +188 -33
  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 +40 -1
  60. package/esm/core/constants.js +1 -1
  61. package/esm/modules/widget/file-selector/file-selector.d.ts +10 -1
  62. package/esm/modules/widget/file-selector/file-selector.js +30 -18
  63. package/esm/plugins/file/file.d.ts +24 -1
  64. package/esm/plugins/file/file.js +17 -1
  65. package/esm/plugins/iframe/config.js +4 -0
  66. package/esm/plugins/image-processor/image-processor.d.ts +5 -0
  67. package/esm/plugins/image-processor/image-processor.js +29 -2
  68. package/esm/plugins/inline-popup/config/items/img.js +22 -1
  69. package/esm/plugins/source/editor/engines/ace.js +2 -1
  70. package/esm/plugins/source/source.d.ts +17 -0
  71. package/esm/plugins/source/source.js +58 -10
  72. package/esm/plugins/video/config.d.ts +21 -1
  73. package/esm/plugins/video/config.js +22 -2
  74. package/package.json +1 -1
  75. package/types/config.d.ts +40 -1
  76. package/types/modules/widget/file-selector/file-selector.d.ts +10 -1
  77. package/types/plugins/file/file.d.ts +24 -1
  78. package/types/plugins/image-processor/image-processor.d.ts +5 -0
  79. package/types/plugins/source/source.d.ts +17 -0
  80. package/types/plugins/video/config.d.ts +21 -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.16.0
4
+ * Version: v4.17.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.16.0
4
+ * Version: v4.17.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.16.0
4
+ * Version: v4.17.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.16.0
4
+ * Version: v4.17.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.16.0
4
+ * Version: v4.17.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.16.0
4
+ * Version: v4.17.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.16.0
4
+ * Version: v4.17.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.16.0
4
+ * Version: v4.17.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.16.0
4
+ * Version: v4.17.0
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
package/esm/config.d.ts CHANGED
@@ -8,9 +8,10 @@ import type { HTMLTagNames } from "./types/index";
8
8
  import type { AiAssistantSettings } from "./plugins/ai-assistant/interface";
9
9
  import type { IDictionary } from "./types/index";
10
10
  import type { Nullable } from "./types/index";
11
+ import type { IJodit } from "./types/index";
12
+ import type { IUploaderData } from "./types/index";
11
13
  import type { ImagePropertiesOptions } from "./plugins/image-properties/interface";
12
14
  import type { IControlType } from "./types/index";
13
- import type { IJodit } from "./types/index";
14
15
  import type { formTemplate } from "./plugins/link/template";
15
16
  import type { IUIForm } from "./types/index";
16
17
  import type { IUIOption } from "./types/index";
@@ -1264,6 +1265,27 @@ interface Config {
1264
1265
  blockLimits: IDictionary<1>;
1265
1266
  };
1266
1267
  }
1268
+ interface Config {
1269
+ file: {
1270
+ /**
1271
+ * Inserts the files uploaded from the file popup's Upload tab. By default
1272
+ * each one goes in as a link, an image as well, the way
1273
+ * `uploader.defaultHandlerSuccess` inserts a file that isn't an image.
1274
+ * ```javascript
1275
+ * Jodit.make('#editor', {
1276
+ * file: {
1277
+ * defaultHandlerSuccess(data) {
1278
+ * data.files.forEach(file => {
1279
+ * this.s.insertHTML(`<a class="file" href="${data.baseurl + file}">${file}</a>`);
1280
+ * });
1281
+ * }
1282
+ * }
1283
+ * });
1284
+ * ```
1285
+ */
1286
+ defaultHandlerSuccess?: (this: IJodit, data: IUploaderData) => void;
1287
+ };
1288
+ }
1267
1289
  interface Config {
1268
1290
  autofocus: boolean;
1269
1291
  cursorAfterAutofocus: 'start' | 'end';
@@ -1971,6 +1993,23 @@ interface Config {
1971
1993
  * Default height for video iframe. Default: 345
1972
1994
  */
1973
1995
  defaultHeight?: number;
1996
+ /**
1997
+ * Inserts the videos uploaded from the popup's Upload tab, which is shown
1998
+ * when `uploader.url` or `uploader.customUploadFunction` is set. By default
1999
+ * each one goes in as `<video controls src="…">`.
2000
+ * ```javascript
2001
+ * Jodit.make('#editor', {
2002
+ * video: {
2003
+ * defaultHandlerSuccess(data) {
2004
+ * data.files.forEach(file => {
2005
+ * this.s.insertHTML(`<video controls preload="metadata" src="${data.baseurl + file}"></video>`);
2006
+ * });
2007
+ * }
2008
+ * }
2009
+ * });
2010
+ * ```
2011
+ */
2012
+ defaultHandlerSuccess?: (this: IJodit, data: IUploaderData) => void;
1974
2013
  };
1975
2014
  }
1976
2015
  interface Config {
@@ -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.16.0";
6
+ export const APP_VERSION = "4.17.0";
7
7
  // prettier-ignore
8
8
  export const ES = "es2020";
9
9
  export const IS_ES_MODERN = true;
@@ -8,7 +8,8 @@
8
8
  * @packageDocumentation
9
9
  * @module modules/widget/file-selector
10
10
  */
11
- import type { IFileBrowserCallBackData, IJodit } from "../../../types/index";
11
+ import type { IFileBrowserCallBackData, IJodit, IUploaderData } from "../../../types/index";
12
+ import { type TabOption } from "../tabs/tabs";
12
13
  interface ImageSelectorCallbacks {
13
14
  /**
14
15
  * Function that will be called when the user enters the URL of the tab image and alternative text for images
@@ -34,4 +35,12 @@ interface ImageSelectorCallbacks {
34
35
  * functions with different parameters
35
36
  */
36
37
  export declare const FileSelectorWidget: (editor: IJodit, callbacks: ImageSelectorCallbacks, elm: HTMLElement | null, close: () => void, isImage?: boolean) => HTMLDivElement;
38
+ /**
39
+ * The Upload tab: a box that uploads the files dropped on it or chosen in its
40
+ * file input, then passes the uploader's answer to `onSuccess` and closes the popup
41
+ */
42
+ export declare const UploadTab: (editor: IJodit, onSuccess: (this: IJodit, data: IUploaderData) => void, { label, accept }: {
43
+ label: string;
44
+ accept: string;
45
+ }) => TabOption;
37
46
  export {};
@@ -25,28 +25,17 @@ export const FileSelectorWidget = (editor, callbacks, elm, close, isImage = true
25
25
  (options.url ||
26
26
  options.insertImageAsBase64URI ||
27
27
  options.customUploadFunction)) {
28
- const dragBox = editor.c.fromHTML('<div class="jodit-drag-and-drop__file-box">' +
29
- `<strong>${editor.i18n(isImage ? 'Drop image' : 'Drop file')}</strong>` +
30
- `<span><br>${editor.i18n('or click')}</span>` +
31
- `<input type="file" accept="${isImage ? 'image/*' : '*'}" tabindex="-1" dir="auto" multiple=""/>` +
32
- '</div>');
33
- editor.uploader.bind(dragBox, resp => {
34
- const handler = isFunction(callbacks.upload)
35
- ? callbacks.upload
28
+ const { upload } = callbacks;
29
+ tabs.push(UploadTab(editor, resp => {
30
+ const handler = isFunction(upload)
31
+ ? upload
36
32
  : options.defaultHandlerSuccess;
37
33
  if (isFunction(handler)) {
38
34
  handler.call(editor, resp);
39
35
  }
40
- editor.e.fire('closeAllPopups');
41
- }, error => {
42
- editor.message.error(error.message);
43
- editor.e.fire('closeAllPopups');
44
- });
45
- tabs.push({
46
- icon: 'upload',
47
- name: 'Upload',
48
- content: dragBox
49
- });
36
+ }, isImage
37
+ ? { label: 'Drop image', accept: 'image/*' }
38
+ : { label: 'Drop file', accept: '*' }));
50
39
  }
51
40
  if (callbacks.filebrowser) {
52
41
  if (editor.o.filebrowser.ajax.url || editor.o.filebrowser.items.url) {
@@ -115,6 +104,29 @@ export const FileSelectorWidget = (editor, callbacks, elm, close, isImage = true
115
104
  box.classList.add('jodit-file-selector');
116
105
  return box;
117
106
  };
107
+ /**
108
+ * The Upload tab: a box that uploads the files dropped on it or chosen in its
109
+ * file input, then passes the uploader's answer to `onSuccess` and closes the popup
110
+ */
111
+ export const UploadTab = (editor, onSuccess, { label, accept }) => {
112
+ const dragBox = editor.c.fromHTML('<div class="jodit-drag-and-drop__file-box">' +
113
+ `<strong>${editor.i18n(label)}</strong>` +
114
+ `<span><br>${editor.i18n('or click')}</span>` +
115
+ `<input type="file" accept="${accept}" tabindex="-1" dir="auto" multiple=""/>` +
116
+ '</div>');
117
+ editor.uploader.bind(dragBox, resp => {
118
+ onSuccess.call(editor, resp);
119
+ editor.e.fire('closeAllPopups');
120
+ }, error => {
121
+ editor.message.error(error.message);
122
+ editor.e.fire('closeAllPopups');
123
+ });
124
+ return {
125
+ icon: 'upload',
126
+ name: 'Upload',
127
+ content: dragBox
128
+ };
129
+ };
118
130
  function val(elm, name, value) {
119
131
  const child = Dom.first(elm, node => Dom.isTag(node, 'input') && attr(node, 'name') === name);
120
132
  if (!child) {
@@ -8,5 +8,28 @@
8
8
  * @packageDocumentation
9
9
  * @module plugins/file
10
10
  */
11
- import type { IJodit } from "../../types/index";
11
+ import type { IJodit, IUploaderData } from "../../types/index";
12
+ declare module 'jodit/config' {
13
+ interface Config {
14
+ file: {
15
+ /**
16
+ * Inserts the files uploaded from the file popup's Upload tab. By default
17
+ * each one goes in as a link, an image as well, the way
18
+ * `uploader.defaultHandlerSuccess` inserts a file that isn't an image.
19
+ * ```javascript
20
+ * Jodit.make('#editor', {
21
+ * file: {
22
+ * defaultHandlerSuccess(data) {
23
+ * data.files.forEach(file => {
24
+ * this.s.insertHTML(`<a class="file" href="${data.baseurl + file}">${file}</a>`);
25
+ * });
26
+ * }
27
+ * }
28
+ * });
29
+ * ```
30
+ */
31
+ defaultHandlerSuccess?: (this: IJodit, data: IUploaderData) => void;
32
+ };
33
+ }
34
+ }
12
35
  export declare function file(editor: IJodit): void;
@@ -8,6 +8,19 @@ import { pluginSystem } from "../../core/global.js";
8
8
  import { attr } from "../../core/helpers/utils/attr.js";
9
9
  import { Config } from "../../config.js";
10
10
  import { FileSelectorWidget } from "../../modules/widget/index.js";
11
+ function insertLinks(data) {
12
+ var _a;
13
+ (_a = data.files) === null || _a === void 0 ? void 0 : _a.forEach(file => {
14
+ const link = this.createInside.element('a', {
15
+ href: data.baseurl + file
16
+ });
17
+ link.textContent = this.o.uploader.getDisplayName.call(this.uploader, data.baseurl, file);
18
+ this.s.insertNode(link);
19
+ });
20
+ }
21
+ Config.prototype.file = {
22
+ defaultHandlerSuccess: insertLinks
23
+ };
11
24
  Config.prototype.controls.file = {
12
25
  popup: (editor, current, close) => {
13
26
  const insert = (url, title = '') => {
@@ -27,7 +40,10 @@ Config.prototype.controls.file = {
27
40
  data.files.forEach(file => insert(data.baseurl + file));
28
41
  close();
29
42
  },
30
- upload: true,
43
+ upload: (data) => {
44
+ var _a, _b;
45
+ ((_b = (_a = editor.o.file) === null || _a === void 0 ? void 0 : _a.defaultHandlerSuccess) !== null && _b !== void 0 ? _b : insertLinks).call(editor, data);
46
+ },
31
47
  url: (url, text) => {
32
48
  if (sourceAnchor) {
33
49
  attr(sourceAnchor, 'href', url);
@@ -69,6 +69,10 @@ Config.prototype.iframeStyle =
69
69
  'display: block;' +
70
70
  'background: rgba(0, 0, 0, 0);' +
71
71
  '} ' +
72
+ '.jodit-wysiwyg_image-selected::selection,' +
73
+ '.jodit-wysiwyg_image-selected *:not(img)::selection{' +
74
+ 'background:transparent' +
75
+ '}' +
72
76
  '.jodit_disabled{' +
73
77
  'user-select: none;' +
74
78
  '-o-user-select: none;' +
@@ -23,6 +23,11 @@ export declare class imageProcessor extends Plugin {
23
23
  protected onBeforeSetElementValue(data: {
24
24
  value: string;
25
25
  }): void;
26
+ /**
27
+ * While the selection is an image, only the image is highlighted: Safari
28
+ * paints the rest of a block image's line with its parent's highlight. See #1528
29
+ */
30
+ private onSelectionChange;
26
31
  protected afterChange(data: {
27
32
  value: string;
28
33
  }): Promise<void>;
@@ -14,7 +14,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
14
14
  return c > 3 && r && Object.defineProperty(target, key, r), r;
15
15
  };
16
16
  import { INVISIBLE_SPACE_REG_EXP, SOURCE_CONSUMER } from "../../core/constants.js";
17
- import { cached, debounce, watch } from "../../core/decorators/index.js";
17
+ import { autobind, cached, debounce, watch } from "../../core/decorators/index.js";
18
18
  import { Dom } from "../../core/dom/dom.js";
19
19
  import { pluginSystem } from "../../core/global.js";
20
20
  import { $$, dataBind } from "../../core/helpers/index.js";
@@ -23,12 +23,17 @@ import { dataURItoBlob } from "../../modules/uploader/helpers/data-uri-to-blob.j
23
23
  import "./config.js";
24
24
  const JODIT_IMAGE_PROCESSOR_BINDED = '__jodit_imageprocessor_binded';
25
25
  const JODIT_IMAGE_BLOB_ID = JODIT_IMAGE_PROCESSOR_BINDED + 'blob-id';
26
+ const IMAGE_SELECTED = 'jodit-wysiwyg_image-selected';
26
27
  /**
27
28
  * Change editor's size after load all images
28
29
  */
29
30
  export class imageProcessor extends Plugin {
30
- afterInit(jodit) { }
31
+ afterInit(jodit) {
32
+ jodit.e.on(jodit.ed, 'selectionchange', this.onSelectionChange);
33
+ }
31
34
  beforeDestruct(jodit) {
35
+ jodit.e.off(jodit.ed, 'selectionchange', this.onSelectionChange);
36
+ jodit.editor.classList.remove(IMAGE_SELECTED);
32
37
  const buffer = cached(jodit, 'buffer');
33
38
  const list = buffer === null || buffer === void 0 ? void 0 : buffer.get(JODIT_IMAGE_BLOB_ID);
34
39
  if (buffer && list) {
@@ -59,6 +64,14 @@ export class imageProcessor extends Plugin {
59
64
  }
60
65
  }
61
66
  }
67
+ /**
68
+ * While the selection is an image, only the image is highlighted: Safari
69
+ * paints the rest of a block image's line with its parent's highlight. See #1528
70
+ */
71
+ onSelectionChange() {
72
+ const { jodit } = this;
73
+ jodit.editor.classList.toggle(IMAGE_SELECTED, isImageSelected(jodit));
74
+ }
62
75
  async afterChange(data) {
63
76
  const { jodit: editor } = this;
64
77
  if (!editor.editor) {
@@ -91,10 +104,24 @@ __decorate([
91
104
  __decorate([
92
105
  watch(':beforeSetElementValue')
93
106
  ], imageProcessor.prototype, "onBeforeSetElementValue", null);
107
+ __decorate([
108
+ autobind
109
+ ], imageProcessor.prototype, "onSelectionChange", null);
94
110
  __decorate([
95
111
  watch([':change', ':afterInit', ':changePlace']),
96
112
  debounce()
97
113
  ], imageProcessor.prototype, "afterChange", null);
114
+ function isImageSelected(jodit) {
115
+ const { sel } = jodit.s;
116
+ if (!sel || !sel.rangeCount) {
117
+ return false;
118
+ }
119
+ const { startContainer, startOffset, endContainer, endOffset } = sel.getRangeAt(0);
120
+ return (startContainer === endContainer &&
121
+ endOffset === startOffset + 1 &&
122
+ Dom.isTag(startContainer.childNodes[startOffset], 'img') &&
123
+ Dom.isOrContains(jodit.editor, startContainer));
124
+ }
98
125
  /**
99
126
  * Remove the invisible-space text beside an image, which only held a caret.
100
127
  * Selecting the image replaces the caret, and with the text still after a
@@ -10,6 +10,26 @@ import { css } from "../../../../core/helpers/utils/css.js";
10
10
  // Images align with `imageAlignClasses`; the wrappers of iframes and media
11
11
  // keep inline styles
12
12
  const alignClasses = (editor, elm) => Dom.isTag(elm, 'img') ? editor.o.imageAlignClasses : null;
13
+ // A Normal image sits where the text of its line is aligned (#1531)
14
+ const lineAlign = (editor, elm) => {
15
+ const block = (elm && Dom.closest(elm, Dom.isBlock, editor.editor)) || editor.editor;
16
+ const rtl = css(block, 'direction') === 'rtl';
17
+ switch (css(block, 'text-align')) {
18
+ case 'center':
19
+ case '-webkit-center':
20
+ return 'center';
21
+ case 'justify':
22
+ return 'justify';
23
+ case 'right':
24
+ return 'right';
25
+ case 'end':
26
+ return rtl ? 'left' : 'right';
27
+ case 'start':
28
+ return rtl ? 'right' : 'left';
29
+ default:
30
+ return 'left';
31
+ }
32
+ };
13
33
  const currentAlign = (editor, elm) => (elm ? getHAlign(elm, alignClasses(editor, elm)) : 'normal');
14
34
  export const align = {
15
35
  name: 'left',
@@ -17,7 +37,8 @@ export const align = {
17
37
  list: ['Left', 'Right', 'Center', 'Normal'],
18
38
  update: (editor, button) => {
19
39
  const current = currentAlign(editor, button.target);
20
- button.state.icon.name = current === 'normal' ? 'left' : current;
40
+ button.state.icon.name =
41
+ current === 'normal' ? lineAlign(editor, button.target) : current;
21
42
  },
22
43
  isActive: (editor, button) => currentAlign(editor, button.target) !== 'normal',
23
44
  isChildActive: (editor, button) => {
@@ -146,7 +146,8 @@ export class AceEditor extends SourceEditor {
146
146
  }
147
147
  return;
148
148
  }
149
- this.fromWYSIWYG();
149
+ // The source plugin carries the fallback editor's text over when this
150
+ // one is ready; refilling from WYSIWYG here would drop unsynced typing
150
151
  tryInitAceEditor();
151
152
  };
152
153
  editor.e.on('afterSetMode', initAceEditor);
@@ -21,6 +21,11 @@ export declare class source extends Plugin {
21
21
  private mirrorContainer;
22
22
  private __lock;
23
23
  private __oldMirrorValue;
24
+ /**
25
+ * What the source view was last filled with from WYSIWYG, read back from
26
+ * the editor; while the view still holds it, nothing has been typed there
27
+ */
28
+ private __filledValue;
24
29
  private tempMarkerStart;
25
30
  private tempMarkerStartReg;
26
31
  private tempMarkerEnd;
@@ -50,6 +55,18 @@ export declare class source extends Plugin {
50
55
  afterInit(editor: IJodit): void;
51
56
  private syncValueFromWYSIWYG;
52
57
  private initSourceEditor;
58
+ /**
59
+ * Replaces the source editor (the textarea shown while ACE loads) with
60
+ * the one that just became ready, carrying its text, selection and focus
61
+ * over. Filling the new editor from WYSIWYG instead would drop what was
62
+ * typed since the last, debounced, sync and normalise half-typed markup.
63
+ */
64
+ private takeOver;
65
+ /**
66
+ * The beautifier arrived after the source view was opened: format the
67
+ * view if nothing has been typed into it, otherwise leave the typing alone
68
+ */
69
+ private beautifyUntouched;
53
70
  /** @override */
54
71
  beforeDestruct(): void;
55
72
  }
@@ -37,6 +37,11 @@ export class source extends Plugin {
37
37
  ];
38
38
  this.__lock = false;
39
39
  this.__oldMirrorValue = '';
40
+ /**
41
+ * What the source view was last filled with from WYSIWYG, read back from
42
+ * the editor; while the view still holds it, nothing has been typed there
43
+ */
44
+ this.__filledValue = '';
40
45
  this.tempMarkerStart = '{start-jodit-selection}';
41
46
  this.tempMarkerStartReg = /{start-jodit-selection}/g;
42
47
  this.tempMarkerEnd = '{end-jodit-selection}';
@@ -69,6 +74,7 @@ export class source extends Plugin {
69
74
  if (new_value !== this.getMirrorValue()) {
70
75
  this.setMirrorValue(new_value);
71
76
  }
77
+ this.__filledValue = this.getMirrorValue();
72
78
  this.__lock = false;
73
79
  }
74
80
  }
@@ -272,11 +278,7 @@ export class source extends Plugin {
272
278
  }
273
279
  loading = true;
274
280
  loadNext(editor, editor.o.beautifyHTMLCDNUrlsJS).then(() => {
275
- // The source view was filled before the
276
- // beautifier arrived — format it now.
277
- if (addEventListener()) {
278
- this.syncValueFromWYSIWYG(true);
279
- }
281
+ addEventListener() && this.beautifyUntouched();
280
282
  }, () => null);
281
283
  };
282
284
  editor.e.on('afterSetMode.source', loadBeautify);
@@ -298,11 +300,9 @@ export class source extends Plugin {
298
300
  if (editor.o.sourceEditor !== 'area') {
299
301
  const sourceEditor = createSourceEditor(editor.o.sourceEditor, editor, this.mirrorContainer, this.toWYSIWYG, this.fromWYSIWYG);
300
302
  sourceEditor.onReadyAlways(() => {
301
- var _a, _b;
302
- (_a = this.sourceEditor) === null || _a === void 0 ? void 0 : _a.destruct();
303
- this.sourceEditor = sourceEditor;
304
- this.syncValueFromWYSIWYG(true);
305
- (_b = editor.events) === null || _b === void 0 ? void 0 : _b.fire('sourceEditorReady', editor);
303
+ var _a;
304
+ this.takeOver(sourceEditor);
305
+ (_a = editor.events) === null || _a === void 0 ? void 0 : _a.fire('sourceEditorReady', editor);
306
306
  });
307
307
  }
308
308
  else {
@@ -313,6 +313,54 @@ export class source extends Plugin {
313
313
  });
314
314
  }
315
315
  }
316
+ /**
317
+ * Replaces the source editor (the textarea shown while ACE loads) with
318
+ * the one that just became ready, carrying its text, selection and focus
319
+ * over. Filling the new editor from WYSIWYG instead would drop what was
320
+ * typed since the last, debounced, sync and normalise half-typed markup.
321
+ */
322
+ takeOver(sourceEditor) {
323
+ const previous = this.sourceEditor;
324
+ if (!previous || previous === sourceEditor) {
325
+ this.sourceEditor = sourceEditor;
326
+ this.syncValueFromWYSIWYG(true);
327
+ return;
328
+ }
329
+ const value = previous.getValue(), start = previous.getSelectionStart(), end = previous.getSelectionEnd(), focused = previous.isFocused, untouched = value === this.__filledValue;
330
+ previous.destruct();
331
+ this.sourceEditor = sourceEditor;
332
+ if (untouched) {
333
+ this.syncValueFromWYSIWYG(true);
334
+ }
335
+ else {
336
+ sourceEditor.setValue(value);
337
+ this.toWYSIWYG();
338
+ }
339
+ if (start >= 0) {
340
+ sourceEditor.setSelectionRange(start, end);
341
+ }
342
+ focused && sourceEditor.focus();
343
+ }
344
+ /**
345
+ * The beautifier arrived after the source view was opened: format the
346
+ * view if nothing has been typed into it, otherwise leave the typing alone
347
+ */
348
+ beautifyUntouched() {
349
+ var _a, _b;
350
+ const value = this.getMirrorValue();
351
+ if (this.j.o.editHTMLDocumentMode ||
352
+ !value ||
353
+ value !== this.__filledValue) {
354
+ return;
355
+ }
356
+ const html = this.j.e.fire('beautifyHTML', value);
357
+ if (isString(html) && html !== value) {
358
+ const focused = (_a = this.sourceEditor) === null || _a === void 0 ? void 0 : _a.isFocused;
359
+ this.setMirrorValue(html);
360
+ this.__filledValue = this.getMirrorValue();
361
+ focused && ((_b = this.sourceEditor) === null || _b === void 0 ? void 0 : _b.focus());
362
+ }
363
+ }
316
364
  /** @override */
317
365
  beforeDestruct() {
318
366
  if (this.sourceEditor) {
@@ -3,6 +3,10 @@
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
+ /**
7
+ * @module plugins/video
8
+ */
9
+ import type { IJodit, IUploaderData } from "../../types/index";
6
10
  declare module 'jodit/config' {
7
11
  interface Config {
8
12
  video: {
@@ -29,7 +33,23 @@ declare module 'jodit/config' {
29
33
  * Default height for video iframe. Default: 345
30
34
  */
31
35
  defaultHeight?: number;
36
+ /**
37
+ * Inserts the videos uploaded from the popup's Upload tab, which is shown
38
+ * when `uploader.url` or `uploader.customUploadFunction` is set. By default
39
+ * each one goes in as `<video controls src="…">`.
40
+ * ```javascript
41
+ * Jodit.make('#editor', {
42
+ * video: {
43
+ * defaultHandlerSuccess(data) {
44
+ * data.files.forEach(file => {
45
+ * this.s.insertHTML(`<video controls preload="metadata" src="${data.baseurl + file}"></video>`);
46
+ * });
47
+ * }
48
+ * }
49
+ * });
50
+ * ```
51
+ */
52
+ defaultHandlerSuccess?: (this: IJodit, data: IUploaderData) => void;
32
53
  };
33
54
  }
34
55
  }
35
- export {};