@ckeditor/ckeditor5-image 27.1.0 → 29.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -1
- package/README.md +3 -3
- package/build/image.js +1 -1
- package/build/translations/ar.js +1 -0
- package/build/translations/ast.js +1 -0
- package/build/translations/az.js +1 -0
- package/build/translations/bg.js +1 -0
- package/build/translations/cs.js +1 -0
- package/build/translations/da.js +1 -0
- package/build/translations/de-ch.js +1 -0
- package/build/translations/de.js +1 -0
- package/build/translations/el.js +1 -0
- package/build/translations/en-au.js +1 -0
- package/build/translations/en-gb.js +1 -0
- package/build/translations/eo.js +1 -0
- package/build/translations/es.js +1 -0
- package/build/translations/et.js +1 -0
- package/build/translations/eu.js +1 -0
- package/build/translations/fa.js +1 -0
- package/build/translations/fi.js +1 -0
- package/build/translations/fr.js +1 -0
- package/build/translations/gl.js +1 -0
- package/build/translations/he.js +1 -0
- package/build/translations/hi.js +1 -0
- package/build/translations/hr.js +1 -0
- package/build/translations/hu.js +1 -0
- package/build/translations/id.js +1 -0
- package/build/translations/it.js +1 -0
- package/build/translations/ja.js +1 -0
- package/build/translations/km.js +1 -0
- package/build/translations/kn.js +1 -0
- package/build/translations/ko.js +1 -0
- package/build/translations/ku.js +1 -0
- package/build/translations/lt.js +1 -0
- package/build/translations/lv.js +1 -0
- package/build/translations/nb.js +1 -0
- package/build/translations/ne.js +1 -0
- package/build/translations/nl.js +1 -0
- package/build/translations/no.js +1 -0
- package/build/translations/pl.js +1 -0
- package/build/translations/pt-br.js +1 -0
- package/build/translations/pt.js +1 -0
- package/build/translations/ro.js +1 -0
- package/build/translations/ru.js +1 -0
- package/build/translations/si.js +1 -0
- package/build/translations/sk.js +1 -0
- package/build/translations/sq.js +1 -0
- package/build/translations/sr-latn.js +1 -0
- package/build/translations/sr.js +1 -0
- package/build/translations/sv.js +1 -0
- package/build/translations/th.js +1 -0
- package/build/translations/tk.js +1 -0
- package/build/translations/tr.js +1 -0
- package/build/translations/ug.js +1 -0
- package/build/translations/uk.js +1 -0
- package/build/translations/vi.js +1 -0
- package/build/translations/zh-cn.js +1 -0
- package/build/translations/zh.js +1 -0
- package/ckeditor5-metadata.json +233 -0
- package/lang/contexts.json +3 -0
- package/lang/translations/ar.po +12 -0
- package/lang/translations/ast.po +12 -0
- package/lang/translations/az.po +12 -0
- package/lang/translations/bg.po +12 -0
- package/lang/translations/cs.po +12 -0
- package/lang/translations/da.po +12 -0
- package/lang/translations/de-ch.po +113 -0
- package/lang/translations/de.po +15 -3
- package/lang/translations/el.po +12 -0
- package/lang/translations/en-au.po +12 -0
- package/lang/translations/en-gb.po +12 -0
- package/lang/translations/en.po +12 -0
- package/lang/translations/eo.po +12 -0
- package/lang/translations/es.po +12 -0
- package/lang/translations/et.po +12 -0
- package/lang/translations/eu.po +12 -0
- package/lang/translations/fa.po +12 -0
- package/lang/translations/fi.po +12 -0
- package/lang/translations/fr.po +12 -0
- package/lang/translations/gl.po +12 -0
- package/lang/translations/he.po +12 -0
- package/lang/translations/hi.po +12 -0
- package/lang/translations/hr.po +12 -0
- package/lang/translations/hu.po +13 -1
- package/lang/translations/id.po +21 -9
- package/lang/translations/it.po +12 -0
- package/lang/translations/ja.po +12 -0
- package/lang/translations/km.po +12 -0
- package/lang/translations/kn.po +12 -0
- package/lang/translations/ko.po +12 -0
- package/lang/translations/ku.po +12 -0
- package/lang/translations/lt.po +12 -0
- package/lang/translations/lv.po +12 -0
- package/lang/translations/nb.po +12 -0
- package/lang/translations/ne.po +12 -0
- package/lang/translations/nl.po +12 -0
- package/lang/translations/no.po +12 -0
- package/lang/translations/pl.po +20 -8
- package/lang/translations/pt-br.po +12 -0
- package/lang/translations/pt.po +12 -0
- package/lang/translations/ro.po +21 -9
- package/lang/translations/ru.po +12 -0
- package/lang/translations/si.po +12 -0
- package/lang/translations/sk.po +12 -0
- package/lang/translations/sq.po +12 -0
- package/lang/translations/sr-latn.po +12 -0
- package/lang/translations/sr.po +12 -0
- package/lang/translations/sv.po +12 -0
- package/lang/translations/th.po +12 -0
- package/lang/translations/tk.po +12 -0
- package/lang/translations/tr.po +12 -0
- package/lang/translations/ug.po +12 -0
- package/lang/translations/uk.po +12 -0
- package/lang/translations/vi.po +12 -0
- package/lang/translations/zh-cn.po +12 -0
- package/lang/translations/zh.po +12 -0
- package/package.json +36 -29
- package/src/autoimage.js +6 -4
- package/src/image/converters.js +192 -13
- package/src/image/imageblockediting.js +179 -0
- package/src/image/imageediting.js +13 -70
- package/src/image/imageinlineediting.js +204 -0
- package/src/image/imagetypecommand.js +105 -0
- package/src/image/insertimagecommand.js +77 -10
- package/src/image/ui/utils.js +3 -3
- package/src/image/utils.js +70 -121
- package/src/image.js +7 -19
- package/src/imageblock.js +46 -0
- package/src/imagecaption/imagecaptionediting.js +202 -230
- package/src/imagecaption/imagecaptionui.js +78 -0
- package/src/imagecaption/toggleimagecaptioncommand.js +165 -0
- package/src/imagecaption/utils.js +25 -40
- package/src/imagecaption.js +3 -2
- package/src/imageinline.js +46 -0
- package/src/imageinsert/imageinsertui.js +5 -6
- package/src/imageinsert.js +16 -4
- package/src/imageresize/imageresizebuttons.js +1 -1
- package/src/imageresize/imageresizeediting.js +21 -8
- package/src/imageresize/imageresizehandles.js +30 -8
- package/src/imageresize/resizeimagecommand.js +8 -5
- package/src/imagestyle/converters.js +25 -17
- package/src/imagestyle/imagestylecommand.js +73 -33
- package/src/imagestyle/imagestyleediting.js +113 -52
- package/src/imagestyle/imagestyleui.js +197 -31
- package/src/imagestyle/utils.js +300 -85
- package/src/imagestyle.js +218 -47
- package/src/imagetextalternative/imagetextalternativecommand.js +10 -7
- package/src/imagetextalternative/imagetextalternativeediting.js +9 -1
- package/src/imagetextalternative/imagetextalternativeui.js +2 -2
- package/src/imagetextalternative.js +1 -1
- package/src/imagetoolbar.js +33 -11
- package/src/imageupload/imageuploadediting.js +91 -31
- package/src/imageupload/imageuploadprogress.js +17 -9
- package/src/imageupload/imageuploadui.js +1 -1
- package/src/imageupload/uploadimagecommand.js +50 -24
- package/src/imageupload/utils.js +3 -2
- package/src/imageupload.js +1 -1
- package/src/imageutils.js +342 -0
- package/src/index.js +22 -47
- package/src/pictureediting.js +149 -0
- package/theme/image.css +101 -21
- package/theme/imagecaption.css +24 -2
- package/theme/imageresize.css +11 -0
- package/theme/imagestyle.css +76 -0
- package/theme/imageuploadicon.css +8 -2
- package/theme/imageuploadprogress.css +12 -8
- package/build/image.js.map +0 -1
|
@@ -16,16 +16,16 @@ import { ClipboardPipeline } from 'ckeditor5/src/clipboard';
|
|
|
16
16
|
import { FileRepository } from 'ckeditor5/src/upload';
|
|
17
17
|
import { env } from 'ckeditor5/src/utils';
|
|
18
18
|
|
|
19
|
+
import ImageUtils from '../imageutils';
|
|
19
20
|
import UploadImageCommand from './uploadimagecommand';
|
|
20
21
|
import { fetchLocalImage, isLocalImage } from '../../src/imageupload/utils';
|
|
21
22
|
import { createImageTypeRegExp } from './utils';
|
|
22
|
-
import { getViewImgFromWidget } from '../image/utils';
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* The editing part of the image upload feature. It registers the `'uploadImage'` command
|
|
26
|
-
* and `imageUpload` command as an aliased name.
|
|
26
|
+
* and the `imageUpload` command as an aliased name.
|
|
27
27
|
*
|
|
28
|
-
* When an image is uploaded it fires the {@link ~ImageUploadEditing#event:uploadComplete `uploadComplete` event
|
|
28
|
+
* When an image is uploaded, it fires the {@link ~ImageUploadEditing#event:uploadComplete `uploadComplete`} event
|
|
29
29
|
* that allows adding custom attributes to the {@link module:engine/model/element~Element image element}.
|
|
30
30
|
*
|
|
31
31
|
* @extends module:core/plugin~Plugin
|
|
@@ -35,7 +35,7 @@ export default class ImageUploadEditing extends Plugin {
|
|
|
35
35
|
* @inheritDoc
|
|
36
36
|
*/
|
|
37
37
|
static get requires() {
|
|
38
|
-
return [ FileRepository, Notification, ClipboardPipeline ];
|
|
38
|
+
return [ FileRepository, Notification, ClipboardPipeline, ImageUtils ];
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
static get pluginName() {
|
|
@@ -53,6 +53,21 @@ export default class ImageUploadEditing extends Plugin {
|
|
|
53
53
|
types: [ 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff' ]
|
|
54
54
|
}
|
|
55
55
|
} );
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* An internal mapping of {@link module:upload/filerepository~FileLoader#id file loader UIDs} and
|
|
59
|
+
* model elements during the upload.
|
|
60
|
+
*
|
|
61
|
+
* Model element of the uploaded image can change, for instance, when {@link module:image/image/imagetypecommand~ImageTypeCommand}
|
|
62
|
+
* is executed as a result of adding caption or changing image style. As a result, the upload logic must keep track of the model
|
|
63
|
+
* element (reference) and resolve the upload for the correct model element (instead of the one that landed in the `$graveyard`
|
|
64
|
+
* after image type changed).
|
|
65
|
+
*
|
|
66
|
+
* @private
|
|
67
|
+
* @readonly
|
|
68
|
+
* @member {Map.<String,module:engine/model/element~Element>}
|
|
69
|
+
*/
|
|
70
|
+
this._uploadImageElements = new Map();
|
|
56
71
|
}
|
|
57
72
|
|
|
58
73
|
/**
|
|
@@ -61,17 +76,10 @@ export default class ImageUploadEditing extends Plugin {
|
|
|
61
76
|
init() {
|
|
62
77
|
const editor = this.editor;
|
|
63
78
|
const doc = editor.model.document;
|
|
64
|
-
const schema = editor.model.schema;
|
|
65
79
|
const conversion = editor.conversion;
|
|
66
80
|
const fileRepository = editor.plugins.get( FileRepository );
|
|
67
|
-
|
|
81
|
+
const imageUtils = editor.plugins.get( 'ImageUtils' );
|
|
68
82
|
const imageTypes = createImageTypeRegExp( editor.config.get( 'image.upload.types' ) );
|
|
69
|
-
|
|
70
|
-
// Setup schema to allow uploadId and uploadStatus for images.
|
|
71
|
-
schema.extend( 'image', {
|
|
72
|
-
allowAttributes: [ 'uploadId', 'uploadStatus' ]
|
|
73
|
-
} );
|
|
74
|
-
|
|
75
83
|
const uploadImageCommand = new UploadImageCommand( editor );
|
|
76
84
|
|
|
77
85
|
// Register `uploadImage` command and add `imageUpload` command as an alias for backward compatibility.
|
|
@@ -133,7 +141,7 @@ export default class ImageUploadEditing extends Plugin {
|
|
|
133
141
|
// (see Document#change listener below).
|
|
134
142
|
this.listenTo( editor.plugins.get( 'ClipboardPipeline' ), 'inputTransformation', ( evt, data ) => {
|
|
135
143
|
const fetchableImages = Array.from( editor.editing.view.createRangeIn( data.content ) )
|
|
136
|
-
.filter( value => isLocalImage( value.item ) && !value.item.getAttribute( 'uploadProcessed' ) )
|
|
144
|
+
.filter( value => isLocalImage( imageUtils, value.item ) && !value.item.getAttribute( 'uploadProcessed' ) )
|
|
137
145
|
.map( value => { return { promise: fetchLocalImage( value.item ), imageElement: value.item }; } );
|
|
138
146
|
|
|
139
147
|
if ( !fetchableImages.length ) {
|
|
@@ -162,16 +170,20 @@ export default class ImageUploadEditing extends Plugin {
|
|
|
162
170
|
|
|
163
171
|
// Upload placeholder images that appeared in the model.
|
|
164
172
|
doc.on( 'change', () => {
|
|
165
|
-
|
|
173
|
+
// Note: Reversing changes to start with insertions and only then handle removals. If it was the other way around,
|
|
174
|
+
// loaders for **all** images that land in the $graveyard would abort while in fact only those that were **not** replaced
|
|
175
|
+
// by other images should be aborted.
|
|
176
|
+
const changes = doc.differ.getChanges( { includeChangesInGraveyard: true } ).reverse();
|
|
177
|
+
const insertedImagesIds = new Set();
|
|
166
178
|
|
|
167
179
|
for ( const entry of changes ) {
|
|
168
180
|
if ( entry.type == 'insert' && entry.name != '$text' ) {
|
|
169
181
|
const item = entry.position.nodeAfter;
|
|
170
|
-
const
|
|
182
|
+
const isInsertedInGraveyard = entry.position.root.rootName == '$graveyard';
|
|
171
183
|
|
|
172
|
-
for ( const
|
|
184
|
+
for ( const imageElement of getImagesFromChangeItem( editor, item ) ) {
|
|
173
185
|
// Check if the image element still has upload id.
|
|
174
|
-
const uploadId =
|
|
186
|
+
const uploadId = imageElement.getAttribute( 'uploadId' );
|
|
175
187
|
|
|
176
188
|
if ( !uploadId ) {
|
|
177
189
|
continue;
|
|
@@ -184,12 +196,28 @@ export default class ImageUploadEditing extends Plugin {
|
|
|
184
196
|
continue;
|
|
185
197
|
}
|
|
186
198
|
|
|
187
|
-
if (
|
|
188
|
-
// If the image was inserted to the graveyard
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
199
|
+
if ( isInsertedInGraveyard ) {
|
|
200
|
+
// If the image was inserted to the graveyard for good (**not** replaced by another image),
|
|
201
|
+
// only then abort the loading process.
|
|
202
|
+
if ( !insertedImagesIds.has( uploadId ) ) {
|
|
203
|
+
loader.abort();
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
// Remember the upload id of the inserted image. If it acted as a replacement for another
|
|
207
|
+
// image (which landed in the $graveyard), the related loader will not be aborted because
|
|
208
|
+
// this is still the same image upload.
|
|
209
|
+
insertedImagesIds.add( uploadId );
|
|
210
|
+
|
|
211
|
+
// Keep the mapping between the upload ID and the image model element so the upload
|
|
212
|
+
// can later resolve in the context of the correct model element. The model element could
|
|
213
|
+
// change for the same upload if one image was replaced by another (e.g. image type was changed),
|
|
214
|
+
// so this may also replace an existing mapping.
|
|
215
|
+
this._uploadImageElements.set( uploadId, imageElement );
|
|
216
|
+
|
|
217
|
+
if ( loader.status == 'idle' ) {
|
|
218
|
+
// If the image was inserted into content and has not been loaded yet, start loading it.
|
|
219
|
+
this._readAndUpload( loader );
|
|
220
|
+
}
|
|
193
221
|
}
|
|
194
222
|
}
|
|
195
223
|
}
|
|
@@ -207,6 +235,28 @@ export default class ImageUploadEditing extends Plugin {
|
|
|
207
235
|
}, { priority: 'low' } );
|
|
208
236
|
}
|
|
209
237
|
|
|
238
|
+
/**
|
|
239
|
+
* @inheritDoc
|
|
240
|
+
*/
|
|
241
|
+
afterInit() {
|
|
242
|
+
const schema = this.editor.model.schema;
|
|
243
|
+
|
|
244
|
+
// Setup schema to allow uploadId and uploadStatus for images.
|
|
245
|
+
// Wait for ImageBlockEditing or ImageInlineEditing to register their elements first,
|
|
246
|
+
// that's why doing this in afterInit() instead of init().
|
|
247
|
+
if ( this.editor.plugins.has( 'ImageBlockEditing' ) ) {
|
|
248
|
+
schema.extend( 'imageBlock', {
|
|
249
|
+
allowAttributes: [ 'uploadId', 'uploadStatus' ]
|
|
250
|
+
} );
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if ( this.editor.plugins.has( 'ImageInlineEditing' ) ) {
|
|
254
|
+
schema.extend( 'imageInline', {
|
|
255
|
+
allowAttributes: [ 'uploadId', 'uploadStatus' ]
|
|
256
|
+
} );
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
210
260
|
/**
|
|
211
261
|
* Reads and uploads an image.
|
|
212
262
|
*
|
|
@@ -216,30 +266,32 @@ export default class ImageUploadEditing extends Plugin {
|
|
|
216
266
|
*
|
|
217
267
|
* @protected
|
|
218
268
|
* @param {module:upload/filerepository~FileLoader} loader
|
|
219
|
-
* @param {module:engine/model/element~Element} imageElement
|
|
220
269
|
* @returns {Promise}
|
|
221
270
|
*/
|
|
222
|
-
_readAndUpload( loader
|
|
271
|
+
_readAndUpload( loader ) {
|
|
223
272
|
const editor = this.editor;
|
|
224
273
|
const model = editor.model;
|
|
225
274
|
const t = editor.locale.t;
|
|
226
275
|
const fileRepository = editor.plugins.get( FileRepository );
|
|
227
276
|
const notification = editor.plugins.get( Notification );
|
|
277
|
+
const imageUtils = editor.plugins.get( 'ImageUtils' );
|
|
278
|
+
const imageUploadElements = this._uploadImageElements;
|
|
228
279
|
|
|
229
280
|
model.enqueueChange( 'transparent', writer => {
|
|
230
|
-
writer.setAttribute( 'uploadStatus', 'reading',
|
|
281
|
+
writer.setAttribute( 'uploadStatus', 'reading', imageUploadElements.get( loader.id ) );
|
|
231
282
|
} );
|
|
232
283
|
|
|
233
284
|
return loader.read()
|
|
234
285
|
.then( () => {
|
|
235
286
|
const promise = loader.upload();
|
|
287
|
+
const imageElement = imageUploadElements.get( loader.id );
|
|
236
288
|
|
|
237
289
|
// Force re–paint in Safari. Without it, the image will display with a wrong size.
|
|
238
290
|
// https://github.com/ckeditor/ckeditor5/issues/1975
|
|
239
291
|
/* istanbul ignore next */
|
|
240
292
|
if ( env.isSafari ) {
|
|
241
293
|
const viewFigure = editor.editing.mapper.toViewElement( imageElement );
|
|
242
|
-
const viewImg =
|
|
294
|
+
const viewImg = imageUtils.findViewImgElement( viewFigure );
|
|
243
295
|
|
|
244
296
|
editor.editing.view.once( 'render', () => {
|
|
245
297
|
// Early returns just to be safe. There might be some code ran
|
|
@@ -273,6 +325,8 @@ export default class ImageUploadEditing extends Plugin {
|
|
|
273
325
|
} )
|
|
274
326
|
.then( data => {
|
|
275
327
|
model.enqueueChange( 'transparent', writer => {
|
|
328
|
+
const imageElement = imageUploadElements.get( loader.id );
|
|
329
|
+
|
|
276
330
|
writer.setAttribute( 'uploadStatus', 'complete', imageElement );
|
|
277
331
|
|
|
278
332
|
/**
|
|
@@ -323,18 +377,22 @@ export default class ImageUploadEditing extends Plugin {
|
|
|
323
377
|
} );
|
|
324
378
|
}
|
|
325
379
|
|
|
326
|
-
clean();
|
|
327
|
-
|
|
328
380
|
// Permanently remove image from insertion batch.
|
|
329
381
|
model.enqueueChange( 'transparent', writer => {
|
|
330
|
-
writer.remove(
|
|
382
|
+
writer.remove( imageUploadElements.get( loader.id ) );
|
|
331
383
|
} );
|
|
384
|
+
|
|
385
|
+
clean();
|
|
332
386
|
} );
|
|
333
387
|
|
|
334
388
|
function clean() {
|
|
335
389
|
model.enqueueChange( 'transparent', writer => {
|
|
390
|
+
const imageElement = imageUploadElements.get( loader.id );
|
|
391
|
+
|
|
336
392
|
writer.removeAttribute( 'uploadId', imageElement );
|
|
337
393
|
writer.removeAttribute( 'uploadStatus', imageElement );
|
|
394
|
+
|
|
395
|
+
imageUploadElements.delete( loader.id );
|
|
338
396
|
} );
|
|
339
397
|
|
|
340
398
|
fileRepository.destroyLoader( loader );
|
|
@@ -389,7 +447,9 @@ export function isHtmlIncluded( dataTransfer ) {
|
|
|
389
447
|
}
|
|
390
448
|
|
|
391
449
|
function getImagesFromChangeItem( editor, item ) {
|
|
450
|
+
const imageUtils = editor.plugins.get( 'ImageUtils' );
|
|
451
|
+
|
|
392
452
|
return Array.from( editor.model.createRangeOn( item ) )
|
|
393
|
-
.filter( value => value.item
|
|
453
|
+
.filter( value => imageUtils.isImage( value.item ) )
|
|
394
454
|
.map( value => value.item );
|
|
395
455
|
}
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
|
|
12
12
|
import { Plugin } from 'ckeditor5/src/core';
|
|
13
13
|
import { FileRepository } from 'ckeditor5/src/upload';
|
|
14
|
-
import { getViewImgFromWidget } from '../image/utils';
|
|
15
14
|
|
|
16
15
|
import uploadingPlaceholder from '../../theme/icons/image_placeholder.svg';
|
|
17
16
|
|
|
@@ -55,7 +54,13 @@ export default class ImageUploadProgress extends Plugin {
|
|
|
55
54
|
const editor = this.editor;
|
|
56
55
|
|
|
57
56
|
// Upload status change - update image's view according to that status.
|
|
58
|
-
editor.
|
|
57
|
+
if ( editor.plugins.has( 'ImageBlockEditing' ) ) {
|
|
58
|
+
editor.editing.downcastDispatcher.on( 'attribute:uploadStatus:imageBlock', ( ...args ) => this.uploadStatusChange( ...args ) );
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if ( editor.plugins.has( 'ImageInlineEditing' ) ) {
|
|
62
|
+
editor.editing.downcastDispatcher.on( 'attribute:uploadStatus:imageInline', ( ...args ) => this.uploadStatusChange( ...args ) );
|
|
63
|
+
}
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
/**
|
|
@@ -74,6 +79,7 @@ export default class ImageUploadProgress extends Plugin {
|
|
|
74
79
|
return;
|
|
75
80
|
}
|
|
76
81
|
|
|
82
|
+
const imageUtils = editor.plugins.get( 'ImageUtils' );
|
|
77
83
|
const fileRepository = editor.plugins.get( FileRepository );
|
|
78
84
|
const status = uploadId ? data.attributeNewValue : null;
|
|
79
85
|
const placeholder = this.placeholder;
|
|
@@ -84,7 +90,7 @@ export default class ImageUploadProgress extends Plugin {
|
|
|
84
90
|
// Start "appearing" effect and show placeholder with infinite progress bar on the top
|
|
85
91
|
// while image is read from disk.
|
|
86
92
|
_startAppearEffect( viewFigure, viewWriter );
|
|
87
|
-
_showPlaceholder( placeholder, viewFigure, viewWriter );
|
|
93
|
+
_showPlaceholder( imageUtils, placeholder, viewFigure, viewWriter );
|
|
88
94
|
|
|
89
95
|
return;
|
|
90
96
|
}
|
|
@@ -100,12 +106,12 @@ export default class ImageUploadProgress extends Plugin {
|
|
|
100
106
|
// There is no loader associated with uploadId - this means that image came from external changes.
|
|
101
107
|
// In such cases we still want to show the placeholder until image is fully uploaded.
|
|
102
108
|
// Show placeholder if needed - see https://github.com/ckeditor/ckeditor5-image/issues/191.
|
|
103
|
-
_showPlaceholder( placeholder, viewFigure, viewWriter );
|
|
109
|
+
_showPlaceholder( imageUtils, placeholder, viewFigure, viewWriter );
|
|
104
110
|
} else {
|
|
105
111
|
// Hide placeholder and initialize progress bar showing upload progress.
|
|
106
112
|
_hidePlaceholder( viewFigure, viewWriter );
|
|
107
113
|
_showProgressBar( viewFigure, viewWriter, loader, editor.editing.view );
|
|
108
|
-
_displayLocalImage( viewFigure, viewWriter, loader );
|
|
114
|
+
_displayLocalImage( imageUtils, viewFigure, viewWriter, loader );
|
|
109
115
|
}
|
|
110
116
|
|
|
111
117
|
return;
|
|
@@ -142,15 +148,16 @@ function _stopAppearEffect( viewFigure, writer ) {
|
|
|
142
148
|
|
|
143
149
|
// Shows placeholder together with infinite progress bar on given image figure.
|
|
144
150
|
//
|
|
151
|
+
// @param {module:image/imageutils~ImageUtils} imageUtils
|
|
145
152
|
// @param {String} Data-uri with a svg placeholder.
|
|
146
153
|
// @param {module:engine/view/containerelement~ContainerElement} viewFigure
|
|
147
154
|
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
|
|
148
|
-
function _showPlaceholder( placeholder, viewFigure, writer ) {
|
|
155
|
+
function _showPlaceholder( imageUtils, placeholder, viewFigure, writer ) {
|
|
149
156
|
if ( !viewFigure.hasClass( 'ck-image-upload-placeholder' ) ) {
|
|
150
157
|
writer.addClass( 'ck-image-upload-placeholder', viewFigure );
|
|
151
158
|
}
|
|
152
159
|
|
|
153
|
-
const viewImg =
|
|
160
|
+
const viewImg = imageUtils.findViewImgElement( viewFigure );
|
|
154
161
|
|
|
155
162
|
if ( viewImg.getAttribute( 'src' ) !== placeholder ) {
|
|
156
163
|
writer.setAttribute( 'src', placeholder, viewImg );
|
|
@@ -272,12 +279,13 @@ function _removeUIElement( viewFigure, writer, uniqueProperty ) {
|
|
|
272
279
|
|
|
273
280
|
// Displays local data from file loader.
|
|
274
281
|
//
|
|
282
|
+
// @param {module:image/imageutils~ImageUtils} imageUtils
|
|
275
283
|
// @param {module:engine/view/element~Element} imageFigure
|
|
276
284
|
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
|
|
277
285
|
// @param {module:upload/filerepository~FileLoader} loader
|
|
278
|
-
function _displayLocalImage( viewFigure, writer, loader ) {
|
|
286
|
+
function _displayLocalImage( imageUtils, viewFigure, writer, loader ) {
|
|
279
287
|
if ( loader.data ) {
|
|
280
|
-
const viewImg =
|
|
288
|
+
const viewImg = imageUtils.findViewImgElement( viewFigure );
|
|
281
289
|
|
|
282
290
|
writer.setAttribute( 'src', loader.data, viewImg );
|
|
283
291
|
}
|
|
@@ -14,7 +14,7 @@ import { createImageTypeRegExp } from './utils';
|
|
|
14
14
|
/**
|
|
15
15
|
* The image upload button plugin.
|
|
16
16
|
*
|
|
17
|
-
* For a detailed overview, check the {@glink features/image-upload/image-upload Image upload feature} documentation.
|
|
17
|
+
* For a detailed overview, check the {@glink features/images/image-upload/image-upload Image upload feature} documentation.
|
|
18
18
|
*
|
|
19
19
|
* Adds the `'uploadImage'` button to the {@link module:ui/componentfactory~ComponentFactory UI component factory}
|
|
20
20
|
* and also the `imageUpload` button as an alias for backward compatibility.
|
|
@@ -7,8 +7,6 @@ import { FileRepository } from 'ckeditor5/src/upload';
|
|
|
7
7
|
import { Command } from 'ckeditor5/src/core';
|
|
8
8
|
import { toArray } from 'ckeditor5/src/utils';
|
|
9
9
|
|
|
10
|
-
import { insertImage, isImageAllowed } from '../image/utils';
|
|
11
|
-
|
|
12
10
|
/**
|
|
13
11
|
* @module image/imageupload/uploadimagecommand
|
|
14
12
|
*/
|
|
@@ -20,7 +18,7 @@ import { insertImage, isImageAllowed } from '../image/utils';
|
|
|
20
18
|
* and it is also available via aliased `imageUpload` name.
|
|
21
19
|
*
|
|
22
20
|
* In order to upload an image at the current selection position
|
|
23
|
-
* (according to the {@link module:widget/utils~
|
|
21
|
+
* (according to the {@link module:widget/utils~findOptimalInsertionRange} algorithm),
|
|
24
22
|
* execute the command and pass the native image file instance:
|
|
25
23
|
*
|
|
26
24
|
* this.listenTo( editor.editing.view.document, 'clipboardInput', ( evt, data ) => {
|
|
@@ -47,10 +45,12 @@ export default class UploadImageCommand extends Command {
|
|
|
47
45
|
* @inheritDoc
|
|
48
46
|
*/
|
|
49
47
|
refresh() {
|
|
50
|
-
const
|
|
51
|
-
const
|
|
48
|
+
const editor = this.editor;
|
|
49
|
+
const imageUtils = editor.plugins.get( 'ImageUtils' );
|
|
50
|
+
const selectedElement = editor.model.document.selection.getSelectedElement();
|
|
52
51
|
|
|
53
|
-
|
|
52
|
+
// TODO: This needs refactoring.
|
|
53
|
+
this.isEnabled = imageUtils.isImageAllowed() || imageUtils.isImage( selectedElement );
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
@@ -61,28 +61,54 @@ export default class UploadImageCommand extends Command {
|
|
|
61
61
|
* @param {File|Array.<File>} options.file The image file or an array of image files to upload.
|
|
62
62
|
*/
|
|
63
63
|
execute( options ) {
|
|
64
|
-
const
|
|
65
|
-
const
|
|
64
|
+
const files = toArray( options.file );
|
|
65
|
+
const selection = this.editor.model.document.selection;
|
|
66
|
+
const imageUtils = this.editor.plugins.get( 'ImageUtils' );
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
// In case of multiple files, each file (starting from the 2nd) will be inserted at a position that
|
|
69
|
+
// follows the previous one. That will move the selection and, to stay on the safe side and make sure
|
|
70
|
+
// all images inherit the same selection attributes, they are collected beforehand.
|
|
71
|
+
//
|
|
72
|
+
// Applying these attributes ensures, for instance, that inserting an (inline) image into a link does
|
|
73
|
+
// not split that link but preserves its continuity.
|
|
74
|
+
//
|
|
75
|
+
// Note: Selection attributes that do not make sense for images will be filtered out by insertImage() anyway.
|
|
76
|
+
const selectionAttributes = Object.fromEntries( selection.getAttributes() );
|
|
68
77
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
78
|
+
files.forEach( ( file, index ) => {
|
|
79
|
+
const selectedElement = selection.getSelectedElement();
|
|
74
80
|
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
function uploadImage( model, fileRepository, file ) {
|
|
80
|
-
const loader = fileRepository.createLoader( file );
|
|
81
|
+
// Inserting of an inline image replace the selected element and make a selection on the inserted image.
|
|
82
|
+
// Therefore inserting multiple inline images requires creating position after each element.
|
|
83
|
+
if ( index && selectedElement && imageUtils.isImage( selectedElement ) ) {
|
|
84
|
+
const position = this.editor.model.createPositionAfter( selectedElement );
|
|
81
85
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
this._uploadImage( file, selectionAttributes, position );
|
|
87
|
+
} else {
|
|
88
|
+
this._uploadImage( file, selectionAttributes );
|
|
89
|
+
}
|
|
90
|
+
} );
|
|
85
91
|
}
|
|
86
92
|
|
|
87
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Handles uploading single file.
|
|
95
|
+
*
|
|
96
|
+
* @private
|
|
97
|
+
* @param {File} file
|
|
98
|
+
* @param {Object} attributes
|
|
99
|
+
* @param {module:engine/model/position~Position} position
|
|
100
|
+
*/
|
|
101
|
+
_uploadImage( file, attributes, position ) {
|
|
102
|
+
const editor = this.editor;
|
|
103
|
+
const fileRepository = editor.plugins.get( FileRepository );
|
|
104
|
+
const loader = fileRepository.createLoader( file );
|
|
105
|
+
const imageUtils = editor.plugins.get( 'ImageUtils' );
|
|
106
|
+
|
|
107
|
+
// Do not throw when upload adapter is not set. FileRepository will log an error anyway.
|
|
108
|
+
if ( !loader ) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
imageUtils.insertImage( { ...attributes, uploadId: loader.id }, position );
|
|
113
|
+
}
|
|
88
114
|
}
|
package/src/imageupload/utils.js
CHANGED
|
@@ -64,11 +64,12 @@ export function fetchLocalImage( image ) {
|
|
|
64
64
|
/**
|
|
65
65
|
* Checks whether a given node is an image element with a local source (Base64 or blob).
|
|
66
66
|
*
|
|
67
|
+
* @param {module:image/imageutils~ImageUtils} imageUtils
|
|
67
68
|
* @param {module:engine/view/node~Node} node The node to check.
|
|
68
69
|
* @returns {Boolean}
|
|
69
70
|
*/
|
|
70
|
-
export function isLocalImage( node ) {
|
|
71
|
-
if ( !
|
|
71
|
+
export function isLocalImage( imageUtils, node ) {
|
|
72
|
+
if ( !imageUtils.isInlineImageView( node ) || !node.getAttribute( 'src' ) ) {
|
|
72
73
|
return false;
|
|
73
74
|
}
|
|
74
75
|
|
package/src/imageupload.js
CHANGED
|
@@ -15,7 +15,7 @@ import ImageUploadEditing from './imageupload/imageuploadediting';
|
|
|
15
15
|
/**
|
|
16
16
|
* The image upload plugin.
|
|
17
17
|
*
|
|
18
|
-
* For a detailed overview, check the {@glink features/image-upload/image-upload image upload feature} documentation.
|
|
18
|
+
* For a detailed overview, check the {@glink features/images/image-upload/image-upload image upload feature} documentation.
|
|
19
19
|
*
|
|
20
20
|
* This plugin does not do anything directly, but it loads a set of specific plugins to enable image uploading:
|
|
21
21
|
*
|