UNPKG

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