@ckeditor/ckeditor5-image 39.0.2 → 40.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.
Files changed (116) hide show
  1. package/build/image.js +1 -1
  2. package/build/image.js.map +1 -0
  3. package/build/translations/pt-br.js +1 -1
  4. package/ckeditor5-metadata.json +12 -0
  5. package/lang/translations/pt-br.po +1 -1
  6. package/package.json +3 -3
  7. package/src/augmentation.d.ts +56 -55
  8. package/src/augmentation.js +5 -5
  9. package/src/autoimage.d.ts +52 -52
  10. package/src/autoimage.js +132 -132
  11. package/src/image/converters.d.ts +66 -66
  12. package/src/image/converters.js +232 -242
  13. package/src/image/imageblockediting.d.ts +58 -55
  14. package/src/image/imageblockediting.js +152 -136
  15. package/src/image/imageediting.d.ts +30 -30
  16. package/src/image/imageediting.js +63 -74
  17. package/src/image/imageinlineediting.d.ts +59 -56
  18. package/src/image/imageinlineediting.js +176 -160
  19. package/src/image/imageloadobserver.d.ts +48 -48
  20. package/src/image/imageloadobserver.js +52 -52
  21. package/src/image/imagetypecommand.d.ts +44 -40
  22. package/src/image/imagetypecommand.js +80 -77
  23. package/src/image/insertimagecommand.d.ts +66 -66
  24. package/src/image/insertimagecommand.js +120 -120
  25. package/src/image/replaceimagesourcecommand.d.ts +34 -34
  26. package/src/image/replaceimagesourcecommand.js +44 -44
  27. package/src/image/ui/utils.d.ts +25 -25
  28. package/src/image/ui/utils.js +44 -44
  29. package/src/image/utils.d.ts +64 -52
  30. package/src/image/utils.js +121 -100
  31. package/src/image.d.ts +34 -34
  32. package/src/image.js +38 -38
  33. package/src/imageblock.d.ts +33 -33
  34. package/src/imageblock.js +37 -37
  35. package/src/imagecaption/imagecaptionediting.d.ts +89 -89
  36. package/src/imagecaption/imagecaptionediting.js +225 -225
  37. package/src/imagecaption/imagecaptionui.d.ts +26 -26
  38. package/src/imagecaption/imagecaptionui.js +61 -61
  39. package/src/imagecaption/imagecaptionutils.d.ts +38 -38
  40. package/src/imagecaption/imagecaptionutils.js +62 -62
  41. package/src/imagecaption/toggleimagecaptioncommand.d.ts +66 -66
  42. package/src/imagecaption/toggleimagecaptioncommand.js +138 -138
  43. package/src/imagecaption.d.ts +26 -26
  44. package/src/imagecaption.js +30 -30
  45. package/src/imageconfig.d.ts +713 -713
  46. package/src/imageconfig.js +5 -5
  47. package/src/imageinline.d.ts +33 -33
  48. package/src/imageinline.js +37 -37
  49. package/src/imageinsert/imageinsertui.d.ts +44 -44
  50. package/src/imageinsert/imageinsertui.js +141 -141
  51. package/src/imageinsert/ui/imageinsertformrowview.d.ts +61 -61
  52. package/src/imageinsert/ui/imageinsertformrowview.js +54 -54
  53. package/src/imageinsert/ui/imageinsertpanelview.d.ts +106 -106
  54. package/src/imageinsert/ui/imageinsertpanelview.js +161 -161
  55. package/src/imageinsert/utils.d.ts +25 -25
  56. package/src/imageinsert/utils.js +58 -58
  57. package/src/imageinsert.d.ts +33 -33
  58. package/src/imageinsert.js +37 -37
  59. package/src/imageinsertviaurl.d.ts +30 -30
  60. package/src/imageinsertviaurl.js +34 -34
  61. package/src/imageresize/imageresizebuttons.d.ts +67 -67
  62. package/src/imageresize/imageresizebuttons.js +217 -217
  63. package/src/imageresize/imageresizeediting.d.ts +37 -37
  64. package/src/imageresize/imageresizeediting.js +165 -114
  65. package/src/imageresize/imageresizehandles.d.ts +31 -30
  66. package/src/imageresize/imageresizehandles.js +114 -107
  67. package/src/imageresize/resizeimagecommand.d.ts +42 -42
  68. package/src/imageresize/resizeimagecommand.js +63 -61
  69. package/src/imageresize.d.ts +27 -27
  70. package/src/imageresize.js +31 -31
  71. package/src/imagesizeattributes.d.ts +34 -0
  72. package/src/imagesizeattributes.js +143 -0
  73. package/src/imagestyle/converters.d.ts +24 -24
  74. package/src/imagestyle/converters.js +79 -79
  75. package/src/imagestyle/imagestylecommand.d.ts +68 -65
  76. package/src/imagestyle/imagestylecommand.js +107 -101
  77. package/src/imagestyle/imagestyleediting.d.ts +50 -50
  78. package/src/imagestyle/imagestyleediting.js +108 -108
  79. package/src/imagestyle/imagestyleui.d.ts +56 -56
  80. package/src/imagestyle/imagestyleui.js +192 -192
  81. package/src/imagestyle/utils.d.ts +101 -101
  82. package/src/imagestyle/utils.js +329 -329
  83. package/src/imagestyle.d.ts +32 -32
  84. package/src/imagestyle.js +36 -36
  85. package/src/imagetextalternative/imagetextalternativecommand.d.ts +34 -34
  86. package/src/imagetextalternative/imagetextalternativecommand.js +44 -44
  87. package/src/imagetextalternative/imagetextalternativeediting.d.ts +28 -28
  88. package/src/imagetextalternative/imagetextalternativeediting.js +35 -35
  89. package/src/imagetextalternative/imagetextalternativeui.d.ts +68 -68
  90. package/src/imagetextalternative/imagetextalternativeui.js +173 -173
  91. package/src/imagetextalternative/ui/textalternativeformview.d.ts +72 -72
  92. package/src/imagetextalternative/ui/textalternativeformview.js +121 -121
  93. package/src/imagetextalternative.d.ts +29 -29
  94. package/src/imagetextalternative.js +33 -33
  95. package/src/imagetoolbar.d.ts +35 -35
  96. package/src/imagetoolbar.js +57 -57
  97. package/src/imageupload/imageuploadediting.d.ts +111 -111
  98. package/src/imageupload/imageuploadediting.js +337 -335
  99. package/src/imageupload/imageuploadprogress.d.ts +42 -42
  100. package/src/imageupload/imageuploadprogress.js +211 -211
  101. package/src/imageupload/imageuploadui.d.ts +23 -23
  102. package/src/imageupload/imageuploadui.js +57 -57
  103. package/src/imageupload/uploadimagecommand.d.ts +60 -60
  104. package/src/imageupload/uploadimagecommand.js +100 -100
  105. package/src/imageupload/utils.d.ts +33 -33
  106. package/src/imageupload/utils.js +112 -112
  107. package/src/imageupload.d.ts +32 -32
  108. package/src/imageupload.js +36 -36
  109. package/src/imageutils.d.ts +125 -102
  110. package/src/imageutils.js +306 -248
  111. package/src/index.d.ts +48 -47
  112. package/src/index.js +39 -38
  113. package/src/pictureediting.d.ts +88 -88
  114. package/src/pictureediting.js +130 -130
  115. package/theme/image.css +38 -11
  116. package/theme/imageresize.css +5 -0
