@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
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @module image/image/imageinlineediting
|
|
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
|
+
createImageViewElement,
|
|
25
|
+
determineImageTypeForInsertionAtSelection
|
|
26
|
+
} from '../image/utils';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The image inline plugin.
|
|
30
|
+
*
|
|
31
|
+
* It registers:
|
|
32
|
+
*
|
|
33
|
+
* * `<imageInline>` as an inline element in the document schema, and allows `alt`, `src` and `srcset` attributes.
|
|
34
|
+
* * converters for editing and data pipelines.
|
|
35
|
+
* * {@link module:image/image/imagetypecommand~ImageTypeCommand `'imageTypeInline'`} command that converts block images into
|
|
36
|
+
* inline images.
|
|
37
|
+
*
|
|
38
|
+
* @extends module:core/plugin~Plugin
|
|
39
|
+
*/
|
|
40
|
+
export default class ImageInlineEditing extends Plugin {
|
|
41
|
+
/**
|
|
42
|
+
* @inheritDoc
|
|
43
|
+
*/
|
|
44
|
+
static get requires() {
|
|
45
|
+
return [ ImageEditing, ImageUtils, ClipboardPipeline ];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @inheritDoc
|
|
50
|
+
*/
|
|
51
|
+
static get pluginName() {
|
|
52
|
+
return 'ImageInlineEditing';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @inheritDoc
|
|
57
|
+
*/
|
|
58
|
+
init() {
|
|
59
|
+
const editor = this.editor;
|
|
60
|
+
const schema = editor.model.schema;
|
|
61
|
+
|
|
62
|
+
// Converters 'alt' and 'srcset' are added in 'ImageEditing' plugin.
|
|
63
|
+
schema.register( 'imageInline', {
|
|
64
|
+
isObject: true,
|
|
65
|
+
isInline: true,
|
|
66
|
+
allowWhere: '$text',
|
|
67
|
+
allowAttributes: [ 'alt', 'src', 'srcset' ]
|
|
68
|
+
} );
|
|
69
|
+
|
|
70
|
+
// Disallow inline images in captions (for now). This is the best spot to do that because
|
|
71
|
+
// independent packages can introduce captions (ImageCaption, TableCaption, etc.) so better this
|
|
72
|
+
// be future-proof.
|
|
73
|
+
schema.addChildCheck( ( context, childDefinition ) => {
|
|
74
|
+
if ( context.endsWith( 'caption' ) && childDefinition.name === 'imageInline' ) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
} );
|
|
78
|
+
|
|
79
|
+
this._setupConversion();
|
|
80
|
+
|
|
81
|
+
if ( editor.plugins.has( 'ImageBlockEditing' ) ) {
|
|
82
|
+
editor.commands.add( 'imageTypeInline', new ImageTypeCommand( this.editor, 'imageInline' ) );
|
|
83
|
+
|
|
84
|
+
this._setupClipboardIntegration();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Configures conversion pipelines to support upcasting and downcasting
|
|
90
|
+
* inline images (inline image widgets) and their attributes.
|
|
91
|
+
*
|
|
92
|
+
* @private
|
|
93
|
+
*/
|
|
94
|
+
_setupConversion() {
|
|
95
|
+
const editor = this.editor;
|
|
96
|
+
const t = editor.t;
|
|
97
|
+
const conversion = editor.conversion;
|
|
98
|
+
const imageUtils = editor.plugins.get( 'ImageUtils' );
|
|
99
|
+
|
|
100
|
+
conversion.for( 'dataDowncast' )
|
|
101
|
+
.elementToElement( {
|
|
102
|
+
model: 'imageInline',
|
|
103
|
+
view: ( modelElement, { writer } ) => writer.createEmptyElement( 'img' )
|
|
104
|
+
} );
|
|
105
|
+
|
|
106
|
+
conversion.for( 'editingDowncast' )
|
|
107
|
+
.elementToElement( {
|
|
108
|
+
model: 'imageInline',
|
|
109
|
+
view: ( modelElement, { writer } ) => imageUtils.toImageWidget(
|
|
110
|
+
createImageViewElement( writer, 'imageInline' ), writer, t( 'image widget' )
|
|
111
|
+
)
|
|
112
|
+
} );
|
|
113
|
+
|
|
114
|
+
conversion.for( 'downcast' )
|
|
115
|
+
.add( downcastImageAttribute( imageUtils, 'imageInline', 'src' ) )
|
|
116
|
+
.add( downcastImageAttribute( imageUtils, 'imageInline', 'alt' ) )
|
|
117
|
+
.add( downcastSrcsetAttribute( imageUtils, 'imageInline' ) );
|
|
118
|
+
|
|
119
|
+
// More image related upcasts are in 'ImageEditing' plugin.
|
|
120
|
+
conversion.for( 'upcast' )
|
|
121
|
+
.elementToElement( {
|
|
122
|
+
view: getImgViewElementMatcher( editor, 'imageInline' ),
|
|
123
|
+
model: ( viewImage, { writer } ) => writer.createElement( 'imageInline', { src: viewImage.getAttribute( 'src' ) } )
|
|
124
|
+
} );
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Integrates the plugin with the clipboard pipeline.
|
|
129
|
+
*
|
|
130
|
+
* Idea is that the feature should recognize the user's intent when an **block** image is
|
|
131
|
+
* pasted or dropped. If such an image is pasted/dropped into a non-empty block
|
|
132
|
+
* (e.g. a paragraph with some text) it gets converted into an inline image on the fly.
|
|
133
|
+
*
|
|
134
|
+
* We assume this is the user's intent if they decided to put their image there.
|
|
135
|
+
*
|
|
136
|
+
* **Note**: If a block image has a caption, it will not be converted to an inline image
|
|
137
|
+
* to avoid the confusion. Captions are added on purpose and they should never be lost
|
|
138
|
+
* in the clipboard pipeline.
|
|
139
|
+
*
|
|
140
|
+
* See the `ImageBlockEditing` for the similar integration that works in the opposite direction.
|
|
141
|
+
*
|
|
142
|
+
* @private
|
|
143
|
+
*/
|
|
144
|
+
_setupClipboardIntegration() {
|
|
145
|
+
const editor = this.editor;
|
|
146
|
+
const model = editor.model;
|
|
147
|
+
const editingView = editor.editing.view;
|
|
148
|
+
const imageUtils = editor.plugins.get( 'ImageUtils' );
|
|
149
|
+
|
|
150
|
+
this.listenTo( editor.plugins.get( 'ClipboardPipeline' ), 'inputTransformation', ( evt, data ) => {
|
|
151
|
+
const docFragmentChildren = Array.from( data.content.getChildren() );
|
|
152
|
+
let modelRange;
|
|
153
|
+
|
|
154
|
+
// Make sure only <figure class="image"></figure> elements are dropped or pasted. Otherwise, if there some other HTML
|
|
155
|
+
// mixed up, this should be handled as a regular paste.
|
|
156
|
+
if ( !docFragmentChildren.every( imageUtils.isBlockImageView ) ) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// When drag and dropping, data.targetRanges specifies where to drop because
|
|
161
|
+
// this is usually a different place than the current model selection (the user
|
|
162
|
+
// uses a drop marker to specify the drop location).
|
|
163
|
+
if ( data.targetRanges ) {
|
|
164
|
+
modelRange = editor.editing.mapper.toModelRange( data.targetRanges[ 0 ] );
|
|
165
|
+
}
|
|
166
|
+
// Pasting, however, always occurs at the current model selection.
|
|
167
|
+
else {
|
|
168
|
+
modelRange = model.document.selection.getFirstRange();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const selection = model.createSelection( modelRange );
|
|
172
|
+
|
|
173
|
+
// Convert block images into inline images only when pasting or dropping into non-empty blocks
|
|
174
|
+
// and when the block is not an object (e.g. pasting to replace another widget).
|
|
175
|
+
if ( determineImageTypeForInsertionAtSelection( model.schema, selection ) === 'imageInline' ) {
|
|
176
|
+
const writer = new UpcastWriter( editingView.document );
|
|
177
|
+
|
|
178
|
+
// Unwrap <figure class="image"><img .../></figure> -> <img ... />
|
|
179
|
+
// but <figure class="image"><img .../><figcaption>...</figcaption></figure> -> stays the same
|
|
180
|
+
const inlineViewImages = docFragmentChildren.map( blockViewImage => {
|
|
181
|
+
// If there's just one child, it can be either <img /> or <a><img></a>.
|
|
182
|
+
// If there are other children than <img>, this means that the block image
|
|
183
|
+
// has a caption or some other features and this kind of image should be
|
|
184
|
+
// pasted/dropped without modifications.
|
|
185
|
+
if ( blockViewImage.childCount === 1 ) {
|
|
186
|
+
// Pass the attributes which are present only in the <figure> to the <img>
|
|
187
|
+
// (e.g. the style="width:10%" attribute applied by the ImageResize plugin).
|
|
188
|
+
Array.from( blockViewImage.getAttributes() )
|
|
189
|
+
.forEach( attribute => writer.setAttribute(
|
|
190
|
+
...attribute,
|
|
191
|
+
imageUtils.findViewImgElement( blockViewImage )
|
|
192
|
+
) );
|
|
193
|
+
|
|
194
|
+
return blockViewImage.getChild( 0 );
|
|
195
|
+
} else {
|
|
196
|
+
return blockViewImage;
|
|
197
|
+
}
|
|
198
|
+
} );
|
|
199
|
+
|
|
200
|
+
data.content = writer.createDocumentFragment( inlineViewImages );
|
|
201
|
+
}
|
|
202
|
+
} );
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @module image/image/imagetypecommand
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Command } from 'ckeditor5/src/core';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The image type command. It changes the type of a selected image, depending on the configuration.
|
|
14
|
+
*
|
|
15
|
+
* @extends module:core/command~Command
|
|
16
|
+
*/
|
|
17
|
+
export default class ImageTypeCommand extends Command {
|
|
18
|
+
/**
|
|
19
|
+
* @inheritDoc
|
|
20
|
+
*
|
|
21
|
+
* @param {module:core/editor/editor~Editor} editor
|
|
22
|
+
* @param {'imageBlock'|'imageInline'} modelElementName Model element name the command converts to.
|
|
23
|
+
*/
|
|
24
|
+
constructor( editor, modelElementName ) {
|
|
25
|
+
super( editor );
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Model element name the command converts to.
|
|
29
|
+
*
|
|
30
|
+
* @readonly
|
|
31
|
+
* @private
|
|
32
|
+
* @member {'imageBlock'|'imageInline'}
|
|
33
|
+
*/
|
|
34
|
+
this._modelElementName = modelElementName;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @inheritDoc
|
|
39
|
+
*/
|
|
40
|
+
refresh() {
|
|
41
|
+
const editor = this.editor;
|
|
42
|
+
const imageUtils = editor.plugins.get( 'ImageUtils' );
|
|
43
|
+
const element = imageUtils.getClosestSelectedImageElement( this.editor.model.document.selection );
|
|
44
|
+
|
|
45
|
+
if ( this._modelElementName === 'imageBlock' ) {
|
|
46
|
+
this.isEnabled = imageUtils.isInlineImage( element );
|
|
47
|
+
} else {
|
|
48
|
+
this.isEnabled = imageUtils.isBlockImage( element );
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Executes the command and changes the type of a selected image.
|
|
54
|
+
*
|
|
55
|
+
* @fires execute
|
|
56
|
+
* @returns {Object|null} An object containing references to old and new model image elements
|
|
57
|
+
* (for before and after the change) so external integrations can hook into the decorated
|
|
58
|
+
* `execute` event and handle this change. `null` if the type change failed.
|
|
59
|
+
*/
|
|
60
|
+
execute() {
|
|
61
|
+
const editor = this.editor;
|
|
62
|
+
const model = this.editor.model;
|
|
63
|
+
const imageUtils = editor.plugins.get( 'ImageUtils' );
|
|
64
|
+
const oldElement = imageUtils.getClosestSelectedImageElement( model.document.selection );
|
|
65
|
+
const attributes = Object.fromEntries( oldElement.getAttributes() );
|
|
66
|
+
|
|
67
|
+
// Don't change image type if "src" is missing (a broken image), unless there's "uploadId" set.
|
|
68
|
+
// This state may happen during image upload (before it finishes) and it should be possible to change type
|
|
69
|
+
// of the image in the meantime.
|
|
70
|
+
if ( !attributes.src && !attributes.uploadId ) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return model.change( writer => {
|
|
75
|
+
// Get all markers that contain the old image element.
|
|
76
|
+
const markers = Array.from( model.markers )
|
|
77
|
+
.filter( marker => marker.getRange().containsItem( oldElement ) );
|
|
78
|
+
|
|
79
|
+
const newElement = imageUtils.insertImage( attributes, model.createSelection( oldElement, 'on' ), this._modelElementName );
|
|
80
|
+
|
|
81
|
+
if ( !newElement ) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const newElementRange = writer.createRangeOn( newElement );
|
|
86
|
+
|
|
87
|
+
// Expand the previously intersecting markers' ranges to include the new image element.
|
|
88
|
+
for ( const marker of markers ) {
|
|
89
|
+
const markerRange = marker.getRange();
|
|
90
|
+
|
|
91
|
+
// Join the survived part of the old marker range with the new element range
|
|
92
|
+
// (loosely because there could be some new paragraph or the existing one might got split).
|
|
93
|
+
const range = markerRange.root.rootName != '$graveyard' ?
|
|
94
|
+
markerRange.getJoined( newElementRange, true ) : newElementRange;
|
|
95
|
+
|
|
96
|
+
writer.updateMarker( marker, { range } );
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
oldElement,
|
|
101
|
+
newElement
|
|
102
|
+
};
|
|
103
|
+
} );
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { Command } from 'ckeditor5/src/core';
|
|
7
|
-
import { toArray } from 'ckeditor5/src/utils';
|
|
8
|
-
|
|
9
|
-
import { insertImage, isImageAllowed } from './utils';
|
|
7
|
+
import { logWarning, toArray } from 'ckeditor5/src/utils';
|
|
10
8
|
|
|
11
9
|
/**
|
|
12
10
|
* @module image/image/insertimagecommand
|
|
@@ -19,7 +17,7 @@ import { insertImage, isImageAllowed } from './utils';
|
|
|
19
17
|
* and it is also available via aliased `imageInsert` name.
|
|
20
18
|
*
|
|
21
19
|
* In order to insert an image at the current selection position
|
|
22
|
-
* (according to the {@link module:widget/utils~
|
|
20
|
+
* (according to the {@link module:widget/utils~findOptimalInsertionRange} algorithm),
|
|
23
21
|
* execute the command and specify the image source:
|
|
24
22
|
*
|
|
25
23
|
* editor.execute( 'insertImage', { source: 'http://url.to.the/image' } );
|
|
@@ -33,14 +31,56 @@ import { insertImage, isImageAllowed } from './utils';
|
|
|
33
31
|
* ]
|
|
34
32
|
* } );
|
|
35
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
|
+
*
|
|
36
43
|
* @extends module:core/command~Command
|
|
37
44
|
*/
|
|
38
45
|
export default class InsertImageCommand extends Command {
|
|
46
|
+
/**
|
|
47
|
+
* @inheritDoc
|
|
48
|
+
*/
|
|
49
|
+
constructor( editor ) {
|
|
50
|
+
super( editor );
|
|
51
|
+
|
|
52
|
+
const configImageInsertType = editor.config.get( 'image.insert.type' );
|
|
53
|
+
|
|
54
|
+
if ( !editor.plugins.has( 'ImageBlockEditing' ) ) {
|
|
55
|
+
if ( configImageInsertType === 'block' ) {
|
|
56
|
+
/**
|
|
57
|
+
* The {@link module:image/imageblock~ImageBlock} plugin must be enabled to allow inserting block images. See
|
|
58
|
+
* {@link module:image/imageinsert~ImageInsertConfig#type} to learn more.
|
|
59
|
+
*
|
|
60
|
+
* @error image-block-plugin-required
|
|
61
|
+
*/
|
|
62
|
+
logWarning( 'image-block-plugin-required' );
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if ( !editor.plugins.has( 'ImageInlineEditing' ) ) {
|
|
67
|
+
if ( configImageInsertType === 'inline' ) {
|
|
68
|
+
/**
|
|
69
|
+
* The {@link module:image/imageinline~ImageInline} plugin must be enabled to allow inserting inline images. See
|
|
70
|
+
* {@link module:image/imageinsert~ImageInsertConfig#type} to learn more.
|
|
71
|
+
*
|
|
72
|
+
* @error image-inline-plugin-required
|
|
73
|
+
*/
|
|
74
|
+
logWarning( 'image-inline-plugin-required' );
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
39
79
|
/**
|
|
40
80
|
* @inheritDoc
|
|
41
81
|
*/
|
|
42
82
|
refresh() {
|
|
43
|
-
this.isEnabled =
|
|
83
|
+
this.isEnabled = this.editor.plugins.get( 'ImageUtils' ).isImageAllowed();
|
|
44
84
|
}
|
|
45
85
|
|
|
46
86
|
/**
|
|
@@ -48,13 +88,40 @@ export default class InsertImageCommand extends Command {
|
|
|
48
88
|
*
|
|
49
89
|
* @fires execute
|
|
50
90
|
* @param {Object} options Options for the executed command.
|
|
51
|
-
* @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.
|
|
52
93
|
*/
|
|
53
94
|
execute( options ) {
|
|
54
|
-
const
|
|
95
|
+
const sourceDefinitions = toArray( options.source );
|
|
96
|
+
const selection = this.editor.model.document.selection;
|
|
97
|
+
const imageUtils = this.editor.plugins.get( 'ImageUtils' );
|
|
55
98
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
99
|
+
// In case of multiple images, each image (starting from the 2nd) will be inserted at a position that
|
|
100
|
+
// follows the previous one. That will move the selection and, to stay on the safe side and make sure
|
|
101
|
+
// all images inherit the same selection attributes, they are collected beforehand.
|
|
102
|
+
//
|
|
103
|
+
// Applying these attributes ensures, for instance, that inserting an (inline) image into a link does
|
|
104
|
+
// not split that link but preserves its continuity.
|
|
105
|
+
//
|
|
106
|
+
// Note: Selection attributes that do not make sense for images will be filtered out by insertImage() anyway.
|
|
107
|
+
const selectionAttributes = Object.fromEntries( selection.getAttributes() );
|
|
108
|
+
|
|
109
|
+
sourceDefinitions.forEach( ( sourceDefinition, index ) => {
|
|
110
|
+
const selectedElement = selection.getSelectedElement();
|
|
111
|
+
|
|
112
|
+
if ( typeof sourceDefinition === 'string' ) {
|
|
113
|
+
sourceDefinition = { src: sourceDefinition };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Inserting of an inline image replace the selected element and make a selection on the inserted image.
|
|
117
|
+
// Therefore inserting multiple inline images requires creating position after each element.
|
|
118
|
+
if ( index && selectedElement && imageUtils.isImage( selectedElement ) ) {
|
|
119
|
+
const position = this.editor.model.createPositionAfter( selectedElement );
|
|
120
|
+
|
|
121
|
+
imageUtils.insertImage( { ...sourceDefinition, ...selectionAttributes }, position );
|
|
122
|
+
} else {
|
|
123
|
+
imageUtils.insertImage( { ...sourceDefinition, ...selectionAttributes } );
|
|
124
|
+
}
|
|
125
|
+
} );
|
|
59
126
|
}
|
|
60
127
|
}
|
package/src/image/ui/utils.js
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { BalloonPanelView } from 'ckeditor5/src/ui';
|
|
11
|
-
import { getSelectedImageWidget } from '../utils';
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* A helper utility that positions the
|
|
@@ -20,7 +19,7 @@ import { getSelectedImageWidget } from '../utils';
|
|
|
20
19
|
export function repositionContextualBalloon( editor ) {
|
|
21
20
|
const balloon = editor.plugins.get( 'ContextualBalloon' );
|
|
22
21
|
|
|
23
|
-
if (
|
|
22
|
+
if ( editor.plugins.get( 'ImageUtils' ).getClosestSelectedImageWidget( editor.editing.view.document.selection ) ) {
|
|
24
23
|
const position = getBalloonPositionData( editor );
|
|
25
24
|
|
|
26
25
|
balloon.updatePosition( position );
|
|
@@ -38,9 +37,10 @@ export function repositionContextualBalloon( editor ) {
|
|
|
38
37
|
export function getBalloonPositionData( editor ) {
|
|
39
38
|
const editingView = editor.editing.view;
|
|
40
39
|
const defaultPositions = BalloonPanelView.defaultPositions;
|
|
40
|
+
const imageUtils = editor.plugins.get( 'ImageUtils' );
|
|
41
41
|
|
|
42
42
|
return {
|
|
43
|
-
target: editingView.domConverter.viewToDom( editingView.document.selection
|
|
43
|
+
target: editingView.domConverter.viewToDom( imageUtils.getClosestSelectedImageWidget( editingView.document.selection ) ),
|
|
44
44
|
positions: [
|
|
45
45
|
defaultPositions.northArrowSouth,
|
|
46
46
|
defaultPositions.northArrowSouthWest,
|