@ckeditor/ckeditor5-ckbox 40.0.0 → 40.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. package/LICENSE.md +6 -2
  2. package/build/ckbox.js +2 -2
  3. package/build/translations/ar.js +1 -1
  4. package/build/translations/az.js +1 -1
  5. package/build/translations/bg.js +1 -1
  6. package/build/translations/bn.js +1 -1
  7. package/build/translations/ca.js +1 -1
  8. package/build/translations/cs.js +1 -1
  9. package/build/translations/da.js +1 -1
  10. package/build/translations/de.js +1 -1
  11. package/build/translations/el.js +1 -1
  12. package/build/translations/en-au.js +1 -1
  13. package/build/translations/es-co.js +1 -1
  14. package/build/translations/es.js +1 -1
  15. package/build/translations/et.js +1 -1
  16. package/build/translations/fa.js +1 -1
  17. package/build/translations/fi.js +1 -1
  18. package/build/translations/fr.js +1 -1
  19. package/build/translations/gl.js +1 -1
  20. package/build/translations/he.js +1 -1
  21. package/build/translations/hi.js +1 -1
  22. package/build/translations/hr.js +1 -1
  23. package/build/translations/hu.js +1 -1
  24. package/build/translations/id.js +1 -1
  25. package/build/translations/it.js +1 -1
  26. package/build/translations/ja.js +1 -1
  27. package/build/translations/ko.js +1 -1
  28. package/build/translations/lt.js +1 -1
  29. package/build/translations/lv.js +1 -1
  30. package/build/translations/ms.js +1 -1
  31. package/build/translations/nl.js +1 -1
  32. package/build/translations/no.js +1 -1
  33. package/build/translations/pl.js +1 -1
  34. package/build/translations/pt-br.js +1 -1
  35. package/build/translations/pt.js +1 -1
  36. package/build/translations/ro.js +1 -1
  37. package/build/translations/ru.js +1 -1
  38. package/build/translations/sk.js +1 -1
  39. package/build/translations/sq.js +1 -1
  40. package/build/translations/sr-latn.js +1 -1
  41. package/build/translations/sr.js +1 -1
  42. package/build/translations/sv.js +1 -1
  43. package/build/translations/th.js +1 -1
  44. package/build/translations/tr.js +1 -1
  45. package/build/translations/ug.js +1 -1
  46. package/build/translations/uk.js +1 -1
  47. package/build/translations/ur.js +1 -1
  48. package/build/translations/uz.js +1 -1
  49. package/build/translations/vi.js +1 -1
  50. package/build/translations/zh-cn.js +1 -1
  51. package/build/translations/zh.js +1 -1
  52. package/ckeditor5-metadata.json +17 -0
  53. package/lang/contexts.json +6 -2
  54. package/lang/translations/ar.po +18 -2
  55. package/lang/translations/az.po +18 -2
  56. package/lang/translations/bg.po +18 -2
  57. package/lang/translations/bn.po +18 -2
  58. package/lang/translations/ca.po +18 -2
  59. package/lang/translations/cs.po +18 -2
  60. package/lang/translations/da.po +18 -2
  61. package/lang/translations/de.po +18 -2
  62. package/lang/translations/el.po +18 -2
  63. package/lang/translations/en-au.po +18 -2
  64. package/lang/translations/en.po +18 -2
  65. package/lang/translations/es-co.po +18 -2
  66. package/lang/translations/es.po +18 -2
  67. package/lang/translations/et.po +18 -2
  68. package/lang/translations/fa.po +18 -2
  69. package/lang/translations/fi.po +18 -2
  70. package/lang/translations/fr.po +18 -2
  71. package/lang/translations/gl.po +18 -2
  72. package/lang/translations/he.po +18 -2
  73. package/lang/translations/hi.po +18 -2
  74. package/lang/translations/hr.po +18 -2
  75. package/lang/translations/hu.po +18 -2
  76. package/lang/translations/id.po +18 -2
  77. package/lang/translations/it.po +18 -2
  78. package/lang/translations/ja.po +18 -2
  79. package/lang/translations/ko.po +18 -2
  80. package/lang/translations/lt.po +18 -2
  81. package/lang/translations/lv.po +18 -2
  82. package/lang/translations/ms.po +18 -2
  83. package/lang/translations/nl.po +18 -2
  84. package/lang/translations/no.po +18 -2
  85. package/lang/translations/pl.po +18 -2
  86. package/lang/translations/pt-br.po +18 -2
  87. package/lang/translations/pt.po +18 -2
  88. package/lang/translations/ro.po +18 -2
  89. package/lang/translations/ru.po +18 -2
  90. package/lang/translations/sk.po +18 -2
  91. package/lang/translations/sq.po +18 -2
  92. package/lang/translations/sr-latn.po +18 -2
  93. package/lang/translations/sr.po +18 -2
  94. package/lang/translations/sv.po +18 -2
  95. package/lang/translations/th.po +18 -2
  96. package/lang/translations/tr.po +18 -2
  97. package/lang/translations/ug.po +18 -2
  98. package/lang/translations/uk.po +18 -2
  99. package/lang/translations/ur.po +18 -2
  100. package/lang/translations/uz.po +18 -2
  101. package/lang/translations/vi.po +18 -2
  102. package/lang/translations/zh-cn.po +18 -2
  103. package/lang/translations/zh.po +18 -2
  104. package/package.json +4 -2
  105. package/src/augmentation.d.ts +32 -22
  106. package/src/augmentation.js +5 -5
  107. package/src/ckbox.d.ts +33 -33
  108. package/src/ckbox.js +37 -37
  109. package/src/ckboxcommand.d.ts +114 -110
  110. package/src/ckboxcommand.js +332 -302
  111. package/src/ckboxconfig.d.ts +325 -283
  112. package/src/ckboxconfig.js +5 -5
  113. package/src/ckboxediting.d.ts +45 -52
  114. package/src/ckboxediting.js +321 -362
  115. package/src/ckboximageedit/ckboximageeditcommand.d.ts +97 -0
  116. package/src/ckboximageedit/ckboximageeditcommand.js +298 -0
  117. package/src/ckboximageedit/ckboximageeditediting.d.ts +28 -0
  118. package/src/ckboximageedit/ckboximageeditediting.js +36 -0
  119. package/src/ckboximageedit/ckboximageeditui.d.ts +24 -0
  120. package/src/ckboximageedit/ckboximageeditui.js +48 -0
  121. package/src/ckboximageedit/utils.d.ts +10 -0
  122. package/src/ckboximageedit/utils.js +48 -0
  123. package/src/ckboximageedit.d.ts +24 -0
  124. package/src/ckboximageedit.js +28 -0
  125. package/src/ckboxui.d.ts +21 -21
  126. package/src/ckboxui.js +74 -47
  127. package/src/ckboxuploadadapter.d.ts +33 -38
  128. package/src/ckboxuploadadapter.js +130 -275
  129. package/src/ckboxutils.d.ts +50 -0
  130. package/src/ckboxutils.js +183 -0
  131. package/src/index.d.ts +17 -13
  132. package/src/index.js +14 -11
  133. package/src/utils.d.ts +63 -28
  134. package/src/utils.js +175 -49
  135. package/theme/ckboximageedit.css +53 -0
  136. package/theme/icons/ckbox-image-edit.svg +1 -0
  137. package/build/ckbox.js.map +0 -1
