@ckeditor/ckeditor5-image 47.6.1-alpha.1 → 48.0.0-alpha.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 (289) hide show
  1. package/LICENSE.md +1 -1
  2. package/ckeditor5-metadata.json +25 -25
  3. package/{src → dist}/autoimage.d.ts +4 -4
  4. package/{src → dist}/image/converters.d.ts +1 -1
  5. package/{src → dist}/image/imageblockediting.d.ts +2 -2
  6. package/{src → dist}/image/imageediting.d.ts +1 -1
  7. package/{src → dist}/image/imageinlineediting.d.ts +2 -2
  8. package/{src → dist}/image/imageloadobserver.d.ts +1 -1
  9. package/{src → dist}/image/imageplaceholder.d.ts +1 -1
  10. package/{src → dist}/image/imagetypecommand.d.ts +2 -2
  11. package/{src → dist}/image/insertimagecommand.d.ts +2 -2
  12. package/{src → dist}/image/replaceimagesourcecommand.d.ts +2 -2
  13. package/{src → dist}/image/ui/utils.d.ts +2 -2
  14. package/{src → dist}/image/utils.d.ts +2 -2
  15. package/{src → dist}/image.d.ts +1 -1
  16. package/{src → dist}/imageblock.d.ts +2 -2
  17. package/{src → dist}/imagecaption/imagecaptionediting.d.ts +2 -2
  18. package/{src → dist}/imagecaption/imagecaptionui.d.ts +1 -1
  19. package/{src → dist}/imagecaption/imagecaptionutils.d.ts +2 -2
  20. package/{src → dist}/imagecaption/toggleimagecaptioncommand.d.ts +1 -1
  21. package/{src → dist}/imagecaption.d.ts +1 -1
  22. package/{src → dist}/imageconfig.d.ts +25 -16
  23. package/{src → dist}/imageinline.d.ts +2 -2
  24. package/{src → dist}/imageinsert/imageinsertui.d.ts +3 -3
  25. package/{src → dist}/imageinsert/imageinsertviaurlui.d.ts +2 -2
  26. package/{src → dist}/imageinsert/ui/imageinsertformview.d.ts +2 -2
  27. package/{src → dist}/imageinsert/ui/imageinserturlview.d.ts +2 -2
  28. package/{src → dist}/imageinsert.d.ts +1 -1
  29. package/{src → dist}/imageinsertviaurl.d.ts +1 -1
  30. package/{src → dist}/imageresize/imagecustomresizeui.d.ts +2 -2
  31. package/{src → dist}/imageresize/imageresizebuttons.d.ts +1 -1
  32. package/{src → dist}/imageresize/imageresizeediting.d.ts +1 -1
  33. package/{src → dist}/imageresize/imageresizehandles.d.ts +2 -2
  34. package/{src → dist}/imageresize/resizeimagecommand.d.ts +1 -1
  35. package/{src → dist}/imageresize/ui/imagecustomresizeformview.d.ts +2 -4
  36. package/{src → dist}/imageresize/utils/getselectedimageeditornodes.d.ts +2 -2
  37. package/{src → dist}/imageresize/utils/getselectedimagepossibleresizerange.d.ts +1 -1
  38. package/{src → dist}/imageresize/utils/getselectedimagewidthinunits.d.ts +1 -1
  39. package/{src → dist}/imageresize.d.ts +1 -1
  40. package/{src → dist}/imagesizeattributes.d.ts +1 -1
  41. package/{src → dist}/imagestyle/converters.d.ts +2 -2
  42. package/{src → dist}/imagestyle/imagestylecommand.d.ts +2 -2
  43. package/{src → dist}/imagestyle/imagestyleediting.d.ts +1 -1
  44. package/{src → dist}/imagestyle/imagestyleui.d.ts +1 -1
  45. package/{src → dist}/imagestyle/utils.d.ts +1 -1
  46. package/{src → dist}/imagestyle.d.ts +1 -1
  47. package/{src → dist}/imagetextalternative/imagetextalternativecommand.d.ts +1 -1
  48. package/{src → dist}/imagetextalternative/imagetextalternativeediting.d.ts +1 -1
  49. package/{src → dist}/imagetextalternative/imagetextalternativeui.d.ts +2 -2
  50. package/{src → dist}/imagetextalternative/ui/textalternativeformview.d.ts +2 -4
  51. package/{src → dist}/imagetextalternative.d.ts +1 -1
  52. package/{src → dist}/imagetoolbar.d.ts +2 -2
  53. package/{src → dist}/imageupload/imageuploadediting.d.ts +5 -5
  54. package/{src → dist}/imageupload/imageuploadprogress.d.ts +1 -1
  55. package/{src → dist}/imageupload/imageuploadui.d.ts +1 -1
  56. package/{src → dist}/imageupload/uploadimagecommand.d.ts +2 -2
  57. package/{src → dist}/imageupload/utils.d.ts +1 -1
  58. package/{src → dist}/imageupload.d.ts +1 -1
  59. package/{src → dist}/imageutils.d.ts +2 -2
  60. package/dist/index-content.css +97 -100
  61. package/dist/index-editor.css +313 -190
  62. package/dist/index.css +388 -458
  63. package/dist/index.css.map +1 -1
  64. package/dist/index.js +1 -2
  65. package/dist/index.js.map +1 -1
  66. package/{src → dist}/pictureediting.d.ts +1 -1
  67. package/package.json +29 -53
  68. package/build/image.js +0 -5
  69. package/build/translations/af.js +0 -1
  70. package/build/translations/ar.js +0 -1
  71. package/build/translations/ast.js +0 -1
  72. package/build/translations/az.js +0 -1
  73. package/build/translations/be.js +0 -1
  74. package/build/translations/bg.js +0 -1
  75. package/build/translations/bn.js +0 -1
  76. package/build/translations/bs.js +0 -1
  77. package/build/translations/ca.js +0 -1
  78. package/build/translations/cs.js +0 -1
  79. package/build/translations/da.js +0 -1
  80. package/build/translations/de-ch.js +0 -1
  81. package/build/translations/de.js +0 -1
  82. package/build/translations/el.js +0 -1
  83. package/build/translations/en-au.js +0 -1
  84. package/build/translations/en-gb.js +0 -1
  85. package/build/translations/eo.js +0 -1
  86. package/build/translations/es-co.js +0 -1
  87. package/build/translations/es.js +0 -1
  88. package/build/translations/et.js +0 -1
  89. package/build/translations/eu.js +0 -1
  90. package/build/translations/fa.js +0 -1
  91. package/build/translations/fi.js +0 -1
  92. package/build/translations/fr.js +0 -1
  93. package/build/translations/gl.js +0 -1
  94. package/build/translations/gu.js +0 -1
  95. package/build/translations/he.js +0 -1
  96. package/build/translations/hi.js +0 -1
  97. package/build/translations/hr.js +0 -1
  98. package/build/translations/hu.js +0 -1
  99. package/build/translations/hy.js +0 -1
  100. package/build/translations/id.js +0 -1
  101. package/build/translations/it.js +0 -1
  102. package/build/translations/ja.js +0 -1
  103. package/build/translations/jv.js +0 -1
  104. package/build/translations/kk.js +0 -1
  105. package/build/translations/km.js +0 -1
  106. package/build/translations/kn.js +0 -1
  107. package/build/translations/ko.js +0 -1
  108. package/build/translations/ku.js +0 -1
  109. package/build/translations/lt.js +0 -1
  110. package/build/translations/lv.js +0 -1
  111. package/build/translations/ms.js +0 -1
  112. package/build/translations/nb.js +0 -1
  113. package/build/translations/ne.js +0 -1
  114. package/build/translations/nl.js +0 -1
  115. package/build/translations/no.js +0 -1
  116. package/build/translations/oc.js +0 -1
  117. package/build/translations/pl.js +0 -1
  118. package/build/translations/pt-br.js +0 -1
  119. package/build/translations/pt.js +0 -1
  120. package/build/translations/ro.js +0 -1
  121. package/build/translations/ru.js +0 -1
  122. package/build/translations/si.js +0 -1
  123. package/build/translations/sk.js +0 -1
  124. package/build/translations/sl.js +0 -1
  125. package/build/translations/sq.js +0 -1
  126. package/build/translations/sr-latn.js +0 -1
  127. package/build/translations/sr.js +0 -1
  128. package/build/translations/sv.js +0 -1
  129. package/build/translations/th.js +0 -1
  130. package/build/translations/ti.js +0 -1
  131. package/build/translations/tk.js +0 -1
  132. package/build/translations/tr.js +0 -1
  133. package/build/translations/tt.js +0 -1
  134. package/build/translations/ug.js +0 -1
  135. package/build/translations/uk.js +0 -1
  136. package/build/translations/ur.js +0 -1
  137. package/build/translations/uz.js +0 -1
  138. package/build/translations/vi.js +0 -1
  139. package/build/translations/zh-cn.js +0 -1
  140. package/build/translations/zh.js +0 -1
  141. package/lang/contexts.json +0 -48
  142. package/lang/translations/af.po +0 -196
  143. package/lang/translations/ar.po +0 -196
  144. package/lang/translations/ast.po +0 -196
  145. package/lang/translations/az.po +0 -196
  146. package/lang/translations/be.po +0 -196
  147. package/lang/translations/bg.po +0 -196
  148. package/lang/translations/bn.po +0 -196
  149. package/lang/translations/bs.po +0 -196
  150. package/lang/translations/ca.po +0 -196
  151. package/lang/translations/cs.po +0 -196
  152. package/lang/translations/da.po +0 -196
  153. package/lang/translations/de-ch.po +0 -196
  154. package/lang/translations/de.po +0 -196
  155. package/lang/translations/el.po +0 -196
  156. package/lang/translations/en-au.po +0 -196
  157. package/lang/translations/en-gb.po +0 -196
  158. package/lang/translations/en.po +0 -196
  159. package/lang/translations/eo.po +0 -196
  160. package/lang/translations/es-co.po +0 -196
  161. package/lang/translations/es.po +0 -196
  162. package/lang/translations/et.po +0 -196
  163. package/lang/translations/eu.po +0 -196
  164. package/lang/translations/fa.po +0 -196
  165. package/lang/translations/fi.po +0 -196
  166. package/lang/translations/fr.po +0 -196
  167. package/lang/translations/gl.po +0 -196
  168. package/lang/translations/gu.po +0 -196
  169. package/lang/translations/he.po +0 -196
  170. package/lang/translations/hi.po +0 -196
  171. package/lang/translations/hr.po +0 -196
  172. package/lang/translations/hu.po +0 -196
  173. package/lang/translations/hy.po +0 -196
  174. package/lang/translations/id.po +0 -196
  175. package/lang/translations/it.po +0 -196
  176. package/lang/translations/ja.po +0 -196
  177. package/lang/translations/jv.po +0 -196
  178. package/lang/translations/kk.po +0 -196
  179. package/lang/translations/km.po +0 -196
  180. package/lang/translations/kn.po +0 -196
  181. package/lang/translations/ko.po +0 -196
  182. package/lang/translations/ku.po +0 -196
  183. package/lang/translations/lt.po +0 -196
  184. package/lang/translations/lv.po +0 -196
  185. package/lang/translations/ms.po +0 -196
  186. package/lang/translations/nb.po +0 -196
  187. package/lang/translations/ne.po +0 -196
  188. package/lang/translations/nl.po +0 -196
  189. package/lang/translations/no.po +0 -196
  190. package/lang/translations/oc.po +0 -196
  191. package/lang/translations/pl.po +0 -196
  192. package/lang/translations/pt-br.po +0 -196
  193. package/lang/translations/pt.po +0 -196
  194. package/lang/translations/ro.po +0 -196
  195. package/lang/translations/ru.po +0 -196
  196. package/lang/translations/si.po +0 -196
  197. package/lang/translations/sk.po +0 -196
  198. package/lang/translations/sl.po +0 -196
  199. package/lang/translations/sq.po +0 -196
  200. package/lang/translations/sr-latn.po +0 -196
  201. package/lang/translations/sr.po +0 -196
  202. package/lang/translations/sv.po +0 -196
  203. package/lang/translations/th.po +0 -196
  204. package/lang/translations/ti.po +0 -196
  205. package/lang/translations/tk.po +0 -196
  206. package/lang/translations/tr.po +0 -196
  207. package/lang/translations/tt.po +0 -196
  208. package/lang/translations/ug.po +0 -196
  209. package/lang/translations/uk.po +0 -196
  210. package/lang/translations/ur.po +0 -196
  211. package/lang/translations/uz.po +0 -196
  212. package/lang/translations/vi.po +0 -196
  213. package/lang/translations/zh-cn.po +0 -196
  214. package/lang/translations/zh.po +0 -196
  215. package/src/augmentation.js +0 -5
  216. package/src/autoimage.js +0 -148
  217. package/src/image/converters.js +0 -236
  218. package/src/image/imageblockediting.js +0 -159
  219. package/src/image/imageediting.js +0 -69
  220. package/src/image/imageinlineediting.js +0 -178
  221. package/src/image/imageloadobserver.js +0 -52
  222. package/src/image/imageplaceholder.js +0 -119
  223. package/src/image/imagetypecommand.js +0 -84
  224. package/src/image/insertimagecommand.js +0 -125
  225. package/src/image/replaceimagesourcecommand.js +0 -75
  226. package/src/image/ui/utils.js +0 -46
  227. package/src/image/utils.js +0 -125
  228. package/src/image.js +0 -44
  229. package/src/imageblock.js +0 -44
  230. package/src/imagecaption/imagecaptionediting.js +0 -238
  231. package/src/imagecaption/imagecaptionui.js +0 -68
  232. package/src/imagecaption/imagecaptionutils.js +0 -68
  233. package/src/imagecaption/toggleimagecaptioncommand.js +0 -138
  234. package/src/imagecaption.js +0 -36
  235. package/src/imageconfig.js +0 -5
  236. package/src/imageinline.js +0 -44
  237. package/src/imageinsert/imageinsertui.js +0 -216
  238. package/src/imageinsert/imageinsertviaurlui.js +0 -175
  239. package/src/imageinsert/ui/imageinsertformview.js +0 -117
  240. package/src/imageinsert/ui/imageinserturlview.js +0 -102
  241. package/src/imageinsert.js +0 -43
  242. package/src/imageinsertviaurl.js +0 -41
  243. package/src/imageresize/imagecustomresizeui.js +0 -177
  244. package/src/imageresize/imageresizebuttons.js +0 -303
  245. package/src/imageresize/imageresizeediting.js +0 -206
  246. package/src/imageresize/imageresizehandles.js +0 -118
  247. package/src/imageresize/resizeimagecommand.js +0 -63
  248. package/src/imageresize/ui/imagecustomresizeformview.js +0 -264
  249. package/src/imageresize/utils/getselectedimageeditornodes.js +0 -25
  250. package/src/imageresize/utils/getselectedimagepossibleresizerange.js +0 -33
  251. package/src/imageresize/utils/getselectedimagewidthinunits.js +0 -42
  252. package/src/imageresize/utils/tryparsedimensionwithunit.js +0 -58
  253. package/src/imageresize.js +0 -38
  254. package/src/imagesizeattributes.js +0 -163
  255. package/src/imagestyle/converters.js +0 -118
  256. package/src/imagestyle/imagestylecommand.js +0 -117
  257. package/src/imagestyle/imagestyleediting.js +0 -127
  258. package/src/imagestyle/imagestyleui.js +0 -198
  259. package/src/imagestyle/utils.js +0 -333
  260. package/src/imagestyle.js +0 -42
  261. package/src/imagetextalternative/imagetextalternativecommand.js +0 -44
  262. package/src/imagetextalternative/imagetextalternativeediting.js +0 -41
  263. package/src/imagetextalternative/imagetextalternativeui.js +0 -183
  264. package/src/imagetextalternative/ui/textalternativeformview.js +0 -193
  265. package/src/imagetextalternative.js +0 -39
  266. package/src/imagetoolbar.js +0 -63
  267. package/src/imageupload/imageuploadediting.js +0 -482
  268. package/src/imageupload/imageuploadprogress.js +0 -222
  269. package/src/imageupload/imageuploadui.js +0 -135
  270. package/src/imageupload/uploadimagecommand.js +0 -110
  271. package/src/imageupload/utils.js +0 -114
  272. package/src/imageupload.js +0 -42
  273. package/src/imageutils.js +0 -309
  274. package/src/index.js +0 -67
  275. package/src/pictureediting.js +0 -136
  276. package/theme/image.css +0 -143
  277. package/theme/imagecaption.css +0 -53
  278. package/theme/imagecustomresizeform.css +0 -4
  279. package/theme/imageinsert.css +0 -14
  280. package/theme/imageplaceholder.css +0 -10
  281. package/theme/imageresize.css +0 -53
  282. package/theme/imagestyle.css +0 -120
  283. package/theme/imageuploadicon.css +0 -23
  284. package/theme/imageuploadloader.css +0 -18
  285. package/theme/imageuploadprogress.css +0 -19
  286. package/theme/textalternativeform.css +0 -4
  287. /package/{src → dist}/augmentation.d.ts +0 -0
  288. /package/{src → dist}/imageresize/utils/tryparsedimensionwithunit.d.ts +0 -0
  289. /package/{src → dist}/index.d.ts +0 -0
