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 { Command } from 'ckeditor5/src/core.js';
|
6 | /**
|
7 | * The image type command. It changes the type of a selected image, depending on the configuration.
|
8 | */
|
9 | export default class ImageTypeCommand extends Command {
|
10 | /**
|
11 | * @inheritDoc
|
12 | *
|
13 | * @param modelElementName Model element name the command converts to.
|
14 | */
|
15 | constructor(editor, modelElementName) {
|
16 | super(editor);
|
17 | this._modelElementName = modelElementName;
|
18 | }
|
19 | /**
|
20 | * @inheritDoc
|
21 | */
|
22 | refresh() {
|
23 | const editor = this.editor;
|
24 | const imageUtils = editor.plugins.get('ImageUtils');
|
25 | const element = imageUtils.getClosestSelectedImageElement(this.editor.model.document.selection);
|
26 | if (this._modelElementName === 'imageBlock') {
|
27 | this.isEnabled = imageUtils.isInlineImage(element);
|
28 | }
|
29 | else {
|
30 | this.isEnabled = imageUtils.isBlockImage(element);
|
31 | }
|
32 | }
|
33 | /**
|
34 | * Executes the command and changes the type of a selected image.
|
35 | *
|
36 | * @fires execute
|
37 | * @param options.setImageSizes Specifies whether the image `width` and `height` attributes should be set automatically.
|
38 | * The default is `true`.
|
39 | * @returns An object containing references to old and new model image elements
|
40 | * (for before and after the change) so external integrations can hook into the decorated
|
41 | * `execute` event and handle this change. `null` if the type change failed.
|
42 | */
|
43 | execute(options = {}) {
|
44 | const editor = this.editor;
|
45 | const model = this.editor.model;
|
46 | const imageUtils = editor.plugins.get('ImageUtils');
|
47 | const oldElement = imageUtils.getClosestSelectedImageElement(model.document.selection);
|
48 | const attributes = Object.fromEntries(oldElement.getAttributes());
|
49 | // Don't change image type if "src" is missing (a broken image), unless there's "uploadId" set.
|
50 | // This state may happen during image upload (before it finishes) and it should be possible to change type
|
51 | // of the image in the meantime.
|
52 | if (!attributes.src && !attributes.uploadId) {
|
53 | return null;
|
54 | }
|
55 | return model.change(writer => {
|
56 | const { setImageSizes = true } = options;
|
57 | // Get all markers that contain the old image element.
|
58 | const markers = Array.from(model.markers)
|
59 | .filter(marker => marker.getRange().containsItem(oldElement));
|
60 | const newElement = imageUtils.insertImage(attributes, model.createSelection(oldElement, 'on'), this._modelElementName, { setImageSizes });
|
61 | if (!newElement) {
|
62 | return null;
|
63 | }
|
64 | const newElementRange = writer.createRangeOn(newElement);
|
65 | // Expand the previously intersecting markers' ranges to include the new image element.
|
66 | for (const marker of markers) {
|
67 | const markerRange = marker.getRange();
|
68 | // Join the survived part of the old marker range with the new element range
|
69 | // (loosely because there could be some new paragraph or the existing one might got split).
|
70 | const range = markerRange.root.rootName != '$graveyard' ?
|
71 | markerRange.getJoined(newElementRange, true) : newElementRange;
|
72 | writer.updateMarker(marker, { range });
|
73 | }
|
74 | return {
|
75 | oldElement,
|
76 | newElement
|
77 | };
|
78 | });
|
79 | }
|
80 | }
|