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 | import ImageBlockEditing from '../image/imageblockediting.js';
|
7 | /**
|
8 | * The toggle image caption command.
|
9 | *
|
10 | * This command is registered by {@link module:image/imagecaption/imagecaptionediting~ImageCaptionEditing} as the
|
11 | * `'toggleImageCaption'` editor command.
|
12 | *
|
13 | * Executing this command:
|
14 | *
|
15 | * * either adds or removes the image caption of a selected image (depending on whether the caption is present or not),
|
16 | * * removes the image caption if the selection is anchored in one.
|
17 | *
|
18 | * ```ts
|
19 | * // Toggle the presence of the caption.
|
20 | * editor.execute( 'toggleImageCaption' );
|
21 | * ```
|
22 | *
|
23 | * **Note**: Upon executing this command, the selection will be set on the image if previously anchored in the caption element.
|
24 | *
|
25 | * **Note**: You can move the selection to the caption right away as it shows up upon executing this command by using
|
26 | * the `focusCaptionOnShow` option:
|
27 | *
|
28 | * ```ts
|
29 | * editor.execute( 'toggleImageCaption', { focusCaptionOnShow: true } );
|
30 | * ```
|
31 | */
|
32 | export default class ToggleImageCaptionCommand extends Command {
|
33 | /**
|
34 | * @inheritDoc
|
35 | */
|
36 | refresh() {
|
37 | const editor = this.editor;
|
38 | const imageCaptionUtils = editor.plugins.get('ImageCaptionUtils');
|
39 | const imageUtils = editor.plugins.get('ImageUtils');
|
40 | // Only block images can get captions.
|
41 | if (!editor.plugins.has(ImageBlockEditing)) {
|
42 | this.isEnabled = false;
|
43 | this.value = false;
|
44 | return;
|
45 | }
|
46 | const selection = editor.model.document.selection;
|
47 | const selectedElement = selection.getSelectedElement();
|
48 | if (!selectedElement) {
|
49 | const ancestorCaptionElement = imageCaptionUtils.getCaptionFromModelSelection(selection);
|
50 | this.isEnabled = !!ancestorCaptionElement;
|
51 | this.value = !!ancestorCaptionElement;
|
52 | return;
|
53 | }
|
54 | // Block images support captions by default but the command should also be enabled for inline
|
55 | // images because toggling the caption when one is selected should convert it into a block image.
|
56 | this.isEnabled = imageUtils.isImage(selectedElement);
|
57 | if (!this.isEnabled) {
|
58 | this.value = false;
|
59 | }
|
60 | else {
|
61 | this.value = !!imageCaptionUtils.getCaptionFromImageModelElement(selectedElement);
|
62 | }
|
63 | }
|
64 | /**
|
65 | * Executes the command.
|
66 | *
|
67 | * ```ts
|
68 | * editor.execute( 'toggleImageCaption' );
|
69 | * ```
|
70 | *
|
71 | * @param options Options for the executed command.
|
72 | * @param options.focusCaptionOnShow When true and the caption shows up, the selection will be moved into it straight away.
|
73 | * @fires execute
|
74 | */
|
75 | execute(options = {}) {
|
76 | const { focusCaptionOnShow } = options;
|
77 | this.editor.model.change(writer => {
|
78 | if (this.value) {
|
79 | this._hideImageCaption(writer);
|
80 | }
|
81 | else {
|
82 | this._showImageCaption(writer, focusCaptionOnShow);
|
83 | }
|
84 | });
|
85 | }
|
86 | /**
|
87 | * Shows the caption of the `<imageBlock>` or `<imageInline>`. Also:
|
88 | *
|
89 | * * it converts `<imageInline>` to `<imageBlock>` to show the caption,
|
90 | * * it attempts to restore the caption content from the `ImageCaptionEditing` caption registry,
|
91 | * * it moves the selection to the caption right away, it the `focusCaptionOnShow` option was set.
|
92 | */
|
93 | _showImageCaption(writer, focusCaptionOnShow) {
|
94 | const model = this.editor.model;
|
95 | const selection = model.document.selection;
|
96 | const imageCaptionEditing = this.editor.plugins.get('ImageCaptionEditing');
|
97 | const imageUtils = this.editor.plugins.get('ImageUtils');
|
98 | let selectedImage = selection.getSelectedElement();
|
99 | const savedCaption = imageCaptionEditing._getSavedCaption(selectedImage);
|
100 | // Convert imageInline -> image first.
|
101 | if (imageUtils.isInlineImage(selectedImage)) {
|
102 | this.editor.execute('imageTypeBlock');
|
103 | // Executing the command created a new model element. Let's pick it again.
|
104 | selectedImage = selection.getSelectedElement();
|
105 | }
|
106 | // Try restoring the caption from the ImageCaptionEditing plugin storage.
|
107 | const newCaptionElement = savedCaption || writer.createElement('caption');
|
108 | writer.append(newCaptionElement, selectedImage);
|
109 | if (focusCaptionOnShow) {
|
110 | writer.setSelection(newCaptionElement, 'in');
|
111 | }
|
112 | }
|
113 | /**
|
114 | * Hides the caption of a selected image (or an image caption the selection is anchored to).
|
115 | *
|
116 | * The content of the caption is stored in the `ImageCaptionEditing` caption registry to make this
|
117 | * a reversible action.
|
118 | */
|
119 | _hideImageCaption(writer) {
|
120 | const editor = this.editor;
|
121 | const selection = editor.model.document.selection;
|
122 | const imageCaptionEditing = editor.plugins.get('ImageCaptionEditing');
|
123 | const imageCaptionUtils = editor.plugins.get('ImageCaptionUtils');
|
124 | let selectedImage = selection.getSelectedElement();
|
125 | let captionElement;
|
126 | if (selectedImage) {
|
127 | captionElement = imageCaptionUtils.getCaptionFromImageModelElement(selectedImage);
|
128 | }
|
129 | else {
|
130 | captionElement = imageCaptionUtils.getCaptionFromModelSelection(selection);
|
131 | selectedImage = captionElement.parent;
|
132 | }
|
133 | // Store the caption content so it can be restored quickly if the user changes their mind even if they toggle image<->imageInline.
|
134 | imageCaptionEditing._saveCaption(selectedImage, captionElement);
|
135 | writer.setSelection(selectedImage, 'on');
|
136 | writer.remove(captionElement);
|
137 | }
|
138 | }
|