@@ -0,0 +1,50 @@
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 ckbox/ckboxutils
7
+ */
8
+ import type { InitializedToken } from '@ckeditor/ckeditor5-cloud-services';
9
+ import { Plugin } from 'ckeditor5/src/core';
10
+ /**
11
+ * The CKBox utilities plugin.
12
+ */
13
+ export default class CKBoxUtils extends Plugin {
14
+ /**
15
+ * CKEditor Cloud Services access token.
16
+ */
17
+ private _token;
18
+ /**
19
+ * @inheritDoc
20
+ */
21
+ static get pluginName(): "CKBoxUtils";
22
+ /**
23
+ * @inheritDoc
24
+ */
25
+ static get requires(): readonly ["CloudServices"];
26
+ /**
27
+ * @inheritDoc
28
+ */
29
+ init(): Promise<void>;
30
+ /**
31
+ * Returns a token used by the CKBox plugin for communication with the CKBox service.
32
+ */
33
+ getToken(): InitializedToken;
34
+ /**
35
+ * The ID of workspace to use when uploading an image.
36
+ */
37
+ getWorkspaceId(): string;
38
+ /**
39
+ * Resolves a promise with an object containing a category with which the uploaded file is associated or an error code.
40
+ */
41
+ getCategoryIdForFile(fileOrUrl: File | string, options: {
42
+ signal: AbortSignal;
43
+ }): Promise<string>;
44
+ /**
45
+ * Resolves a promise with an array containing available categories with which the uploaded file can be associated.
46
+ *
47
+ * If the API returns limited results, the method will collect all items.
48
+ */
49
+ private _getAvailableCategories;
50
+ }
@@ -0,0 +1,183 @@
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
+ import { CKEditorError, logError } from 'ckeditor5/src/utils';
6
+ import { Plugin } from 'ckeditor5/src/core';
7
+ import { convertMimeTypeToExtension, getContentTypeOfUrl, getFileExtension, getWorkspaceId, sendHttpRequest } from './utils';
8
+ const DEFAULT_CKBOX_THEME_NAME = 'lark';
9
+ /**
10
+ * The CKBox utilities plugin.
11
+ */
12
+ export default class CKBoxUtils extends Plugin {
13
+ /**
14
+ * @inheritDoc
15
+ */
16
+ static get pluginName() {
17
+ return 'CKBoxUtils';
18
+ }
19
+ /**
20
+ * @inheritDoc
21
+ */
22
+ static get requires() {
23
+ return ['CloudServices'];
24
+ }
25
+ /**
26
+ * @inheritDoc
27
+ */
28
+ async init() {
29
+ const editor = this.editor;
30
+ const hasConfiguration = !!editor.config.get('ckbox');
31
+ const isLibraryLoaded = !!window.CKBox;
32
+ // Proceed with plugin initialization only when the integrator intentionally wants to use it, i.e. when the `config.ckbox` exists or
33
+ // the CKBox JavaScript library is loaded.
34
+ if (!hasConfiguration && !isLibraryLoaded) {
35
+ return;
36
+ }
37
+ editor.config.define('ckbox', {
38
+ serviceOrigin: 'https://api.ckbox.io',
39
+ defaultUploadCategories: null,
40
+ ignoreDataId: false,
41
+ language: editor.locale.uiLanguage,
42
+ theme: DEFAULT_CKBOX_THEME_NAME,
43
+ tokenUrl: editor.config.get('cloudServices.tokenUrl')
44
+ });
45
+ const cloudServices = editor.plugins.get('CloudServices');
46
+ const cloudServicesTokenUrl = editor.config.get('cloudServices.tokenUrl');
47
+ const ckboxTokenUrl = editor.config.get('ckbox.tokenUrl');
48
+ if (!ckboxTokenUrl) {
49
+ /**
50
+ * The {@link module:ckbox/ckboxconfig~CKBoxConfig#tokenUrl `config.ckbox.tokenUrl`} or the
51
+ * {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl `config.cloudServices.tokenUrl`}
52
+ * configuration is required for the CKBox plugin.
53
+ *
54
+ * ```ts
55
+ * ClassicEditor.create( document.createElement( 'div' ), {
56
+ * ckbox: {
57
+ * tokenUrl: "YOUR_TOKEN_URL"
58
+ * // ...
59
+ * }
60
+ * // ...
61
+ * } );
62
+ * ```
63
+ *
64
+ * @error ckbox-plugin-missing-token-url
65
+ */
66
+ throw new CKEditorError('ckbox-plugin-missing-token-url', this);
67
+ }
68
+ if (ckboxTokenUrl == cloudServicesTokenUrl) {
69
+ this._token = cloudServices.token;
70
+ }
71
+ else {
72
+ this._token = await cloudServices.registerTokenUrl(ckboxTokenUrl);
73
+ }
74
+ }
75
+ /**
76
+ * Returns a token used by the CKBox plugin for communication with the CKBox service.
77
+ */
78
+ getToken() {
79
+ return this._token;
80
+ }
81
+ /**
82
+ * The ID of workspace to use when uploading an image.
83
+ */
84
+ getWorkspaceId() {
85
+ const t = this.editor.t;
86
+ const cannotAccessDefaultWorkspaceError = t('Cannot access default workspace.');
87
+ const defaultWorkspaceId = this.editor.config.get('ckbox.defaultUploadWorkspaceId');
88
+ const workspaceId = getWorkspaceId(this._token, defaultWorkspaceId);
89
+ if (workspaceId == null) {
90
+ /**
91
+ * The user is not authorized to access the workspace defined in the`ckbox.defaultUploadWorkspaceId` configuration.
92
+ *
93
+ * @error ckbox-access-default-workspace-error
94
+ */
95
+ logError('ckbox-access-default-workspace-error');
96
+ throw cannotAccessDefaultWorkspaceError;
97
+ }
98
+ return workspaceId;
99
+ }
100
+ /**
101
+ * Resolves a promise with an object containing a category with which the uploaded file is associated or an error code.
102
+ */
103
+ async getCategoryIdForFile(fileOrUrl, options) {
104
+ const t = this.editor.t;
105
+ const cannotFindCategoryError = t('Cannot determine a category for the uploaded file.');
106
+ const defaultCategories = this.editor.config.get('ckbox.defaultUploadCategories');
107
+ const allCategoriesPromise = this._getAvailableCategories(options);
108
+ const extension = typeof fileOrUrl == 'string' ?
109
+ convertMimeTypeToExtension(await getContentTypeOfUrl(fileOrUrl, options)) :
110
+ getFileExtension(fileOrUrl);
111
+ const allCategories = await allCategoriesPromise;
112
+ // Couldn't fetch all categories. Perhaps the authorization token is invalid.
113
+ if (!allCategories) {
114
+ throw cannotFindCategoryError;
115
+ }
116
+ // If a user specifies the plugin configuration, find the first category that accepts the uploaded file.
117
+ if (defaultCategories) {
118
+ const userCategory = Object.keys(defaultCategories).find(category => {
119
+ return defaultCategories[category].find(e => e.toLowerCase() == extension);
120
+ });
121
+ // If found, return its ID if the category exists on the server side.
122
+ if (userCategory) {
123
+ const serverCategory = allCategories.find(category => category.id === userCategory || category.name === userCategory);
124
+ if (!serverCategory) {
125
+ throw cannotFindCategoryError;
126
+ }
127
+ return serverCategory.id;
128
+ }
129
+ }
130
+ // Otherwise, find the first category that accepts the uploaded file and returns its ID.
131
+ const category = allCategories.find(category => category.extensions.find(e => e.toLowerCase() == extension));
132
+ if (!category) {
133
+ throw cannotFindCategoryError;
134
+ }
135
+ return category.id;
136
+ }
137
+ /**
138
+ * Resolves a promise with an array containing available categories with which the uploaded file can be associated.
139
+ *
140
+ * If the API returns limited results, the method will collect all items.
141
+ */
142
+ async _getAvailableCategories(options) {
143
+ const ITEMS_PER_REQUEST = 50;
144
+ const editor = this.editor;
145
+ const token = this._token;
146
+ const { signal } = options;
147
+ const serviceOrigin = editor.config.get('ckbox.serviceOrigin');
148
+ const workspaceId = this.getWorkspaceId();
149
+ try {
150
+ const result = [];
151
+ let offset = 0;
152
+ let remainingItems;
153
+ do {
154
+ const data = await fetchCategories(offset);
155
+ result.push(...data.items);
156
+ remainingItems = data.totalCount - (offset + ITEMS_PER_REQUEST);
157
+ offset += ITEMS_PER_REQUEST;
158
+ } while (remainingItems > 0);
159
+ return result;
160
+ }
161
+ catch {
162
+ signal.throwIfAborted();
163
+ /**
164
+ * Fetching a list of available categories with which an uploaded file can be associated failed.
165
+ *
166
+ * @error ckbox-fetch-category-http-error
167
+ */
168
+ logError('ckbox-fetch-category-http-error');
169
+ return undefined;
170
+ }
171
+ function fetchCategories(offset) {
172
+ const categoryUrl = new URL('categories', serviceOrigin);
173
+ categoryUrl.searchParams.set('limit', ITEMS_PER_REQUEST.toString());
174
+ categoryUrl.searchParams.set('offset', offset.toString());
175
+ categoryUrl.searchParams.set('workspaceId', workspaceId);
176
+ return sendHttpRequest({
177
+ url: categoryUrl,
178
+ signal,
179
+ authorization: token.value
180
+ });
181
+ }
182
+ }
183
+ }
package/src/index.d.ts CHANGED
@@ -1,13 +1,17 @@
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 ckbox
7
- */
8
- export { default as CKBox } from './ckbox';
9
- export { default as CKBoxEditing } from './ckboxediting';
10
- export { default as CKBoxUI } from './ckboxui';
11
- export type { default as CKBoxCommand } from './ckboxcommand';
12
- export type { CKBoxConfig } from './ckboxconfig';
13
- import './augmentation';
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 ckbox
7
+ */
8
+ export { default as CKBox } from './ckbox';
9
+ export { default as CKBoxEditing } from './ckboxediting';
10
+ export { default as CKBoxUI } from './ckboxui';
11
+ export { default as CKBoxImageEditEditing } from './ckboximageedit/ckboximageeditediting';
12
+ export { default as CKBoxImageEditUI } from './ckboximageedit/ckboximageeditui';
13
+ export { default as CKBoxImageEdit } from './ckboximageedit';
14
+ export type { default as CKBoxCommand } from './ckboxcommand';
15
+ export type { default as CKBoxImageEditCommand } from './ckboximageedit/ckboximageeditcommand';
16
+ export type { CKBoxConfig } from './ckboxconfig';
17
+ import './augmentation';
package/src/index.js CHANGED
@@ -1,11 +1,14 @@
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 ckbox
7
- */
8
- export { default as CKBox } from './ckbox';
9
- export { default as CKBoxEditing } from './ckboxediting';
10
- export { default as CKBoxUI } from './ckboxui';
11
- import './augmentation';
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 ckbox
7
+ */
8
+ export { default as CKBox } from './ckbox';
9
+ export { default as CKBoxEditing } from './ckboxediting';
10
+ export { default as CKBoxUI } from './ckboxui';
11
+ export { default as CKBoxImageEditEditing } from './ckboximageedit/ckboximageeditediting';
12
+ export { default as CKBoxImageEditUI } from './ckboximageedit/ckboximageeditui';
13
+ export { default as CKBoxImageEdit } from './ckboximageedit';
14
+ import './augmentation';
package/src/utils.d.ts CHANGED
@@ -1,28 +1,63 @@
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 ckbox/utils
7
- */
8
- import type { InitializedToken } from '@ckeditor/ckeditor5-cloud-services';
9
- import type { CKBoxImageUrls } from './ckboxconfig';
10
- /**
11
- * Converts image source set provided by the CKBox into an object containing:
12
- * - responsive URLs for the "webp" image format,
13
- * - one fallback URL for browsers that do not support the "webp" format.
14
- */
15
- export declare function getImageUrls(imageUrls: CKBoxImageUrls): {
16
- imageFallbackUrl: string;
17
- imageSources: Array<{
18
- srcset: string;
19
- sizes: string;
20
- type: string;
21
- }>;
22
- };
23
- /**
24
- * Returns a workspace id to use for communication with the CKBox service.
25
- *
26
- * @param defaultWorkspaceId The default workspace to use taken from editor config.
27
- */
28
- export declare function getWorkspaceId(token: InitializedToken, defaultWorkspaceId?: string): string | null;
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 ckbox/utils
7
+ */
8
+ import type { InitializedToken } from '@ckeditor/ckeditor5-cloud-services';
9
+ import type { CKBoxImageUrls } from './ckboxconfig';
10
+ /**
11
+ * Converts image source set provided by the CKBox into an object containing:
12
+ * - responsive URLs for the "webp" image format,
13
+ * - one fallback URL for browsers that do not support the "webp" format.
14
+ */
15
+ export declare function getImageUrls(imageUrls: CKBoxImageUrls): {
16
+ imageFallbackUrl: string;
17
+ imageSources: Array<{
18
+ srcset: string;
19
+ sizes: string;
20
+ type: string;
21
+ }>;
22
+ };
23
+ /**
24
+ * Returns a workspace id to use for communication with the CKBox service.
25
+ *
26
+ * @param defaultWorkspaceId The default workspace to use taken from editor config.
27
+ */
28
+ export declare function getWorkspaceId(token: InitializedToken, defaultWorkspaceId?: string): string | null;
29
+ /**
30
+ * Generates an image data URL from its `blurhash` representation.
31
+ */
32
+ export declare function blurHashToDataUrl(hash?: string): string | undefined;
33
+ /**
34
+ * Sends the HTTP request.
35
+ *
36
+ * @internal
37
+ * @param config.url the URL where the request will be sent.
38
+ * @param config.method The HTTP method.
39
+ * @param config.data Additional data to send.
40
+ * @param config.onUploadProgress A callback informing about the upload progress.
41
+ */
42
+ export declare function sendHttpRequest({ url, method, data, onUploadProgress, signal, authorization }: {
43
+ url: URL;
44
+ signal: AbortSignal;
45
+ authorization: string;
46
+ method?: 'GET' | 'POST';
47
+ data?: FormData | null;
48
+ onUploadProgress?: (evt: ProgressEvent) => void;
49
+ }): Promise<any>;
50
+ /**
51
+ * Returns an extension a typical file in the specified `mimeType` format would have.
52
+ */
53
+ export declare function convertMimeTypeToExtension(mimeType: string): string;
54
+ /**
55
+ * Tries to fetch the given `url` and returns 'content-type' of the response.
56
+ */
57
+ export declare function getContentTypeOfUrl(url: string, options: {
58
+ signal: AbortSignal;
59
+ }): Promise<string>;
60
+ /**
61
+ * Returns an extension from the given value.
62
+ */
63
+ export declare function getFileExtension(file: File): string;
package/src/utils.js CHANGED
@@ -1,49 +1,175 @@
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
- * Converts image source set provided by the CKBox into an object containing:
7
- * - responsive URLs for the "webp" image format,
8
- * - one fallback URL for browsers that do not support the "webp" format.
9
- */
10
- export function getImageUrls(imageUrls) {
11
- const responsiveUrls = [];
12
- let maxWidth = 0;
13
- for (const key in imageUrls) {
14
- const width = parseInt(key, 10);
15
- if (!isNaN(width)) {
16
- if (width > maxWidth) {
17
- maxWidth = width;
18
- }
19
- responsiveUrls.push(`${imageUrls[key]} ${key}w`);
20
- }
21
- }
22
- const imageSources = [{
23
- srcset: responsiveUrls.join(','),
24
- sizes: `(max-width: ${maxWidth}px) 100vw, ${maxWidth}px`,
25
- type: 'image/webp'
26
- }];
27
- return {
28
- imageFallbackUrl: imageUrls.default,
29
- imageSources
30
- };
31
- }
32
- /**
33
- * Returns a workspace id to use for communication with the CKBox service.
34
- *
35
- * @param defaultWorkspaceId The default workspace to use taken from editor config.
36
- */
37
- export function getWorkspaceId(token, defaultWorkspaceId) {
38
- const [, binaryTokenPayload] = token.value.split('.');
39
- const payload = JSON.parse(atob(binaryTokenPayload));
40
- const workspaces = (payload.auth && payload.auth.ckbox && payload.auth.ckbox.workspaces) || [payload.aud];
41
- if (!defaultWorkspaceId) {
42
- return workspaces[0];
43
- }
44
- const role = payload.auth && payload.auth.ckbox && payload.auth.ckbox.role;
45
- if (role == 'superadmin' || workspaces.includes(defaultWorkspaceId)) {
46
- return defaultWorkspaceId;
47
- }
48
- return null;
49
- }
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
+ import { decode } from 'blurhash';
6
+ /**
7
+ * Converts image source set provided by the CKBox into an object containing:
8
+ * - responsive URLs for the "webp" image format,
9
+ * - one fallback URL for browsers that do not support the "webp" format.
10
+ */
11
+ export function getImageUrls(imageUrls) {
12
+ const responsiveUrls = [];
13
+ let maxWidth = 0;
14
+ for (const key in imageUrls) {
15
+ const width = parseInt(key, 10);
16
+ if (!isNaN(width)) {
17
+ if (width > maxWidth) {
18
+ maxWidth = width;
19
+ }
20
+ responsiveUrls.push(`${imageUrls[key]} ${key}w`);
21
+ }
22
+ }
23
+ const imageSources = [{
24
+ srcset: responsiveUrls.join(','),
25
+ sizes: `(max-width: ${maxWidth}px) 100vw, ${maxWidth}px`,
26
+ type: 'image/webp'
27
+ }];
28
+ return {
29
+ imageFallbackUrl: imageUrls.default,
30
+ imageSources
31
+ };
32
+ }
33
+ /**
34
+ * Returns a workspace id to use for communication with the CKBox service.
35
+ *
36
+ * @param defaultWorkspaceId The default workspace to use taken from editor config.
37
+ */
38
+ export function getWorkspaceId(token, defaultWorkspaceId) {
39
+ const [, binaryTokenPayload] = token.value.split('.');
40
+ const payload = JSON.parse(atob(binaryTokenPayload));
41
+ const workspaces = (payload.auth && payload.auth.ckbox && payload.auth.ckbox.workspaces) || [payload.aud];
42
+ if (!defaultWorkspaceId) {
43
+ return workspaces[0];
44
+ }
45
+ const role = payload.auth && payload.auth.ckbox && payload.auth.ckbox.role;
46
+ if (role == 'superadmin' || workspaces.includes(defaultWorkspaceId)) {
47
+ return defaultWorkspaceId;
48
+ }
49
+ return null;
50
+ }
51
+ /**
52
+ * Default resolution for decoding blurhash values.
53
+ * Relatively small values must be used in order to ensure acceptable performance.
54
+ */
55
+ const BLUR_RESOLUTION = 32;
56
+ /**
57
+ * Generates an image data URL from its `blurhash` representation.
58
+ */
59
+ export function blurHashToDataUrl(hash) {
60
+ if (!hash) {
61
+ return;
62
+ }
63
+ try {
64
+ const resolutionInPx = `${BLUR_RESOLUTION}px`;
65
+ const canvas = document.createElement('canvas');
66
+ canvas.setAttribute('width', resolutionInPx);
67
+ canvas.setAttribute('height', resolutionInPx);
68
+ const ctx = canvas.getContext('2d');
69
+ /* istanbul ignore next -- @preserve */
70
+ if (!ctx) {
71
+ return;
72
+ }
73
+ const imageData = ctx.createImageData(BLUR_RESOLUTION, BLUR_RESOLUTION);
74
+ const decoded = decode(hash, BLUR_RESOLUTION, BLUR_RESOLUTION);
75
+ imageData.data.set(decoded);
76
+ ctx.putImageData(imageData, 0, 0);
77
+ return canvas.toDataURL();
78
+ }
79
+ catch (e) {
80
+ return undefined;
81
+ }
82
+ }
83
+ /**
84
+ * Sends the HTTP request.
85
+ *
86
+ * @internal
87
+ * @param config.url the URL where the request will be sent.
88
+ * @param config.method The HTTP method.
89
+ * @param config.data Additional data to send.
90
+ * @param config.onUploadProgress A callback informing about the upload progress.
91
+ */
92
+ export function sendHttpRequest({ url, method = 'GET', data, onUploadProgress, signal, authorization }) {
93
+ const xhr = new XMLHttpRequest();
94
+ xhr.open(method, url.toString());
95
+ xhr.setRequestHeader('Authorization', authorization);
96
+ xhr.setRequestHeader('CKBox-Version', 'CKEditor 5');
97
+ xhr.responseType = 'json';
98
+ // The callback is attached to the `signal#abort` event.
99
+ const abortCallback = () => {
100
+ xhr.abort();
101
+ };
102
+ return new Promise((resolve, reject) => {
103
+ signal.throwIfAborted();
104
+ signal.addEventListener('abort', abortCallback);
105
+ xhr.addEventListener('loadstart', () => {
106
+ signal.addEventListener('abort', abortCallback);
107
+ });
108
+ xhr.addEventListener('loadend', () => {
109
+ signal.removeEventListener('abort', abortCallback);
110
+ });
111
+ xhr.addEventListener('error', () => {
112
+ reject();
113
+ });
114
+ xhr.addEventListener('abort', () => {
115
+ reject();
116
+ });
117
+ xhr.addEventListener('load', () => {
118
+ const response = xhr.response;
119
+ if (!response || response.statusCode >= 400) {
120
+ return reject(response && response.message);
121
+ }
122
+ resolve(response);
123
+ });
124
+ /* istanbul ignore else -- @preserve */
125
+ if (onUploadProgress) {
126
+ xhr.upload.addEventListener('progress', evt => {
127
+ onUploadProgress(evt);
128
+ });
129
+ }
130
+ // Send the request.
131
+ xhr.send(data);
132
+ });
133
+ }
134
+ const MIME_TO_EXTENSION = {
135
+ 'image/gif': 'gif',
136
+ 'image/jpeg': 'jpg',
137
+ 'image/png': 'png',
138
+ 'image/webp': 'webp',
139
+ 'image/bmp': 'bmp',
140
+ 'image/tiff': 'tiff'
141
+ };
142
+ /**
143
+ * Returns an extension a typical file in the specified `mimeType` format would have.
144
+ */
145
+ export function convertMimeTypeToExtension(mimeType) {
146
+ return MIME_TO_EXTENSION[mimeType];
147
+ }
148
+ /**
149
+ * Tries to fetch the given `url` and returns 'content-type' of the response.
150
+ */
151
+ export async function getContentTypeOfUrl(url, options) {
152
+ try {
153
+ const response = await fetch(url, {
154
+ method: 'HEAD',
155
+ cache: 'force-cache',
156
+ ...options
157
+ });
158
+ if (!response.ok) {
159
+ return '';
160
+ }
161
+ return response.headers.get('content-type') || '';
162
+ }
163
+ catch {
164
+ return '';
165
+ }
166
+ }
167
+ /**
168
+ * Returns an extension from the given value.
169
+ */
170
+ export function getFileExtension(file) {
171
+ const fileName = file.name;
172
+ const extensionRegExp = /\.(?<ext>[^.]+)$/;
173
+ const match = fileName.match(extensionRegExp);
174
+ return match.groups.ext.toLowerCase();
175
+ }
@@ -0,0 +1,53 @@
1
+ /*
2
+ * 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
+ :root {
7
+ /* Based on default CKBox theme colors */
8
+ --ck-image-processing-highlight-color: hsl(220, 10%, 98%);
9
+ --ck-image-processing-background-color: hsl(220, 10%, 90%);
10
+ }
11
+
12
+ .ck.ck-editor__editable {
13
+ & .image {
14
+ &.image-processing {
15
+ position: relative;
16
+
17
+ &:before {
18
+ content: '';
19
+
20
+ position: absolute;
21
+ top: 0;
22
+ left: 0;
23
+ z-index: 1;
24
+
25
+ height: 100%;
26
+ width: 100%;
27
+
28
+ background: linear-gradient(
29
+ 90deg,
30
+ var(--ck-image-processing-background-color),
31
+ var(--ck-image-processing-highlight-color),
32
+ var(--ck-image-processing-background-color)
33
+ );
34
+ background-size: 200% 100%;
35
+
36
+ animation: ck-image-processing-animation 2s linear infinite;
37
+ }
38
+
39
+ & img {
40
+ height: 100%;
41
+ }
42
+ }
43
+ }
44
+ }
45
+
46
+ @keyframes ck-image-processing-animation {
47
+ 0% {
48
+ background-position: 200% 0;
49
+ }
50
+ 100% {
51
+ background-position: -200% 0;
52
+ }
53
+ }
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M1.201 1C.538 1 0 1.47 0 2.1v12.86C0 15.603.534 16 1.186 16H6.45l3.647-3.596-3.48-3.254a.694.694 0 0 0-.958-.033L1.5 13.6V2.5h15v4.59a3.477 3.477 0 0 1 1.5.15V2.1c0-.63-.547-1.1-1.2-1.1H1.202Zm11.723 2.805a2.137 2.137 0 0 0-2.045 2.406 2.13 2.13 0 0 0 4.172.277 2.134 2.134 0 0 0-.76-2.244 2.13 2.13 0 0 0-1.367-.44Z"/><path d="M8.1 17.612V20h2.39l7.046-7.046-2.39-2.39L8.1 17.612Zm11.283-6.506a.638.638 0 0 0 .139-.692.603.603 0 0 0-.139-.206L17.892 8.72a.63.63 0 0 0-.898 0l-1.167 1.163 2.391 2.39 1.165-1.167Z"/></svg>