@ckeditor/ckeditor5-upload 48.2.0-alpha.7 → 48.3.0-alpha.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.
package/dist/index.js CHANGED
@@ -2,698 +2,730 @@
2
2
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
4
4
  */
5
- import { Plugin, PendingActions } from '@ckeditor/ckeditor5-core/dist/index.js';
6
- import { ObservableMixin, uid, CKEditorError, Collection, logWarning } from '@ckeditor/ckeditor5-utils/dist/index.js';
5
+ import { PendingActions, Plugin } from "@ckeditor/ckeditor5-core";
6
+ import { CKEditorError, Collection, ObservableMixin, logWarning, uid } from "@ckeditor/ckeditor5-utils";
7
7
 
8
8
  /**
9
- * Wrapper over the native `FileReader`.
10
- */ class FileReader extends /* #__PURE__ */ ObservableMixin() {
11
- total;
12
- /**
13
- * Instance of native FileReader.
14
- */ _reader;
15
- /**
16
- * Holds the data of an already loaded file. The file must be first loaded
17
- * by using {@link module:upload/filereader~FileReader#read `read()`}.
18
- */ _data;
19
- /**
20
- * Creates an instance of the FileReader.
21
- */ constructor(){
22
- super();
23
- const reader = new window.FileReader();
24
- this._reader = reader;
25
- this._data = undefined;
26
- this.set('loaded', 0);
27
- reader.onprogress = (evt)=>{
28
- this.loaded = evt.loaded;
29
- };
30
- }
31
- /**
32
- * Returns error that occurred during file reading.
33
- */ get error() {
34
- return this._reader.error;
35
- }
36
- /**
37
- * Holds the data of an already loaded file. The file must be first loaded
38
- * by using {@link module:upload/filereader~FileReader#read `read()`}.
39
- */ get data() {
40
- return this._data;
41
- }
42
- /**
43
- * Reads the provided file.
44
- *
45
- * @param file Native File object.
46
- * @returns Returns a promise that will be resolved with file's content.
47
- * The promise will be rejected in case of an error or when the reading process is aborted.
48
- */ read(file) {
49
- const reader = this._reader;
50
- this.total = file.size;
51
- return new Promise((resolve, reject)=>{
52
- reader.onload = ()=>{
53
- const result = reader.result;
54
- this._data = result;
55
- resolve(result);
56
- };
57
- reader.onerror = ()=>{
58
- reject('error');
59
- };
60
- reader.onabort = ()=>{
61
- reject('aborted');
62
- };
63
- this._reader.readAsDataURL(file);
64
- });
65
- }
66
- /**
67
- * Aborts file reader.
68
- */ abort() {
69
- this._reader.abort();
70
- }
71
- }
9
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
10
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
11
+ */
12
+ /**
13
+ * @module upload/filereader
14
+ */
15
+ const FileReaderBase = /* #__PURE__ */ ObservableMixin();
16
+ /**
17
+ * Wrapper over the native `FileReader`.
18
+ */
19
+ var FileReader = class extends FileReaderBase {
20
+ total;
21
+ /**
22
+ * Instance of native FileReader.
23
+ */
24
+ _reader;
25
+ /**
26
+ * Holds the data of an already loaded file. The file must be first loaded
27
+ * by using {@link module:upload/filereader~FileReader#read `read()`}.
28
+ */
29
+ _data;
30
+ /**
31
+ * Creates an instance of the FileReader.
32
+ */
33
+ constructor() {
34
+ super();
35
+ const reader = new window.FileReader();
36
+ this._reader = reader;
37
+ this._data = void 0;
38
+ this.set("loaded", 0);
39
+ reader.onprogress = (evt) => {
40
+ this.loaded = evt.loaded;
41
+ };
42
+ }
43
+ /**
44
+ * Returns error that occurred during file reading.
45
+ */
46
+ get error() {
47
+ return this._reader.error;
48
+ }
49
+ /**
50
+ * Holds the data of an already loaded file. The file must be first loaded
51
+ * by using {@link module:upload/filereader~FileReader#read `read()`}.
52
+ */
53
+ get data() {
54
+ return this._data;
55
+ }
56
+ /**
57
+ * Reads the provided file.
58
+ *
59
+ * @param file Native File object.
60
+ * @returns Returns a promise that will be resolved with file's content.
61
+ * The promise will be rejected in case of an error or when the reading process is aborted.
62
+ */
63
+ read(file) {
64
+ const reader = this._reader;
65
+ this.total = file.size;
66
+ return new Promise((resolve, reject) => {
67
+ reader.onload = () => {
68
+ const result = reader.result;
69
+ this._data = result;
70
+ resolve(result);
71
+ };
72
+ reader.onerror = () => {
73
+ reject("error");
74
+ };
75
+ reader.onabort = () => {
76
+ reject("aborted");
77
+ };
78
+ this._reader.readAsDataURL(file);
79
+ });
80
+ }
81
+ /**
82
+ * Aborts file reader.
83
+ */
84
+ abort() {
85
+ this._reader.abort();
86
+ }
87
+ };
72
88
 
