1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | import { Plugin } from 'ckeditor5/src/core';
|
11 | import { ClipboardPipeline } from 'ckeditor5/src/clipboard';
|
12 | import { UpcastWriter } from 'ckeditor5/src/engine';
|
13 |
|
14 | import {
|
15 | downcastImageAttribute,
|
16 | downcastSrcsetAttribute
|
17 | } from './converters';
|
18 |
|
19 | import ImageEditing from './imageediting';
|
20 | import ImageTypeCommand from './imagetypecommand';
|
21 | import ImageUtils from '../imageutils';
|
22 | import {
|
23 | getImgViewElementMatcher,
|
24 | createInlineImageViewElement,
|
25 | determineImageTypeForInsertionAtSelection
|
26 | } from '../image/utils';
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 | export default class ImageInlineEditing extends Plugin {
|
41 | |
42 |
|
43 |
|
44 | static get requires() {
|
45 | return [ ImageEditing, ImageUtils, ClipboardPipeline ];
|
46 | }
|
47 |
|
48 | |
49 |
|
50 |
|
51 | static get pluginName() {
|
52 | return 'ImageInlineEditing';
|
53 | }
|
54 |
|
55 | |
56 |
|
57 |
|
58 | init() {
|
59 | const editor = this.editor;
|
60 | const schema = editor.model.schema;
|
61 |
|
62 |
|
63 | schema.register( 'imageInline', {
|
64 | inheritAllFrom: '$inlineObject',
|
65 | allowAttributes: [ 'alt', 'src', 'srcset' ]
|
66 | } );
|
67 |
|
68 |
|
69 |
|
70 |
|
71 | schema.addChildCheck( ( context, childDefinition ) => {
|
72 | if ( context.endsWith( 'caption' ) && childDefinition.name === 'imageInline' ) {
|
73 | return false;
|
74 | }
|
75 | } );
|
76 |
|
77 | this._setupConversion();
|
78 |
|
79 | if ( editor.plugins.has( 'ImageBlockEditing' ) ) {
|
80 | editor.commands.add( 'imageTypeInline', new ImageTypeCommand( this.editor, 'imageInline' ) );
|
81 |
|
82 | this._setupClipboardIntegration();
|
83 | }
|
84 | }
|
85 |
|
86 | |
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 | _setupConversion() {
|
93 | const editor = this.editor;
|
94 | const t = editor.t;
|
95 | const conversion = editor.conversion;
|
96 | const imageUtils = editor.plugins.get( 'ImageUtils' );
|
97 |
|
98 | conversion.for( 'dataDowncast' )
|
99 | .elementToElement( {
|
100 | model: 'imageInline',
|
101 | view: ( modelElement, { writer } ) => writer.createEmptyElement( 'img' )
|
102 | } );
|
103 |
|
104 | conversion.for( 'editingDowncast' )
|
105 | .elementToStructure( {
|
106 | model: 'imageInline',
|
107 | view: ( modelElement, { writer } ) => imageUtils.toImageWidget(
|
108 | createInlineImageViewElement( writer ), writer, t( 'image widget' )
|
109 | )
|
110 | } );
|
111 |
|
112 | conversion.for( 'downcast' )
|
113 | .add( downcastImageAttribute( imageUtils, 'imageInline', 'src' ) )
|
114 | .add( downcastImageAttribute( imageUtils, 'imageInline', 'alt' ) )
|
115 | .add( downcastSrcsetAttribute( imageUtils, 'imageInline' ) );
|
116 |
|
117 |
|
118 | conversion.for( 'upcast' )
|
119 | .elementToElement( {
|
120 | view: getImgViewElementMatcher( editor, 'imageInline' ),
|
121 | model: ( viewImage, { writer } ) => writer.createElement(
|
122 | 'imageInline',
|
123 | viewImage.hasAttribute( 'src' ) ? { src: viewImage.getAttribute( 'src' ) } : null
|
124 | )
|
125 | } );
|
126 | }
|
127 |
|
128 | |
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 | _setupClipboardIntegration() {
|
146 | const editor = this.editor;
|
147 | const model = editor.model;
|
148 | const editingView = editor.editing.view;
|
149 | const imageUtils = editor.plugins.get( 'ImageUtils' );
|
150 |
|
151 | this.listenTo( editor.plugins.get( 'ClipboardPipeline' ), 'inputTransformation', ( evt, data ) => {
|
152 | const docFragmentChildren = Array.from( data.content.getChildren() );
|
153 | let modelRange;
|
154 |
|
155 |
|
156 |
|
157 | if ( !docFragmentChildren.every( imageUtils.isBlockImageView ) ) {
|
158 | return;
|
159 | }
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | if ( data.targetRanges ) {
|
165 | modelRange = editor.editing.mapper.toModelRange( data.targetRanges[ 0 ] );
|
166 | }
|
167 |
|
168 | else {
|
169 | modelRange = model.document.selection.getFirstRange();
|
170 | }
|
171 |
|
172 | const selection = model.createSelection( modelRange );
|
173 |
|
174 |
|
175 |
|
176 | if ( determineImageTypeForInsertionAtSelection( model.schema, selection ) === 'imageInline' ) {
|
177 | const writer = new UpcastWriter( editingView.document );
|
178 |
|
179 |
|
180 |
|
181 | const inlineViewImages = docFragmentChildren.map( blockViewImage => {
|
182 |
|
183 |
|
184 |
|
185 |
|
186 | if ( blockViewImage.childCount === 1 ) {
|
187 |
|
188 |
|
189 | Array.from( blockViewImage.getAttributes() )
|
190 | .forEach( attribute => writer.setAttribute(
|
191 | ...attribute,
|
192 | imageUtils.findViewImgElement( blockViewImage )
|
193 | ) );
|
194 |
|
195 | return blockViewImage.getChild( 0 );
|
196 | } else {
|
197 | return blockViewImage;
|
198 | }
|
199 | } );
|
200 |
|
201 | data.content = writer.createDocumentFragment( inlineViewImages );
|
202 | }
|
203 | } );
|
204 | }
|
205 | }
|