@ckeditor/ckeditor5-image 29.0.0 → 31.0.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 +1 -1
- 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/translations/de.po +6 -6
- package/lang/translations/gl.po +3 -3
- package/lang/translations/hu.po +3 -3
- package/lang/translations/it.po +3 -3
- package/lang/translations/nl.po +2 -2
- package/lang/translations/ru.po +3 -3
- package/lang/translations/sr-latn.po +3 -3
- package/lang/translations/sr.po +3 -3
- package/package.json +35 -33
- package/src/autoimage.js +4 -1
- package/src/image/converters.js +174 -8
- package/src/image/imageblockediting.js +16 -9
- package/src/image/imageinlineediting.js +15 -9
- package/src/image/imagetypecommand.js +1 -1
- package/src/image/insertimagecommand.js +19 -5
- package/src/image/ui/utils.js +2 -1
- package/src/image/utils.js +5 -10
- package/src/imageblock.js +1 -1
- package/src/imagecaption/imagecaptionediting.js +2 -12
- package/src/imageinline.js +1 -1
- package/src/imageresize/imageresizehandles.js +6 -2
- package/src/imagestyle/converters.js +2 -1
- package/src/imageupload/imageuploadediting.js +2 -2
- package/src/imageupload/imageuploadprogress.js +2 -2
- package/src/imageutils.js +8 -16
- package/src/pictureediting.js +149 -0
- package/theme/image.css +7 -0
- package/CHANGELOG.md +0 -423
- package/build/image.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,49 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ckeditor/ckeditor5-image",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "31.0.0",
|
|
4
4
|
"description": "Image feature for CKEditor 5.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ckeditor",
|
|
7
7
|
"ckeditor5",
|
|
8
8
|
"ckeditor 5",
|
|
9
9
|
"ckeditor5-feature",
|
|
10
|
-
"ckeditor5-plugin"
|
|
10
|
+
"ckeditor5-plugin",
|
|
11
|
+
"ckeditor5-dll"
|
|
11
12
|
],
|
|
12
13
|
"main": "src/index.js",
|
|
13
14
|
"dependencies": {
|
|
14
|
-
"@ckeditor/ckeditor5-ui": "^
|
|
15
|
-
"ckeditor5": "^
|
|
15
|
+
"@ckeditor/ckeditor5-ui": "^31.0.0",
|
|
16
|
+
"ckeditor5": "^31.0.0",
|
|
16
17
|
"lodash-es": "^4.17.15"
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
|
19
|
-
"@ckeditor/ckeditor5-adapter-ckfinder": "^
|
|
20
|
-
"@ckeditor/ckeditor5-autoformat": "^
|
|
21
|
-
"@ckeditor/ckeditor5-basic-styles": "^
|
|
22
|
-
"@ckeditor/ckeditor5-block-quote": "^
|
|
23
|
-
"@ckeditor/ckeditor5-ckfinder": "^
|
|
24
|
-
"@ckeditor/ckeditor5-clipboard": "^
|
|
25
|
-
"@ckeditor/ckeditor5-cloud-services": "^
|
|
26
|
-
"@ckeditor/ckeditor5-core": "^
|
|
27
|
-
"@ckeditor/ckeditor5-dev-utils": "^25.
|
|
28
|
-
"@ckeditor/ckeditor5-easy-image": "^
|
|
29
|
-
"@ckeditor/ckeditor5-editor-classic": "^
|
|
30
|
-
"@ckeditor/ckeditor5-engine": "^
|
|
31
|
-
"@ckeditor/ckeditor5-enter": "^
|
|
32
|
-
"@ckeditor/ckeditor5-essentials": "^
|
|
33
|
-
"@ckeditor/ckeditor5-heading": "^
|
|
34
|
-
"@ckeditor/ckeditor5-html-embed": "^
|
|
35
|
-
"@ckeditor/ckeditor5-indent": "^
|
|
36
|
-
"@ckeditor/ckeditor5-link": "^
|
|
37
|
-
"@ckeditor/ckeditor5-list": "^
|
|
38
|
-
"@ckeditor/ckeditor5-media-embed": "^
|
|
39
|
-
"@ckeditor/ckeditor5-paragraph": "^
|
|
40
|
-
"@ckeditor/ckeditor5-table": "^
|
|
41
|
-
"@ckeditor/ckeditor5-theme-lark": "^
|
|
42
|
-
"@ckeditor/ckeditor5-typing": "^
|
|
43
|
-
"@ckeditor/ckeditor5-undo": "^
|
|
44
|
-
"@ckeditor/ckeditor5-upload": "^
|
|
45
|
-
"@ckeditor/ckeditor5-utils": "^
|
|
46
|
-
"@ckeditor/ckeditor5-widget": "^
|
|
20
|
+
"@ckeditor/ckeditor5-adapter-ckfinder": "^31.0.0",
|
|
21
|
+
"@ckeditor/ckeditor5-autoformat": "^31.0.0",
|
|
22
|
+
"@ckeditor/ckeditor5-basic-styles": "^31.0.0",
|
|
23
|
+
"@ckeditor/ckeditor5-block-quote": "^31.0.0",
|
|
24
|
+
"@ckeditor/ckeditor5-ckfinder": "^31.0.0",
|
|
25
|
+
"@ckeditor/ckeditor5-clipboard": "^31.0.0",
|
|
26
|
+
"@ckeditor/ckeditor5-cloud-services": "^31.0.0",
|
|
27
|
+
"@ckeditor/ckeditor5-core": "^31.0.0",
|
|
28
|
+
"@ckeditor/ckeditor5-dev-utils": "^25.4.0",
|
|
29
|
+
"@ckeditor/ckeditor5-easy-image": "^31.0.0",
|
|
30
|
+
"@ckeditor/ckeditor5-editor-classic": "^31.0.0",
|
|
31
|
+
"@ckeditor/ckeditor5-engine": "^31.0.0",
|
|
32
|
+
"@ckeditor/ckeditor5-enter": "^31.0.0",
|
|
33
|
+
"@ckeditor/ckeditor5-essentials": "^31.0.0",
|
|
34
|
+
"@ckeditor/ckeditor5-heading": "^31.0.0",
|
|
35
|
+
"@ckeditor/ckeditor5-html-embed": "^31.0.0",
|
|
36
|
+
"@ckeditor/ckeditor5-indent": "^31.0.0",
|
|
37
|
+
"@ckeditor/ckeditor5-link": "^31.0.0",
|
|
38
|
+
"@ckeditor/ckeditor5-list": "^31.0.0",
|
|
39
|
+
"@ckeditor/ckeditor5-media-embed": "^31.0.0",
|
|
40
|
+
"@ckeditor/ckeditor5-paragraph": "^31.0.0",
|
|
41
|
+
"@ckeditor/ckeditor5-table": "^31.0.0",
|
|
42
|
+
"@ckeditor/ckeditor5-theme-lark": "^31.0.0",
|
|
43
|
+
"@ckeditor/ckeditor5-typing": "^31.0.0",
|
|
44
|
+
"@ckeditor/ckeditor5-undo": "^31.0.0",
|
|
45
|
+
"@ckeditor/ckeditor5-upload": "^31.0.0",
|
|
46
|
+
"@ckeditor/ckeditor5-utils": "^31.0.0",
|
|
47
|
+
"@ckeditor/ckeditor5-widget": "^31.0.0",
|
|
47
48
|
"webpack": "^4.43.0",
|
|
48
49
|
"webpack-cli": "^3.3.11"
|
|
49
50
|
},
|
|
@@ -64,7 +65,8 @@
|
|
|
64
65
|
"lang",
|
|
65
66
|
"src",
|
|
66
67
|
"theme",
|
|
67
|
-
"build"
|
|
68
|
+
"build",
|
|
69
|
+
"ckeditor5-metadata.json"
|
|
68
70
|
],
|
|
69
71
|
"scripts": {
|
|
70
72
|
"dll:build": "webpack"
|
package/src/autoimage.js
CHANGED
|
@@ -11,6 +11,7 @@ import { Plugin } from 'ckeditor5/src/core';
|
|
|
11
11
|
import { Clipboard } from 'ckeditor5/src/clipboard';
|
|
12
12
|
import { LivePosition, LiveRange } from 'ckeditor5/src/engine';
|
|
13
13
|
import { Undo } from 'ckeditor5/src/undo';
|
|
14
|
+
import { Delete } from 'ckeditor5/src/typing';
|
|
14
15
|
import { global } from 'ckeditor5/src/utils';
|
|
15
16
|
|
|
16
17
|
import ImageUtils from './imageutils';
|
|
@@ -32,7 +33,7 @@ export default class AutoImage extends Plugin {
|
|
|
32
33
|
* @inheritDoc
|
|
33
34
|
*/
|
|
34
35
|
static get requires() {
|
|
35
|
-
return [ Clipboard, ImageUtils, Undo ];
|
|
36
|
+
return [ Clipboard, ImageUtils, Undo, Delete ];
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/**
|
|
@@ -173,6 +174,8 @@ export default class AutoImage extends Plugin {
|
|
|
173
174
|
this._positionToInsert.detach();
|
|
174
175
|
this._positionToInsert = null;
|
|
175
176
|
} );
|
|
177
|
+
|
|
178
|
+
editor.plugins.get( 'Delete' ).requestUndoOnBackspace();
|
|
176
179
|
}, 100 );
|
|
177
180
|
}
|
|
178
181
|
}
|
package/src/image/converters.js
CHANGED
|
@@ -21,10 +21,11 @@ import { first } from 'ckeditor5/src/utils';
|
|
|
21
21
|
* The entire content of the `<figure>` element except the first `<img>` is being converted as children
|
|
22
22
|
* of the `<imageBlock>` model element.
|
|
23
23
|
*
|
|
24
|
+
* @protected
|
|
24
25
|
* @param {module:image/imageutils~ImageUtils} imageUtils
|
|
25
26
|
* @returns {Function}
|
|
26
27
|
*/
|
|
27
|
-
export function
|
|
28
|
+
export function upcastImageFigure( imageUtils ) {
|
|
28
29
|
return dispatcher => {
|
|
29
30
|
dispatcher.on( 'element:figure', converter );
|
|
30
31
|
};
|
|
@@ -36,10 +37,10 @@ export function viewFigureToModel( imageUtils ) {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
// Find an image element inside the figure element.
|
|
39
|
-
const viewImage = imageUtils.
|
|
40
|
+
const viewImage = imageUtils.findViewImgElement( data.viewItem );
|
|
40
41
|
|
|
41
|
-
// Do not convert if image element is absent
|
|
42
|
-
if ( !viewImage || !
|
|
42
|
+
// Do not convert if image element is absent or was already converted.
|
|
43
|
+
if ( !viewImage || !conversionApi.consumable.test( viewImage, { name: true } ) ) {
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -54,6 +55,9 @@ export function viewFigureToModel( imageUtils ) {
|
|
|
54
55
|
return;
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
// Consume the figure to prevent other converters from processing it again.
|
|
59
|
+
conversionApi.consumable.consume( data.viewItem, { name: true, classes: 'image' } );
|
|
60
|
+
|
|
57
61
|
// Convert rest of the figure element's children as an image children.
|
|
58
62
|
conversionApi.convertChildren( data.viewItem, modelImage );
|
|
59
63
|
|
|
@@ -61,14 +65,108 @@ export function viewFigureToModel( imageUtils ) {
|
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Returns a function that converts the image view representation:
|
|
70
|
+
*
|
|
71
|
+
* <picture><source ... /><source ... />...<img ... /></picture>
|
|
72
|
+
*
|
|
73
|
+
* to the model representation as the `sources` attribute:
|
|
74
|
+
*
|
|
75
|
+
* <image[Block|Inline] ... sources="..."></image[Block|Inline]>
|
|
76
|
+
*
|
|
77
|
+
* @protected
|
|
78
|
+
* @param {module:image/imageutils~ImageUtils} imageUtils
|
|
79
|
+
* @returns {Function}
|
|
80
|
+
*/
|
|
81
|
+
export function upcastPicture( imageUtils ) {
|
|
82
|
+
const sourceAttributeNames = [ 'srcset', 'media', 'type' ];
|
|
83
|
+
|
|
84
|
+
return dispatcher => {
|
|
85
|
+
dispatcher.on( 'element:picture', converter );
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
function converter( evt, data, conversionApi ) {
|
|
89
|
+
const pictureViewElement = data.viewItem;
|
|
90
|
+
|
|
91
|
+
// Do not convert <picture> if already consumed.
|
|
92
|
+
if ( !conversionApi.consumable.test( pictureViewElement, { name: true } ) ) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const sources = new Map();
|
|
97
|
+
|
|
98
|
+
// Collect all <source /> elements attribute values.
|
|
99
|
+
for ( const childSourceElement of pictureViewElement.getChildren() ) {
|
|
100
|
+
if ( childSourceElement.is( 'element', 'source' ) ) {
|
|
101
|
+
const attributes = {};
|
|
102
|
+
|
|
103
|
+
for ( const name of sourceAttributeNames ) {
|
|
104
|
+
if ( childSourceElement.hasAttribute( name ) ) {
|
|
105
|
+
// Don't collect <source /> attribute if already consumed somewhere else.
|
|
106
|
+
if ( conversionApi.consumable.test( childSourceElement, { attributes: name } ) ) {
|
|
107
|
+
attributes[ name ] = childSourceElement.getAttribute( name );
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if ( Object.keys( attributes ).length ) {
|
|
113
|
+
sources.set( childSourceElement, attributes );
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const imgViewElement = imageUtils.findViewImgElement( pictureViewElement );
|
|
119
|
+
|
|
120
|
+
// Don't convert when a picture has no <img/> inside (it is broken).
|
|
121
|
+
if ( !imgViewElement ) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let modelImage = data.modelCursor.parent;
|
|
126
|
+
|
|
127
|
+
// - In case of an inline image (cursor parent in a <paragraph>), the <img/> must be converted right away
|
|
128
|
+
// because no converter handled it yet and otherwise there would be no model element to set the sources attribute on.
|
|
129
|
+
// - In case of a block image, the <figure class="image"> converter (in ImageBlockEditing) converts the
|
|
130
|
+
// <img/> right away on its own and the modelCursor is already inside an imageBlock and there's nothing special
|
|
131
|
+
// to do here.
|
|
132
|
+
if ( !modelImage.is( 'element', 'imageBlock' ) ) {
|
|
133
|
+
const conversionResult = conversionApi.convertItem( imgViewElement, data.modelCursor );
|
|
134
|
+
|
|
135
|
+
// Set image range as conversion result.
|
|
136
|
+
data.modelRange = conversionResult.modelRange;
|
|
137
|
+
|
|
138
|
+
// Continue conversion where image conversion ends.
|
|
139
|
+
data.modelCursor = conversionResult.modelCursor;
|
|
140
|
+
|
|
141
|
+
modelImage = first( conversionResult.modelRange.getItems() );
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
conversionApi.consumable.consume( pictureViewElement, { name: true } );
|
|
145
|
+
|
|
146
|
+
// Consume only these <source/> attributes that were actually collected and will be passed on
|
|
147
|
+
// to the image model element.
|
|
148
|
+
for ( const [ sourceElement, attributes ] of sources ) {
|
|
149
|
+
conversionApi.consumable.consume( sourceElement, { attributes: Object.keys( attributes ) } );
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if ( sources.size ) {
|
|
153
|
+
conversionApi.writer.setAttribute( 'sources', Array.from( sources.values() ), modelImage );
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Convert rest of the <picture> children as an image children. Other converters may want to consume them.
|
|
157
|
+
conversionApi.convertChildren( pictureViewElement, modelImage );
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
64
161
|
/**
|
|
65
162
|
* Converter used to convert the `srcset` model image attribute to the `srcset`, `sizes` and `width` attributes in the view.
|
|
66
163
|
*
|
|
164
|
+
* @protected
|
|
67
165
|
* @param {module:image/imageutils~ImageUtils} imageUtils
|
|
68
166
|
* @param {'imageBlock'|'imageInline'} imageType The type of the image.
|
|
69
167
|
* @returns {Function}
|
|
70
168
|
*/
|
|
71
|
-
export function
|
|
169
|
+
export function downcastSrcsetAttribute( imageUtils, imageType ) {
|
|
72
170
|
return dispatcher => {
|
|
73
171
|
dispatcher.on( `attribute:srcset:${ imageType }`, converter );
|
|
74
172
|
};
|
|
@@ -80,7 +178,7 @@ export function srcsetAttributeConverter( imageUtils, imageType ) {
|
|
|
80
178
|
|
|
81
179
|
const writer = conversionApi.writer;
|
|
82
180
|
const element = conversionApi.mapper.toViewElement( data.item );
|
|
83
|
-
const img = imageUtils.
|
|
181
|
+
const img = imageUtils.findViewImgElement( element );
|
|
84
182
|
|
|
85
183
|
if ( data.attributeNewValue === null ) {
|
|
86
184
|
const srcset = data.attributeOldValue;
|
|
@@ -109,15 +207,82 @@ export function srcsetAttributeConverter( imageUtils, imageType ) {
|
|
|
109
207
|
}
|
|
110
208
|
}
|
|
111
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Converts the `source` model attribute to the `<picture><source /><source />...<img /></picture>`
|
|
212
|
+
* view structure.
|
|
213
|
+
*
|
|
214
|
+
* @protected
|
|
215
|
+
* @param {module:image/imageutils~ImageUtils} imageUtils
|
|
216
|
+
* @returns {Function}
|
|
217
|
+
*/
|
|
218
|
+
export function downcastSourcesAttribute( imageUtils ) {
|
|
219
|
+
return dispatcher => {
|
|
220
|
+
dispatcher.on( 'attribute:sources:imageBlock', converter );
|
|
221
|
+
dispatcher.on( 'attribute:sources:imageInline', converter );
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
function converter( evt, data, conversionApi ) {
|
|
225
|
+
if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const viewWriter = conversionApi.writer;
|
|
230
|
+
const element = conversionApi.mapper.toViewElement( data.item );
|
|
231
|
+
const imgElement = imageUtils.findViewImgElement( element );
|
|
232
|
+
|
|
233
|
+
if ( data.attributeNewValue && data.attributeNewValue.length ) {
|
|
234
|
+
// Make sure <picture> does not break attribute elements, for instance <a> in linked images.
|
|
235
|
+
const pictureElement = viewWriter.createContainerElement( 'picture', {}, { isAllowedInsideAttributeElement: true } );
|
|
236
|
+
|
|
237
|
+
for ( const sourceAttributes of data.attributeNewValue ) {
|
|
238
|
+
const sourceElement = viewWriter.createEmptyElement( 'source', sourceAttributes );
|
|
239
|
+
|
|
240
|
+
viewWriter.insert( viewWriter.createPositionAt( pictureElement, 'end' ), sourceElement );
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Collect all wrapping attribute elements.
|
|
244
|
+
const attributeElements = [];
|
|
245
|
+
let viewElement = imgElement.parent;
|
|
246
|
+
|
|
247
|
+
while ( viewElement && viewElement.is( 'attributeElement' ) ) {
|
|
248
|
+
const parentElement = viewElement.parent;
|
|
249
|
+
|
|
250
|
+
viewWriter.unwrap( viewWriter.createRangeOn( imgElement ), viewElement );
|
|
251
|
+
|
|
252
|
+
attributeElements.unshift( viewElement );
|
|
253
|
+
viewElement = parentElement;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Insert the picture and move img into it.
|
|
257
|
+
viewWriter.insert( viewWriter.createPositionBefore( imgElement ), pictureElement );
|
|
258
|
+
viewWriter.move( viewWriter.createRangeOn( imgElement ), viewWriter.createPositionAt( pictureElement, 'end' ) );
|
|
259
|
+
|
|
260
|
+
// Apply collected attribute elements over the new picture element.
|
|
261
|
+
for ( const attributeElement of attributeElements ) {
|
|
262
|
+
viewWriter.wrap( viewWriter.createRangeOn( pictureElement ), attributeElement );
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Both setting "sources" to an empty array and removing the attribute should unwrap the <img />.
|
|
266
|
+
// Unwrap once if the latter followed the former, though.
|
|
267
|
+
else if ( imgElement.parent.is( 'element', 'picture' ) ) {
|
|
268
|
+
const pictureElement = imgElement.parent;
|
|
269
|
+
|
|
270
|
+
viewWriter.move( viewWriter.createRangeOn( imgElement ), viewWriter.createPositionBefore( pictureElement ) );
|
|
271
|
+
viewWriter.remove( pictureElement );
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
112
276
|
/**
|
|
113
277
|
* Converter used to convert a given image attribute from the model to the view.
|
|
114
278
|
*
|
|
279
|
+
* @protected
|
|
115
280
|
* @param {module:image/imageutils~ImageUtils} imageUtils
|
|
116
281
|
* @param {'imageBlock'|'imageInline'} imageType The type of the image.
|
|
117
282
|
* @param {String} attributeKey The name of the attribute to convert.
|
|
118
283
|
* @returns {Function}
|
|
119
284
|
*/
|
|
120
|
-
export function
|
|
285
|
+
export function downcastImageAttribute( imageUtils, imageType, attributeKey ) {
|
|
121
286
|
return dispatcher => {
|
|
122
287
|
dispatcher.on( `attribute:${ attributeKey }:${ imageType }`, converter );
|
|
123
288
|
};
|
|
@@ -129,8 +294,9 @@ export function modelToViewAttributeConverter( imageUtils, imageType, attributeK
|
|
|
129
294
|
|
|
130
295
|
const viewWriter = conversionApi.writer;
|
|
131
296
|
const element = conversionApi.mapper.toViewElement( data.item );
|
|
132
|
-
const img = imageUtils.
|
|
297
|
+
const img = imageUtils.findViewImgElement( element );
|
|
133
298
|
|
|
134
299
|
viewWriter.setAttribute( data.attributeKey, data.attributeNewValue || '', img );
|
|
135
300
|
}
|
|
136
301
|
}
|
|
302
|
+
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -11,13 +11,17 @@ import { Plugin } from 'ckeditor5/src/core';
|
|
|
11
11
|
import { ClipboardPipeline } from 'ckeditor5/src/clipboard';
|
|
12
12
|
import { UpcastWriter } from 'ckeditor5/src/engine';
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
downcastImageAttribute,
|
|
16
|
+
downcastSrcsetAttribute,
|
|
17
|
+
upcastImageFigure
|
|
18
|
+
} from './converters';
|
|
15
19
|
|
|
16
20
|
import ImageEditing from './imageediting';
|
|
17
21
|
import ImageTypeCommand from './imagetypecommand';
|
|
18
22
|
import ImageUtils from '../imageutils';
|
|
19
23
|
import {
|
|
20
|
-
|
|
24
|
+
getImgViewElementMatcher,
|
|
21
25
|
createImageViewElement,
|
|
22
26
|
determineImageTypeForInsertionAtSelection
|
|
23
27
|
} from '../image/utils';
|
|
@@ -100,17 +104,20 @@ export default class ImageBlockEditing extends Plugin {
|
|
|
100
104
|
} );
|
|
101
105
|
|
|
102
106
|
conversion.for( 'downcast' )
|
|
103
|
-
.add(
|
|
104
|
-
.add(
|
|
105
|
-
.add(
|
|
107
|
+
.add( downcastImageAttribute( imageUtils, 'imageBlock', 'src' ) )
|
|
108
|
+
.add( downcastImageAttribute( imageUtils, 'imageBlock', 'alt' ) )
|
|
109
|
+
.add( downcastSrcsetAttribute( imageUtils, 'imageBlock' ) );
|
|
106
110
|
|
|
107
111
|
// More image related upcasts are in 'ImageEditing' plugin.
|
|
108
112
|
conversion.for( 'upcast' )
|
|
109
113
|
.elementToElement( {
|
|
110
|
-
view:
|
|
111
|
-
model: ( viewImage, { writer } ) => writer.createElement(
|
|
114
|
+
view: getImgViewElementMatcher( editor, 'imageBlock' ),
|
|
115
|
+
model: ( viewImage, { writer } ) => writer.createElement(
|
|
116
|
+
'imageBlock',
|
|
117
|
+
viewImage.hasAttribute( 'src' ) ? { src: viewImage.getAttribute( 'src' ) } : null
|
|
118
|
+
)
|
|
112
119
|
} )
|
|
113
|
-
.add(
|
|
120
|
+
.add( upcastImageFigure( imageUtils ) );
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -11,13 +11,16 @@ import { Plugin } from 'ckeditor5/src/core';
|
|
|
11
11
|
import { ClipboardPipeline } from 'ckeditor5/src/clipboard';
|
|
12
12
|
import { UpcastWriter } from 'ckeditor5/src/engine';
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
downcastImageAttribute,
|
|
16
|
+
downcastSrcsetAttribute
|
|
17
|
+
} from './converters';
|
|
15
18
|
|
|
16
19
|
import ImageEditing from './imageediting';
|
|
17
20
|
import ImageTypeCommand from './imagetypecommand';
|
|
18
21
|
import ImageUtils from '../imageutils';
|
|
19
22
|
import {
|
|
20
|
-
|
|
23
|
+
getImgViewElementMatcher,
|
|
21
24
|
createImageViewElement,
|
|
22
25
|
determineImageTypeForInsertionAtSelection
|
|
23
26
|
} from '../image/utils';
|
|
@@ -109,15 +112,18 @@ export default class ImageInlineEditing extends Plugin {
|
|
|
109
112
|
} );
|
|
110
113
|
|
|
111
114
|
conversion.for( 'downcast' )
|
|
112
|
-
.add(
|
|
113
|
-
.add(
|
|
114
|
-
.add(
|
|
115
|
+
.add( downcastImageAttribute( imageUtils, 'imageInline', 'src' ) )
|
|
116
|
+
.add( downcastImageAttribute( imageUtils, 'imageInline', 'alt' ) )
|
|
117
|
+
.add( downcastSrcsetAttribute( imageUtils, 'imageInline' ) );
|
|
115
118
|
|
|
116
119
|
// More image related upcasts are in 'ImageEditing' plugin.
|
|
117
120
|
conversion.for( 'upcast' )
|
|
118
121
|
.elementToElement( {
|
|
119
|
-
view:
|
|
120
|
-
model: ( viewImage, { writer } ) => writer.createElement(
|
|
122
|
+
view: getImgViewElementMatcher( editor, 'imageInline' ),
|
|
123
|
+
model: ( viewImage, { writer } ) => writer.createElement(
|
|
124
|
+
'imageInline',
|
|
125
|
+
viewImage.hasAttribute( 'src' ) ? { src: viewImage.getAttribute( 'src' ) } : null
|
|
126
|
+
)
|
|
121
127
|
} );
|
|
122
128
|
}
|
|
123
129
|
|
|
@@ -185,7 +191,7 @@ export default class ImageInlineEditing extends Plugin {
|
|
|
185
191
|
Array.from( blockViewImage.getAttributes() )
|
|
186
192
|
.forEach( attribute => writer.setAttribute(
|
|
187
193
|
...attribute,
|
|
188
|
-
imageUtils.
|
|
194
|
+
imageUtils.findViewImgElement( blockViewImage )
|
|
189
195
|
) );
|
|
190
196
|
|
|
191
197
|
return blockViewImage.getChild( 0 );
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -31,6 +31,15 @@ import { logWarning, toArray } from 'ckeditor5/src/utils';
|
|
|
31
31
|
* ]
|
|
32
32
|
* } );
|
|
33
33
|
*
|
|
34
|
+
* If you want to take the full control over the process, you can specify individual model attributes:
|
|
35
|
+
*
|
|
36
|
+
* editor.execute( 'insertImage', {
|
|
37
|
+
* source: [
|
|
38
|
+
* { src: 'path/to/image.jpg', alt: 'First alt text' },
|
|
39
|
+
* { src: 'path/to/other-image.jpg', alt: 'Second alt text', customAttribute: 'My attribute value' }
|
|
40
|
+
* ]
|
|
41
|
+
* } );
|
|
42
|
+
*
|
|
34
43
|
* @extends module:core/command~Command
|
|
35
44
|
*/
|
|
36
45
|
export default class InsertImageCommand extends Command {
|
|
@@ -79,10 +88,11 @@ export default class InsertImageCommand extends Command {
|
|
|
79
88
|
*
|
|
80
89
|
* @fires execute
|
|
81
90
|
* @param {Object} options Options for the executed command.
|
|
82
|
-
* @param {String|Array.<String>} options.source The image source or an array of image sources to insert.
|
|
91
|
+
* @param {String|Array.<String>|Array.<Object>} options.source The image source or an array of image sources to insert.
|
|
92
|
+
* See the documentation of the command to learn more about accepted formats.
|
|
83
93
|
*/
|
|
84
94
|
execute( options ) {
|
|
85
|
-
const
|
|
95
|
+
const sourceDefinitions = toArray( options.source );
|
|
86
96
|
const selection = this.editor.model.document.selection;
|
|
87
97
|
const imageUtils = this.editor.plugins.get( 'ImageUtils' );
|
|
88
98
|
|
|
@@ -96,17 +106,21 @@ export default class InsertImageCommand extends Command {
|
|
|
96
106
|
// Note: Selection attributes that do not make sense for images will be filtered out by insertImage() anyway.
|
|
97
107
|
const selectionAttributes = Object.fromEntries( selection.getAttributes() );
|
|
98
108
|
|
|
99
|
-
|
|
109
|
+
sourceDefinitions.forEach( ( sourceDefinition, index ) => {
|
|
100
110
|
const selectedElement = selection.getSelectedElement();
|
|
101
111
|
|
|
112
|
+
if ( typeof sourceDefinition === 'string' ) {
|
|
113
|
+
sourceDefinition = { src: sourceDefinition };
|
|
114
|
+
}
|
|
115
|
+
|
|
102
116
|
// Inserting of an inline image replace the selected element and make a selection on the inserted image.
|
|
103
117
|
// Therefore inserting multiple inline images requires creating position after each element.
|
|
104
118
|
if ( index && selectedElement && imageUtils.isImage( selectedElement ) ) {
|
|
105
119
|
const position = this.editor.model.createPositionAfter( selectedElement );
|
|
106
120
|
|
|
107
|
-
imageUtils.insertImage( {
|
|
121
|
+
imageUtils.insertImage( { ...sourceDefinition, ...selectionAttributes }, position );
|
|
108
122
|
} else {
|
|
109
|
-
imageUtils.insertImage( {
|
|
123
|
+
imageUtils.insertImage( { ...sourceDefinition, ...selectionAttributes } );
|
|
110
124
|
}
|
|
111
125
|
} );
|
|
112
126
|
}
|
package/src/image/ui/utils.js
CHANGED
|
@@ -47,7 +47,8 @@ export function getBalloonPositionData( editor ) {
|
|
|
47
47
|
defaultPositions.northArrowSouthEast,
|
|
48
48
|
defaultPositions.southArrowNorth,
|
|
49
49
|
defaultPositions.southArrowNorthWest,
|
|
50
|
-
defaultPositions.southArrowNorthEast
|
|
50
|
+
defaultPositions.southArrowNorthEast,
|
|
51
|
+
defaultPositions.viewportStickyNorth
|
|
51
52
|
]
|
|
52
53
|
};
|
|
53
54
|
}
|
package/src/image/utils.js
CHANGED
|
@@ -47,21 +47,16 @@ export function createImageViewElement( writer, imageType ) {
|
|
|
47
47
|
* @param {'imageBlock'|'imageInline'} matchImageType The type of created image.
|
|
48
48
|
* @returns {module:engine/view/matcher~MatcherPattern}
|
|
49
49
|
*/
|
|
50
|
-
export function
|
|
50
|
+
export function getImgViewElementMatcher( editor, matchImageType ) {
|
|
51
51
|
if ( editor.plugins.has( 'ImageInlineEditing' ) !== editor.plugins.has( 'ImageBlockEditing' ) ) {
|
|
52
|
-
return {
|
|
53
|
-
name: 'img',
|
|
54
|
-
attributes: {
|
|
55
|
-
src: true
|
|
56
|
-
}
|
|
57
|
-
};
|
|
52
|
+
return { name: 'img' };
|
|
58
53
|
}
|
|
59
54
|
|
|
60
55
|
const imageUtils = editor.plugins.get( 'ImageUtils' );
|
|
61
56
|
|
|
62
57
|
return element => {
|
|
63
|
-
//
|
|
64
|
-
if ( !imageUtils.isInlineImageView( element )
|
|
58
|
+
// Check if view element is an `img`.
|
|
59
|
+
if ( !imageUtils.isInlineImageView( element ) ) {
|
|
65
60
|
return null;
|
|
66
61
|
}
|
|
67
62
|
|
|
@@ -73,7 +68,7 @@ export function getImageTypeMatcher( editor, matchImageType ) {
|
|
|
73
68
|
return null;
|
|
74
69
|
}
|
|
75
70
|
|
|
76
|
-
return { name: true
|
|
71
|
+
return { name: true };
|
|
77
72
|
};
|
|
78
73
|
}
|
|
79
74
|
|
package/src/imageblock.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { Plugin } from 'ckeditor5/src/core';
|
|
11
11
|
import { Element, enablePlaceholder } from 'ckeditor5/src/engine';
|
|
12
|
-
import {
|
|
13
|
-
import { toArray } from 'ckeditor5/src/utils';
|
|
12
|
+
import { toWidgetEditable } from 'ckeditor5/src/widget';
|
|
14
13
|
|
|
15
14
|
import ToggleImageCaptionCommand from './toggleimagecaptioncommand';
|
|
16
15
|
|
|
@@ -132,16 +131,7 @@ export default class ImageCaptionEditing extends Plugin {
|
|
|
132
131
|
keepOnFocus: true
|
|
133
132
|
} );
|
|
134
133
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
setHighlightHandling(
|
|
138
|
-
widgetEditable,
|
|
139
|
-
writer,
|
|
140
|
-
( element, descriptor, writer ) => writer.addClass( toArray( descriptor.classes ), element ),
|
|
141
|
-
( element, descriptor, writer ) => writer.removeClass( toArray( descriptor.classes ), element )
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
return widgetEditable;
|
|
134
|
+
return toWidgetEditable( figcaptionElement, writer );
|
|
145
135
|
}
|
|
146
136
|
} );
|
|
147
137
|
|
package/src/imageinline.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -12,9 +12,13 @@ import { WidgetResize } from 'ckeditor5/src/widget';
|
|
|
12
12
|
|
|
13
13
|
import ImageLoadObserver from '../image/imageloadobserver';
|
|
14
14
|
|
|
15
|
-
const RESIZABLE_IMAGES_CSS_SELECTOR =
|
|
15
|
+
const RESIZABLE_IMAGES_CSS_SELECTOR =
|
|
16
|
+
'figure.image.ck-widget > img,' +
|
|
17
|
+
'figure.image.ck-widget > picture > img,' +
|
|
16
18
|
'figure.image.ck-widget > a > img,' +
|
|
17
|
-
'
|
|
19
|
+
'figure.image.ck-widget > a > picture > img,' +
|
|
20
|
+
'span.image-inline.ck-widget > img,' +
|
|
21
|
+
'span.image-inline.ck-widget > picture > img';
|
|
18
22
|
|
|
19
23
|
const IMAGE_WIDGETS_CLASSES_MATCH_REGEXP = /(image|image-inline)/;
|
|
20
24
|
|
|
@@ -61,7 +61,8 @@ export function viewToModelStyleAttribute( styles ) {
|
|
|
61
61
|
const viewElement = data.viewItem;
|
|
62
62
|
const modelImageElement = first( data.modelRange.getItems() );
|
|
63
63
|
|
|
64
|
-
//
|
|
64
|
+
// Run this converter only if an image has been found in the model.
|
|
65
|
+
// In some cases it may not be found (for example if we run this on a figure with different type than image).
|
|
65
66
|
if ( !modelImageElement ) {
|
|
66
67
|
return;
|
|
67
68
|
}
|