1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | import { Plugin } from 'ckeditor5/src/core';
|
11 | import { Element, enablePlaceholder } from 'ckeditor5/src/engine';
|
12 | import { toWidgetEditable } from 'ckeditor5/src/widget';
|
13 |
|
14 | import ToggleImageCaptionCommand from './toggleimagecaptioncommand';
|
15 |
|
16 | import ImageUtils from '../imageutils';
|
17 | import ImageCaptionUtils from './imagecaptionutils';
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | export default class ImageCaptionEditing extends Plugin {
|
29 | |
30 |
|
31 |
|
32 | static get requires() {
|
33 | return [ ImageUtils, ImageCaptionUtils ];
|
34 | }
|
35 |
|
36 | |
37 |
|
38 |
|
39 | static get pluginName() {
|
40 | return 'ImageCaptionEditing';
|
41 | }
|
42 |
|
43 | |
44 |
|
45 |
|
46 | constructor( editor ) {
|
47 | super( editor );
|
48 |
|
49 | |
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 | this._savedCaptionsMap = new WeakMap();
|
58 | }
|
59 |
|
60 | |
61 |
|
62 |
|
63 | init() {
|
64 | const editor = this.editor;
|
65 | const schema = editor.model.schema;
|
66 |
|
67 |
|
68 | if ( !schema.isRegistered( 'caption' ) ) {
|
69 | schema.register( 'caption', {
|
70 | allowIn: 'imageBlock',
|
71 | allowContentOf: '$block',
|
72 | isLimit: true
|
73 | } );
|
74 | } else {
|
75 | schema.extend( 'caption', {
|
76 | allowIn: 'imageBlock'
|
77 | } );
|
78 | }
|
79 |
|
80 | editor.commands.add( 'toggleImageCaption', new ToggleImageCaptionCommand( this.editor ) );
|
81 |
|
82 | this._setupConversion();
|
83 | this._setupImageTypeCommandsIntegration();
|
84 | }
|
85 |
|
86 | |
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 | _setupConversion() {
|
93 | const editor = this.editor;
|
94 | const view = editor.editing.view;
|
95 | const imageUtils = editor.plugins.get( 'ImageUtils' );
|
96 | const imageCaptionUtils = editor.plugins.get( 'ImageCaptionUtils' );
|
97 | const t = editor.t;
|
98 |
|
99 |
|
100 | editor.conversion.for( 'upcast' ).elementToElement( {
|
101 | view: element => imageCaptionUtils.matchImageCaptionViewElement( element ),
|
102 | model: 'caption'
|
103 | } );
|
104 |
|
105 |
|
106 | editor.conversion.for( 'dataDowncast' ).elementToElement( {
|
107 | model: 'caption',
|
108 | view: ( modelElement, { writer } ) => {
|
109 | if ( !imageUtils.isBlockImage( modelElement.parent ) ) {
|
110 | return null;
|
111 | }
|
112 |
|
113 | return writer.createContainerElement( 'figcaption' );
|
114 | }
|
115 | } );
|
116 |
|
117 |
|
118 | editor.conversion.for( 'editingDowncast' ).elementToElement( {
|
119 | model: 'caption',
|
120 | view: ( modelElement, { writer } ) => {
|
121 | if ( !imageUtils.isBlockImage( modelElement.parent ) ) {
|
122 | return null;
|
123 | }
|
124 |
|
125 | const figcaptionElement = writer.createEditableElement( 'figcaption' );
|
126 | writer.setCustomProperty( 'imageCaption', true, figcaptionElement );
|
127 |
|
128 | enablePlaceholder( {
|
129 | view,
|
130 | element: figcaptionElement,
|
131 | text: t( 'Enter image caption' ),
|
132 | keepOnFocus: true
|
133 | } );
|
134 |
|
135 | return toWidgetEditable( figcaptionElement, writer );
|
136 | }
|
137 | } );
|
138 | }
|
139 |
|
140 | |
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 | _setupImageTypeCommandsIntegration() {
|
148 | const editor = this.editor;
|
149 | const imageUtils = editor.plugins.get( 'ImageUtils' );
|
150 | const imageCaptionUtils = editor.plugins.get( 'ImageCaptionUtils' );
|
151 | const imageTypeInlineCommand = editor.commands.get( 'imageTypeInline' );
|
152 | const imageTypeBlockCommand = editor.commands.get( 'imageTypeBlock' );
|
153 |
|
154 | const handleImageTypeChange = evt => {
|
155 |
|
156 | if ( !evt.return ) {
|
157 | return;
|
158 | }
|
159 |
|
160 | const { oldElement, newElement } = evt.return;
|
161 |
|
162 |
|
163 | if ( !oldElement ) {
|
164 | return;
|
165 | }
|
166 |
|
167 | if ( imageUtils.isBlockImage( oldElement ) ) {
|
168 | const oldCaptionElement = imageCaptionUtils.getCaptionFromImageModelElement( oldElement );
|
169 |
|
170 |
|
171 |
|
172 | if ( oldCaptionElement ) {
|
173 | this._saveCaption( newElement, oldCaptionElement );
|
174 |
|
175 | return;
|
176 | }
|
177 | }
|
178 |
|
179 | const savedOldElementCaption = this._getSavedCaption( oldElement );
|
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 | if ( savedOldElementCaption ) {
|
190 |
|
191 |
|
192 | this._saveCaption( newElement, savedOldElementCaption );
|
193 | }
|
194 | };
|
195 |
|
196 |
|
197 | if ( imageTypeInlineCommand ) {
|
198 | this.listenTo( imageTypeInlineCommand, 'execute', handleImageTypeChange, { priority: 'low' } );
|
199 | }
|
200 |
|
201 | if ( imageTypeBlockCommand ) {
|
202 | this.listenTo( imageTypeBlockCommand, 'execute', handleImageTypeChange, { priority: 'low' } );
|
203 | }
|
204 | }
|
205 |
|
206 | |
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 | _getSavedCaption( imageModelElement ) {
|
218 | const jsonObject = this._savedCaptionsMap.get( imageModelElement );
|
219 |
|
220 | return jsonObject ? Element.fromJSON( jsonObject ) : null;
|
221 | }
|
222 |
|
223 | |
224 |
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 |
|
240 |
|
241 |
|
242 | _saveCaption( imageModelElement, caption ) {
|
243 | this._savedCaptionsMap.set( imageModelElement, caption.toJSON() );
|
244 | }
|
245 | }
|