73
89
  /**
74
- * File repository plugin. A central point for managing file upload.
75
- *
76
- * To use it, first you need an upload adapter. Upload adapter's job is to handle communication with the server
77
- * (sending the file and handling server's response). You can use one of the existing plugins introducing upload adapters
78
- * (e.g. {@link module:adapter-ckfinder/uploadadapter~CKFinderUploadAdapter}) or write your own one – see
79
- * the {@glink framework/deep-dive/upload-adapter Custom image upload adapter deep-dive} guide.
80
- *
81
- * Then, you can use {@link module:upload/filerepository~FileRepository#createLoader `createLoader()`} and the returned
82
- * {@link module:upload/filerepository~FileLoader} instance to load and upload files.
83
- */ class FileRepository extends Plugin {
84
- /**
85
- * Collection of loaders associated with this repository.
86
- */ loaders = new Collection();
87
- /**
88
- * Loaders mappings used to retrieve loaders references.
89
- */ _loadersMap = new Map();
90
- /**
91
- * Reference to a pending action registered in a {@link module:core/pendingactions~PendingActions} plugin
92
- * while upload is in progress. When there is no upload then value is `null`.
93
- */ _pendingAction = null;
94
- /**
95
- * @inheritDoc
96
- */ static get pluginName() {
97
- return 'FileRepository';
98
- }
99
- /**
100
- * @inheritDoc
101
- */ static get isOfficialPlugin() {
102
- return true;
103
- }
104
- /**
105
- * @inheritDoc
106
- */ static get requires() {
107
- return [
108
- PendingActions
109
- ];
110
- }
111
- /**
112
- * @inheritDoc
113
- */ init() {
114
- // Keeps upload in a sync with pending actions.
115
- this.loaders.on('change', ()=>this._updatePendingAction());
116
- this.set('uploaded', 0);
117
- this.set('uploadTotal', null);
118
- this.bind('uploadedPercent').to(this, 'uploaded', this, 'uploadTotal', (uploaded, total)=>{
119
- return total ? uploaded / total * 100 : 0;
120
- });
121
- }
122
- /**
123
- * Returns the loader associated with specified file or promise.
124
- *
125
- * To get loader by id use `fileRepository.loaders.get( id )`.
126
- *
127
- * @param fileOrPromise Native file or promise handle.
128
- */ getLoader(fileOrPromise) {
129
- return this._loadersMap.get(fileOrPromise) || null;
130
- }
131
- /**
132
- * Creates a loader instance for the given file.
133
- *
134
- * Requires {@link #createUploadAdapter} factory to be defined.
135
- *
136
- * @param fileOrPromise Native File object or native Promise object which resolves to a File.
137
- */ createLoader(fileOrPromise) {
138
- if (!this.createUploadAdapter) {
139
- /**
140
- * You need to enable an upload adapter in order to be able to upload files.
141
- *
142
- * This warning shows up when {@link module:upload/filerepository~FileRepository} is being used
143
- * without {@link module:upload/filerepository~FileRepository#createUploadAdapter defining an upload adapter}.
144
- *
145
- * **If you see this warning when using one of the now deprecated CKEditor 5 builds**
146
- * it means that you did not configure any of the upload adapters available by default in those builds.
147
- *
148
- * Predefined builds are a deprecated solution and we strongly advise
149
- * {@glink updating/nim-migration/migration-to-new-installation-methods migrating to new installation methods}.
150
- *
151
- * See the {@glink features/images/image-upload/image-upload comprehensive "Image upload overview"} to learn which upload
152
- * adapters are available in the builds and how to configure them.
153
- *
154
- * Otherwise, if you see this warning, there is a chance that you enabled
155
- * a feature like {@link module:image/imageupload~ImageUpload},
156
- * or {@link module:image/imageupload/imageuploadui~ImageUploadUI} but you did not enable any upload adapter.
157
- * You can choose one of the existing upload adapters listed in the
158
- * {@glink features/images/image-upload/image-upload "Image upload overview"}.
159
- *
160
- * You can also implement your {@glink framework/deep-dive/upload-adapter own image upload adapter}.
161
- *
162
- * @error filerepository-no-upload-adapter
163
- */ logWarning('filerepository-no-upload-adapter');
164
- return null;
165
- }
166
- const loader = new FileLoader(Promise.resolve(fileOrPromise), this.createUploadAdapter);
167
- this.loaders.add(loader);
168
- this._loadersMap.set(fileOrPromise, loader);
169
- // Store also file => loader mapping so loader can be retrieved by file instance returned upon Promise resolution.
170
- if (fileOrPromise instanceof Promise) {
171
- loader.file.then((file)=>{
172
- this._loadersMap.set(file, loader);
173
- })// Every then() must have a catch().
174
- // File loader state (and rejections) are handled in read() and upload().
175
- // Also, see the "does not swallow the file promise rejection" test.
176
- .catch(()=>{});
177
- }
178
- loader.on('change:uploaded', ()=>{
179
- let aggregatedUploaded = 0;
180
- for (const loader of this.loaders){
181
- aggregatedUploaded += loader.uploaded;
182
- }
183
- this.uploaded = aggregatedUploaded;
184
- });
185
- loader.on('change:uploadTotal', ()=>{
186
- let aggregatedTotal = 0;
187
- for (const loader of this.loaders){
188
- if (loader.uploadTotal) {
189
- aggregatedTotal += loader.uploadTotal;
190
- }
191
- }
192
- this.uploadTotal = aggregatedTotal;
193
- });
194
- return loader;
195
- }
196
- /**
197
- * Destroys the given loader.
198
- *
199
- * @param fileOrPromiseOrLoader File or Promise associated with that loader or loader itself.
200
- */ destroyLoader(fileOrPromiseOrLoader) {
201
- const loader = fileOrPromiseOrLoader instanceof FileLoader ? fileOrPromiseOrLoader : this.getLoader(fileOrPromiseOrLoader);
202
- loader._destroy();
203
- this.loaders.remove(loader);
204
- this._loadersMap.forEach((value, key)=>{
205
- if (value === loader) {
206
- this._loadersMap.delete(key);
207
- }
208
- });
209
- }
210
- /**
211
- * Registers or deregisters pending action bound with upload progress.
212
- */ _updatePendingAction() {
213
- const pendingActions = this.editor.plugins.get(PendingActions);
214
- if (this.loaders.length) {
215
- if (!this._pendingAction) {
216
- const t = this.editor.t;
217
- const getMessage = (value)=>`${t('Upload in progress')} ${parseInt(value)}%.`;
218
- this._pendingAction = pendingActions.add(getMessage(this.uploadedPercent));
219
- this._pendingAction.bind('message').to(this, 'uploadedPercent', getMessage);
220
- }
221
- } else {
222
- pendingActions.remove(this._pendingAction);
223
- this._pendingAction = null;
224
- }
225
- }
226
- }
90
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
91
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
92
+ */
93
+ /**
94
+ * @module upload/filerepository
95
+ */
96
+ const FileLoaderBase = /* #__PURE__ */ ObservableMixin();
227
97
  /**
228
- * File loader class.
229
- *
230
- * It is used to control the process of reading the file and uploading it using the specified upload adapter.
231
- */ class FileLoader extends /* #__PURE__ */ ObservableMixin() {
232
- /**
233
- * Unique id of FileLoader instance.
234
- *
235
- * @readonly
236
- */ id;
237
- /**
238
- * Additional wrapper over the initial file promise passed to this loader.
239
- */ _filePromiseWrapper;
240
- /**
241
- * Adapter instance associated with this file loader.
242
- */ _adapter;
243
- /**
244
- * FileReader used by FileLoader.
245
- */ _reader;
246
- /**
247
- * Creates a new instance of `FileLoader`.
248
- *
249
- * @param filePromise A promise which resolves to a file instance.
250
- * @param uploadAdapterCreator The function which returns {@link module:upload/filerepository~UploadAdapter} instance.
251
- */ constructor(filePromise, uploadAdapterCreator){
252
- super();
253
- this.id = uid();
254
- this._filePromiseWrapper = this._createFilePromiseWrapper(filePromise);
255
- this._adapter = uploadAdapterCreator(this);
256
- this._reader = new FileReader();
257
- this.set('status', 'idle');
258
- this.set('uploaded', 0);
259
- this.set('uploadTotal', null);
260
- this.bind('uploadedPercent').to(this, 'uploaded', this, 'uploadTotal', (uploaded, total)=>{
261
- return total ? uploaded / total * 100 : 0;
262
- });
263
- this.set('uploadResponse', null);
264
- }
265
- /**
266
- * A `Promise` which resolves to a `File` instance associated with this file loader.
267
- */ get file() {
268
- if (!this._filePromiseWrapper) {
269
- // Loader was destroyed, return promise which resolves to null.
270
- return Promise.resolve(null);
271
- } else {
272
- // The `this._filePromiseWrapper.promise` is chained and not simply returned to handle a case when:
273
- //
274
- // * The `loader.file.then( ... )` is called by external code (returned promise is pending).
275
- // * Then `loader._destroy()` is called (call is synchronous) which destroys the `loader`.
276
- // * Promise returned by the first `loader.file.then( ... )` call is resolved.
277
- //
278
- // Returning `this._filePromiseWrapper.promise` will still resolve to a `File` instance so there
279
- // is an additional check needed in the chain to see if `loader` was destroyed in the meantime.
280
- return this._filePromiseWrapper.promise.then((file)=>this._filePromiseWrapper ? file : null);
281
- }
282
- }
283
- /**
284
- * Returns the file data. To read its data, you need for first load the file
285
- * by using the {@link module:upload/filerepository~FileLoader#read `read()`} method.
286
- */ get data() {
287
- return this._reader.data;
288
- }
289
- /**
290
- * Reads file using {@link module:upload/filereader~FileReader}.
291
- *
292
- * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `filerepository-read-wrong-status` when status
293
- * is different than `idle`.
294
- *
295
- * Example usage:
296
- *
297
- * ```ts
298
- * fileLoader.read()
299
- * .then( data => { ... } )
300
- * .catch( err => {
301
- * if ( err === 'aborted' ) {
302
- * console.log( 'Reading aborted.' );
303
- * } else {
304
- * console.log( 'Reading error.', err );
305
- * }
306
- * } );
307
- * ```
308
- *
309
- * @returns Returns promise that will be resolved with read data. Promise will be rejected if error
310
- * occurs or if read process is aborted.
311
- */ read() {
312
- if (this.status != 'idle') {
313
- /**
314
- * You cannot call read if the status is different than idle.
315
- *
316
- * @error filerepository-read-wrong-status
317
- */ throw new CKEditorError('filerepository-read-wrong-status', this);
318
- }
319
- this.status = 'reading';
320
- return this.file.then((file)=>this._reader.read(file)).then((data)=>{
321
- // Edge case: reader was aborted after file was read - double check for proper status.
322
- // It can happen when image was deleted during its upload.
323
- if (this.status !== 'reading') {
324
- throw this.status;
325
- }
326
- this.status = 'idle';
327
- return data;
328
- }).catch((err)=>{
329
- if (err === 'aborted') {
330
- this.status = 'aborted';
331
- throw 'aborted';
332
- }
333
- this.status = 'error';
334
- throw this._reader.error ? this._reader.error : err;
335
- });
336
- }
337
- /**
338
- * Reads file using the provided {@link module:upload/filerepository~UploadAdapter}.
339
- *
340
- * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `filerepository-upload-wrong-status` when status
341
- * is different than `idle`.
342
- * Example usage:
343
- *
344
- * ```ts
345
- * fileLoader.upload()
346
- * .then( data => { ... } )
347
- * .catch( e => {
348
- * if ( e === 'aborted' ) {
349
- * console.log( 'Uploading aborted.' );
350
- * } else {
351
- * console.log( 'Uploading error.', e );
352
- * }
353
- * } );
354
- * ```
355
- *
356
- * @returns Returns promise that will be resolved with response data. Promise will be rejected if error
357
- * occurs or if read process is aborted.
358
- */ upload() {
359
- if (this.status != 'idle') {
360
- /**
361
- * You cannot call upload if the status is different than idle.
362
- *
363
- * @error filerepository-upload-wrong-status
364
- */ throw new CKEditorError('filerepository-upload-wrong-status', this);
365
- }
366
- this.status = 'uploading';
367
- return this.file.then(()=>this._adapter.upload()).then((data)=>{
368
- this.uploadResponse = data;
369
- this.status = 'idle';
370
- return data;
371
- }).catch((err)=>{
372
- if (this.status === 'aborted') {
373
- throw 'aborted';
374
- }
375
- this.status = 'error';
376
- throw err;
377
- });
378
- }
379
- /**
380
- * Aborts loading process.
381
- */ abort() {
382
- const status = this.status;
383
- this.status = 'aborted';
384
- if (!this._filePromiseWrapper.isFulfilled) {
385
- // Edge case: file loader is aborted before read() is called
386
- // so it might happen that no one handled the rejection of this promise.
387
- // See https://github.com/ckeditor/ckeditor5-upload/pull/100
388
- this._filePromiseWrapper.promise.catch(()=>{});
389
- this._filePromiseWrapper.rejecter('aborted');
390
- } else if (status == 'reading') {
391
- this._reader.abort();
392
- } else if (status == 'uploading' && this._adapter.abort) {
393
- this._adapter.abort();
394
- }
395
- this._destroy();
396
- }
397
- /**
398
- * Performs cleanup.
399
- *
400
- * @internal
401
- */ _destroy() {
402
- this._filePromiseWrapper = undefined;
403
- this._reader = undefined;
404
- this._adapter = undefined;
405
- this.uploadResponse = undefined;
406
- }
407
- /**
408
- * Wraps a given file promise into another promise giving additional
409
- * control (resolving, rejecting, checking if fulfilled) over it.
410
- *
411
- * @param filePromise The initial file promise to be wrapped.
412
- */ _createFilePromiseWrapper(filePromise) {
413
- const wrapper = {};
414
- wrapper.promise = new Promise((resolve, reject)=>{
415
- wrapper.rejecter = reject;
416
- wrapper.isFulfilled = false;
417
- filePromise.then((file)=>{
418
- wrapper.isFulfilled = true;
419
- resolve(file);
420
- }).catch((err)=>{
421
- wrapper.isFulfilled = true;
422
- reject(err);
423
- });
424
- });
425
- return wrapper;
426
- }
427
- }
98
+ * File repository plugin. A central point for managing file upload.
99
+ *
100
+ * To use it, first you need an upload adapter. Upload adapter's job is to handle communication with the server
101
+ * (sending the file and handling server's response). You can use one of the existing plugins introducing upload adapters
102
+ * (e.g. {@link module:adapter-ckfinder/uploadadapter~CKFinderUploadAdapter}) or write your own one – see
103
+ * the {@glink framework/deep-dive/upload-adapter Custom image upload adapter deep-dive} guide.
104
+ *
105
+ * Then, you can use {@link module:upload/filerepository~FileRepository#createLoader `createLoader()`} and the returned
106
+ * {@link module:upload/filerepository~FileLoader} instance to load and upload files.
107
+ */
108
+ var FileRepository = class extends Plugin {
109
+ /**
110
+ * Collection of loaders associated with this repository.
111
+ */
112
+ loaders = new Collection();
113
+ /**
114
+ * Loaders mappings used to retrieve loaders references.
115
+ */
116
+ _loadersMap = /* @__PURE__ */ new Map();
117
+ /**
118
+ * Reference to a pending action registered in a {@link module:core/pendingactions~PendingActions} plugin
119
+ * while upload is in progress. When there is no upload then value is `null`.
120
+ */
121
+ _pendingAction = null;
122
+ /**
123
+ * @inheritDoc
124
+ */
125
+ static get pluginName() {
126
+ return "FileRepository";
127
+ }
128
+ /**
129
+ * @inheritDoc
130
+ */
131
+ static get isOfficialPlugin() {
132
+ return true;
133
+ }
134
+ /**
135
+ * @inheritDoc
136
+ */
137
+ static get requires() {
138
+ return [PendingActions];
139
+ }
140
+ /**
141
+ * @inheritDoc
142
+ */
143
+ init() {
144
+ this.loaders.on("change", () => this._updatePendingAction());
145
+ this.set("uploaded", 0);
146
+ this.set("uploadTotal", null);
147
+ this.bind("uploadedPercent").to(this, "uploaded", this, "uploadTotal", (uploaded, total) => {
148
+ return total ? uploaded / total * 100 : 0;
149
+ });
150
+ }
151
+ /**
152
+ * Returns the loader associated with specified file or promise.
153
+ *
154
+ * To get loader by id use `fileRepository.loaders.get( id )`.
155
+ *
156
+ * @param fileOrPromise Native file or promise handle.
157
+ */
158
+ getLoader(fileOrPromise) {
159
+ return this._loadersMap.get(fileOrPromise) || null;
160
+ }
161
+ /**
162
+ * Creates a loader instance for the given file.
163
+ *
164
+ * Requires {@link #createUploadAdapter} factory to be defined.
165
+ *
166
+ * @param fileOrPromise Native File object or native Promise object which resolves to a File.
167
+ */
168
+ createLoader(fileOrPromise) {
169
+ if (!this.createUploadAdapter) {
170
+ /**
171
+ * You need to enable an upload adapter in order to be able to upload files.
172
+ *
173
+ * This warning shows up when {@link module:upload/filerepository~FileRepository} is being used
174
+ * without {@link module:upload/filerepository~FileRepository#createUploadAdapter defining an upload adapter}.
175
+ *
176
+ * **If you see this warning when using one of the now deprecated CKEditor 5 builds**
177
+ * it means that you did not configure any of the upload adapters available by default in those builds.
178
+ *
179
+ * Predefined builds are a deprecated solution and we strongly advise
180
+ * {@glink updating/nim-migration/migration-to-new-installation-methods migrating to new installation methods}.
181
+ *
182
+ * See the {@glink features/images/image-upload/image-upload comprehensive "Image upload overview"} to learn which upload
183
+ * adapters are available in the builds and how to configure them.
184
+ *
185
+ * Otherwise, if you see this warning, there is a chance that you enabled
186
+ * a feature like {@link module:image/imageupload~ImageUpload},
187
+ * or {@link module:image/imageupload/imageuploadui~ImageUploadUI} but you did not enable any upload adapter.
188
+ * You can choose one of the existing upload adapters listed in the
189
+ * {@glink features/images/image-upload/image-upload "Image upload overview"}.
190
+ *
191
+ * You can also implement your {@glink framework/deep-dive/upload-adapter own image upload adapter}.
192
+ *
193
+ * @error filerepository-no-upload-adapter
194
+ */
195
+ logWarning("filerepository-no-upload-adapter");
196
+ return null;
197
+ }
198
+ const loader = new FileLoader(Promise.resolve(fileOrPromise), this.createUploadAdapter);
199
+ this.loaders.add(loader);
200
+ this._loadersMap.set(fileOrPromise, loader);
201
+ if (fileOrPromise instanceof Promise) loader.file.then((file) => {
202
+ this._loadersMap.set(file, loader);
203
+ }).catch(() => {});
204
+ loader.on("change:uploaded", () => {
205
+ let aggregatedUploaded = 0;
206
+ for (const loader of this.loaders) aggregatedUploaded += loader.uploaded;
207
+ this.uploaded = aggregatedUploaded;
208
+ });
209
+ loader.on("change:uploadTotal", () => {
210
+ let aggregatedTotal = 0;
211
+ for (const loader of this.loaders) if (loader.uploadTotal) aggregatedTotal += loader.uploadTotal;
212
+ this.uploadTotal = aggregatedTotal;
213
+ });
214
+ return loader;
215
+ }
216
+ /**
217
+ * Destroys the given loader.
218
+ *
219
+ * @param fileOrPromiseOrLoader File or Promise associated with that loader or loader itself.
220
+ */
221
+ destroyLoader(fileOrPromiseOrLoader) {
222
+ const loader = fileOrPromiseOrLoader instanceof FileLoader ? fileOrPromiseOrLoader : this.getLoader(fileOrPromiseOrLoader);
223
+ loader._destroy();
224
+ this.loaders.remove(loader);
225
+ this._loadersMap.forEach((value, key) => {
226
+ if (value === loader) this._loadersMap.delete(key);
227
+ });
228
+ }
229
+ /**
230
+ * Registers or deregisters pending action bound with upload progress.
231
+ */
232
+ _updatePendingAction() {
233
+ const pendingActions = this.editor.plugins.get(PendingActions);
234
+ if (this.loaders.length) {
235
+ if (!this._pendingAction) {
236
+ const t = this.editor.t;
237
+ const getMessage = (value) => `${t("Upload in progress")} ${parseInt(value)}%.`;
238
+ this._pendingAction = pendingActions.add(getMessage(this.uploadedPercent));
239
+ this._pendingAction.bind("message").to(this, "uploadedPercent", getMessage);
240
+ }
241
+ } else {
242
+ pendingActions.remove(this._pendingAction);
243
+ this._pendingAction = null;
244
+ }
245
+ }
246
+ };
247
+ /**
248
+ * File loader class.
249
+ *
250
+ * It is used to control the process of reading the file and uploading it using the specified upload adapter.
251
+ */
252
+ var FileLoader = class extends FileLoaderBase {
253
+ /**
254
+ * Unique id of FileLoader instance.
255
+ *
256
+ * @readonly
257
+ */
258
+ id;
259
+ /**
260
+ * Additional wrapper over the initial file promise passed to this loader.
261
+ */
262
+ _filePromiseWrapper;
263
+ /**
264
+ * Adapter instance associated with this file loader.
265
+ */
266
+ _adapter;
267
+ /**
268
+ * FileReader used by FileLoader.
269
+ */
270
+ _reader;
271
+ /**
272
+ * Creates a new instance of `FileLoader`.
273
+ *
274
+ * @param filePromise A promise which resolves to a file instance.
275
+ * @param uploadAdapterCreator The function which returns {@link module:upload/filerepository~UploadAdapter} instance.
276
+ */
277
+ constructor(filePromise, uploadAdapterCreator) {
278
+ super();
279
+ this.id = uid();
280
+ this._filePromiseWrapper = this._createFilePromiseWrapper(filePromise);
281
+ this._adapter = uploadAdapterCreator(this);
282
+ this._reader = new FileReader();
283
+ this.set("status", "idle");
284
+ this.set("uploaded", 0);
285
+ this.set("uploadTotal", null);
286
+ this.bind("uploadedPercent").to(this, "uploaded", this, "uploadTotal", (uploaded, total) => {
287
+ return total ? uploaded / total * 100 : 0;
288
+ });
289
+ this.set("uploadResponse", null);
290
+ }
291
+ /**
292
+ * A `Promise` which resolves to a `File` instance associated with this file loader.
293
+ */
294
+ get file() {
295
+ if (!this._filePromiseWrapper) return Promise.resolve(null);
296
+ else return this._filePromiseWrapper.promise.then((file) => this._filePromiseWrapper ? file : null);
297
+ }
298
+ /**
299
+ * Returns the file data. To read its data, you need for first load the file
300
+ * by using the {@link module:upload/filerepository~FileLoader#read `read()`} method.
301
+ */
302
+ get data() {
303
+ return this._reader.data;
304
+ }
305
+ /**
306
+ * Reads file using {@link module:upload/filereader~FileReader}.
307
+ *
308
+ * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `filerepository-read-wrong-status` when status
309
+ * is different than `idle`.
310
+ *
311
+ * Example usage:
312
+ *
313
+ * ```ts
314
+ * fileLoader.read()
315
+ * .then( data => { ... } )
316
+ * .catch( err => {
317
+ * if ( err === 'aborted' ) {
318
+ * console.log( 'Reading aborted.' );
319
+ * } else {
320
+ * console.log( 'Reading error.', err );
321
+ * }
322
+ * } );
323
+ * ```
324
+ *
325
+ * @returns Returns promise that will be resolved with read data. Promise will be rejected if error
326
+ * occurs or if read process is aborted.
327
+ */
328
+ read() {
329
+ if (this.status != "idle")
330
+ /**
331
+ * You cannot call read if the status is different than idle.
332
+ *
333
+ * @error filerepository-read-wrong-status
334
+ */
335
+ throw new CKEditorError("filerepository-read-wrong-status", this);
336
+ this.status = "reading";
337
+ return this.file.then((file) => this._reader.read(file)).then((data) => {
338
+ if (this.status !== "reading") throw this.status;
339
+ this.status = "idle";
340
+ return data;
341
+ }).catch((err) => {
342
+ if (err === "aborted") {
343
+ this.status = "aborted";
344
+ throw "aborted";
345
+ }
346
+ this.status = "error";
347
+ throw this._reader.error ? this._reader.error : err;
348
+ });
349
+ }
350
+ /**
351
+ * Reads file using the provided {@link module:upload/filerepository~UploadAdapter}.
352
+ *
353
+ * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `filerepository-upload-wrong-status` when status
354
+ * is different than `idle`.
355
+ * Example usage:
356
+ *
357
+ * ```ts
358
+ * fileLoader.upload()
359
+ * .then( data => { ... } )
360
+ * .catch( e => {
361
+ * if ( e === 'aborted' ) {
362
+ * console.log( 'Uploading aborted.' );
363
+ * } else {
364
+ * console.log( 'Uploading error.', e );
365
+ * }
366
+ * } );
367
+ * ```
368
+ *
369
+ * @returns Returns promise that will be resolved with response data. Promise will be rejected if error
370
+ * occurs or if read process is aborted.
371
+ */
372
+ upload() {
373
+ if (this.status != "idle")
374
+ /**
375
+ * You cannot call upload if the status is different than idle.
376
+ *
377
+ * @error filerepository-upload-wrong-status
378
+ */
379
+ throw new CKEditorError("filerepository-upload-wrong-status", this);
380
+ this.status = "uploading";
381
+ return this.file.then(() => this._adapter.upload()).then((data) => {
382
+ this.uploadResponse = data;
383
+ this.status = "idle";
384
+ return data;
385
+ }).catch((err) => {
386
+ if (this.status === "aborted") throw "aborted";
387
+ this.status = "error";
388
+ throw err;
389
+ });
390
+ }
391
+ /**
392
+ * Aborts loading process.
393
+ */
394
+ abort() {
395
+ const status = this.status;
396
+ this.status = "aborted";
397
+ if (!this._filePromiseWrapper.isFulfilled) {
398
+ this._filePromiseWrapper.promise.catch(() => {});
399
+ this._filePromiseWrapper.rejecter("aborted");
400
+ } else if (status == "reading") this._reader.abort();
401
+ else if (status == "uploading" && this._adapter.abort) this._adapter.abort();
402
+ this._destroy();
403
+ }
404
+ /**
405
+ * Performs cleanup.
406
+ *
407
+ * @internal
408
+ */
409
+ _destroy() {
410
+ this._filePromiseWrapper = void 0;
411
+ this._reader = void 0;
412
+ this._adapter = void 0;
413
+ this.uploadResponse = void 0;
414
+ }
415
+ /**
416
+ * Wraps a given file promise into another promise giving additional
417
+ * control (resolving, rejecting, checking if fulfilled) over it.
418
+ *
419
+ * @param filePromise The initial file promise to be wrapped.
420
+ */
421
+ _createFilePromiseWrapper(filePromise) {
422
+ const wrapper = {};
423
+ wrapper.promise = new Promise((resolve, reject) => {
424
+ wrapper.rejecter = reject;
425
+ wrapper.isFulfilled = false;
426
+ filePromise.then((file) => {
427
+ wrapper.isFulfilled = true;
428
+ resolve(file);
429
+ }).catch((err) => {
430
+ wrapper.isFulfilled = true;
431
+ reject(err);
432
+ });
433
+ });
434
+ return wrapper;
435
+ }
436
+ };
428
437
 
429
438
  /**
430
- * A plugin that converts images inserted into the editor into [Base64 strings](https://en.wikipedia.org/wiki/Base64)
431
- * in the {@glink getting-started/setup/getting-and-setting-data editor output}.
432
- *
433
- * This kind of image upload does not require server processing – images are stored with the rest of the text and
434
- * displayed by the web browser without additional requests.
435
- *
436
- * Check out the {@glink features/images/image-upload/image-upload comprehensive "Image upload overview"} to learn about
437
- * other ways to upload images into CKEditor 5.
438
- */ class Base64UploadAdapter extends Plugin {
439
- /**
440
- * @inheritDoc
441
- */ static get requires() {
442
- return [
443
- FileRepository
444
- ];
445
- }
446
- /**
447
- * @inheritDoc
448
- */ static get pluginName() {
449
- return 'Base64UploadAdapter';
450
- }
451
- /**
452
- * @inheritDoc
453
- * @internal
454
- */ static get licenseFeatureCode() {
455
- return 'B64A';
456
- }
457
- /**
458
- * @inheritDoc
459
- */ static get isOfficialPlugin() {
460
- return true;
461
- }
462
- /**
463
- * @inheritDoc
464
- */ static get isPremiumPlugin() {
465
- return true;
466
- }
467
- /**
468
- * @inheritDoc
469
- */ init() {
470
- this.editor.plugins.get(FileRepository).createUploadAdapter = (loader)=>new Adapter$1(loader);
471
- }
472
- }
439
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
440
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
441
+ */
442
+ /**
443
+ * @module upload/adapters/base64uploadadapter
444
+ */
473
445
  /**
474
- * The upload adapter that converts images inserted into the editor into Base64 strings.
475
- */ let Adapter$1 = class Adapter {
476
- /**
477
- * `FileLoader` instance to use during the upload.
478
- */ loader;
479
- reader;
480
- /**
481
- * Creates a new adapter instance.
482
- */ constructor(loader){
483
- this.loader = loader;
484
- }
485
- /**
486
- * Starts the upload process.
487
- *
488
- * @see module:upload/filerepository~UploadAdapter#upload
489
- */ upload() {
490
- return new Promise((resolve, reject)=>{
491
- const reader = this.reader = new window.FileReader();
492
- reader.addEventListener('load', ()=>{
493
- resolve({
494
- default: reader.result
495
- });
496
- });
497
- reader.addEventListener('error', (err)=>{
498
- reject(err);
499
- });
500
- reader.addEventListener('abort', ()=>{
501
- reject();
502
- });
503
- this.loader.file.then((file)=>{
504
- reader.readAsDataURL(file);
505
- });
506
- });
507
- }
508
- /**
509
- * Aborts the upload process.
510
- *
511
- * @see module:upload/filerepository~UploadAdapter#abort
512
- */ abort() {
513
- this.reader.abort();
514
- }
446
+ * A plugin that converts images inserted into the editor into [Base64 strings](https://en.wikipedia.org/wiki/Base64)
447
+ * in the {@glink getting-started/setup/getting-and-setting-data editor output}.
448
+ *
449
+ * This kind of image upload does not require server processing – images are stored with the rest of the text and
450
+ * displayed by the web browser without additional requests.
451
+ *
452
+ * Check out the {@glink features/images/image-upload/image-upload comprehensive "Image upload overview"} to learn about
453
+ * other ways to upload images into CKEditor 5.
454
+ */
455
+ var Base64UploadAdapter = class extends Plugin {
456
+ /**
457
+ * @inheritDoc
458
+ */
459
+ static get requires() {
460
+ return [FileRepository];
461
+ }
462
+ /**
463
+ * @inheritDoc
464
+ */
465
+ static get pluginName() {
466
+ return "Base64UploadAdapter";
467
+ }
468
+ /**
469
+ * @inheritDoc
470
+ * @internal
471
+ */
472
+ static get licenseFeatureCode() {
473
+ return "B64A";
474
+ }
475
+ /**
476
+ * @inheritDoc
477
+ */
478
+ static get isOfficialPlugin() {
479
+ return true;
480
+ }
481
+ /**
482
+ * @inheritDoc
483
+ */
484
+ static get isPremiumPlugin() {
485
+ return true;
486
+ }
487
+ /**
488
+ * @inheritDoc
489
+ */
490
+ init() {
491
+ this.editor.plugins.get(FileRepository).createUploadAdapter = (loader) => new Adapter$1(loader);
492
+ }
493
+ };
494
+ /**
495
+ * The upload adapter that converts images inserted into the editor into Base64 strings.
496
+ */
497
+ var Adapter$1 = class {
498
+ /**
499
+ * `FileLoader` instance to use during the upload.
500
+ */
501
+ loader;
502
+ reader;
503
+ /**
504
+ * Creates a new adapter instance.
505
+ */
506
+ constructor(loader) {
507
+ this.loader = loader;
508
+ }
509
+ /**
510
+ * Starts the upload process.
511
+ *
512
+ * @see module:upload/filerepository~UploadAdapter#upload
513
+ */
514
+ upload() {
515
+ return new Promise((resolve, reject) => {
516
+ const reader = this.reader = new window.FileReader();
517
+ reader.addEventListener("load", () => {
518
+ resolve({ default: reader.result });
519
+ });
520
+ reader.addEventListener("error", (err) => {
521
+ reject(err);
522
+ });
523
+ reader.addEventListener("abort", () => {
524
+ reject();
525
+ });
526
+ this.loader.file.then((file) => {
527
+ reader.readAsDataURL(file);
528
+ });
529
+ });
530
+ }
531
+ /**
532
+ * Aborts the upload process.
533
+ *
534
+ * @see module:upload/filerepository~UploadAdapter#abort
535
+ */
536
+ abort() {
537
+ this.reader.abort();
538
+ }
515
539
  };
516
540
 
517
541
  /**
518
- * The Simple upload adapter allows uploading images to an application running on your server using
519
- * the [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) API with a
520
- * minimal {@link module:upload/uploadconfig~SimpleUploadConfig editor configuration}.
521
- *
522
- * ```ts
523
- * ClassicEditor
524
- * .create( {
525
- * simpleUpload: {
526
- * uploadUrl: 'http://example.com',
527
- * headers: {
528
- * ...
529
- * }
530
- * }
531
- * } )
532
- * .then( ... )
533
- * .catch( ... );
534
- * ```
535
- *
536
- * See the {@glink features/images/image-upload/simple-upload-adapter "Simple upload adapter"} guide to learn how to
537
- * learn more about the feature (configuration, server–side requirements, etc.).
538
- *
539
- * Check out the {@glink features/images/image-upload/image-upload comprehensive "Image upload overview"} to learn about
540
- * other ways to upload images into CKEditor 5.
541
- */ class SimpleUploadAdapter extends Plugin {
542
- /**
543
- * @inheritDoc
544
- */ static get requires() {
545
- return [
546
- FileRepository
547
- ];
548
- }
549
- /**
550
- * @inheritDoc
551
- */ static get pluginName() {
552
- return 'SimpleUploadAdapter';
553
- }
554
- /**
555
- * @inheritDoc
556
- * @internal
557
- */ static get licenseFeatureCode() {
558
- return 'SUA';
559
- }
560
- /**
561
- * @inheritDoc
562
- */ static get isOfficialPlugin() {
563
- return true;
564
- }
565
- /**
566
- * @inheritDoc
567
- */ static get isPremiumPlugin() {
568
- return true;
569
- }
570
- /**
571
- * @inheritDoc
572
- */ init() {
573
- const options = this.editor.config.get('simpleUpload');
574
- if (!options) {
575
- return;
576
- }
577
- if (!options.uploadUrl) {
578
- /**
579
- * The {@link module:upload/uploadconfig~SimpleUploadConfig#uploadUrl `config.simpleUpload.uploadUrl`}
580
- * configuration required by the {@link module:upload/adapters/simpleuploadadapter~SimpleUploadAdapter `SimpleUploadAdapter`}
581
- * is missing. Make sure the correct URL is specified for the image upload to work properly.
582
- *
583
- * @error simple-upload-adapter-missing-uploadurl
584
- */ logWarning('simple-upload-adapter-missing-uploadurl');
585
- return;
586
- }
587
- this.editor.plugins.get(FileRepository).createUploadAdapter = (loader)=>{
588
- return new Adapter(loader, options);
589
- };
590
- }
591
- }
542
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
543
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
544
+ */
545
+ /**
546
+ * @module upload/adapters/simpleuploadadapter
547
+ */
548
+ /**
549
+ * The Simple upload adapter allows uploading images to an application running on your server using
550
+ * the [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) API with a
551
+ * minimal {@link module:upload/uploadconfig~SimpleUploadConfig editor configuration}.
552
+ *
553
+ * ```ts
554
+ * ClassicEditor
555
+ * .create( {
556
+ * simpleUpload: {
557
+ * uploadUrl: 'http://example.com',
558
+ * headers: {
559
+ * ...
560
+ * }
561
+ * }
562
+ * } )
563
+ * .then( ... )
564
+ * .catch( ... );
565
+ * ```
566
+ *
567
+ * See the {@glink features/images/image-upload/simple-upload-adapter "Simple upload adapter"} guide to learn how to
568
+ * learn more about the feature (configuration, server–side requirements, etc.).
569
+ *
570
+ * Check out the {@glink features/images/image-upload/image-upload comprehensive "Image upload overview"} to learn about
571
+ * other ways to upload images into CKEditor 5.
572
+ */
573
+ var SimpleUploadAdapter = class extends Plugin {
574
+ /**
575
+ * @inheritDoc
576
+ */
577
+ static get requires() {
578
+ return [FileRepository];
579
+ }
580
+ /**
581
+ * @inheritDoc
582
+ */
583
+ static get pluginName() {
584
+ return "SimpleUploadAdapter";
585
+ }
586
+ /**
587
+ * @inheritDoc
588
+ * @internal
589
+ */
590
+ static get licenseFeatureCode() {
591
+ return "SUA";
592
+ }
593
+ /**
594
+ * @inheritDoc
595
+ */
596
+ static get isOfficialPlugin() {
597
+ return true;
598
+ }
599
+ /**
600
+ * @inheritDoc
601
+ */
602
+ static get isPremiumPlugin() {
603
+ return true;
604
+ }
605
+ /**
606
+ * @inheritDoc
607
+ */
608
+ init() {
609
+ const options = this.editor.config.get("simpleUpload");
610
+ if (!options) return;
611
+ if (!options.uploadUrl) {
612
+ /**
613
+ * The {@link module:upload/uploadconfig~SimpleUploadConfig#uploadUrl `config.simpleUpload.uploadUrl`}
614
+ * configuration required by the {@link module:upload/adapters/simpleuploadadapter~SimpleUploadAdapter `SimpleUploadAdapter`}
615
+ * is missing. Make sure the correct URL is specified for the image upload to work properly.
616
+ *
617
+ * @error simple-upload-adapter-missing-uploadurl
618
+ */
619
+ logWarning("simple-upload-adapter-missing-uploadurl");
620
+ return;
621
+ }
622
+ this.editor.plugins.get(FileRepository).createUploadAdapter = (loader) => {
623
+ return new Adapter(loader, options);
624
+ };
625
+ }
626
+ };
627
+ /**
628
+ * Upload adapter.
629
+ */
630
+ var Adapter = class {
631
+ /**
632
+ * FileLoader instance to use during the upload.
633
+ */
634
+ loader;
635
+ /**
636
+ * The configuration of the adapter.
637
+ */
638
+ options;
639
+ xhr;
640
+ /**
641
+ * Creates a new adapter instance.
642
+ */
643
+ constructor(loader, options) {
644
+ this.loader = loader;
645
+ this.options = options;
646
+ }
647
+ /**
648
+ * Starts the upload process.
649
+ *
650
+ * @see module:upload/filerepository~UploadAdapter#upload
651
+ */
652
+ upload() {
653
+ return this.loader.file.then((file) => new Promise((resolve, reject) => {
654
+ this._initRequest();
655
+ this._initListeners(resolve, reject, file);
656
+ this._sendRequest(file);
657
+ }));
658
+ }
659
+ /**
660
+ * Aborts the upload process.
661
+ *
662
+ * @see module:upload/filerepository~UploadAdapter#abort
663
+ */
664
+ abort() {
665
+ if (this.xhr) this.xhr.abort();
666
+ }
667
+ /**
668
+ * Initializes the `XMLHttpRequest` object using the URL specified as
669
+ * {@link module:upload/uploadconfig~SimpleUploadConfig#uploadUrl `simpleUpload.uploadUrl`} in the editor's
670
+ * configuration.
671
+ */
672
+ _initRequest() {
673
+ const xhr = this.xhr = new XMLHttpRequest();
674
+ xhr.open("POST", this.options.uploadUrl, true);
675
+ xhr.responseType = "json";
676
+ }
677
+ /**
678
+ * Initializes XMLHttpRequest listeners
679
+ *
680
+ * @param resolve Callback function to be called when the request is successful.
681
+ * @param reject Callback function to be called when the request cannot be completed.
682
+ * @param file Native File object.
683
+ */
684
+ _initListeners(resolve, reject, file) {
685
+ const xhr = this.xhr;
686
+ const loader = this.loader;
687
+ const genericErrorText = `Couldn't upload file: ${file.name}.`;
688
+ xhr.addEventListener("error", () => reject(genericErrorText));
689
+ xhr.addEventListener("abort", () => reject());
690
+ xhr.addEventListener("load", () => {
691
+ const response = xhr.response;
692
+ if (!response || response.error) return reject(response && response.error && response.error.message ? response.error.message : genericErrorText);
693
+ const urls = response.url ? { default: response.url } : response.urls;
694
+ resolve({
695
+ ...response,
696
+ urls
697
+ });
698
+ });
699
+ /* v8 ignore else -- @preserve */
700
+ if (xhr.upload) xhr.upload.addEventListener("progress", (evt) => {
701
+ /* v8 ignore else -- @preserve */
702
+ if (evt.lengthComputable) {
703
+ loader.uploadTotal = evt.total;
704
+ loader.uploaded = evt.loaded;
705
+ }
706
+ });
707
+ }
708
+ /**
709
+ * Prepares the data and sends the request.
710
+ *
711
+ * @param file File instance to be uploaded.
712
+ */
713
+ _sendRequest(file) {
714
+ let headers = this.options.headers || {};
715
+ if (typeof headers === "function") headers = headers(file);
716
+ const withCredentials = this.options.withCredentials || false;
717
+ for (const headerName of Object.keys(headers)) this.xhr.setRequestHeader(headerName, headers[headerName]);
718
+ this.xhr.withCredentials = withCredentials;
719
+ const data = new FormData();
720
+ data.append("upload", file);
721
+ this.xhr.send(data);
722
+ }
723
+ };
724
+
592
725
  /**
593
- * Upload adapter.
594
- */ class Adapter {
595
- /**
596
- * FileLoader instance to use during the upload.
597
- */ loader;
598
- /**
599
- * The configuration of the adapter.
600
- */ options;
601
- xhr;
602
- /**
603
- * Creates a new adapter instance.
604
- */ constructor(loader, options){
605
- this.loader = loader;
606
- this.options = options;
607
- }
608
- /**
609
- * Starts the upload process.
610
- *
611
- * @see module:upload/filerepository~UploadAdapter#upload
612
- */ upload() {
613
- return this.loader.file.then((file)=>new Promise((resolve, reject)=>{
614
- this._initRequest();
615
- this._initListeners(resolve, reject, file);
616
- this._sendRequest(file);
617
- }));
618
- }
619
- /**
620
- * Aborts the upload process.
621
- *
622
- * @see module:upload/filerepository~UploadAdapter#abort
623
- */ abort() {
624
- if (this.xhr) {
625
- this.xhr.abort();
626
- }
627
- }
628
- /**
629
- * Initializes the `XMLHttpRequest` object using the URL specified as
630
- * {@link module:upload/uploadconfig~SimpleUploadConfig#uploadUrl `simpleUpload.uploadUrl`} in the editor's
631
- * configuration.
632
- */ _initRequest() {
633
- const xhr = this.xhr = new XMLHttpRequest();
634
- xhr.open('POST', this.options.uploadUrl, true);
635
- xhr.responseType = 'json';
636
- }
637
- /**
638
- * Initializes XMLHttpRequest listeners
639
- *
640
- * @param resolve Callback function to be called when the request is successful.
641
- * @param reject Callback function to be called when the request cannot be completed.
642
- * @param file Native File object.
643
- */ _initListeners(resolve, reject, file) {
644
- const xhr = this.xhr;
645
- const loader = this.loader;
646
- const genericErrorText = `Couldn't upload file: ${file.name}.`;
647
- xhr.addEventListener('error', ()=>reject(genericErrorText));
648
- xhr.addEventListener('abort', ()=>reject());
649
- xhr.addEventListener('load', ()=>{
650
- const response = xhr.response;
651
- if (!response || response.error) {
652
- return reject(response && response.error && response.error.message ? response.error.message : genericErrorText);
653
- }
654
- const urls = response.url ? {
655
- default: response.url
656
- } : response.urls;
657
- // Resolve with the normalized `urls` property and pass the rest of the response
658
- // to allow customizing the behavior of features relying on the upload adapters.
659
- resolve({
660
- ...response,
661
- urls
662
- });
663
- });
664
- // Upload progress when it is supported.
665
- /* istanbul ignore else -- @preserve */ if (xhr.upload) {
666
- xhr.upload.addEventListener('progress', (evt)=>{
667
- if (evt.lengthComputable) {
668
- loader.uploadTotal = evt.total;
669
- loader.uploaded = evt.loaded;
670
- }
671
- });
672
- }
673
- }
674
- /**
675
- * Prepares the data and sends the request.
676
- *
677
- * @param file File instance to be uploaded.
678
- */ _sendRequest(file) {
679
- // Set headers if specified.
680
- let headers = this.options.headers || {};
681
- if (typeof headers === 'function') {
682
- headers = headers(file);
683
- }
684
- // Use the withCredentials flag if specified.
685
- const withCredentials = this.options.withCredentials || false;
686
- for (const headerName of Object.keys(headers)){
687
- this.xhr.setRequestHeader(headerName, headers[headerName]);
688
- }
689
- this.xhr.withCredentials = withCredentials;
690
- // Prepare the form data.
691
- const data = new FormData();
692
- data.append('upload', file);
693
- // Send the request.
694
- this.xhr.send(data);
695
- }
696
- }
726
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
727
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
728
+ */
697
729
 
698
730
  export { Base64UploadAdapter, FileLoader, FileReader, FileRepository, SimpleUploadAdapter };
699
- //# sourceMappingURL=index.js.map
731
+ //# sourceMappingURL=index.js.map