@@ -1,89 +1,89 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @module image/imagecaption/imagecaptionediting
7
- */
8
- import { type Editor, Plugin } from 'ckeditor5/src/core';
9
- import { Element } from 'ckeditor5/src/engine';
10
- import ImageUtils from '../imageutils';
11
- import ImageCaptionUtils from './imagecaptionutils';
12
- /**
13
- * The image caption engine plugin. It is responsible for:
14
- *
15
- * * registering converters for the caption element,
16
- * * registering converters for the caption model attribute,
17
- * * registering the {@link module:image/imagecaption/toggleimagecaptioncommand~ToggleImageCaptionCommand `toggleImageCaption`} command.
18
- */
19
- export default class ImageCaptionEditing extends Plugin {
20
- /**
21
- * @inheritDoc
22
- */
23
- static get requires(): readonly [typeof ImageUtils, typeof ImageCaptionUtils];
24
- /**
25
- * @inheritDoc
26
- */
27
- static get pluginName(): "ImageCaptionEditing";
28
- /**
29
- * A map that keeps saved JSONified image captions and image model elements they are
30
- * associated with.
31
- *
32
- * To learn more about this system, see {@link #_saveCaption}.
33
- */
34
- private _savedCaptionsMap;
35
- /**
36
- * @inheritDoc
37
- */
38
- constructor(editor: Editor);
39
- /**
40
- * @inheritDoc
41
- */
42
- init(): void;
43
- /**
44
- * Configures conversion pipelines to support upcasting and downcasting
45
- * image captions.
46
- */
47
- private _setupConversion;
48
- /**
49
- * Integrates with {@link module:image/image/imagetypecommand~ImageTypeCommand image type commands}
50
- * to make sure the caption is preserved when the type of an image changes so it can be restored
51
- * in the future if the user decides they want their caption back.
52
- */
53
- private _setupImageTypeCommandsIntegration;
54
- /**
55
- * Returns the saved {@link module:engine/model/element~Element#toJSON JSONified} caption
56
- * of an image model element.
57
- *
58
- * See {@link #_saveCaption}.
59
- *
60
- * @internal
61
- * @param imageModelElement The model element the caption should be returned for.
62
- * @returns The model caption element or `null` if there is none.
63
- */
64
- _getSavedCaption(imageModelElement: Element): Element | null;
65
- /**
66
- * Saves a {@link module:engine/model/element~Element#toJSON JSONified} caption for
67
- * an image element to allow restoring it in the future.
68
- *
69
- * A caption is saved every time it gets hidden and/or the type of an image changes. The
70
- * user should be able to restore it on demand.
71
- *
72
- * **Note**: The caption cannot be stored in the image model element attribute because,
73
- * for instance, when the model state propagates to collaborators, the attribute would get
74
- * lost (mainly because it does not convert to anything when the caption is hidden) and
75
- * the states of collaborators' models would de-synchronize causing numerous issues.
76
- *
77
- * See {@link #_getSavedCaption}.
78
- *
79
- * @internal
80
- * @param imageModelElement The model element the caption is saved for.
81
- * @param caption The caption model element to be saved.
82
- */
83
- _saveCaption(imageModelElement: Element, caption: Element): void;
84
- /**
85
- * Reconverts image caption when image alt attribute changes.
86
- * The change of alt attribute is reflected in caption's aria-label attribute.
87
- */
88
- private _registerCaptionReconversion;
89
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module image/imagecaption/imagecaptionediting
7
+ */
8
+ import { type Editor, Plugin } from 'ckeditor5/src/core';
9
+ import { Element } from 'ckeditor5/src/engine';
10
+ import ImageUtils from '../imageutils';
11
+ import ImageCaptionUtils from './imagecaptionutils';
12
+ /**
13
+ * The image caption engine plugin. It is responsible for:
14
+ *
15
+ * * registering converters for the caption element,
16
+ * * registering converters for the caption model attribute,
17
+ * * registering the {@link module:image/imagecaption/toggleimagecaptioncommand~ToggleImageCaptionCommand `toggleImageCaption`} command.
18
+ */
19
+ export default class ImageCaptionEditing extends Plugin {
20
+ /**
21
+ * @inheritDoc
22
+ */
23
+ static get requires(): readonly [typeof ImageUtils, typeof ImageCaptionUtils];
24
+ /**
25
+ * @inheritDoc
26
+ */
27
+ static get pluginName(): "ImageCaptionEditing";
28
+ /**
29
+ * A map that keeps saved JSONified image captions and image model elements they are
30
+ * associated with.
31
+ *
32
+ * To learn more about this system, see {@link #_saveCaption}.
33
+ */
34
+ private _savedCaptionsMap;
35
+ /**
36
+ * @inheritDoc
37
+ */
38
+ constructor(editor: Editor);
39
+ /**
40
+ * @inheritDoc
41
+ */
42
+ init(): void;
43
+ /**
44
+ * Configures conversion pipelines to support upcasting and downcasting
45
+ * image captions.
46
+ */
47
+ private _setupConversion;
48
+ /**
49
+ * Integrates with {@link module:image/image/imagetypecommand~ImageTypeCommand image type commands}
50
+ * to make sure the caption is preserved when the type of an image changes so it can be restored
51
+ * in the future if the user decides they want their caption back.
52
+ */
53
+ private _setupImageTypeCommandsIntegration;
54
+ /**
55
+ * Returns the saved {@link module:engine/model/element~Element#toJSON JSONified} caption
56
+ * of an image model element.
57
+ *
58
+ * See {@link #_saveCaption}.
59
+ *
60
+ * @internal
61
+ * @param imageModelElement The model element the caption should be returned for.
62
+ * @returns The model caption element or `null` if there is none.
63
+ */
64
+ _getSavedCaption(imageModelElement: Element): Element | null;
65
+ /**
66
+ * Saves a {@link module:engine/model/element~Element#toJSON JSONified} caption for
67
+ * an image element to allow restoring it in the future.
68
+ *
69
+ * A caption is saved every time it gets hidden and/or the type of an image changes. The
70
+ * user should be able to restore it on demand.
71
+ *
72
+ * **Note**: The caption cannot be stored in the image model element attribute because,
73
+ * for instance, when the model state propagates to collaborators, the attribute would get
74
+ * lost (mainly because it does not convert to anything when the caption is hidden) and
75
+ * the states of collaborators' models would de-synchronize causing numerous issues.
76
+ *
77
+ * See {@link #_getSavedCaption}.
78
+ *
79
+ * @internal
80
+ * @param imageModelElement The model element the caption is saved for.
81
+ * @param caption The caption model element to be saved.
82
+ */
83
+ _saveCaption(imageModelElement: Element, caption: Element): void;
84
+ /**
85
+ * Reconverts image caption when image alt attribute changes.
86
+ * The change of alt attribute is reflected in caption's aria-label attribute.
87
+ */
88
+ private _registerCaptionReconversion;
89
+ }
@@ -1,225 +1,225 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @module image/imagecaption/imagecaptionediting
7
- */
8
- import { Plugin } from 'ckeditor5/src/core';
9
- import { Element, enablePlaceholder } from 'ckeditor5/src/engine';
10
- import { toWidgetEditable } from 'ckeditor5/src/widget';
11
- import ToggleImageCaptionCommand from './toggleimagecaptioncommand';
12
- import ImageUtils from '../imageutils';
13
- import ImageCaptionUtils from './imagecaptionutils';
14
- /**
15
- * The image caption engine plugin. It is responsible for:
16
- *
17
- * * registering converters for the caption element,
18
- * * registering converters for the caption model attribute,
19
- * * registering the {@link module:image/imagecaption/toggleimagecaptioncommand~ToggleImageCaptionCommand `toggleImageCaption`} command.
20
- */
21
- export default class ImageCaptionEditing extends Plugin {
22
- /**
23
- * @inheritDoc
24
- */
25
- static get requires() {
26
- return [ImageUtils, ImageCaptionUtils];
27
- }
28
- /**
29
- * @inheritDoc
30
- */
31
- static get pluginName() {
32
- return 'ImageCaptionEditing';
33
- }
34
- /**
35
- * @inheritDoc
36
- */
37
- constructor(editor) {
38
- super(editor);
39
- this._savedCaptionsMap = new WeakMap();
40
- }
41
- /**
42
- * @inheritDoc
43
- */
44
- init() {
45
- const editor = this.editor;
46
- const schema = editor.model.schema;
47
- // Schema configuration.
48
- if (!schema.isRegistered('caption')) {
49
- schema.register('caption', {
50
- allowIn: 'imageBlock',
51
- allowContentOf: '$block',
52
- isLimit: true
53
- });
54
- }
55
- else {
56
- schema.extend('caption', {
57
- allowIn: 'imageBlock'
58
- });
59
- }
60
- editor.commands.add('toggleImageCaption', new ToggleImageCaptionCommand(this.editor));
61
- this._setupConversion();
62
- this._setupImageTypeCommandsIntegration();
63
- this._registerCaptionReconversion();
64
- }
65
- /**
66
- * Configures conversion pipelines to support upcasting and downcasting
67
- * image captions.
68
- */
69
- _setupConversion() {
70
- const editor = this.editor;
71
- const view = editor.editing.view;
72
- const imageUtils = editor.plugins.get('ImageUtils');
73
- const imageCaptionUtils = editor.plugins.get('ImageCaptionUtils');
74
- const t = editor.t;
75
- // View -> model converter for the data pipeline.
76
- editor.conversion.for('upcast').elementToElement({
77
- view: element => imageCaptionUtils.matchImageCaptionViewElement(element),
78
- model: 'caption'
79
- });
80
- // Model -> view converter for the data pipeline.
81
- editor.conversion.for('dataDowncast').elementToElement({
82
- model: 'caption',
83
- view: (modelElement, { writer }) => {
84
- if (!imageUtils.isBlockImage(modelElement.parent)) {
85
- return null;
86
- }
87
- return writer.createContainerElement('figcaption');
88
- }
89
- });
90
- // Model -> view converter for the editing pipeline.
91
- editor.conversion.for('editingDowncast').elementToElement({
92
- model: 'caption',
93
- view: (modelElement, { writer }) => {
94
- if (!imageUtils.isBlockImage(modelElement.parent)) {
95
- return null;
96
- }
97
- const figcaptionElement = writer.createEditableElement('figcaption');
98
- writer.setCustomProperty('imageCaption', true, figcaptionElement);
99
- figcaptionElement.placeholder = t('Enter image caption');
100
- enablePlaceholder({
101
- view,
102
- element: figcaptionElement,
103
- keepOnFocus: true
104
- });
105
- const imageAlt = modelElement.parent.getAttribute('alt');
106
- const label = imageAlt ? t('Caption for image: %0', [imageAlt]) : t('Caption for the image');
107
- return toWidgetEditable(figcaptionElement, writer, { label });
108
- }
109
- });
110
- }
111
- /**
112
- * Integrates with {@link module:image/image/imagetypecommand~ImageTypeCommand image type commands}
113
- * to make sure the caption is preserved when the type of an image changes so it can be restored
114
- * in the future if the user decides they want their caption back.
115
- */
116
- _setupImageTypeCommandsIntegration() {
117
- const editor = this.editor;
118
- const imageUtils = editor.plugins.get('ImageUtils');
119
- const imageCaptionUtils = editor.plugins.get('ImageCaptionUtils');
120
- const imageTypeInlineCommand = editor.commands.get('imageTypeInline');
121
- const imageTypeBlockCommand = editor.commands.get('imageTypeBlock');
122
- const handleImageTypeChange = evt => {
123
- // The image type command execution can be unsuccessful.
124
- if (!evt.return) {
125
- return;
126
- }
127
- const { oldElement, newElement } = evt.return;
128
- /* istanbul ignore if: paranoid check -- @preserve */
129
- if (!oldElement) {
130
- return;
131
- }
132
- if (imageUtils.isBlockImage(oldElement)) {
133
- const oldCaptionElement = imageCaptionUtils.getCaptionFromImageModelElement(oldElement);
134
- // If the old element was a captioned block image (the caption was visible),
135
- // simply save it so it can be restored.
136
- if (oldCaptionElement) {
137
- this._saveCaption(newElement, oldCaptionElement);
138
- return;
139
- }
140
- }
141
- const savedOldElementCaption = this._getSavedCaption(oldElement);
142
- // If either:
143
- //
144
- // * the block image didn't have a visible caption,
145
- // * the block image caption was hidden (and already saved),
146
- // * the inline image was passed
147
- //
148
- // just try to "pass" the saved caption from the old image to the new image
149
- // so it can be retrieved in the future if the user wants it back.
150
- if (savedOldElementCaption) {
151
- // Note: Since we're writing to a WeakMap, we don't bother with removing the
152
- // [ oldElement, savedOldElementCaption ] pair from it.
153
- this._saveCaption(newElement, savedOldElementCaption);
154
- }
155
- };
156
- // Presence of the commands depends on the Image(Inline|Block)Editing plugins loaded in the editor.
157
- if (imageTypeInlineCommand) {
158
- this.listenTo(imageTypeInlineCommand, 'execute', handleImageTypeChange, { priority: 'low' });
159
- }
160
- if (imageTypeBlockCommand) {
161
- this.listenTo(imageTypeBlockCommand, 'execute', handleImageTypeChange, { priority: 'low' });
162
- }
163
- }
164
- /**
165
- * Returns the saved {@link module:engine/model/element~Element#toJSON JSONified} caption
166
- * of an image model element.
167
- *
168
- * See {@link #_saveCaption}.
169
- *
170
- * @internal
171
- * @param imageModelElement The model element the caption should be returned for.
172
- * @returns The model caption element or `null` if there is none.
173
- */
174
- _getSavedCaption(imageModelElement) {
175
- const jsonObject = this._savedCaptionsMap.get(imageModelElement);
176
- return jsonObject ? Element.fromJSON(jsonObject) : null;
177
- }
178
- /**
179
- * Saves a {@link module:engine/model/element~Element#toJSON JSONified} caption for
180
- * an image element to allow restoring it in the future.
181
- *
182
- * A caption is saved every time it gets hidden and/or the type of an image changes. The
183
- * user should be able to restore it on demand.
184
- *
185
- * **Note**: The caption cannot be stored in the image model element attribute because,
186
- * for instance, when the model state propagates to collaborators, the attribute would get
187
- * lost (mainly because it does not convert to anything when the caption is hidden) and
188
- * the states of collaborators' models would de-synchronize causing numerous issues.
189
- *
190
- * See {@link #_getSavedCaption}.
191
- *
192
- * @internal
193
- * @param imageModelElement The model element the caption is saved for.
194
- * @param caption The caption model element to be saved.
195
- */
196
- _saveCaption(imageModelElement, caption) {
197
- this._savedCaptionsMap.set(imageModelElement, caption.toJSON());
198
- }
199
- /**
200
- * Reconverts image caption when image alt attribute changes.
201
- * The change of alt attribute is reflected in caption's aria-label attribute.
202
- */
203
- _registerCaptionReconversion() {
204
- const editor = this.editor;
205
- const model = editor.model;
206
- const imageUtils = editor.plugins.get('ImageUtils');
207
- const imageCaptionUtils = editor.plugins.get('ImageCaptionUtils');
208
- model.document.on('change:data', () => {
209
- const changes = model.document.differ.getChanges();
210
- for (const change of changes) {
211
- if (change.attributeKey !== 'alt') {
212
- continue;
213
- }
214
- const image = change.range.start.nodeAfter;
215
- if (imageUtils.isBlockImage(image)) {
216
- const caption = imageCaptionUtils.getCaptionFromImageModelElement(image);
217
- if (!caption) {
218
- return;
219
- }
220
- editor.editing.reconvertItem(caption);
221
- }
222
- }
223
- });
224
- }
225
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module image/imagecaption/imagecaptionediting
7
+ */
8
+ import { Plugin } from 'ckeditor5/src/core';
9
+ import { Element, enablePlaceholder } from 'ckeditor5/src/engine';
10
+ import { toWidgetEditable } from 'ckeditor5/src/widget';
11
+ import ToggleImageCaptionCommand from './toggleimagecaptioncommand';
12
+ import ImageUtils from '../imageutils';
13
+ import ImageCaptionUtils from './imagecaptionutils';
14
+ /**
15
+ * The image caption engine plugin. It is responsible for:
16
+ *
17
+ * * registering converters for the caption element,
18
+ * * registering converters for the caption model attribute,
19
+ * * registering the {@link module:image/imagecaption/toggleimagecaptioncommand~ToggleImageCaptionCommand `toggleImageCaption`} command.
20
+ */
21
+ export default class ImageCaptionEditing extends Plugin {
22
+ /**
23
+ * @inheritDoc
24
+ */
25
+ static get requires() {
26
+ return [ImageUtils, ImageCaptionUtils];
27
+ }
28
+ /**
29
+ * @inheritDoc
30
+ */
31
+ static get pluginName() {
32
+ return 'ImageCaptionEditing';
33
+ }
34
+ /**
35
+ * @inheritDoc
36
+ */
37
+ constructor(editor) {
38
+ super(editor);
39
+ this._savedCaptionsMap = new WeakMap();
40
+ }
41
+ /**
42
+ * @inheritDoc
43
+ */
44
+ init() {
45
+ const editor = this.editor;
46
+ const schema = editor.model.schema;
47
+ // Schema configuration.
48
+ if (!schema.isRegistered('caption')) {
49
+ schema.register('caption', {
50
+ allowIn: 'imageBlock',
51
+ allowContentOf: '$block',
52
+ isLimit: true
53
+ });
54
+ }
55
+ else {
56
+ schema.extend('caption', {
57
+ allowIn: 'imageBlock'
58
+ });
59
+ }
60
+ editor.commands.add('toggleImageCaption', new ToggleImageCaptionCommand(this.editor));
61
+ this._setupConversion();
62
+ this._setupImageTypeCommandsIntegration();
63
+ this._registerCaptionReconversion();
64
+ }
65
+ /**
66
+ * Configures conversion pipelines to support upcasting and downcasting
67
+ * image captions.
68
+ */
69
+ _setupConversion() {
70
+ const editor = this.editor;
71
+ const view = editor.editing.view;
72
+ const imageUtils = editor.plugins.get('ImageUtils');
73
+ const imageCaptionUtils = editor.plugins.get('ImageCaptionUtils');
74
+ const t = editor.t;
75
+ // View -> model converter for the data pipeline.
76
+ editor.conversion.for('upcast').elementToElement({
77
+ view: element => imageCaptionUtils.matchImageCaptionViewElement(element),
78
+ model: 'caption'
79
+ });
80
+ // Model -> view converter for the data pipeline.
81
+ editor.conversion.for('dataDowncast').elementToElement({
82
+ model: 'caption',
83
+ view: (modelElement, { writer }) => {
84
+ if (!imageUtils.isBlockImage(modelElement.parent)) {
85
+ return null;
86
+ }
87
+ return writer.createContainerElement('figcaption');
88
+ }
89
+ });
90
+ // Model -> view converter for the editing pipeline.
91
+ editor.conversion.for('editingDowncast').elementToElement({
92
+ model: 'caption',
93
+ view: (modelElement, { writer }) => {
94
+ if (!imageUtils.isBlockImage(modelElement.parent)) {
95
+ return null;
96
+ }
97
+ const figcaptionElement = writer.createEditableElement('figcaption');
98
+ writer.setCustomProperty('imageCaption', true, figcaptionElement);
99
+ figcaptionElement.placeholder = t('Enter image caption');
100
+ enablePlaceholder({
101
+ view,
102
+ element: figcaptionElement,
103
+ keepOnFocus: true
104
+ });
105
+ const imageAlt = modelElement.parent.getAttribute('alt');
106
+ const label = imageAlt ? t('Caption for image: %0', [imageAlt]) : t('Caption for the image');
107
+ return toWidgetEditable(figcaptionElement, writer, { label });
108
+ }
109
+ });
110
+ }
111
+ /**
112
+ * Integrates with {@link module:image/image/imagetypecommand~ImageTypeCommand image type commands}
113
+ * to make sure the caption is preserved when the type of an image changes so it can be restored
114
+ * in the future if the user decides they want their caption back.
115
+ */
116
+ _setupImageTypeCommandsIntegration() {
117
+ const editor = this.editor;
118
+ const imageUtils = editor.plugins.get('ImageUtils');
119
+ const imageCaptionUtils = editor.plugins.get('ImageCaptionUtils');
120
+ const imageTypeInlineCommand = editor.commands.get('imageTypeInline');
121
+ const imageTypeBlockCommand = editor.commands.get('imageTypeBlock');
122
+ const handleImageTypeChange = evt => {
123
+ // The image type command execution can be unsuccessful.
124
+ if (!evt.return) {
125
+ return;
126
+ }
127
+ const { oldElement, newElement } = evt.return;
128
+ /* istanbul ignore if: paranoid check -- @preserve */
129
+ if (!oldElement) {
130
+ return;
131
+ }
132
+ if (imageUtils.isBlockImage(oldElement)) {
133
+ const oldCaptionElement = imageCaptionUtils.getCaptionFromImageModelElement(oldElement);
134
+ // If the old element was a captioned block image (the caption was visible),
135
+ // simply save it so it can be restored.
136
+ if (oldCaptionElement) {
137
+ this._saveCaption(newElement, oldCaptionElement);
138
+ return;
139
+ }
140
+ }
141
+ const savedOldElementCaption = this._getSavedCaption(oldElement);
142
+ // If either:
143
+ //
144
+ // * the block image didn't have a visible caption,
145
+ // * the block image caption was hidden (and already saved),
146
+ // * the inline image was passed
147
+ //
148
+ // just try to "pass" the saved caption from the old image to the new image
149
+ // so it can be retrieved in the future if the user wants it back.
150
+ if (savedOldElementCaption) {
151
+ // Note: Since we're writing to a WeakMap, we don't bother with removing the
152
+ // [ oldElement, savedOldElementCaption ] pair from it.
153
+ this._saveCaption(newElement, savedOldElementCaption);
154
+ }
155
+ };
156
+ // Presence of the commands depends on the Image(Inline|Block)Editing plugins loaded in the editor.
157
+ if (imageTypeInlineCommand) {
158
+ this.listenTo(imageTypeInlineCommand, 'execute', handleImageTypeChange, { priority: 'low' });
159
+ }
160
+ if (imageTypeBlockCommand) {
161
+ this.listenTo(imageTypeBlockCommand, 'execute', handleImageTypeChange, { priority: 'low' });
162
+ }
163
+ }
164
+ /**
165
+ * Returns the saved {@link module:engine/model/element~Element#toJSON JSONified} caption
166
+ * of an image model element.
167
+ *
168
+ * See {@link #_saveCaption}.
169
+ *
170
+ * @internal
171
+ * @param imageModelElement The model element the caption should be returned for.
172
+ * @returns The model caption element or `null` if there is none.
173
+ */
174
+ _getSavedCaption(imageModelElement) {
175
+ const jsonObject = this._savedCaptionsMap.get(imageModelElement);
176
+ return jsonObject ? Element.fromJSON(jsonObject) : null;
177
+ }
178
+ /**
179
+ * Saves a {@link module:engine/model/element~Element#toJSON JSONified} caption for
180
+ * an image element to allow restoring it in the future.
181
+ *
182
+ * A caption is saved every time it gets hidden and/or the type of an image changes. The
183
+ * user should be able to restore it on demand.
184
+ *
185
+ * **Note**: The caption cannot be stored in the image model element attribute because,
186
+ * for instance, when the model state propagates to collaborators, the attribute would get
187
+ * lost (mainly because it does not convert to anything when the caption is hidden) and
188
+ * the states of collaborators' models would de-synchronize causing numerous issues.
189
+ *
190
+ * See {@link #_getSavedCaption}.
191
+ *
192
+ * @internal
193
+ * @param imageModelElement The model element the caption is saved for.
194
+ * @param caption The caption model element to be saved.
195
+ */
196
+ _saveCaption(imageModelElement, caption) {
197
+ this._savedCaptionsMap.set(imageModelElement, caption.toJSON());
198
+ }
199
+ /**
200
+ * Reconverts image caption when image alt attribute changes.
201
+ * The change of alt attribute is reflected in caption's aria-label attribute.
202
+ */
203
+ _registerCaptionReconversion() {
204
+ const editor = this.editor;
205
+ const model = editor.model;
206
+ const imageUtils = editor.plugins.get('ImageUtils');
207
+ const imageCaptionUtils = editor.plugins.get('ImageCaptionUtils');
208
+ model.document.on('change:data', () => {
209
+ const changes = model.document.differ.getChanges();
210
+ for (const change of changes) {
211
+ if (change.attributeKey !== 'alt') {
212
+ continue;
213
+ }
214
+ const image = change.range.start.nodeAfter;
215
+ if (imageUtils.isBlockImage(image)) {
216
+ const caption = imageCaptionUtils.getCaptionFromImageModelElement(image);
217
+ if (!caption) {
218
+ return;
219
+ }
220
+ editor.editing.reconvertItem(caption);
221
+ }
222
+ }
223
+ });
224
+ }
225
+ }