jodit 3.18.2 → 3.18.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/.idea/workspace.xml +114 -101
  2. package/CHANGELOG.MD +71 -35
  3. package/README.md +2 -2
  4. package/build/jodit.css +66 -5
  5. package/build/jodit.es2018.css +19 -4
  6. package/build/jodit.es2018.en.css +19 -4
  7. package/build/jodit.es2018.en.js +326 -91
  8. package/build/jodit.es2018.en.min.css +1 -1
  9. package/build/jodit.es2018.en.min.js +1 -1
  10. package/build/jodit.es2018.js +326 -91
  11. package/build/jodit.es2018.min.css +1 -1
  12. package/build/jodit.es2018.min.js +1 -1
  13. package/build/jodit.js +2036 -1755
  14. package/build/jodit.min.css +2 -2
  15. package/build/jodit.min.js +1 -1
  16. package/build/vdom.css +1 -1
  17. package/build/vdom.js +5 -3
  18. package/package.json +1 -1
  19. package/src/config.ts +5 -1
  20. package/src/core/async/async.ts +5 -3
  21. package/src/core/helpers/html/safe-html.ts +4 -0
  22. package/src/core/helpers/utils/error/error.ts +34 -0
  23. package/src/core/helpers/utils/error/errors/abort-error.ts +16 -0
  24. package/src/core/helpers/utils/error/errors/connection-error.ts +16 -0
  25. package/src/core/helpers/utils/{error.ts → error/errors/index.ts} +3 -6
  26. package/src/core/helpers/utils/error/errors/options-error.ts +16 -0
  27. package/{types/core/helpers/utils/error.d.ts → src/core/helpers/utils/error/index.ts} +4 -4
  28. package/src/core/helpers/utils/reset.ts +4 -0
  29. package/src/core/helpers/utils/scroll-into-view.ts +4 -0
  30. package/src/core/request/ajax.ts +22 -11
  31. package/src/modules/file-browser/data-provider.ts +11 -9
  32. package/src/modules/file-browser/fetch/load-items.ts +5 -3
  33. package/src/modules/file-browser/fetch/load-tree.ts +3 -10
  34. package/src/modules/file-browser/file-browser.test.js +1 -1
  35. package/src/modules/file-browser/file-browser.ts +10 -1
  36. package/src/modules/file-browser/listeners/self-listeners.ts +4 -4
  37. package/src/modules/table/table.test.js +1 -1
  38. package/src/modules/uploader/README.md +18 -0
  39. package/src/modules/uploader/config.ts +9 -0
  40. package/src/modules/uploader/helpers/send-files.ts +10 -2
  41. package/src/modules/uploader/uploader.test.js +36 -0
  42. package/src/plugins/clipboard/paste/helpers.ts +13 -4
  43. package/src/plugins/fix/clean-html/clean-html.test.js +3 -3
  44. package/src/plugins/fix/clean-html/helpers/remove-format/remove-format-for-selection.ts +1 -1
  45. package/src/plugins/print/helpers.ts +1 -1
  46. package/src/plugins/print/lib/generate-critical-css.ts +135 -0
  47. package/src/plugins/print/print.ts +7 -4
  48. package/src/plugins/source/editor/engines/ace.ts +4 -0
  49. package/src/plugins/source/editor/engines/area.ts +4 -0
  50. package/src/plugins/source/source.ts +10 -12
  51. package/src/plugins/speech/speech-recognize/README.md +0 -0
  52. package/src/plugins/speech/speech-recognize/config.ts +23 -0
  53. package/src/plugins/speech/speech-recognize/helpers/microphone-input.ts +218 -0
  54. package/src/plugins/speech/speech-recognize/helpers/voice-command.ts +118 -0
  55. package/src/plugins/speech/speech-recognize/icon.svg +5 -0
  56. package/src/plugins/speech/speech-recognize/speech-recognize.ts +38 -0
  57. package/src/plugins/table/config.ts +0 -4
  58. package/src/plugins/table/table.common.less +1 -4
  59. package/src/plugins/table/table.test.js +43 -0
  60. package/src/styles/mixins.less +8 -0
  61. package/src/types/create.d.ts +1 -1
  62. package/src/types/events.d.ts +12 -1
  63. package/src/types/source.d.ts +1 -0
  64. package/src/types/uploader.d.ts +20 -0
  65. package/types/core/helpers/utils/error/error.d.ts +13 -0
  66. package/types/core/helpers/utils/error/errors/abort-error.d.ts +11 -0
  67. package/types/core/helpers/utils/error/errors/connection-error.d.ts +11 -0
  68. package/types/core/helpers/utils/error/errors/index.d.ts +11 -0
  69. package/types/core/helpers/utils/error/errors/options-error.d.ts +11 -0
  70. package/types/core/helpers/utils/error/index.d.ts +10 -0
  71. package/types/core/helpers/utils/reset.d.ts +3 -0
  72. package/types/core/request/ajax.d.ts +1 -1
  73. package/types/plugins/print/lib/generate-critical-css.d.ts +7 -0
  74. package/types/plugins/source/editor/engines/ace.d.ts +1 -0
  75. package/types/plugins/source/editor/engines/area.d.ts +1 -0
  76. package/types/types/create.d.ts +1 -1
  77. package/types/types/events.d.ts +12 -1
  78. package/types/types/source.d.ts +1 -0
  79. package/types/types/uploader.d.ts +20 -0
