1010import Plugin from '@ckeditor/ckeditor5-core/src/plugin' ;
1111import FileRepository from '@ckeditor/ckeditor5-upload/src/filerepository' ;
1212import Notification from '@ckeditor/ckeditor5-ui/src/notification/notification' ;
13+ import UpcastWriter from '@ckeditor/ckeditor5-engine/src/view/upcastwriter' ;
14+ import { upcastAttributeToAttribute } from '@ckeditor/ckeditor5-engine/src/conversion/upcast-converters' ;
1315
1416import ImageUploadCommand from '../../src/imageupload/imageuploadcommand' ;
15- import { isImageType } from '../../src/imageupload/utils' ;
17+ import { isImageType , isLocalImage , fetchLocalImage } from '../../src/imageupload/utils' ;
1618
1719/**
1820 * The editing part of the image upload feature. It registers the `'imageUpload'` command.
@@ -34,6 +36,7 @@ export default class ImageUploadEditing extends Plugin {
3436 const editor = this . editor ;
3537 const doc = editor . model . document ;
3638 const schema = editor . model . schema ;
39+ const conversion = editor . conversion ;
3740 const fileRepository = editor . plugins . get ( FileRepository ) ;
3841
3942 // Setup schema to allow uploadId and uploadStatus for images.
@@ -44,6 +47,16 @@ export default class ImageUploadEditing extends Plugin {
4447 // Register imageUpload command.
4548 editor . commands . add ( 'imageUpload' , new ImageUploadCommand ( editor ) ) ;
4649
50+ // Register upcast converter for uploadId.
51+ conversion . for ( 'upcast' )
52+ . add ( upcastAttributeToAttribute ( {
53+ view : {
54+ name : 'img' ,
55+ key : 'uploadId'
56+ } ,
57+ model : 'uploadId'
58+ } ) ) ;
59+
4760 // Handle pasted images.
4861 // For every image file, a new file loader is created and a placeholder image is
4962 // inserted into the content. Then, those images are uploaded once they appear in the model
@@ -81,6 +94,48 @@ export default class ImageUploadEditing extends Plugin {
8194 } ) ;
8295 } ) ;
8396
97+ // Handle HTML pasted with images with base64 or blob sources.
98+ // For every image file, a new file loader is created and a placeholder image is
99+ // inserted into the content. Then, those images are uploaded once they appear in the model
100+ // (see Document#change listener below).
101+ this . listenTo ( editor . plugins . get ( 'Clipboard' ) , 'inputTransformation' , ( evt , data ) => {
102+ const fetchableImages = Array . from ( editor . editing . view . createRangeIn ( data . content ) )
103+ . filter ( value => isLocalImage ( value . item ) && ! value . item . getAttribute ( 'uploadProcessed' ) )
104+ . map ( value => fetchLocalImage ( value . item ) ) ;
105+
106+ if ( ! fetchableImages . length ) {
107+ return ;
108+ }
109+
110+ evt . stop ( ) ;
111+
112+ Promise . all ( fetchableImages ) . then ( items => {
113+ const writer = new UpcastWriter ( ) ;
114+
115+ for ( const item of items ) {
116+ if ( ! item . file ) {
117+ // Failed to fetch image or create a file instance, remove image element.
118+ writer . remove ( item . image ) ;
119+ } else {
120+ // Set attribute marking the image as processed.
121+ writer . setAttribute ( 'uploadProcessed' , true , item . image ) ;
122+
123+ const loader = fileRepository . createLoader ( item . file ) ;
124+
125+ if ( loader ) {
126+ writer . setAttribute ( 'src' , '' , item . image ) ;
127+ writer . setAttribute ( 'uploadId' , loader . id , item . image ) ;
128+ }
129+ }
130+ }
131+
132+ editor . plugins . get ( 'Clipboard' ) . fire ( 'inputTransformation' , {
133+ content : data . content ,
134+ dataTransfer : data . dataTransfer
135+ } ) ;
136+ } ) ;
137+ } ) ;
138+
84139 // Prevents from the browser redirecting to the dropped image.
85140 editor . editing . view . document . on ( 'dragover' , ( evt , data ) => {
86141 data . preventDefault ( ) ;
@@ -163,33 +218,7 @@ export default class ImageUploadEditing extends Plugin {
163218 . then ( data => {
164219 model . enqueueChange ( 'transparent' , writer => {
165220 writer . setAttributes ( { uploadStatus : 'complete' , src : data . default } , imageElement ) ;
166-
167- // Srcset attribute for responsive images support.
168- let maxWidth = 0 ;
169- const srcsetAttribute = Object . keys ( data )
170- // Filter out keys that are not integers.
171- . filter ( key => {
172- const width = parseInt ( key , 10 ) ;
173-
174- if ( ! isNaN ( width ) ) {
175- maxWidth = Math . max ( maxWidth , width ) ;
176-
177- return true ;
178- }
179- } )
180-
181- // Convert each key to srcset entry.
182- . map ( key => `${ data [ key ] } ${ key } w` )
183-
184- // Join all entries.
185- . join ( ', ' ) ;
186-
187- if ( srcsetAttribute != '' ) {
188- writer . setAttribute ( 'srcset' , {
189- data : srcsetAttribute ,
190- width : maxWidth
191- } , imageElement ) ;
192- }
221+ this . _parseAndSetSrcsetAttributeOnImage ( data , imageElement , writer ) ;
193222 } ) ;
194223
195224 clean ( ) ;
@@ -226,6 +255,44 @@ export default class ImageUploadEditing extends Plugin {
226255 fileRepository . destroyLoader ( loader ) ;
227256 }
228257 }
258+
259+ /**
260+ * Creates `srcset` attribute based on a given file upload response and sets it as an attribute to a specific image element.
261+ *
262+ * @protected
263+ * @param {Object } data Data object from which `srcset` will be created.
264+ * @param {module:engine/model/element~Element } image The image element on which `srcset` attribute will be set.
265+ * @param {module:engine/model/writer~Writer } writer
266+ */
267+ _parseAndSetSrcsetAttributeOnImage ( data , image , writer ) {
268+ // Srcset attribute for responsive images support.
269+ let maxWidth = 0 ;
270+
271+ const srcsetAttribute = Object . keys ( data )
272+ // Filter out keys that are not integers.
273+ . filter ( key => {
274+ const width = parseInt ( key , 10 ) ;
275+
276+ if ( ! isNaN ( width ) ) {
277+ maxWidth = Math . max ( maxWidth , width ) ;
278+
279+ return true ;
280+ }
281+ } )
282+
283+ // Convert each key to srcset entry.
284+ . map ( key => `${ data [ key ] } ${ key } w` )
285+
286+ // Join all entries.
287+ . join ( ', ' ) ;
288+
289+ if ( srcsetAttribute != '' ) {
290+ writer . setAttribute ( 'srcset' , {
291+ data : srcsetAttribute ,
292+ width : maxWidth
293+ } , image ) ;
294+ }
295+ }
229296}
230297
231298// Returns `true` if non-empty `text/html` is included in the data transfer.
0 commit comments