@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.
Files changed (167) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +3 -3
  3. package/build/image.js +1 -1
  4. package/build/translations/ar.js +1 -0
  5. package/build/translations/ast.js +1 -0
  6. package/build/translations/az.js +1 -0
  7. package/build/translations/bg.js +1 -0
  8. package/build/translations/cs.js +1 -0
  9. package/build/translations/da.js +1 -0
  10. package/build/translations/de-ch.js +1 -0
  11. package/build/translations/de.js +1 -0
  12. package/build/translations/el.js +1 -0
  13. package/build/translations/en-au.js +1 -0
  14. package/build/translations/en-gb.js +1 -0
  15. package/build/translations/eo.js +1 -0
  16. package/build/translations/es.js +1 -0
  17. package/build/translations/et.js +1 -0
  18. package/build/translations/eu.js +1 -0
  19. package/build/translations/fa.js +1 -0
  20. package/build/translations/fi.js +1 -0
  21. package/build/translations/fr.js +1 -0
  22. package/build/translations/gl.js +1 -0
  23. package/build/translations/he.js +1 -0
  24. package/build/translations/hi.js +1 -0
  25. package/build/translations/hr.js +1 -0
  26. package/build/translations/hu.js +1 -0
  27. package/build/translations/id.js +1 -0
  28. package/build/translations/it.js +1 -0
  29. package/build/translations/ja.js +1 -0
  30. package/build/translations/km.js +1 -0
  31. package/build/translations/kn.js +1 -0
  32. package/build/translations/ko.js +1 -0
  33. package/build/translations/ku.js +1 -0
  34. package/build/translations/lt.js +1 -0
  35. package/build/translations/lv.js +1 -0
  36. package/build/translations/nb.js +1 -0
  37. package/build/translations/ne.js +1 -0
  38. package/build/translations/nl.js +1 -0
  39. package/build/translations/no.js +1 -0
  40. package/build/translations/pl.js +1 -0
  41. package/build/translations/pt-br.js +1 -0
  42. package/build/translations/pt.js +1 -0
  43. package/build/translations/ro.js +1 -0
  44. package/build/translations/ru.js +1 -0
  45. package/build/translations/si.js +1 -0
  46. package/build/translations/sk.js +1 -0
  47. package/build/translations/sq.js +1 -0
  48. package/build/translations/sr-latn.js +1 -0
  49. package/build/translations/sr.js +1 -0
  50. package/build/translations/sv.js +1 -0
  51. package/build/translations/th.js +1 -0
  52. package/build/translations/tk.js +1 -0
  53. package/build/translations/tr.js +1 -0
  54. package/build/translations/ug.js +1 -0
  55. package/build/translations/uk.js +1 -0
  56. package/build/translations/vi.js +1 -0
  57. package/build/translations/zh-cn.js +1 -0
  58. package/build/translations/zh.js +1 -0
  59. package/ckeditor5-metadata.json +233 -0
  60. package/lang/contexts.json +3 -0
  61. package/lang/translations/ar.po +12 -0
  62. package/lang/translations/ast.po +12 -0
  63. package/lang/translations/az.po +12 -0
  64. package/lang/translations/bg.po +12 -0
  65. package/lang/translations/cs.po +12 -0
  66. package/lang/translations/da.po +12 -0
  67. package/lang/translations/de-ch.po +113 -0
  68. package/lang/translations/de.po +15 -3
  69. package/lang/translations/el.po +12 -0
  70. package/lang/translations/en-au.po +12 -0
  71. package/lang/translations/en-gb.po +12 -0
  72. package/lang/translations/en.po +12 -0
  73. package/lang/translations/eo.po +12 -0
  74. package/lang/translations/es.po +12 -0
  75. package/lang/translations/et.po +12 -0
  76. package/lang/translations/eu.po +12 -0
  77. package/lang/translations/fa.po +12 -0
  78. package/lang/translations/fi.po +12 -0
  79. package/lang/translations/fr.po +12 -0
  80. package/lang/translations/gl.po +12 -0
  81. package/lang/translations/he.po +12 -0
  82. package/lang/translations/hi.po +12 -0
  83. package/lang/translations/hr.po +12 -0
  84. package/lang/translations/hu.po +13 -1
  85. package/lang/translations/id.po +21 -9
  86. package/lang/translations/it.po +12 -0
  87. package/lang/translations/ja.po +12 -0
  88. package/lang/translations/km.po +12 -0
  89. package/lang/translations/kn.po +12 -0
  90. package/lang/translations/ko.po +12 -0
  91. package/lang/translations/ku.po +12 -0
  92. package/lang/translations/lt.po +12 -0
  93. package/lang/translations/lv.po +12 -0
  94. package/lang/translations/nb.po +12 -0
  95. package/lang/translations/ne.po +12 -0
  96. package/lang/translations/nl.po +12 -0
  97. package/lang/translations/no.po +12 -0
  98. package/lang/translations/pl.po +20 -8
  99. package/lang/translations/pt-br.po +12 -0
  100. package/lang/translations/pt.po +12 -0
  101. package/lang/translations/ro.po +21 -9
  102. package/lang/translations/ru.po +12 -0
  103. package/lang/translations/si.po +12 -0
  104. package/lang/translations/sk.po +12 -0
  105. package/lang/translations/sq.po +12 -0
  106. package/lang/translations/sr-latn.po +12 -0
  107. package/lang/translations/sr.po +12 -0
  108. package/lang/translations/sv.po +12 -0
  109. package/lang/translations/th.po +12 -0
  110. package/lang/translations/tk.po +12 -0
  111. package/lang/translations/tr.po +12 -0
  112. package/lang/translations/ug.po +12 -0
  113. package/lang/translations/uk.po +12 -0
  114. package/lang/translations/vi.po +12 -0
  115. package/lang/translations/zh-cn.po +12 -0
  116. package/lang/translations/zh.po +12 -0
  117. package/package.json +36 -29
  118. package/src/autoimage.js +6 -4
  119. package/src/image/converters.js +192 -13
  120. package/src/image/imageblockediting.js +179 -0
  121. package/src/image/imageediting.js +13 -70
  122. package/src/image/imageinlineediting.js +204 -0
  123. package/src/image/imagetypecommand.js +105 -0
  124. package/src/image/insertimagecommand.js +77 -10
  125. package/src/image/ui/utils.js +3 -3
  126. package/src/image/utils.js +70 -121
  127. package/src/image.js +7 -19
  128. package/src/imageblock.js +46 -0
  129. package/src/imagecaption/imagecaptionediting.js +202 -230
  130. package/src/imagecaption/imagecaptionui.js +78 -0
  131. package/src/imagecaption/toggleimagecaptioncommand.js +165 -0
  132. package/src/imagecaption/utils.js +25 -40
  133. package/src/imagecaption.js +3 -2
  134. package/src/imageinline.js +46 -0
  135. package/src/imageinsert/imageinsertui.js +5 -6
  136. package/src/imageinsert.js +16 -4
  137. package/src/imageresize/imageresizebuttons.js +1 -1
  138. package/src/imageresize/imageresizeediting.js +21 -8
  139. package/src/imageresize/imageresizehandles.js +30 -8
  140. package/src/imageresize/resizeimagecommand.js +8 -5
  141. package/src/imagestyle/converters.js +25 -17
  142. package/src/imagestyle/imagestylecommand.js +73 -33
  143. package/src/imagestyle/imagestyleediting.js +113 -52
  144. package/src/imagestyle/imagestyleui.js +197 -31
  145. package/src/imagestyle/utils.js +300 -85
  146. package/src/imagestyle.js +218 -47
  147. package/src/imagetextalternative/imagetextalternativecommand.js +10 -7
  148. package/src/imagetextalternative/imagetextalternativeediting.js +9 -1
  149. package/src/imagetextalternative/imagetextalternativeui.js +2 -2
  150. package/src/imagetextalternative.js +1 -1
  151. package/src/imagetoolbar.js +33 -11
  152. package/src/imageupload/imageuploadediting.js +91 -31
  153. package/src/imageupload/imageuploadprogress.js +17 -9
  154. package/src/imageupload/imageuploadui.js +1 -1
  155. package/src/imageupload/uploadimagecommand.js +50 -24
  156. package/src/imageupload/utils.js +3 -2
  157. package/src/imageupload.js +1 -1
  158. package/src/imageutils.js +342 -0
  159. package/src/index.js +22 -47
  160. package/src/pictureediting.js +149 -0
  161. package/theme/image.css +101 -21
  162. package/theme/imagecaption.css +24 -2
  163. package/theme/imageresize.css +11 -0
  164. package/theme/imagestyle.css +76 -0
  165. package/theme/imageuploadicon.css +8 -2
  166. package/theme/imageuploadprogress.css +12 -8
  167. package/build/image.js.map +0 -1