package/build/vdom.css CHANGED
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * jodit - Jodit is awesome and usefully wysiwyg editor with filebrowser
3
3
  * Author: Chupurnov <chupurnov@gmail.com> (https://xdsoft.net/)
4
- * Version: v3.18.2
4
+ * Version: v3.18.5
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
package/build/vdom.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * jodit - Jodit is awesome and usefully wysiwyg editor with filebrowser
3
3
  * Author: Chupurnov <chupurnov@gmail.com> (https://xdsoft.net/)
4
- * Version: v3.18.2
4
+ * Version: v3.18.5
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
@@ -834,9 +834,11 @@ var Async = (function () {
834
834
  return promise;
835
835
  };
836
836
  }
837
- promise.finally(function () {
837
+ promise
838
+ .finally(function () {
838
839
  _this.promisesRejections.delete(rejectCallback);
839
- });
840
+ })
841
+ .catch(function () { return null; });
840
842
  promise.rejectCallback = rejectCallback;
841
843
  return promise;
842
844
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jodit",
3
- "version": "3.18.2",
3
+ "version": "3.18.5",
4
4
  "description": "Jodit is awesome and usefully wysiwyg editor with filebrowser",
5
5
  "main": "build/jodit.min.js",
6
6
  "types": "./types/index.d.ts",
package/src/config.ts CHANGED
@@ -584,7 +584,11 @@ export class Config implements IViewOptions {
584
584
  * <JoditEditor config={config}/>
585
585
  * ```
586
586
  */
587
- createAttributes: IDictionary<Attributes | NodeFunction> = {};
587
+ createAttributes: IDictionary<Attributes | NodeFunction> = {
588
+ table: {
589
+ style: 'border-collapse:collapse;width: 100%;'
590
+ }
591
+ };
588
592
 
589
593
  /**
590
594
  * The width of the editor, accepted as the biggest. Used to the responsive version of the editor
@@ -228,9 +228,11 @@ export class Async implements IAsync {
228
228
  };
229
229
  }
230
230
 
231
- promise.finally(() => {
232
- this.promisesRejections.delete(rejectCallback);
233
- });
231
+ promise
232
+ .finally(() => {
233
+ this.promisesRejections.delete(rejectCallback);
234
+ })
235
+ .catch(() => null);
234
236
 
235
237
  (promise as RejectablePromise<T>).rejectCallback = rejectCallback;
236
238
 
@@ -4,6 +4,10 @@
4
4
  * Copyright (c) 2013-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
6
 
7
+ /**
8
+ * @module helpers/html
9
+ */
10
+
7
11
  import { $$, attr } from '../utils';
8
12
  import { Dom } from 'jodit/core/dom/dom';
9
13
 
@@ -0,0 +1,34 @@
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-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
+ */
6
+
7
+ /**
8
+ * @module helpers/utils
9
+ */
10
+
11
+ import { AbortError, ConnectionError, OptionsError } from './errors';
12
+
13
+ /**
14
+ * Helper for create Error object
15
+ */
16
+ export function error(message: string): Error {
17
+ return new TypeError(message);
18
+ }
19
+
20
+ export function connection(message: string): Error {
21
+ return new ConnectionError(message);
22
+ }
23
+
24
+ export function options(message: string): Error {
25
+ return new OptionsError(message);
26
+ }
27
+
28
+ export function abort(message: string): Error {
29
+ return new AbortError(message);
30
+ }
31
+
32
+ export function isAbort(error: unknown): boolean {
33
+ return error instanceof AbortError;
34
+ }
@@ -0,0 +1,16 @@
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-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
+ */
6
+
7
+ /**
8
+ * @module helpers/utils
9
+ */
10
+
11
+ export class AbortError extends Error {
12
+ constructor(m: string) {
13
+ super(m);
14
+ Object.setPrototypeOf(this, AbortError.prototype);
15
+ }
16
+ }
@@ -0,0 +1,16 @@
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-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
+ */
6
+
7
+ /**
8
+ * @module helpers/utils
9
+ */
10
+
11
+ export class ConnectionError extends Error {
12
+ constructor(m: string) {
13
+ super(m);
14
+ Object.setPrototypeOf(this, ConnectionError.prototype);
15
+ }
16
+ }
@@ -8,9 +8,6 @@
8
8
  * @module helpers/utils
9
9
  */
10
10
 
11
- /**
12
- * Helper for create Error object
13
- */
14
- export function error(message: string): Error {
15
- return new TypeError(message);
16
- }
11
+ export * from './abort-error';
12
+ export * from './connection-error';
13
+ export * from './options-error';
@@ -0,0 +1,16 @@
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-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
+ */
6
+
7
+ /**
8
+ * @module helpers/utils
9
+ */
10
+
11
+ export class OptionsError extends TypeError {
12
+ constructor(m: string) {
13
+ super(m);
14
+ Object.setPrototypeOf(this, OptionsError.prototype);
15
+ }
16
+ }
@@ -3,10 +3,10 @@
3
3
  * Released under MIT see LICENSE.txt in the project root for license information.
4
4
  * Copyright (c) 2013-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
+
6
7
  /**
7
8
  * @module helpers/utils
8
9
  */
9
- /**
10
- * Helper for create Error object
11
- */
12
- export declare function error(message: string): Error;
10
+
11
+ export * from './error';
12
+ export * from './errors/';
@@ -4,6 +4,10 @@
4
4
  * Copyright (c) 2013-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
6
 
7
+ /**
8
+ * @module helpers/utils
9
+ */
10
+
7
11
  import type { IDictionary, Nullable } from 'jodit/types';
8
12
  import { get } from './get';
9
13
  import { isFunction } from '../checker/is-function';
@@ -4,6 +4,10 @@
4
4
  * Copyright (c) 2013-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
6
 
7
+ /**
8
+ * @module helpers/utils
9
+ */
10
+
7
11
  import { Dom } from 'jodit/core/dom/dom';
8
12
  import type { Nullable } from 'jodit/types';
9
13
 
@@ -23,7 +23,6 @@ import type {
23
23
  import { Config } from 'jodit/config';
24
24
 
25
25
  import {
26
- error,
27
26
  isPlainObject,
28
27
  parseQuery,
29
28
  buildQuery,
@@ -31,6 +30,7 @@ import {
31
30
  isFunction,
32
31
  ConfigProto
33
32
  } from 'jodit/core/helpers';
33
+ import * as error from 'jodit/core/helpers/utils/error';
34
34
  import { Response } from './response';
35
35
 
36
36
  import './config';
@@ -83,14 +83,19 @@ export class Ajax<T extends object = any> implements IAjax<T> {
83
83
  }
84
84
 
85
85
  abort(): Ajax {
86
+ if (this.isFulfilled) {
87
+ return this;
88
+ }
89
+
86
90
  try {
91
+ this.isFulfilled = true;
87
92
  this.xhr.abort();
88
93
  } catch {}
89
94
 
90
95
  return this;
91
96
  }
92
97
 
93
- private resolved = false;
98
+ private isFulfilled = false;
94
99
 
95
100
  private activated = false;
96
101
 
@@ -103,11 +108,12 @@ export class Ajax<T extends object = any> implements IAjax<T> {
103
108
 
104
109
  return this.j.async.promise((resolve, reject) => {
105
110
  const onReject = (): void => {
106
- reject(error('Connection error'));
111
+ this.isFulfilled = true;
112
+ reject(error.connection('Connection error'));
107
113
  };
108
114
 
109
115
  const onResolve = (): void => {
110
- this.resolved = true;
116
+ this.isFulfilled = true;
111
117
 
112
118
  resolve(
113
119
  new Response<T>(
@@ -120,7 +126,11 @@ export class Ajax<T extends object = any> implements IAjax<T> {
120
126
  };
121
127
 
122
128
  xhr.onload = onResolve;
123
- xhr.onabort = onReject;
129
+ xhr.onabort = (): void => {
130
+ this.isFulfilled = true;
131
+ reject(error.abort('Abort connection'));
132
+ };
133
+
124
134
  xhr.onerror = onReject;
125
135
  xhr.ontimeout = onReject;
126
136
 
@@ -144,8 +154,9 @@ export class Ajax<T extends object = any> implements IAjax<T> {
144
154
  if (xhr.readyState === XMLHttpRequest.DONE) {
145
155
  if (o.successStatuses.includes(xhr.status)) {
146
156
  onResolve();
147
- } else {
148
- reject(error(xhr.statusText || 'Connection error'));
157
+ } else if (xhr.statusText) {
158
+ this.isFulfilled = true;
159
+ reject(error.connection(xhr.statusText));
149
160
  }
150
161
  }
151
162
  };
@@ -177,7 +188,7 @@ export class Ajax<T extends object = any> implements IAjax<T> {
177
188
 
178
189
  prepareRequest(): IRequest {
179
190
  if (!this.o.url) {
180
- throw error('Need URL for AJAX request');
191
+ throw error.error('Need URL for AJAX request');
181
192
  }
182
193
 
183
194
  let url: string = this.o.url;
@@ -192,7 +203,7 @@ export class Ajax<T extends object = any> implements IAjax<T> {
192
203
  const urlData = parseQuery(url);
193
204
 
194
205
  url =
195
- url.substr(0, qIndex) +
206
+ url.substring(0, qIndex) +
196
207
  '?' +
197
208
  buildQuery({ ...urlData, ...(data as IDictionary) });
198
209
  } else {
@@ -213,9 +224,9 @@ export class Ajax<T extends object = any> implements IAjax<T> {
213
224
  }
214
225
 
215
226
  destruct(): void {
216
- if (this.activated && !this.resolved) {
227
+ if (this.activated && !this.isFulfilled) {
217
228
  this.abort();
218
- this.resolved = true;
229
+ this.isFulfilled = true;
219
230
  }
220
231
  }
221
232
  }
@@ -106,12 +106,14 @@ export default class DataProvider implements IFileBrowserDataProvider {
106
106
 
107
107
  const promise = ajax.send();
108
108
 
109
- promise.finally(() => {
110
- ajax.destruct();
111
- ai.delete(name);
109
+ promise
110
+ .finally(() => {
111
+ ajax.destruct();
112
+ ai.delete(name);
112
113
 
113
- this.progressHandler(100);
114
- });
114
+ this.progressHandler(100);
115
+ })
116
+ .catch(() => null);
115
117
 
116
118
  return promise
117
119
  .then(resp => resp.json())
@@ -155,7 +157,7 @@ export default class DataProvider implements IFileBrowserDataProvider {
155
157
  }
156
158
 
157
159
  if (process) {
158
- const respData: IFileBrowserAnswer = process.call(
160
+ const respData = process.call(
159
161
  self,
160
162
  resp
161
163
  ) as IFileBrowserAnswer;
@@ -191,7 +193,7 @@ export default class DataProvider implements IFileBrowserDataProvider {
191
193
  /**
192
194
  * Load items list by path and source
193
195
  */
194
- async items(
196
+ items(
195
197
  path: string,
196
198
  source: string,
197
199
  mods: IFileBrowserDataProviderItemsMods = {}
@@ -262,12 +264,12 @@ export default class DataProvider implements IFileBrowserDataProvider {
262
264
  async tree(path: string, source: string): Promise<ISourcesFiles> {
263
265
  path = normalizeRelativePath(path);
264
266
 
265
- await this.permissions(path, source);
266
-
267
267
  if (!this.o.folder) {
268
268
  return Promise.reject('Set Folder Api options');
269
269
  }
270
270
 
271
+ await this.permissions(path, source);
272
+
271
273
  this.o.folder.data.path = path;
272
274
  this.o.folder.data.source = source;
273
275
 
@@ -13,7 +13,7 @@ import type { IFileBrowser } from 'jodit/types';
13
13
  /**
14
14
  * Loads a list of files and adds them to the state
15
15
  */
16
- export async function loadItems(fb: IFileBrowser): Promise<any> {
16
+ export function loadItems(fb: IFileBrowser): Promise<any> {
17
17
  fb.files.setMod('active', true);
18
18
  fb.files.setMod('loading', true);
19
19
 
@@ -24,8 +24,10 @@ export async function loadItems(fb: IFileBrowser): Promise<any> {
24
24
  filterWord: fb.state.filterWord
25
25
  })
26
26
  .then(resp => {
27
- fb.state.elements = resp;
28
- fb.state.activeElements = [];
27
+ if (resp) {
28
+ fb.state.elements = resp;
29
+ fb.state.activeElements = [];
30
+ }
29
31
  })
30
32
  .catch(fb.status)
31
33
  .finally(() => fb.files.setMod('loading', false));
@@ -9,7 +9,6 @@
9
9
  */
10
10
 
11
11
  import type { IFileBrowser } from 'jodit/types';
12
- import { error } from 'jodit/core/helpers/utils/error';
13
12
  import { Dom } from 'jodit/core/dom';
14
13
  import { loadItems } from 'jodit/modules/file-browser/fetch/load-items';
15
14
 
@@ -17,10 +16,6 @@ import { loadItems } from 'jodit/modules/file-browser/fetch/load-items';
17
16
  * Loads a list of directories
18
17
  */
19
18
  export async function loadTree(fb: IFileBrowser): Promise<any> {
20
- const errorUni = (e: string | Error): Error => {
21
- throw e instanceof Error ? e : error(e);
22
- };
23
-
24
19
  fb.tree.setMod('active', true);
25
20
 
26
21
  Dom.detach(fb.tree.container);
@@ -35,15 +30,13 @@ export async function loadTree(fb: IFileBrowser): Promise<any> {
35
30
  .then(resp => {
36
31
  fb.state.sources = resp;
37
32
  })
38
- .catch(e => {
39
- errorUni(e);
40
- })
33
+ .catch(fb.status)
41
34
  .finally(() => fb.tree.setMod('loading', false));
42
35
 
43
- return Promise.all([tree, items]).catch(error);
36
+ return Promise.all([tree, items]);
44
37
  }
45
38
 
46
39
  fb.tree.setMod('active', false);
47
40
 
48
- return items.catch(error);
41
+ return items;
49
42
  }
@@ -999,7 +999,7 @@ describe('Jodit FileBrowser Tests', function () {
999
999
  done();
1000
1000
  });
1001
1001
 
1002
- simulateEvent('drop', 0, editor.editor, function (data) {
1002
+ simulateEvent('drop', editor.editor, function (data) {
1003
1003
  Object.defineProperty(data, 'dataTransfer', {
1004
1004
  value: {
1005
1005
  files: [
@@ -38,7 +38,8 @@ import {
38
38
  isFunction,
39
39
  isString,
40
40
  ConfigProto,
41
- trim
41
+ trim,
42
+ isAbort
42
43
  } from 'jodit/core/helpers';
43
44
  import { ViewWithToolbar } from 'jodit/core/view/view-with-toolbar';
44
45
 
@@ -128,6 +129,10 @@ export class FileBrowser extends ViewWithToolbar implements IFileBrowser {
128
129
  }
129
130
 
130
131
  private errorHandler = (resp: Error | IFileBrowserAnswer): void => {
132
+ if (isAbort(resp)) {
133
+ return;
134
+ }
135
+
131
136
  if (resp instanceof Error) {
132
137
  this.status(this.i18n(resp.message));
133
138
  } else {
@@ -162,6 +167,10 @@ export class FileBrowser extends ViewWithToolbar implements IFileBrowser {
162
167
  */
163
168
  @autobind
164
169
  status(message: string | Error, success?: boolean): void {
170
+ if (!message || isAbort(message)) {
171
+ return;
172
+ }
173
+
165
174
  if (!isString(message)) {
166
175
  message = message.message;
167
176
  }
@@ -32,13 +32,13 @@ export function selfListeners(this: IFileBrowser): void {
32
32
  .on('sort.filebrowser', (value: string) => {
33
33
  if (value !== state.sortBy) {
34
34
  state.sortBy = value;
35
- loadItems(self).catch(self.status);
35
+ loadItems(self);
36
36
  }
37
37
  })
38
38
  .on('filter.filebrowser', (value: string) => {
39
39
  if (value !== state.filterWord) {
40
40
  state.filterWord = value;
41
- loadItems(self).catch(self.status);
41
+ loadItems(self);
42
42
  }
43
43
  })
44
44
  .on('openFolder.filebrowser', (data: IDictionary): void => {
@@ -167,7 +167,7 @@ export function selfListeners(this: IFileBrowser): void {
167
167
  self.state.activeElements = [];
168
168
  self.status(message, true);
169
169
 
170
- loadItems(self).catch(self.status);
170
+ loadItems(self);
171
171
  })
172
172
  .catch(self.status);
173
173
 
@@ -180,6 +180,6 @@ export function selfListeners(this: IFileBrowser): void {
180
180
  }
181
181
  )
182
182
  .on('update.filebrowser', () => {
183
- loadTree(this).then(this.status);
183
+ loadTree(this).then(this.status, this.status);
184
184
  });
185
185
  }
@@ -861,7 +861,7 @@ describe('Tables Jodit Editor Tests', function () {
861
861
  editor.s.insertNode(editor.createInside.text('ok'));
862
862
 
863
863
  expect(editor.value).equals(
864
- '<table><tr><td></td><td>ok</td></tr></table>'
864
+ '<table style="border-collapse:collapse;width: 100%;"><tr><td></td><td>ok</td></tr></table>'
865
865
  );
866
866
  });
867
867
 
@@ -105,6 +105,24 @@ Default success result processor. In first param it get `uploader.process` resul
105
105
 
106
106
  Default error result processor.
107
107
 
108
+ ## uploader.processFileName
109
+
110
+ - Type: `function`
111
+ - Default: `(key, file, name) => [key, file, name]`
112
+
113
+ The method can be used to change the name of the uploaded file.
114
+
115
+ ```js
116
+ Jodit.make('#editor', {
117
+ uploader: {
118
+ url: 'some-connector.php',
119
+ processFileName: (key, file, name) => {
120
+ return [key, file, 'some-prefix_' + name];
121
+ }
122
+ }
123
+ });
124
+ ```
125
+
108
126
  ## Examples
109
127
 
110
128
  ### Example 1
@@ -68,6 +68,15 @@ Config.prototype.uploader = {
68
68
  : '';
69
69
  },
70
70
 
71
+ processFileName(
72
+ this: IUploader,
73
+ key: string,
74
+ file: File,
75
+ name: string
76
+ ): [string, File, string] {
77
+ return [key, file, name];
78
+ },
79
+
71
80
  process(this: IUploader, resp: IUploaderAnswer): IUploaderData {
72
81
  return resp.data;
73
82
  },
@@ -96,6 +96,7 @@ export function sendFiles(
96
96
  file = fileList[i];
97
97
 
98
98
  if (file) {
99
+ const hasRealExtension = /\.[\d\w]+$/.test(file.name);
99
100
  const mime = file.type.match(/\/([a-z0-9]+)/i) as string[];
100
101
 
101
102
  const extension: string =
@@ -105,7 +106,7 @@ export function sendFiles(
105
106
  fileList[i].name ||
106
107
  Math.random().toString().replace('.', '');
107
108
 
108
- if (extension) {
109
+ if (!hasRealExtension && extension) {
109
110
  let extForReg = extension;
110
111
 
111
112
  if (['jpeg', 'jpg'].includes(extForReg)) {
@@ -119,7 +120,14 @@ export function sendFiles(
119
120
  }
120
121
  }
121
122
 
122
- form.append(o.filesVariableName(i), fileList[i], newName);
123
+ const [key, iFile, name] = o.processFileName.call(
124
+ uploader,
125
+ o.filesVariableName(i),
126
+ fileList[i],
127
+ newName
128
+ );
129
+
130
+ form.append(key, iFile, name);
123
131
  }
124
132
  }
125
133
 
@@ -3,6 +3,7 @@
3
3
  * Released under MIT see LICENSE.txt in the project root for license information.
4
4
  * Copyright (c) 2013-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
+
6
7
  describe('Test uploader module', function () {
7
8
  describe('Drop file', function () {
8
9
  describe('Drop Image like base64', function () {
@@ -75,6 +76,41 @@ describe('Test uploader module', function () {
75
76
  });
76
77
  });
77
78
 
79
+ describe('Change filename', () => {
80
+ it('Should upload file with different filename', function (done) {
81
+ const file = new FileImage(),
82
+ editor = getJodit({
83
+ uploader: {
84
+ url: 'https://xdsoft.net/jodit/connector/index.php?action=fileUpload',
85
+ processFileName: (key, file, name) => {
86
+ return [key, file, 'test_' + name];
87
+ }
88
+ },
89
+ events: {
90
+ afterInsertImage: function (img) {
91
+ expect(img.src).equals(
92
+ 'https://xdsoft.net/jodit/files/test_logo.gif'
93
+ );
94
+
95
+ expect(sortAttributes(editor.value)).equals(
96
+ '<p><img src="https://xdsoft.net/jodit/files/test_logo.gif" style="width:300px"></p>'
97
+ );
98
+
99
+ done();
100
+ }
101
+ }
102
+ });
103
+
104
+ simulateEvent('drop', editor.editor, function (data) {
105
+ Object.defineProperty(data, 'dataTransfer', {
106
+ value: {
107
+ files: [file]
108
+ }
109
+ });
110
+ });
111
+ });
112
+ });
113
+
78
114
  describe('For iframe mode', function () {
79
115
  it('Should upload file and insert image with SRC from server', function (done) {
80
116
  const timer = setTimeout(function () {