@@ -1,482 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
4
- */
5
- /**
6
- * @module image/imageupload/imageuploadediting
7
- */
8
- import { Plugin } from 'ckeditor5/src/core.js';
9
- import { ViewUpcastWriter } from 'ckeditor5/src/engine.js';
10
- import { Notification } from 'ckeditor5/src/ui.js';
11
- import { ClipboardPipeline } from 'ckeditor5/src/clipboard.js';
12
- import { FileRepository } from 'ckeditor5/src/upload.js';
13
- import { env } from 'ckeditor5/src/utils.js';
14
- import { ImageUtils } from '../imageutils.js';
15
- import { UploadImageCommand } from './uploadimagecommand.js';
16
- import { fetchLocalImage, isLocalImage } from '../../src/imageupload/utils.js';
17
- import { createImageTypeRegExp } from './utils.js';
18
- /**
19
- * The editing part of the image upload feature. It registers the `'uploadImage'` command
20
- * and the `imageUpload` command as an aliased name.
21
- *
22
- * When an image is uploaded, it fires the {@link ~ImageUploadEditing#event:uploadComplete `uploadComplete`} event
23
- * that allows adding custom attributes to the {@link module:engine/model/element~ModelElement image element}.
24
- */
25
- export class ImageUploadEditing extends Plugin {
26
- /**
27
- * @inheritDoc
28
- */
29
- static get requires() {
30
- return [FileRepository, Notification, ClipboardPipeline, ImageUtils];
31
- }
32
- static get pluginName() {
33
- return 'ImageUploadEditing';
34
- }
35
- /**
36
- * @inheritDoc
37
- */
38
- static get isOfficialPlugin() {
39
- return true;
40
- }
41
- /**
42
- * An internal mapping of {@link module:upload/filerepository~FileLoader#id file loader UIDs} and
43
- * model elements during the upload.
44
- *
45
- * Model element of the uploaded image can change, for instance, when {@link module:image/image/imagetypecommand~ImageTypeCommand}
46
- * is executed as a result of adding caption or changing image style. As a result, the upload logic must keep track of the model
47
- * element (reference) and resolve the upload for the correct model element (instead of the one that landed in the `$graveyard`
48
- * after image type changed).
49
- */
50
- _uploadImageElements;
51
- /**
52
- * An internal mapping of {@link module:upload/filerepository~FileLoader#id file loader UIDs} and
53
- * upload responses for handling images dragged during their upload process. When such images are later
54
- * dropped, their original upload IDs no longer exist in the registry (as the original upload completed).
55
- * This map preserves the upload responses to properly handle such cases.
56
- */
57
- _uploadedImages = new Map();
58
- /**
59
- * @inheritDoc
60
- */
61
- constructor(editor) {
62
- super(editor);
63
- editor.config.define('image', {
64
- upload: {
65
- types: ['jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff']
66
- }
67
- });
68
- this._uploadImageElements = new Map();
69
- }
70
- /**
71
- * @inheritDoc
72
- */
73
- init() {
74
- const editor = this.editor;
75
- const doc = editor.model.document;
76
- const conversion = editor.conversion;
77
- const fileRepository = editor.plugins.get(FileRepository);
78
- const imageUtils = editor.plugins.get('ImageUtils');
79
- const clipboardPipeline = editor.plugins.get('ClipboardPipeline');
80
- const imageTypes = createImageTypeRegExp(editor.config.get('image.upload.types'));
81
- const uploadImageCommand = new UploadImageCommand(editor);
82
- // Register `uploadImage` command and add `imageUpload` command as an alias for backward compatibility.
83
- editor.commands.add('uploadImage', uploadImageCommand);
84
- editor.commands.add('imageUpload', uploadImageCommand);
85
- // Register upcast converter for uploadId.
86
- conversion.for('upcast')
87
- .attributeToAttribute({
88
- view: {
89
- name: 'img',
90
- key: 'uploadId'
91
- },
92
- model: 'uploadId'
93
- })
94
- // Handle the case when the image is not fully uploaded yet but it's being moved.
95
- // See more: https://github.com/ckeditor/ckeditor5/pull/17327
96
- .add(dispatcher => dispatcher.on('element:img', (evt, data, conversionApi) => {
97
- if (!conversionApi.consumable.test(data.viewItem, { attributes: ['data-ck-upload-id'] })) {
98
- return;
99
- }
100
- const uploadId = data.viewItem.getAttribute('data-ck-upload-id');
101
- if (!uploadId) {
102
- return;
103
- }
104
- const [modelElement] = Array.from(data.modelRange.getItems({ shallow: true }));
105
- const loader = fileRepository.loaders.get(uploadId);
106
- if (modelElement) {
107
- // Handle case when `uploadId` is set on the image element but the loader is not present in the registry.
108
- // It may happen when the image was successfully uploaded and the loader was removed from the registry.
109
- // It's still present in the `_uploadedImages` map though. It's why we do not place this line in the condition below.
110
- conversionApi.writer.setAttribute('uploadId', uploadId, modelElement);
111
- conversionApi.consumable.consume(data.viewItem, { attributes: ['data-ck-upload-id'] });
112
- if (loader && loader.data) {
113
- conversionApi.writer.setAttribute('uploadStatus', loader.status, modelElement);
114
- }
115
- }
116
- }, { priority: 'low' }));
117
- // Handle pasted images.
118
- // For every image file, a new file loader is created and a placeholder image is
119
- // inserted into the content. Then, those images are uploaded once they appear in the model
120
- // (see Document#change listener below).
121
- this.listenTo(editor.editing.view.document, 'clipboardInput', (evt, data) => {
122
- // Skip if non empty HTML data is included.
123
- // https://github.com/ckeditor/ckeditor5-upload/issues/68
124
- if (isHtmlInDataTransfer(data.dataTransfer)) {
125
- return;
126
- }
127
- const images = Array.from(data.dataTransfer.files).filter(file => {
128
- // See https://github.com/ckeditor/ckeditor5-image/pull/254.
129
- if (!file) {
130
- return false;
131
- }
132
- return imageTypes.test(file.type);
133
- });
134
- if (!images.length) {
135
- return;
136
- }
137
- evt.stop();
138
- editor.model.change(writer => {
139
- // Set selection to paste target.
140
- if (data.targetRanges) {
141
- writer.setSelection(data.targetRanges.map(viewRange => editor.editing.mapper.toModelRange(viewRange)));
142
- }
143
- editor.execute('uploadImage', { file: images });
144
- });
145
- const uploadImageCommand = editor.commands.get('uploadImage');
146
- if (!uploadImageCommand.isAccessAllowed) {
147
- const notification = editor.plugins.get('Notification');
148
- const t = editor.locale.t;
149
- notification.showWarning(t('You have no image upload permissions.'), {
150
- namespace: 'image'
151
- });
152
- }
153
- });
154
- // Handle HTML pasted with images with base64 or blob sources.
155
- // For every image file, a new file loader is created and a placeholder image is
156
- // inserted into the content. Then, those images are uploaded once they appear in the model
157
- // (see Document#change listener below).
158
- this.listenTo(clipboardPipeline, 'inputTransformation', (evt, data) => {
159
- const fetchableImages = Array.from(editor.editing.view.createRangeIn(data.content))
160
- .map(value => value.item)
161
- .filter(viewElement => isLocalImage(imageUtils, viewElement) &&
162
- !viewElement.getAttribute('uploadProcessed'))
163
- .map(viewElement => { return { promise: fetchLocalImage(viewElement), imageElement: viewElement }; });
164
- if (!fetchableImages.length) {
165
- return;
166
- }
167
- const writer = new ViewUpcastWriter(editor.editing.view.document);
168
- for (const fetchableImage of fetchableImages) {
169
- // Set attribute marking that the image was processed already.
170
- writer.setAttribute('uploadProcessed', true, fetchableImage.imageElement);
171
- const loader = fileRepository.createLoader(fetchableImage.promise);
172
- if (loader) {
173
- writer.setAttribute('src', '', fetchableImage.imageElement);
174
- writer.setAttribute('uploadId', loader.id, fetchableImage.imageElement);
175
- }
176
- }
177
- });
178
- // Prevents from the browser redirecting to the dropped image.
179
- editor.editing.view.document.on('dragover', (evt, data) => {
180
- data.preventDefault();
181
- });
182
- // Upload placeholder images that appeared in the model.
183
- doc.on('change', () => {
184
- // Note: Reversing changes to start with insertions and only then handle removals. If it was the other way around,
185
- // loaders for **all** images that land in the $graveyard would abort while in fact only those that were **not** replaced
186
- // by other images should be aborted.
187
- const changes = doc.differ.getChanges({ includeChangesInGraveyard: true }).reverse();
188
- const insertedImagesIds = new Set();
189
- for (const entry of changes) {
190
- if (entry.type == 'insert' && entry.name != '$text') {
191
- const item = entry.position.nodeAfter;
192
- const isInsertedInGraveyard = entry.position.root.rootName == '$graveyard';
193
- for (const imageElement of getImagesFromChangeItem(editor, item)) {
194
- // Check if the image element still has upload id.
195
- const uploadId = imageElement.getAttribute('uploadId');
196
- const uploadStatus = imageElement.getAttribute('uploadStatus');
197
- // Check if status is complete to ensure that there were no additional reconversions
198
- // in the same batch that set the status to 'complete'. It may happen when list item
199
- // performed refresh of its children (including images) because `uploadStatus` attribute
200
- // has been changed.
201
- if (!uploadId || uploadStatus == 'complete') {
202
- continue;
203
- }
204
- // Check if the image is loaded on this client.
205
- const loader = fileRepository.loaders.get(uploadId);
206
- if (!loader) {
207
- // If the loader does not exist, it means that the image was already uploaded
208
- // and the loader promise was removed from the registry. In that scenario we need
209
- // to restore response object from the internal map.
210
- if (!isInsertedInGraveyard && this._uploadedImages.has(uploadId)) {
211
- // Fire `uploadComplete` to set proper attributes on the image element.
212
- editor.model.enqueueChange({ isUndoable: false }, writer => {
213
- writer.setAttribute('uploadStatus', 'complete', imageElement);
214
- this.fire('uploadComplete', {
215
- data: this._uploadedImages.get(uploadId),
216
- imageElement: imageElement
217
- });
218
- });
219
- // While it makes sense to remove the image from the `_uploadedImages` map here,
220
- // it's counterintuitive for the user that pastes image in uploading several times.
221
- // It'll work the first time, but the next time the image will be empty because the
222
- // `_uploadedImages` no longer contain the response.
223
- }
224
- continue;
225
- }
226
- if (isInsertedInGraveyard) {
227
- // If the image was inserted to the graveyard for good (**not** replaced by another image),
228
- // only then abort the loading process.
229
- if (!insertedImagesIds.has(uploadId)) {
230
- // ... but abort it only if all remain images that share the same loader are in the graveyard too.
231
- // This is to prevent situation when we have two images in uploading state and one of them is being
232
- // placed in the graveyard (e.g. using undo). The other one should not be aborted.
233
- const allImagesThatShareUploaderInGraveyard = Array
234
- .from(this._uploadImageElements.get(uploadId))
235
- .every(element => element.root.rootName == '$graveyard');
236
- if (allImagesThatShareUploaderInGraveyard) {
237
- loader.abort();
238
- }
239
- }
240
- }
241
- else {
242
- // Remember the upload id of the inserted image. If it acted as a replacement for another
243
- // image (which landed in the $graveyard), the related loader will not be aborted because
244
- // this is still the same image upload.
245
- insertedImagesIds.add(uploadId);
246
- // Keep the mapping between the upload ID and the image model element so the upload
247
- // can later resolve in the context of the correct model element. The model element could
248
- // change for the same upload if one image was replaced by another (e.g. image type was changed),
249
- // so this may also replace an existing mapping.
250
- if (!this._uploadImageElements.has(uploadId)) {
251
- this._uploadImageElements.set(uploadId, new Set([imageElement]));
252
- }
253
- else {
254
- this._uploadImageElements.get(uploadId).add(imageElement);
255
- }
256
- if (loader.status == 'idle') {
257
- // If the image was inserted into content and has not been loaded yet, start loading it.
258
- this._readAndUpload(loader);
259
- }
260
- }
261
- }
262
- }
263
- }
264
- });
265
- // Set the default handler for feeding the image element with `src` and `srcset` attributes.
266
- // Also set the natural `width` and `height` attributes (if not already set).
267
- this.on('uploadComplete', (evt, { imageElement, data }) => {
268
- const urls = data.urls ? data.urls : data;
269
- this.editor.model.change(writer => {
270
- writer.setAttribute('src', urls.default, imageElement);
271
- this._parseAndSetSrcsetAttributeOnImage(urls, imageElement, writer);
272
- imageUtils.setImageNaturalSizeAttributes(imageElement);
273
- });
274
- }, { priority: 'low' });
275
- }
276
- /**
277
- * @inheritDoc
278
- */
279
- afterInit() {
280
- const schema = this.editor.model.schema;
281
- // Setup schema to allow uploadId and uploadStatus for images.
282
- // Wait for ImageBlockEditing or ImageInlineEditing to register their elements first,
283
- // that's why doing this in afterInit() instead of init().
284
- if (this.editor.plugins.has('ImageBlockEditing')) {
285
- schema.extend('imageBlock', {
286
- allowAttributes: ['uploadId', 'uploadStatus']
287
- });
288
- this._registerConverters('imageBlock');
289
- }
290
- if (this.editor.plugins.has('ImageInlineEditing')) {
291
- schema.extend('imageInline', {
292
- allowAttributes: ['uploadId', 'uploadStatus']
293
- });
294
- this._registerConverters('imageInline');
295
- }
296
- }
297
- /**
298
- * Reads and uploads an image.
299
- *
300
- * The image is read from the disk and as a Base64-encoded string it is set temporarily to
301
- * `image[src]`. When the image is successfully uploaded, the temporary data is replaced with the target
302
- * image's URL (the URL to the uploaded image on the server).
303
- */
304
- _readAndUpload(loader) {
305
- const editor = this.editor;
306
- const model = editor.model;
307
- const t = editor.locale.t;
308
- const fileRepository = editor.plugins.get(FileRepository);
309
- const notification = editor.plugins.get(Notification);
310
- const imageUtils = editor.plugins.get('ImageUtils');
311
- const imageUploadElements = this._uploadImageElements;
312
- model.enqueueChange({ isUndoable: false }, writer => {
313
- const elements = imageUploadElements.get(loader.id);
314
- for (const element of elements) {
315
- writer.setAttribute('uploadStatus', 'reading', element);
316
- }
317
- });
318
- return loader.read()
319
- .then(() => {
320
- const promise = loader.upload();
321
- if (editor.ui) {
322
- editor.ui.ariaLiveAnnouncer.announce(t('Uploading image'));
323
- }
324
- for (const imageElement of imageUploadElements.get(loader.id)) {
325
- // Force re–paint in Safari. Without it, the image will display with a wrong size.
326
- // https://github.com/ckeditor/ckeditor5/issues/1975
327
- /* istanbul ignore next -- @preserve */
328
- if (env.isSafari) {
329
- const viewFigure = editor.editing.mapper.toViewElement(imageElement);
330
- const viewImg = imageUtils.findViewImgElement(viewFigure);
331
- editor.editing.view.once('render', () => {
332
- // Early returns just to be safe. There might be some code ran
333
- // in between the outer scope and this callback.
334
- if (!viewImg.parent) {
335
- return;
336
- }
337
- const domFigure = editor.editing.view.domConverter.mapViewToDom(viewImg.parent);
338
- if (!domFigure) {
339
- return;
340
- }
341
- const originalDisplay = domFigure.style.display;
342
- domFigure.style.display = 'none';
343
- // Make sure this line will never be removed during minification for having "no effect".
344
- domFigure._ckHack = domFigure.offsetHeight;
345
- domFigure.style.display = originalDisplay;
346
- });
347
- }
348
- model.enqueueChange({ isUndoable: false }, writer => {
349
- writer.setAttribute('uploadStatus', 'uploading', imageElement);
350
- });
351
- }
352
- return promise;
353
- })
354
- .then(data => {
355
- model.enqueueChange({ isUndoable: false }, writer => {
356
- for (const imageElement of imageUploadElements.get(loader.id)) {
357
- writer.setAttribute('uploadStatus', 'complete', imageElement);
358
- this.fire('uploadComplete', { data, imageElement });
359
- }
360
- if (editor.ui) {
361
- editor.ui.ariaLiveAnnouncer.announce(t('Image upload complete'));
362
- }
363
- this._uploadedImages.set(loader.id, data);
364
- });
365
- clean();
366
- })
367
- .catch(error => {
368
- if (editor.ui) {
369
- editor.ui.ariaLiveAnnouncer.announce(t('Error during image upload'));
370
- }
371
- // If status is not 'error' nor 'aborted' - throw error because it means that something else went wrong,
372
- // it might be generic error and it would be real pain to find what is going on.
373
- if (loader.status !== 'error' && loader.status !== 'aborted') {
374
- throw error;
375
- }
376
- // Might be 'aborted'.
377
- if (loader.status == 'error' && error) {
378
- notification.showWarning(error, {
379
- title: t('Upload failed'),
380
- namespace: 'upload'
381
- });
382
- }
383
- // Permanently remove image from insertion batch.
384
- model.enqueueChange({ isUndoable: false }, writer => {
385
- for (const imageElement of imageUploadElements.get(loader.id)) {
386
- // Handle situation when the image has been removed and then `abort` exception was thrown.
387
- // See: https://github.com/ckeditor/ckeditor5-commercial/issues/6817
388
- if (imageElement.root.rootName !== '$graveyard') {
389
- writer.remove(imageElement);
390
- }
391
- }
392
- });
393
- clean();
394
- });
395
- function clean() {
396
- model.enqueueChange({ isUndoable: false }, writer => {
397
- for (const imageElement of imageUploadElements.get(loader.id)) {
398
- writer.removeAttribute('uploadId', imageElement);
399
- writer.removeAttribute('uploadStatus', imageElement);
400
- }
401
- imageUploadElements.delete(loader.id);
402
- });
403
- fileRepository.destroyLoader(loader);
404
- }
405
- }
406
- /**
407
- * Creates the `srcset` attribute based on a given file upload response and sets it as an attribute to a specific image element.
408
- *
409
- * @param data Data object from which `srcset` will be created.
410
- * @param image The image element on which the `srcset` attribute will be set.
411
- */
412
- _parseAndSetSrcsetAttributeOnImage(data, image, writer) {
413
- // Srcset attribute for responsive images support.
414
- let maxWidth = 0;
415
- const srcsetAttribute = Object.keys(data)
416
- // Filter out keys that are not integers.
417
- .filter(key => {
418
- const width = parseInt(key, 10);
419
- if (!isNaN(width)) {
420
- maxWidth = Math.max(maxWidth, width);
421
- return true;
422
- }
423
- })
424
- // Convert each key to srcset entry.
425
- .map(key => `${data[key]} ${key}w`)
426
- // Join all entries.
427
- .join(', ');
428
- if (srcsetAttribute != '') {
429
- const attributes = {
430
- srcset: srcsetAttribute
431
- };
432
- if (!image.hasAttribute('width') && !image.hasAttribute('height')) {
433
- attributes.width = maxWidth;
434
- }
435
- writer.setAttributes(attributes, image);
436
- }
437
- }
438
- /**
439
- * Registers image upload converters.
440
- *
441
- * @param imageType The type of the image.
442
- */
443
- _registerConverters(imageType) {
444
- const { conversion, plugins } = this.editor;
445
- const fileRepository = plugins.get(FileRepository);
446
- const imageUtils = plugins.get(ImageUtils);
447
- // It sets `data-ck-upload-id` attribute on the view image elements that are not fully uploaded.
448
- // It avoids the situation when image disappears when it's being moved and upload is not finished yet.
449
- // See more: https://github.com/ckeditor/ckeditor5/issues/16967
450
- conversion.for('dataDowncast').add(dispatcher => {
451
- dispatcher.on(`attribute:uploadId:${imageType}`, (evt, data, conversionApi) => {
452
- if (!conversionApi.consumable.test(data.item, evt.name)) {
453
- return;
454
- }
455
- const loader = fileRepository.loaders.get(data.attributeNewValue);
456
- if (!loader || !loader.data) {
457
- return null;
458
- }
459
- const viewElement = conversionApi.mapper.toViewElement(data.item);
460
- const img = imageUtils.findViewImgElement(viewElement);
461
- if (img) {
462
- conversionApi.consumable.consume(data.item, evt.name);
463
- conversionApi.writer.setAttribute('data-ck-upload-id', loader.id, img);
464
- }
465
- });
466
- });
467
- }
468
- }
469
- /**
470
- * TODO move this to the clipboard package.
471
- *
472
- * Returns `true` if non-empty `text/html` is included in the data transfer.
473
- */
474
- export function isHtmlInDataTransfer(dataTransfer) {
475
- return Array.from(dataTransfer.types).includes('text/html') && dataTransfer.getData('text/html') !== '';
476
- }
477
- function getImagesFromChangeItem(editor, item) {
478
- const imageUtils = editor.plugins.get('ImageUtils');
479
- return Array.from(editor.model.createRangeOn(item))
480
- .filter(value => imageUtils.isImage(value.item))
481
- .map(value => value.item);
482
- }