1 | /**
|
2 | * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4 | */
|
5 | import { FileRepository } from 'ckeditor5/src/upload.js';
|
6 | import { Command } from 'ckeditor5/src/core.js';
|
7 | import { toArray } from 'ckeditor5/src/utils.js';
|
8 | /**
|
9 | * @module image/imageupload/uploadimagecommand
|
10 | */
|
11 | /**
|
12 | * The upload image command.
|
13 | *
|
14 | * The command is registered by the {@link module:image/imageupload/imageuploadediting~ImageUploadEditing} plugin as `uploadImage`
|
15 | * and it is also available via aliased `imageUpload` name.
|
16 | *
|
17 | * In order to upload an image at the current selection position
|
18 | * (according to the {@link module:widget/utils~findOptimalInsertionRange} algorithm),
|
19 | * execute the command and pass the native image file instance:
|
20 | *
|
21 | * ```ts
|
22 | * this.listenTo( editor.editing.view.document, 'clipboardInput', ( evt, data ) => {
|
23 | * // Assuming that only images were pasted:
|
24 | * const images = Array.from( data.dataTransfer.files );
|
25 | *
|
26 | * // Upload the first image:
|
27 | * editor.execute( 'uploadImage', { file: images[ 0 ] } );
|
28 | * } );
|
29 | * ```
|
30 | *
|
31 | * It is also possible to insert multiple images at once:
|
32 | *
|
33 | * ```ts
|
34 | * editor.execute( 'uploadImage', {
|
35 | * file: [
|
36 | * file1,
|
37 | * file2
|
38 | * ]
|
39 | * } );
|
40 | * ```
|
41 | */
|
42 | export default class UploadImageCommand extends Command {
|
43 | /**
|
44 | * @inheritDoc
|
45 | */
|
46 | refresh() {
|
47 | const editor = this.editor;
|
48 | const imageUtils = editor.plugins.get('ImageUtils');
|
49 | const selectedElement = editor.model.document.selection.getSelectedElement();
|
50 | // TODO: This needs refactoring.
|
51 | this.isEnabled = imageUtils.isImageAllowed() || imageUtils.isImage(selectedElement);
|
52 | }
|
53 | /**
|
54 | * Executes the command.
|
55 | *
|
56 | * @fires execute
|
57 | * @param options Options for the executed command.
|
58 | * @param options.file The image file or an array of image files to upload.
|
59 | */
|
60 | execute(options) {
|
61 | const files = toArray(options.file);
|
62 | const selection = this.editor.model.document.selection;
|
63 | const imageUtils = this.editor.plugins.get('ImageUtils');
|
64 | // In case of multiple files, each file (starting from the 2nd) will be inserted at a position that
|
65 | // follows the previous one. That will move the selection and, to stay on the safe side and make sure
|
66 | // all images inherit the same selection attributes, they are collected beforehand.
|
67 | //
|
68 | // Applying these attributes ensures, for instance, that inserting an (inline) image into a link does
|
69 | // not split that link but preserves its continuity.
|
70 | //
|
71 | // Note: Selection attributes that do not make sense for images will be filtered out by insertImage() anyway.
|
72 | const selectionAttributes = Object.fromEntries(selection.getAttributes());
|
73 | files.forEach((file, index) => {
|
74 | const selectedElement = selection.getSelectedElement();
|
75 | // Inserting of an inline image replace the selected element and make a selection on the inserted image.
|
76 | // Therefore inserting multiple inline images requires creating position after each element.
|
77 | if (index && selectedElement && imageUtils.isImage(selectedElement)) {
|
78 | const position = this.editor.model.createPositionAfter(selectedElement);
|
79 | this._uploadImage(file, selectionAttributes, position);
|
80 | }
|
81 | else {
|
82 | this._uploadImage(file, selectionAttributes);
|
83 | }
|
84 | });
|
85 | }
|
86 | /**
|
87 | * Handles uploading single file.
|
88 | */
|
89 | _uploadImage(file, attributes, position) {
|
90 | const editor = this.editor;
|
91 | const fileRepository = editor.plugins.get(FileRepository);
|
92 | const loader = fileRepository.createLoader(file);
|
93 | const imageUtils = editor.plugins.get('ImageUtils');
|
94 | // Do not throw when upload adapter is not set. FileRepository will log an error anyway.
|
95 | if (!loader) {
|
96 | return;
|
97 | }
|
98 | imageUtils.insertImage({ ...attributes, uploadId: loader.id }, position);
|
99 | }
|
100 | }
|