@@ -0,0 +1,165 @@
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/imagecaption/toggleimagecaptioncommand
8
+ */
9
+
10
+ import { Command } from 'ckeditor5/src/core';
11
+
12
+ import ImageBlockEditing from '../image/imageblockediting';
13
+ import { getCaptionFromImageModelElement, getCaptionFromModelSelection } from './utils';
14
+
15
+ /**
16
+ * The toggle image caption command.
17
+ *
18
+ * This command is registered by {@link module:image/imagecaption/imagecaptionediting~ImageCaptionEditing} as the
19
+ * `'toggleImageCaption'` editor command.
20
+ *
21
+ * Executing this command:
22
+ *
23
+ * * either adds or removes the image caption of a selected image (depending on whether the caption is present or not),
24
+ * * removes the image caption if the selection is anchored in one.
25
+ *
26
+ * // Toggle the presence of the caption.
27
+ * editor.execute( 'toggleImageCaption' );
28
+ *
29
+ * **Note**: Upon executing this command, the selection will be set on the image if previously anchored in the caption element.
30
+ *
31
+ * **Note**: You can move the selection to the caption right away as it shows up upon executing this command by using
32
+ * the `focusCaptionOnShow` option:
33
+ *
34
+ * editor.execute( 'toggleImageCaption', { focusCaptionOnShow: true } );
35
+ *
36
+ * @extends module:core/command~Command
37
+ */
38
+ export default class ToggleImageCaptionCommand extends Command {
39
+ /**
40
+ * @inheritDoc
41
+ */
42
+ refresh() {
43
+ const editor = this.editor;
44
+ const imageUtils = editor.plugins.get( 'ImageUtils' );
45
+
46
+ // Only block images can get captions.
47
+ if ( !editor.plugins.has( ImageBlockEditing ) ) {
48
+ this.isEnabled = false;
49
+ this.value = false;
50
+
51
+ return;
52
+ }
53
+
54
+ const selection = editor.model.document.selection;
55
+ const selectedElement = selection.getSelectedElement();
56
+
57
+ if ( !selectedElement ) {
58
+ const ancestorCaptionElement = getCaptionFromModelSelection( imageUtils, selection );
59
+
60
+ this.isEnabled = !!ancestorCaptionElement;
61
+ this.value = !!ancestorCaptionElement;
62
+
63
+ return;
64
+ }
65
+
66
+ // Block images support captions by default but the command should also be enabled for inline
67
+ // images because toggling the caption when one is selected should convert it into a block image.
68
+ this.isEnabled = this.editor.plugins.get( 'ImageUtils' ).isImage( selectedElement );
69
+
70
+ if ( !this.isEnabled ) {
71
+ this.value = false;
72
+ } else {
73
+ this.value = !!getCaptionFromImageModelElement( selectedElement );
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Executes the command.
79
+ *
80
+ * editor.execute( 'toggleImageCaption' );
81
+ *
82
+ * @param {Object} [options] Options for the executed command.
83
+ * @param {String} [options.focusCaptionOnShow] When true and the caption shows up, the selection will be moved into it straight away.
84
+ * @fires execute
85
+ */
86
+ execute( options = {} ) {
87
+ const { focusCaptionOnShow } = options;
88
+
89
+ this.editor.model.change( writer => {
90
+ if ( this.value ) {
91
+ this._hideImageCaption( writer );
92
+ } else {
93
+ this._showImageCaption( writer, focusCaptionOnShow );
94
+ }
95
+ } );
96
+ }
97
+
98
+ /**
99
+ * Shows the caption of the `<imageBlock>` or `<imageInline>`. Also:
100
+ *
101
+ * * it converts `<imageInline>` to `<imageBlock>` to show the caption,
102
+ * * it attempts to restore the caption content from the `ImageCaptionEditing` caption registry,
103
+ * * it moves the selection to the caption right away, it the `focusCaptionOnShow` option was set.
104
+ *
105
+ * @private
106
+ * @param {module:engine/model/writer~Writer} writer
107
+ */
108
+ _showImageCaption( writer, focusCaptionOnShow ) {
109
+ const model = this.editor.model;
110
+ const selection = model.document.selection;
111
+ const imageCaptionEditing = this.editor.plugins.get( 'ImageCaptionEditing' );
112
+
113
+ let selectedImage = selection.getSelectedElement();
114
+
115
+ const savedCaption = imageCaptionEditing._getSavedCaption( selectedImage );
116
+
117
+ // Convert imageInline -> image first.
118
+ if ( this.editor.plugins.get( 'ImageUtils' ).isInlineImage( selectedImage ) ) {
119
+ this.editor.execute( 'imageTypeBlock' );
120
+
121
+ // Executing the command created a new model element. Let's pick it again.
122
+ selectedImage = selection.getSelectedElement();
123
+ }
124
+
125
+ // Try restoring the caption from the ImageCaptionEditing plugin storage.
126
+ const newCaptionElement = savedCaption || writer.createElement( 'caption' );
127
+
128
+ writer.append( newCaptionElement, selectedImage );
129
+
130
+ if ( focusCaptionOnShow ) {
131
+ writer.setSelection( newCaptionElement, 'in' );
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Hides the caption of a selected image (or an image caption the selection is anchored to).
137
+ *
138
+ * The content of the caption is stored in the `ImageCaptionEditing` caption registry to make this
139
+ * a reversible action.
140
+ *
141
+ * @private
142
+ * @param {module:engine/model/writer~Writer} writer
143
+ */
144
+ _hideImageCaption( writer ) {
145
+ const editor = this.editor;
146
+ const selection = editor.model.document.selection;
147
+ const imageCaptionEditing = editor.plugins.get( 'ImageCaptionEditing' );
148
+ const imageUtils = editor.plugins.get( 'ImageUtils' );
149
+ let selectedImage = selection.getSelectedElement();
150
+ let captionElement;
151
+
152
+ if ( selectedImage ) {
153
+ captionElement = getCaptionFromImageModelElement( selectedImage );
154
+ } else {
155
+ captionElement = getCaptionFromModelSelection( imageUtils, selection );
156
+ selectedImage = captionElement.parent;
157
+ }
158
+
159
+ // Store the caption content so it can be restored quickly if the user changes their mind even if they toggle image<->imageInline.
160
+ imageCaptionEditing._saveCaption( selectedImage, captionElement );
161
+
162
+ writer.setSelection( selectedImage, 'on' );
163
+ writer.remove( captionElement );
164
+ }
165
+ }
@@ -7,48 +7,13 @@
7
7
  * @module image/imagecaption/utils
8
8
  */
9
9
 
10
- import { enablePlaceholder } from 'ckeditor5/src/engine';
11
- import { toWidgetEditable } from 'ckeditor5/src/widget';
12
-
13
- /**
14
- * Returns a function that creates a caption editable element for the given {@link module:engine/view/document~Document}.
15
- *
16
- * @param {module:engine/view/view~View} view
17
- * @param {String} placeholderText The text to be displayed when the caption is empty.
18
- * @returns {Function}
19
- */
20
- export function captionElementCreator( view, placeholderText ) {
21
- return writer => {
22
- const editable = writer.createEditableElement( 'figcaption' );
23
- writer.setCustomProperty( 'imageCaption', true, editable );
24
-
25
- enablePlaceholder( {
26
- view,
27
- element: editable,
28
- text: placeholderText
29
- } );
30
-
31
- return toWidgetEditable( editable, writer );
32
- };
33
- }
34
-
35
- /**
36
- * Returns `true` if a given view element is the image caption editable.
37
- *
38
- * @param {module:engine/view/element~Element} viewElement
39
- * @returns {Boolean}
40
- */
41
- export function isCaption( viewElement ) {
42
- return !!viewElement.getCustomProperty( 'imageCaption' );
43
- }
44
-
45
10
  /**
46
11
  * Returns the caption model element from a given image element. Returns `null` if no caption is found.
47
12
  *
48
13
  * @param {module:engine/model/element~Element} imageModelElement
49
14
  * @returns {module:engine/model/element~Element|null}
50
15
  */
51
- export function getCaptionFromImage( imageModelElement ) {
16
+ export function getCaptionFromImageModelElement( imageModelElement ) {
52
17
  for ( const node of imageModelElement.getChildren() ) {
53
18
  if ( !!node && node.is( 'element', 'caption' ) ) {
54
19
  return node;
@@ -58,19 +23,39 @@ export function getCaptionFromImage( imageModelElement ) {
58
23
  return null;
59
24
  }
60
25
 
26
+ /**
27
+ * Returns the caption model element for a model selection. Returns `null` if the selection has no caption element ancestor.
28
+ *
29
+ * @param {module:image/imageutils~ImageUtils} imageUtils
30
+ * @param {module:engine/model/selection~Selection} selection
31
+ * @returns {module:engine/model/element~Element|null}
32
+ */
33
+ export function getCaptionFromModelSelection( imageUtils, selection ) {
34
+ const captionElement = selection.getFirstPosition().findAncestor( 'caption' );
35
+
36
+ if ( !captionElement ) {
37
+ return null;
38
+ }
39
+
40
+ if ( imageUtils.isBlockImage( captionElement.parent ) ) {
41
+ return captionElement;
42
+ }
43
+
44
+ return null;
45
+ }
46
+
61
47
  /**
62
48
  * {@link module:engine/view/matcher~Matcher} pattern. Checks if a given element is a `<figcaption>` element that is placed
63
49
  * inside the image `<figure>` element.
64
50
  *
51
+ * @param {module:image/imageutils~ImageUtils} imageUtils
65
52
  * @param {module:engine/view/element~Element} element
66
53
  * @returns {Object|null} Returns the object accepted by {@link module:engine/view/matcher~Matcher} or `null` if the element
67
54
  * cannot be matched.
68
55
  */
69
- export function matchImageCaption( element ) {
70
- const parent = element.parent;
71
-
56
+ export function matchImageCaptionViewElement( imageUtils, element ) {
72
57
  // Convert only captions for images.
73
- if ( element.name == 'figcaption' && parent && parent.name == 'figure' && parent.hasClass( 'image' ) ) {
58
+ if ( element.name == 'figcaption' && imageUtils.isBlockImageView( element.parent ) ) {
74
59
  return { name: true };
75
60
  }
76
61
 
@@ -9,13 +9,14 @@
9
9
 
10
10
  import { Plugin } from 'ckeditor5/src/core';
11
11
  import ImageCaptionEditing from './imagecaption/imagecaptionediting';
12
+ import ImageCaptionUI from './imagecaption/imagecaptionui';
12
13
 
13
14
  import '../theme/imagecaption.css';
14
15
 
15
16
  /**
16
17
  * The image caption plugin.
17
18
  *
18
- * For a detailed overview, check the {@glink features/image#image-captions image caption} documentation.
19
+ * For a detailed overview, check the {@glink features/images/images-captions image caption} documentation.
19
20
  *
20
21
  * @extends module:core/plugin~Plugin
21
22
  */
@@ -24,7 +25,7 @@ export default class ImageCaption extends Plugin {
24
25
  * @inheritDoc
25
26
  */
26
27
  static get requires() {
27
- return [ ImageCaptionEditing ];
28
+ return [ ImageCaptionEditing, ImageCaptionUI ];
28
29
  }
29
30
 
30
31
  /**
@@ -0,0 +1,46 @@
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/imageinline
8
+ */
9
+
10
+ import { Plugin } from 'ckeditor5/src/core';
11
+ import { Widget } from 'ckeditor5/src/widget';
12
+
13
+ import ImageTextAlternative from './imagetextalternative';
14
+ import ImageInlineEditing from './image/imageinlineediting';
15
+
16
+ import '../theme/image.css';
17
+
18
+ /**
19
+ * The image inline plugin.
20
+ *
21
+ * This is a "glue" plugin which loads the following plugins:
22
+ *
23
+ * * {@link module:image/image/imageinlineediting~ImageInlineEditing},
24
+ * * {@link module:image/imagetextalternative~ImageTextAlternative}.
25
+ *
26
+ * Usually, it is used in conjunction with other plugins from this package. See the {@glink api/image package page}
27
+ * for more information.
28
+ *
29
+ * @extends module:core/plugin~Plugin
30
+ */
31
+ export default class ImageInline extends Plugin {
32
+ /**
33
+ * @inheritDoc
34
+ */
35
+ static get requires() {
36
+ return [ ImageInlineEditing, Widget, ImageTextAlternative ];
37
+ }
38
+
39
+ /**
40
+ * @inheritDoc
41
+ */
42
+ static get pluginName() {
43
+ return 'ImageInline';
44
+ }
45
+ }
46
+
@@ -11,13 +11,11 @@ import { Plugin } from 'ckeditor5/src/core';
11
11
  import ImageInsertPanelView from './ui/imageinsertpanelview';
12
12
  import { prepareIntegrations } from './utils';
13
13
 
14
- import { isImage } from '../image/utils';
15
-
16
14
  /**
17
15
  * The image insert dropdown plugin.
18
16
  *
19
- * For a detailed overview, check the {@glink features/image-upload/image-upload Image upload feature}
20
- * and {@glink features/image#inserting-images-via-source-url Insert images via source URL} documentation.
17
+ * For a detailed overview, check the {@glink features/images/image-upload/image-upload Image upload feature}
18
+ * and {@glink features/images/image-upload/images-inserting#inserting-images-via-source-url Insert images via source URL} documentation.
21
19
  *
22
20
  * Adds the `'insertImage'` dropdown to the {@link module:ui/componentfactory~ComponentFactory UI component factory}
23
21
  * and also the `imageInsert` dropdown as an alias for backward compatibility.
@@ -92,6 +90,7 @@ export default class ImageInsertUI extends Plugin {
92
90
  const insertButtonView = imageInsertView.insertButtonView;
93
91
  const insertImageViaUrlForm = imageInsertView.getIntegration( 'insertImageViaUrl' );
94
92
  const panelView = dropdownView.panelView;
93
+ const imageUtils = this.editor.plugins.get( 'ImageUtils' );
95
94
 
96
95
  dropdownView.bind( 'isEnabled' ).to( command );
97
96
 
@@ -107,7 +106,7 @@ export default class ImageInsertUI extends Plugin {
107
106
  if ( dropdownView.isOpen ) {
108
107
  imageInsertView.focus();
109
108
 
110
- if ( isImage( selectedElement ) ) {
109
+ if ( imageUtils.isImage( selectedElement ) ) {
111
110
  imageInsertView.imageURLInputValue = selectedElement.getAttribute( 'src' );
112
111
  insertButtonView.label = t( 'Update' );
113
112
  insertImageViaUrlForm.label = t( 'Update image URL' );
@@ -137,7 +136,7 @@ export default class ImageInsertUI extends Plugin {
137
136
  function onSubmit() {
138
137
  const selectedElement = editor.model.document.selection.getSelectedElement();
139
138
 
140
- if ( isImage( selectedElement ) ) {
139
+ if ( imageUtils.isImage( selectedElement ) ) {
141
140
  editor.model.change( writer => {
142
141
  writer.setAttribute( 'src', imageInsertView.imageURLInputValue, selectedElement );
143
142
  writer.removeAttribute( 'srcset', selectedElement );
@@ -14,8 +14,8 @@ import ImageInsertUI from './imageinsert/imageinsertui';
14
14
  /**
15
15
  * The image insert plugin.
16
16
  *
17
- * For a detailed overview, check the {@glink features/image-upload/image-upload Image upload feature}
18
- * and {@glink features/image#inserting-images-via-source-url Insert images via source URL} documentation.
17
+ * For a detailed overview, check the {@glink features/images/image-upload/image-upload Image upload feature}
18
+ * and {@glink features/images/image-upload/images-inserting#inserting-images-via-source-url Insert images via source URL} documentation.
19
19
  *
20
20
  * This plugin does not do anything directly, but it loads a set of specific plugins
21
21
  * to enable image uploading or inserting via implemented integrations:
@@ -44,7 +44,6 @@ export default class ImageInsert extends Plugin {
44
44
  /**
45
45
  * The image insert configuration.
46
46
  *
47
- * @protected
48
47
  * @member {module:image/imageinsert~ImageInsertConfig} module:image/image~ImageConfig#insert
49
48
  */
50
49
 
@@ -64,7 +63,6 @@ export default class ImageInsert extends Plugin {
64
63
  *
65
64
  * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
66
65
  *
67
- * @protected
68
66
  * @interface module:image/imageinsert~ImageInsertConfig
69
67
  */
70
68
 
@@ -90,6 +88,20 @@ export default class ImageInsert extends Plugin {
90
88
  * }
91
89
  * };
92
90
  *
91
+ * @protected
93
92
  * @member {Array.<String>} module:image/imageinsert~ImageInsertConfig#integrations
94
93
  * @default [ 'insertImageViaUrl' ]
95
94
  */
95
+
96
+ /**
97
+ * This options allows to override the image type used by the {@link module:image/image/insertimagecommand~InsertImageCommand} when the user
98
+ * inserts new images into the editor content. By default, this option is unset which means the editor will choose the optimal image type
99
+ * based on the context of the insertion (e.g. the current selection and availability of plugins)
100
+ *
101
+ * Available options are:
102
+ *
103
+ * * `'block'` – all images inserted into the editor will be block (requires the {@link module:image/imageblock~ImageBlock} plugin),
104
+ * * `'inline'` – all images inserted into the editor will be inline (requires the {@link module:image/imageinline~ImageInline} plugin).
105
+ *
106
+ * @member {'inline'|'block'|undefined} module:image/imageinsert~ImageInsertConfig#type
107
+ */
@@ -268,7 +268,7 @@ function getIsOnButtonCallback( value ) {
268
268
  * * If you configure the feature using the resize dropdown, this name will be used for a list item in the dropdown.
269
269
  * @property {String} value The value of the resize option without the unit
270
270
  * ({@link module:image/image~ImageConfig#resizeUnit configured separately}). `null` resets an image to its original size.
271
- * @property {String} [resizeOptions.icon] An icon used by an individual resize button (see the `name` property to learn more).
271
+ * @property {String} [icon] An icon used by an individual resize button (see the `name` property to learn more).
272
272
  * Available icons are: `'small'`, `'medium'`, `'large'`, `'original'`.
273
273
  * @property {String} [label] An option label displayed in the dropdown or, if the feature is configured using
274
274
  * individual buttons, a {@link module:ui/button/buttonview~ButtonView#tooltip} and an ARIA attribute of a button.
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { Plugin } from 'ckeditor5/src/core';
11
+ import ImageUtils from '../imageutils';
11
12
  import ResizeImageCommand from './resizeimagecommand';
12
13
 
13
14
  /**
@@ -19,6 +20,13 @@ import ResizeImageCommand from './resizeimagecommand';
19
20
  * @extends module:core/plugin~Plugin
20
21
  */
21
22
  export default class ImageResizeEditing extends Plugin {
23
+ /**
24
+ * @inheritDoc
25
+ */
26
+ static get requires() {
27
+ return [ ImageUtils ];
28
+ }
29
+
22
30
  /**
23
31
  * @inheritDoc
24
32
  */
@@ -65,7 +73,8 @@ export default class ImageResizeEditing extends Plugin {
65
73
  const resizeImageCommand = new ResizeImageCommand( editor );
66
74
 
67
75
  this._registerSchema();
68
- this._registerConverters();
76
+ this._registerConverters( 'imageBlock' );
77
+ this._registerConverters( 'imageInline' );
69
78
 
70
79
  // Register `resizeImage` command and add `imageResize` command as an alias for backward compatibility.
71
80
  editor.commands.add( 'resizeImage', resizeImageCommand );
@@ -76,23 +85,27 @@ export default class ImageResizeEditing extends Plugin {
76
85
  * @private
77
86
  */
78
87
  _registerSchema() {
79
- this.editor.model.schema.extend( 'image', { allowAttributes: 'width' } );
80
- this.editor.model.schema.setAttributeProperties( 'width', {
81
- isFormatting: true
82
- } );
88
+ if ( this.editor.plugins.has( 'ImageBlockEditing' ) ) {
89
+ this.editor.model.schema.extend( 'imageBlock', { allowAttributes: 'width' } );
90
+ }
91
+
92
+ if ( this.editor.plugins.has( 'ImageInlineEditing' ) ) {
93
+ this.editor.model.schema.extend( 'imageInline', { allowAttributes: 'width' } );
94
+ }
83
95
  }
84
96
 
85
97
  /**
86
98
  * Registers image resize converters.
87
99
  *
88
100
  * @private
101
+ * @param {'imageBlock'|'imageInline'} imageType The type of the image.
89
102
  */
90
- _registerConverters() {
103
+ _registerConverters( imageType ) {
91
104
  const editor = this.editor;
92
105
 
93
106
  // Dedicated converter to propagate image's attribute to the img tag.
94
107
  editor.conversion.for( 'downcast' ).add( dispatcher =>
95
- dispatcher.on( 'attribute:width:image', ( evt, data, conversionApi ) => {
108
+ dispatcher.on( `attribute:width:${ imageType }`, ( evt, data, conversionApi ) => {
96
109
  if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
97
110
  return;
98
111
  }
@@ -113,7 +126,7 @@ export default class ImageResizeEditing extends Plugin {
113
126
  editor.conversion.for( 'upcast' )
114
127
  .attributeToAttribute( {
115
128
  view: {
116
- name: 'figure',
129
+ name: imageType === 'imageBlock' ? 'figure' : 'img',
117
130
  styles: {
118
131
  width: /.+/
119
132
  }
@@ -12,6 +12,18 @@ import { WidgetResize } from 'ckeditor5/src/widget';
12
12
 
13
13
  import ImageLoadObserver from '../image/imageloadobserver';
14
14
 
15
+ const RESIZABLE_IMAGES_CSS_SELECTOR =
16
+ 'figure.image.ck-widget > img,' +
17
+ 'figure.image.ck-widget > picture > img,' +
18
+ 'figure.image.ck-widget > a > img,' +
19
+ 'figure.image.ck-widget > a > picture > img,' +
20
+ 'span.image-inline.ck-widget > img,' +
21
+ 'span.image-inline.ck-widget > picture > img';
22
+
23
+ const IMAGE_WIDGETS_CLASSES_MATCH_REGEXP = /(image|image-inline)/;
24
+
25
+ const RESIZED_IMAGE_CLASS = 'image_resized';
26
+
15
27
  /**
16
28
  * The image resize by handles feature.
17
29
  *
@@ -58,12 +70,13 @@ export default class ImageResizeHandles extends Plugin {
58
70
 
59
71
  this.listenTo( editingView.document, 'imageLoaded', ( evt, domEvent ) => {
60
72
  // The resizer must be attached only to images loaded by the `ImageInsert`, `ImageUpload` or `LinkImage` plugins.
61
- if ( !domEvent.target.matches( 'figure.image.ck-widget > img, figure.image.ck-widget > a > img' ) ) {
73
+ if ( !domEvent.target.matches( RESIZABLE_IMAGES_CSS_SELECTOR ) ) {
62
74
  return;
63
75
  }
64
76
 
65
- const imageView = editor.editing.view.domConverter.domToView( domEvent.target );
66
- const widgetView = imageView.findAncestor( 'figure' );
77
+ const domConverter = editor.editing.view.domConverter;
78
+ const imageView = domConverter.domToView( domEvent.target );
79
+ const widgetView = imageView.findAncestor( { classes: IMAGE_WIDGETS_CLASSES_MATCH_REGEXP } );
67
80
  let resizer = this.editor.plugins.get( WidgetResize ).getResizerByViewElement( widgetView );
68
81
 
69
82
  if ( resizer ) {
@@ -89,25 +102,34 @@ export default class ImageResizeHandles extends Plugin {
89
102
  getHandleHost( domWidgetElement ) {
90
103
  return domWidgetElement.querySelector( 'img' );
91
104
  },
92
- getResizeHost( domWidgetElement ) {
93
- return domWidgetElement;
105
+ getResizeHost() {
106
+ // Return the model image element parent to avoid setting an inline element (<a>/<span>) as a resize host.
107
+ return domConverter.viewToDom( mapper.toViewElement( imageModel.parent ) );
94
108
  },
95
109
  // TODO consider other positions.
96
110
  isCentered() {
97
111
  const imageStyle = imageModel.getAttribute( 'imageStyle' );
98
112
 
99
- return !imageStyle || imageStyle == 'full' || imageStyle == 'alignCenter';
113
+ return !imageStyle || imageStyle == 'block' || imageStyle == 'alignCenter';
100
114
  },
101
115
 
102
116
  onCommit( newValue ) {
117
+ // Get rid of the CSS class in case the command execution that follows is unsuccessful
118
+ // (e.g. Track Changes can override it and the new dimensions will not apply). Otherwise,
119
+ // the presence of the class and the absence of the width style will cause it to take 100%
120
+ // of the horizontal space.
121
+ editingView.change( writer => {
122
+ writer.removeClass( RESIZED_IMAGE_CLASS, widgetView );
123
+ } );
124
+
103
125
  editor.execute( 'resizeImage', { width: newValue } );
104
126
  }
105
127
  } );
106
128
 
107
129
  resizer.on( 'updateSize', () => {
108
- if ( !widgetView.hasClass( 'image_resized' ) ) {
130
+ if ( !widgetView.hasClass( RESIZED_IMAGE_CLASS ) ) {
109
131
  editingView.change( writer => {
110
- writer.addClass( 'image_resized', widgetView );
132
+ writer.addClass( RESIZED_IMAGE_CLASS, widgetView );
111
133
  } );
112
134
  }
113
135
  } );
@@ -8,7 +8,6 @@
8
8
  */
9
9
 
10
10
  import { Command } from 'ckeditor5/src/core';
11
- import { isImage } from '../image/utils';
12
11
 
13
12
  /**
14
13
  * The resize image command. Currently, it only supports the width attribute.
@@ -20,9 +19,11 @@ export default class ResizeImageCommand extends Command {
20
19
  * @inheritDoc
21
20
  */
22
21
  refresh() {
23
- const element = this.editor.model.document.selection.getSelectedElement();
22
+ const editor = this.editor;
23
+ const imageUtils = editor.plugins.get( 'ImageUtils' );
24
+ const element = imageUtils.getClosestSelectedImageElement( editor.model.document.selection );
24
25
 
25
- this.isEnabled = isImage( element );
26
+ this.isEnabled = !!element;
26
27
 
27
28
  if ( !element || !element.hasAttribute( 'width' ) ) {
28
29
  this.value = null;
@@ -48,8 +49,10 @@ export default class ResizeImageCommand extends Command {
48
49
  * @fires execute
49
50
  */
50
51
  execute( options ) {
51
- const model = this.editor.model;
52
- const imageElement = model.document.selection.getSelectedElement();
52
+ const editor = this.editor;
53
+ const model = editor.model;
54
+ const imageUtils = editor.plugins.get( 'ImageUtils' );
55
+ const imageElement = imageUtils.getClosestSelectedImageElement( model.document.selection );
53
56
 
54
57
  this.value = {
55
58
  width: options.width,