@ckeditor/ckeditor5-ckbox 0.0.0-nightly-20240605.0 → 0.0.0-nightly-20240605.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5,76 +5,123 @@
5
5
  import { Plugin, icons, Command, PendingActions } from '@ckeditor/ckeditor5-core/dist/index.js';
6
6
  import { ButtonView, MenuBarMenuListItemButtonView, Notification } from '@ckeditor/ckeditor5-ui/dist/index.js';
7
7
  import { Range } from '@ckeditor/ckeditor5-engine/dist/index.js';
8
- import { createElement, toMap, CKEditorError, logError, global, retry, abortableDebounce } from '@ckeditor/ckeditor5-utils/dist/index.js';
8
+ import { createElement, toMap, CKEditorError, logError, global, delay, abortableDebounce, retry } from '@ckeditor/ckeditor5-utils/dist/index.js';
9
9
  import { decode } from 'blurhash';
10
10
  import { FileRepository } from '@ckeditor/ckeditor5-upload/dist/index.js';
11
11
  import { isEqual } from 'lodash-es';
12
12
 
13
- class CKBoxUI extends Plugin {
13
+ /**
14
+ * Introduces UI components for the `CKBox` plugin.
15
+ *
16
+ * The plugin introduces two UI components to the {@link module:ui/componentfactory~ComponentFactory UI component factory}:
17
+ *
18
+ * * the `'ckbox'` toolbar button,
19
+ * * the `'menuBar:ckbox'` menu bar component, which is by default added to the `'Insert'` menu.
20
+ *
21
+ * It also integrates with the `insertImage` toolbar component and `menuBar:insertImage` menu component.
22
+ */ class CKBoxUI extends Plugin {
14
23
  /**
15
- * @inheritDoc
16
- */ static get pluginName() {
24
+ * @inheritDoc
25
+ */ static get pluginName() {
17
26
  return 'CKBoxUI';
18
27
  }
19
28
  /**
20
- * @inheritDoc
21
- */ afterInit() {
29
+ * @inheritDoc
30
+ */ afterInit() {
22
31
  const editor = this.editor;
23
32
  // Do not register the `ckbox` button if the command does not exist.
24
33
  // This might happen when CKBox library is not loaded on the page.
25
34
  if (!editor.commands.get('ckbox')) {
26
35
  return;
27
36
  }
28
- const t = editor.t;
29
- const componentFactory = editor.ui.componentFactory;
30
- componentFactory.add('ckbox', ()=>{
31
- const button = this._createButton(ButtonView);
32
- button.tooltip = true;
33
- return button;
34
- });
35
- componentFactory.add('menuBar:ckbox', ()=>this._createButton(MenuBarMenuListItemButtonView));
37
+ editor.ui.componentFactory.add('ckbox', ()=>this._createFileToolbarButton());
38
+ editor.ui.componentFactory.add('menuBar:ckbox', ()=>this._createFileMenuBarButton());
36
39
  if (editor.plugins.has('ImageInsertUI')) {
37
- const imageInsertUI = editor.plugins.get('ImageInsertUI');
38
- imageInsertUI.registerIntegration({
40
+ editor.plugins.get('ImageInsertUI').registerIntegration({
39
41
  name: 'assetManager',
40
42
  observable: ()=>editor.commands.get('ckbox'),
41
- buttonViewCreator: ()=>{
42
- const button = this.editor.ui.componentFactory.create('ckbox');
43
- button.icon = icons.imageAssetManager;
44
- button.bind('label').to(imageInsertUI, 'isImageSelected', (isImageSelected)=>isImageSelected ? t('Replace image with file manager') : t('Insert image with file manager'));
45
- return button;
46
- },
47
- formViewCreator: ()=>{
48
- const button = this.editor.ui.componentFactory.create('ckbox');
49
- button.icon = icons.imageAssetManager;
50
- button.withText = true;
51
- button.bind('label').to(imageInsertUI, 'isImageSelected', (isImageSelected)=>isImageSelected ? t('Replace with file manager') : t('Insert with file manager'));
52
- button.on('execute', ()=>{
53
- imageInsertUI.dropdownView.isOpen = false;
54
- });
55
- return button;
56
- }
43
+ buttonViewCreator: ()=>this._createImageToolbarButton(),
44
+ formViewCreator: ()=>this._createImageDropdownButton(),
45
+ menuBarButtonViewCreator: (isOnly)=>this._createImageMenuBarButton(isOnly ? 'insertOnly' : 'insertNested')
57
46
  });
58
47
  }
59
48
  }
60
49
  /**
61
- * Creates a button for CKBox command to use either in toolbar or in menu bar.
62
- */ _createButton(ButtonClass) {
50
+ * Creates the base for various kinds of the button component provided by this feature.
51
+ */ _createButton(ButtonClass) {
63
52
  const editor = this.editor;
64
53
  const locale = editor.locale;
65
54
  const view = new ButtonClass(locale);
66
55
  const command = editor.commands.get('ckbox');
67
- const t = locale.t;
68
- view.set({
69
- label: t('Open file manager'),
70
- icon: icons.browseFiles
71
- });
56
+ locale.t;
72
57
  view.bind('isOn', 'isEnabled').to(command, 'value', 'isEnabled');
73
58
  view.on('execute', ()=>{
74
59
  editor.execute('ckbox');
75
60
  });
76
61
  return view;
77
62
  }
63
+ /**
64
+ * Creates a simple toolbar button for files management, with an icon and a tooltip.
65
+ */ _createFileToolbarButton() {
66
+ const t = this.editor.locale.t;
67
+ const button = this._createButton(ButtonView);
68
+ button.icon = icons.browseFiles;
69
+ button.label = t('Open file manager');
70
+ button.tooltip = true;
71
+ return button;
72
+ }
73
+ /**
74
+ * Creates a simple toolbar button for images management, with an icon and a tooltip.
75
+ */ _createImageToolbarButton() {
76
+ const t = this.editor.locale.t;
77
+ const imageInsertUI = this.editor.plugins.get('ImageInsertUI');
78
+ const button = this._createButton(ButtonView);
79
+ button.icon = icons.imageAssetManager;
80
+ button.bind('label').to(imageInsertUI, 'isImageSelected', (isImageSelected)=>isImageSelected ? t('Replace image with file manager') : t('Insert image with file manager'));
81
+ button.tooltip = true;
82
+ return button;
83
+ }
84
+ /**
85
+ * Creates a button for images management for the dropdown view, with an icon, text and no tooltip.
86
+ */ _createImageDropdownButton() {
87
+ const t = this.editor.locale.t;
88
+ const imageInsertUI = this.editor.plugins.get('ImageInsertUI');
89
+ const button = this._createButton(ButtonView);
90
+ button.icon = icons.imageAssetManager;
91
+ button.withText = true;
92
+ button.bind('label').to(imageInsertUI, 'isImageSelected', (isImageSelected)=>isImageSelected ? t('Replace with file manager') : t('Insert with file manager'));
93
+ button.on('execute', ()=>{
94
+ imageInsertUI.dropdownView.isOpen = false;
95
+ });
96
+ return button;
97
+ }
98
+ /**
99
+ * Creates a button for files management for the menu bar.
100
+ */ _createFileMenuBarButton() {
101
+ const t = this.editor.locale.t;
102
+ const button = this._createButton(MenuBarMenuListItemButtonView);
103
+ button.icon = icons.browseFiles;
104
+ button.withText = true;
105
+ button.label = t('File');
106
+ return button;
107
+ }
108
+ /**
109
+ * Creates a button for images management for the menu bar.
110
+ */ _createImageMenuBarButton(type) {
111
+ const t = this.editor.locale.t;
112
+ const button = this._createButton(MenuBarMenuListItemButtonView);
113
+ button.icon = icons.imageAssetManager;
114
+ button.withText = true;
115
+ switch(type){
116
+ case 'insertOnly':
117
+ button.label = t('Image');
118
+ break;
119
+ case 'insertNested':
120
+ button.label = t('With file manager');
121
+ break;
122
+ }
123
+ return button;
124
+ }
78
125
  }
79
126
 
80
127
  /**
@@ -235,7 +282,7 @@ const MIME_TO_EXTENSION = {
235
282
  * Returns an extension from the given value.
236
283
  */ function getFileExtension(file) {
237
284
  const fileName = file.name;
238
- const extensionRegExp = RegExp("\\.(?<ext>[^.]+)$");
285
+ const extensionRegExp = /\.(?<ext>[^.]+)$/;
239
286
  const match = fileName.match(extensionRegExp);
240
287
  return match.groups.ext.toLowerCase();
241
288
  }
@@ -244,29 +291,68 @@ const MIME_TO_EXTENSION = {
244
291
  // `CKBoxCommand#_chosenAssets` and it is removed from there automatically after this time. See `CKBoxCommand#_chosenAssets` for more
245
292
  // details.
246
293
  const ASSET_INSERTION_WAIT_TIMEOUT = 1000;
247
- class CKBoxCommand extends Command {
294
+ /**
295
+ * The CKBox command. It is used by the {@link module:ckbox/ckboxediting~CKBoxEditing CKBox editing feature} to open the CKBox file manager.
296
+ * The file manager allows inserting an image or a link to a file into the editor content.
297
+ *
298
+ * ```ts
299
+ * editor.execute( 'ckbox' );
300
+ * ```
301
+ *
302
+ * **Note:** This command uses other features to perform the following tasks:
303
+ * - To insert images it uses the {@link module:image/image/insertimagecommand~InsertImageCommand 'insertImage'} command from the
304
+ * {@link module:image/image~Image Image feature}.
305
+ * - To insert links to other files it uses the {@link module:link/linkcommand~LinkCommand 'link'} command from the
306
+ * {@link module:link/link~Link Link feature}.
307
+ */ class CKBoxCommand extends Command {
308
+ /**
309
+ * A set of all chosen assets. They are stored temporarily and they are automatically removed 1 second after being chosen.
310
+ * Chosen assets have to be "remembered" for a while to be able to map the given asset with the element inserted into the model.
311
+ * This association map is then used to set the ID on the model element.
312
+ *
313
+ * All chosen assets are automatically removed after the timeout, because (theoretically) it may happen that they will never be
314
+ * inserted into the model, even if the {@link module:link/linkcommand~LinkCommand `'link'`} command or the
315
+ * {@link module:image/image/insertimagecommand~InsertImageCommand `'insertImage'`} command is enabled. Such a case may arise when
316
+ * another plugin blocks the command execution. Then, in order not to keep the chosen (but not inserted) assets forever, we delete
317
+ * them automatically to prevent memory leakage. The 1 second timeout is enough to insert the asset into the model and extract the
318
+ * ID from the chosen asset.
319
+ *
320
+ * The assets are stored only if
321
+ * the {@link module:ckbox/ckboxconfig~CKBoxConfig#ignoreDataId `config.ckbox.ignoreDataId`} option is set to `false` (by default).
322
+ *
323
+ * @internal
324
+ */ _chosenAssets = new Set();
325
+ /**
326
+ * The DOM element that acts as a mounting point for the CKBox dialog.
327
+ */ _wrapper = null;
248
328
  /**
249
- * @inheritDoc
250
- */ refresh() {
329
+ * @inheritDoc
330
+ */ constructor(editor){
331
+ super(editor);
332
+ this._initListeners();
333
+ }
334
+ /**
335
+ * @inheritDoc
336
+ */ refresh() {
251
337
  this.value = this._getValue();
252
338
  this.isEnabled = this._checkEnabled();
253
339
  }
254
340
  /**
255
- * @inheritDoc
256
- */ execute() {
341
+ * @inheritDoc
342
+ */ execute() {
257
343
  this.fire('ckbox:open');
258
344
  }
259
345
  /**
260
- * Indicates if the CKBox dialog is already opened.
261
- *
262
- * @protected
263
- * @returns {Boolean}
264
- */ _getValue() {
346
+ * Indicates if the CKBox dialog is already opened.
347
+ *
348
+ * @protected
349
+ * @returns {Boolean}
350
+ */ _getValue() {
265
351
  return this._wrapper !== null;
266
352
  }
267
353
  /**
268
- * Checks whether the command can be enabled in the current context.
269
- */ _checkEnabled() {
354
+ * Checks whether the command can be enabled in the current context.
355
+ */ _checkEnabled() {
270
356
  const imageCommand = this.editor.commands.get('insertImage');
271
357
  const linkCommand = this.editor.commands.get('link');
272
358
  if (!imageCommand.isEnabled && !linkCommand.isEnabled) {
@@ -275,17 +361,17 @@ class CKBoxCommand extends Command {
275
361
  return true;
276
362
  }
277
363
  /**
278
- * Creates the options object for the CKBox dialog.
279
- *
280
- * @returns The object with properties:
281
- * - theme The theme for CKBox dialog.
282
- * - language The language for CKBox dialog.
283
- * - tokenUrl The token endpoint URL.
284
- * - serviceOrigin The base URL of the API service.
285
- * - forceDemoLabel Whether to force "Powered by CKBox" link.
286
- * - dialog.onClose The callback function invoked after closing the CKBox dialog.
287
- * - assets.onChoose The callback function invoked after choosing the assets.
288
- */ _prepareOptions() {
364
+ * Creates the options object for the CKBox dialog.
365
+ *
366
+ * @returns The object with properties:
367
+ * - theme The theme for CKBox dialog.
368
+ * - language The language for CKBox dialog.
369
+ * - tokenUrl The token endpoint URL.
370
+ * - serviceOrigin The base URL of the API service.
371
+ * - forceDemoLabel Whether to force "Powered by CKBox" link.
372
+ * - dialog.onClose The callback function invoked after closing the CKBox dialog.
373
+ * - assets.onChoose The callback function invoked after choosing the assets.
374
+ */ _prepareOptions() {
289
375
  const editor = this.editor;
290
376
  const ckboxConfig = editor.config.get('ckbox');
291
377
  return {
@@ -303,8 +389,8 @@ class CKBoxCommand extends Command {
303
389
  };
304
390
  }
305
391
  /**
306
- * Initializes various event listeners for the `ckbox:*` events, because all functionality of the `ckbox` command is event-based.
307
- */ _initListeners() {
392
+ * Initializes various event listeners for the `ckbox:*` events, because all functionality of the `ckbox` command is event-based.
393
+ */ _initListeners() {
308
394
  const editor = this.editor;
309
395
  const model = editor.model;
310
396
  const shouldInsertDataId = !editor.config.get('ckbox.ignoreDataId');
@@ -373,13 +459,13 @@ class CKBoxCommand extends Command {
373
459
  });
374
460
  }
375
461
  /**
376
- * Inserts the asset into the model.
377
- *
378
- * @param asset The asset to be inserted.
379
- * @param isLastAsset Indicates if the current asset is the last one from the chosen set.
380
- * @param writer An instance of the model writer.
381
- * @param isSingleAsset It's true when only one asset is processed.
382
- */ _insertAsset(asset, isLastAsset, writer, isSingleAsset) {
462
+ * Inserts the asset into the model.
463
+ *
464
+ * @param asset The asset to be inserted.
465
+ * @param isLastAsset Indicates if the current asset is the last one from the chosen set.
466
+ * @param writer An instance of the model writer.
467
+ * @param isSingleAsset It's true when only one asset is processed.
468
+ */ _insertAsset(asset, isLastAsset, writer, isSingleAsset) {
383
469
  const editor = this.editor;
384
470
  const model = editor.model;
385
471
  const selection = model.document.selection;
@@ -397,10 +483,10 @@ class CKBoxCommand extends Command {
397
483
  }
398
484
  }
399
485
  /**
400
- * Inserts the image by calling the `insertImage` command.
401
- *
402
- * @param asset The asset to be inserted.
403
- */ _insertImage(asset) {
486
+ * Inserts the image by calling the `insertImage` command.
487
+ *
488
+ * @param asset The asset to be inserted.
489
+ */ _insertImage(asset) {
404
490
  const editor = this.editor;
405
491
  const { imageFallbackUrl, imageSources, imageTextAlternative, imageWidth, imageHeight, imagePlaceholder } = asset.attributes;
406
492
  editor.execute('insertImage', {
@@ -417,12 +503,12 @@ class CKBoxCommand extends Command {
417
503
  });
418
504
  }
419
505
  /**
420
- * Inserts the link to the asset by calling the `link` command.
421
- *
422
- * @param asset The asset to be inserted.
423
- * @param writer An instance of the model writer.
424
- * @param isSingleAsset It's true when only one asset is processed.
425
- */ _insertLink(asset, writer, isSingleAsset) {
506
+ * Inserts the link to the asset by calling the `link` command.
507
+ *
508
+ * @param asset The asset to be inserted.
509
+ * @param writer An instance of the model writer.
510
+ * @param isSingleAsset It's true when only one asset is processed.
511
+ */ _insertLink(asset, writer, isSingleAsset) {
426
512
  const editor = this.editor;
427
513
  const model = editor.model;
428
514
  const selection = model.document.selection;
@@ -450,32 +536,6 @@ class CKBoxCommand extends Command {
450
536
  }
451
537
  editor.execute('link', linkHref);
452
538
  }
453
- /**
454
- * @inheritDoc
455
- */ constructor(editor){
456
- super(editor);
457
- /**
458
- * A set of all chosen assets. They are stored temporarily and they are automatically removed 1 second after being chosen.
459
- * Chosen assets have to be "remembered" for a while to be able to map the given asset with the element inserted into the model.
460
- * This association map is then used to set the ID on the model element.
461
- *
462
- * All chosen assets are automatically removed after the timeout, because (theoretically) it may happen that they will never be
463
- * inserted into the model, even if the {@link module:link/linkcommand~LinkCommand `'link'`} command or the
464
- * {@link module:image/image/insertimagecommand~InsertImageCommand `'insertImage'`} command is enabled. Such a case may arise when
465
- * another plugin blocks the command execution. Then, in order not to keep the chosen (but not inserted) assets forever, we delete
466
- * them automatically to prevent memory leakage. The 1 second timeout is enough to insert the asset into the model and extract the
467
- * ID from the chosen asset.
468
- *
469
- * The assets are stored only if
470
- * the {@link module:ckbox/ckboxconfig~CKBoxConfig#ignoreDataId `config.ckbox.ignoreDataId`} option is set to `false` (by default).
471
- *
472
- * @internal
473
- */ this._chosenAssets = new Set();
474
- /**
475
- * The DOM element that acts as a mounting point for the CKBox dialog.
476
- */ this._wrapper = null;
477
- this._initListeners();
478
- }
479
539
  }
480
540
  /**
481
541
  * Parses the chosen assets into the internal data format. Filters out chosen assets that are not allowed.
@@ -539,22 +599,27 @@ class CKBoxCommand extends Command {
539
599
  }
540
600
 
541
601
  const DEFAULT_CKBOX_THEME_NAME = 'lark';
542
- class CKBoxUtils extends Plugin {
602
+ /**
603
+ * The CKBox utilities plugin.
604
+ */ class CKBoxUtils extends Plugin {
543
605
  /**
544
- * @inheritDoc
545
- */ static get pluginName() {
606
+ * CKEditor Cloud Services access token.
607
+ */ _token;
608
+ /**
609
+ * @inheritDoc
610
+ */ static get pluginName() {
546
611
  return 'CKBoxUtils';
547
612
  }
548
613
  /**
549
- * @inheritDoc
550
- */ static get requires() {
614
+ * @inheritDoc
615
+ */ static get requires() {
551
616
  return [
552
617
  'CloudServices'
553
618
  ];
554
619
  }
555
620
  /**
556
- * @inheritDoc
557
- */ async init() {
621
+ * @inheritDoc
622
+ */ async init() {
558
623
  const editor = this.editor;
559
624
  const hasConfiguration = !!editor.config.get('ckbox');
560
625
  const isLibraryLoaded = !!window.CKBox;
@@ -576,22 +641,22 @@ class CKBoxUtils extends Plugin {
576
641
  const ckboxTokenUrl = editor.config.get('ckbox.tokenUrl');
577
642
  if (!ckboxTokenUrl) {
578
643
  /**
579
- * The {@link module:ckbox/ckboxconfig~CKBoxConfig#tokenUrl `config.ckbox.tokenUrl`} or the
580
- * {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl `config.cloudServices.tokenUrl`}
581
- * configuration is required for the CKBox plugin.
582
- *
583
- * ```ts
584
- * ClassicEditor.create( document.createElement( 'div' ), {
585
- * ckbox: {
586
- * tokenUrl: "YOUR_TOKEN_URL"
587
- * // ...
588
- * }
589
- * // ...
590
- * } );
591
- * ```
592
- *
593
- * @error ckbox-plugin-missing-token-url
594
- */ throw new CKEditorError('ckbox-plugin-missing-token-url', this);
644
+ * The {@link module:ckbox/ckboxconfig~CKBoxConfig#tokenUrl `config.ckbox.tokenUrl`} or the
645
+ * {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl `config.cloudServices.tokenUrl`}
646
+ * configuration is required for the CKBox plugin.
647
+ *
648
+ * ```ts
649
+ * ClassicEditor.create( document.createElement( 'div' ), {
650
+ * ckbox: {
651
+ * tokenUrl: "YOUR_TOKEN_URL"
652
+ * // ...
653
+ * }
654
+ * // ...
655
+ * } );
656
+ * ```
657
+ *
658
+ * @error ckbox-plugin-missing-token-url
659
+ */ throw new CKEditorError('ckbox-plugin-missing-token-url', this);
595
660
  }
596
661
  if (ckboxTokenUrl == cloudServicesTokenUrl) {
597
662
  this._token = cloudServices.token;
@@ -600,30 +665,30 @@ class CKBoxUtils extends Plugin {
600
665
  }
601
666
  }
602
667
  /**
603
- * Returns a token used by the CKBox plugin for communication with the CKBox service.
604
- */ getToken() {
668
+ * Returns a token used by the CKBox plugin for communication with the CKBox service.
669
+ */ getToken() {
605
670
  return this._token;
606
671
  }
607
672
  /**
608
- * The ID of workspace to use when uploading an image.
609
- */ getWorkspaceId() {
673
+ * The ID of workspace to use when uploading an image.
674
+ */ getWorkspaceId() {
610
675
  const t = this.editor.t;
611
676
  const cannotAccessDefaultWorkspaceError = t('Cannot access default workspace.');
612
677
  const defaultWorkspaceId = this.editor.config.get('ckbox.defaultUploadWorkspaceId');
613
678
  const workspaceId = getWorkspaceId(this._token, defaultWorkspaceId);
614
679
  if (workspaceId == null) {
615
680
  /**
616
- * The user is not authorized to access the workspace defined in the`ckbox.defaultUploadWorkspaceId` configuration.
617
- *
618
- * @error ckbox-access-default-workspace-error
619
- */ logError('ckbox-access-default-workspace-error');
681
+ * The user is not authorized to access the workspace defined in the`ckbox.defaultUploadWorkspaceId` configuration.
682
+ *
683
+ * @error ckbox-access-default-workspace-error
684
+ */ logError('ckbox-access-default-workspace-error');
620
685
  throw cannotAccessDefaultWorkspaceError;
621
686
  }
622
687
  return workspaceId;
623
688
  }
624
689
  /**
625
- * Resolves a promise with an object containing a category with which the uploaded file is associated or an error code.
626
- */ async getCategoryIdForFile(fileOrUrl, options) {
690
+ * Resolves a promise with an object containing a category with which the uploaded file is associated or an error code.
691
+ */ async getCategoryIdForFile(fileOrUrl, options) {
627
692
  const t = this.editor.t;
628
693
  const cannotFindCategoryError = t('Cannot determine a category for the uploaded file.');
629
694
  const defaultCategories = this.editor.config.get('ckbox.defaultUploadCategories');
@@ -656,10 +721,10 @@ class CKBoxUtils extends Plugin {
656
721
  return category.id;
657
722
  }
658
723
  /**
659
- * Resolves a promise with an array containing available categories with which the uploaded file can be associated.
660
- *
661
- * If the API returns limited results, the method will collect all items.
662
- */ async _getAvailableCategories(options) {
724
+ * Resolves a promise with an array containing available categories with which the uploaded file can be associated.
725
+ *
726
+ * If the API returns limited results, the method will collect all items.
727
+ */ async _getAvailableCategories(options) {
663
728
  const ITEMS_PER_REQUEST = 50;
664
729
  const editor = this.editor;
665
730
  const token = this._token;
@@ -680,10 +745,10 @@ class CKBoxUtils extends Plugin {
680
745
  } catch {
681
746
  signal.throwIfAborted();
682
747
  /**
683
- * Fetching a list of available categories with which an uploaded file can be associated failed.
684
- *
685
- * @error ckbox-fetch-category-http-error
686
- */ logError('ckbox-fetch-category-http-error');
748
+ * Fetching a list of available categories with which an uploaded file can be associated failed.
749
+ *
750
+ * @error ckbox-fetch-category-http-error
751
+ */ logError('ckbox-fetch-category-http-error');
687
752
  return undefined;
688
753
  }
689
754
  function fetchCategories(offset) {
@@ -700,10 +765,18 @@ class CKBoxUtils extends Plugin {
700
765
  }
701
766
  }
702
767
 
703
- class CKBoxUploadAdapter extends Plugin {
768
+ /**
769
+ * A plugin that enables file uploads in CKEditor 5 using the CKBox server–side connector.
770
+ * See the {@glink features/file-management/ckbox CKBox file manager integration} guide to learn how to configure
771
+ * and use this feature as well as find out more about the full integration with the file manager
772
+ * provided by the {@link module:ckbox/ckbox~CKBox} plugin.
773
+ *
774
+ * Check out the {@glink features/images/image-upload/image-upload Image upload overview} guide to learn about
775
+ * other ways to upload images into CKEditor 5.
776
+ */ class CKBoxUploadAdapter extends Plugin {
704
777
  /**
705
- * @inheritDoc
706
- */ static get requires() {
778
+ * @inheritDoc
779
+ */ static get requires() {
707
780
  return [
708
781
  'ImageUploadEditing',
709
782
  'ImageUploadProgress',
@@ -712,13 +785,13 @@ class CKBoxUploadAdapter extends Plugin {
712
785
  ];
713
786
  }
714
787
  /**
715
- * @inheritDoc
716
- */ static get pluginName() {
788
+ * @inheritDoc
789
+ */ static get pluginName() {
717
790
  return 'CKBoxUploadAdapter';
718
791
  }
719
792
  /**
720
- * @inheritDoc
721
- */ async afterInit() {
793
+ * @inheritDoc
794
+ */ async afterInit() {
722
795
  const editor = this.editor;
723
796
  const hasConfiguration = !!editor.config.get('ckbox');
724
797
  const isLibraryLoaded = !!window.CKBox;
@@ -747,10 +820,38 @@ class CKBoxUploadAdapter extends Plugin {
747
820
  * Upload adapter for CKBox.
748
821
  */ class Adapter {
749
822
  /**
750
- * Starts the upload process.
751
- *
752
- * @see module:upload/filerepository~UploadAdapter#upload
753
- */ async upload() {
823
+ * FileLoader instance to use during the upload.
824
+ */ loader;
825
+ /**
826
+ * CKEditor Cloud Services access token.
827
+ */ token;
828
+ /**
829
+ * The editor instance.
830
+ */ editor;
831
+ /**
832
+ * The abort controller for aborting asynchronous processes.
833
+ */ controller;
834
+ /**
835
+ * The base URL where all requests should be sent.
836
+ */ serviceOrigin;
837
+ /**
838
+ * The reference to CKBoxUtils plugin.
839
+ */ ckboxUtils;
840
+ /**
841
+ * Creates a new adapter instance.
842
+ */ constructor(loader, editor, ckboxUtils){
843
+ this.loader = loader;
844
+ this.token = ckboxUtils.getToken();
845
+ this.ckboxUtils = ckboxUtils;
846
+ this.editor = editor;
847
+ this.controller = new AbortController();
848
+ this.serviceOrigin = editor.config.get('ckbox.serviceOrigin');
849
+ }
850
+ /**
851
+ * Starts the upload process.
852
+ *
853
+ * @see module:upload/filerepository~UploadAdapter#upload
854
+ */ async upload() {
754
855
  const ckboxUtils = this.ckboxUtils;
755
856
  const t = this.editor.t;
756
857
  const file = await this.loader.file;
@@ -788,33 +889,29 @@ class CKBoxUploadAdapter extends Plugin {
788
889
  });
789
890
  }
790
891
  /**
791
- * Aborts the upload process.
792
- *
793
- * @see module:upload/filerepository~UploadAdapter#abort
794
- */ abort() {
892
+ * Aborts the upload process.
893
+ *
894
+ * @see module:upload/filerepository~UploadAdapter#abort
895
+ */ abort() {
795
896
  this.controller.abort();
796
897
  }
797
- /**
798
- * Creates a new adapter instance.
799
- */ constructor(loader, editor, ckboxUtils){
800
- this.loader = loader;
801
- this.token = ckboxUtils.getToken();
802
- this.ckboxUtils = ckboxUtils;
803
- this.editor = editor;
804
- this.controller = new AbortController();
805
- this.serviceOrigin = editor.config.get('ckbox.serviceOrigin');
806
- }
807
898
  }
808
899
 
809
- class CKBoxEditing extends Plugin {
900
+ /**
901
+ * The CKBox editing feature. It introduces the {@link module:ckbox/ckboxcommand~CKBoxCommand CKBox command} and
902
+ * {@link module:ckbox/ckboxuploadadapter~CKBoxUploadAdapter CKBox upload adapter}.
903
+ */ class CKBoxEditing extends Plugin {
904
+ /**
905
+ * CKEditor Cloud Services access token.
906
+ */ _token;
810
907
  /**
811
- * @inheritDoc
812
- */ static get pluginName() {
908
+ * @inheritDoc
909
+ */ static get pluginName() {
813
910
  return 'CKBoxEditing';
814
911
  }
815
912
  /**
816
- * @inheritDoc
817
- */ static get requires() {
913
+ * @inheritDoc
914
+ */ static get requires() {
818
915
  return [
819
916
  'LinkEditing',
820
917
  'PictureEditing',
@@ -823,8 +920,8 @@ class CKBoxEditing extends Plugin {
823
920
  ];
824
921
  }
825
922
  /**
826
- * @inheritDoc
827
- */ init() {
923
+ * @inheritDoc
924
+ */ init() {
828
925
  const editor = this.editor;
829
926
  if (!this._shouldBeInitialised()) {
830
927
  return;
@@ -836,8 +933,8 @@ class CKBoxEditing extends Plugin {
836
933
  }
837
934
  }
838
935
  /**
839
- * @inheritDoc
840
- */ afterInit() {
936
+ * @inheritDoc
937
+ */ afterInit() {
841
938
  const editor = this.editor;
842
939
  if (!this._shouldBeInitialised()) {
843
940
  return;
@@ -851,35 +948,35 @@ class CKBoxEditing extends Plugin {
851
948
  }
852
949
  }
853
950
  /**
854
- * Returns true only when the integrator intentionally wants to use the plugin, i.e. when the `config.ckbox` exists or
855
- * the CKBox JavaScript library is loaded.
856
- */ _shouldBeInitialised() {
951
+ * Returns true only when the integrator intentionally wants to use the plugin, i.e. when the `config.ckbox` exists or
952
+ * the CKBox JavaScript library is loaded.
953
+ */ _shouldBeInitialised() {
857
954
  const editor = this.editor;
858
955
  const hasConfiguration = !!editor.config.get('ckbox');
859
956
  return hasConfiguration || isLibraryLoaded();
860
957
  }
861
958
  /**
862
- * Checks if at least one image plugin is loaded.
863
- */ _checkImagePlugins() {
959
+ * Checks if at least one image plugin is loaded.
960
+ */ _checkImagePlugins() {
864
961
  const editor = this.editor;
865
962
  if (!editor.plugins.has('ImageBlockEditing') && !editor.plugins.has('ImageInlineEditing')) {
866
963
  /**
867
- * The CKBox feature requires one of the following plugins to be loaded to work correctly:
868
- *
869
- * * {@link module:image/imageblock~ImageBlock},
870
- * * {@link module:image/imageinline~ImageInline},
871
- * * {@link module:image/image~Image} (loads both `ImageBlock` and `ImageInline`)
872
- *
873
- * Please make sure your editor configuration is correct.
874
- *
875
- * @error ckbox-plugin-image-feature-missing
876
- * @param {module:core/editor/editor~Editor} editor
877
- */ logError('ckbox-plugin-image-feature-missing', editor);
964
+ * The CKBox feature requires one of the following plugins to be loaded to work correctly:
965
+ *
966
+ * * {@link module:image/imageblock~ImageBlock},
967
+ * * {@link module:image/imageinline~ImageInline},
968
+ * * {@link module:image/image~Image} (loads both `ImageBlock` and `ImageInline`)
969
+ *
970
+ * Please make sure your editor configuration is correct.
971
+ *
972
+ * @error ckbox-plugin-image-feature-missing
973
+ * @param {module:core/editor/editor~Editor} editor
974
+ */ logError('ckbox-plugin-image-feature-missing', editor);
878
975
  }
879
976
  }
880
977
  /**
881
- * Extends the schema to allow the `ckboxImageId` and `ckboxLinkId` attributes for links and images.
882
- */ _initSchema() {
978
+ * Extends the schema to allow the `ckboxImageId` and `ckboxLinkId` attributes for links and images.
979
+ */ _initSchema() {
883
980
  const editor = this.editor;
884
981
  const schema = editor.model.schema;
885
982
  schema.extend('$text', {
@@ -909,8 +1006,8 @@ class CKBoxEditing extends Plugin {
909
1006
  });
910
1007
  }
911
1008
  /**
912
- * Configures the upcast and downcast conversions for the `ckboxImageId` and `ckboxLinkId` attributes.
913
- */ _initConversion() {
1009
+ * Configures the upcast and downcast conversions for the `ckboxImageId` and `ckboxLinkId` attributes.
1010
+ */ _initConversion() {
914
1011
  const editor = this.editor;
915
1012
  // Convert `ckboxLinkId` => `data-ckbox-resource-id`.
916
1013
  editor.conversion.for('downcast').add((dispatcher)=>{
@@ -1031,8 +1128,8 @@ class CKBoxEditing extends Plugin {
1031
1128
  }
1032
1129
  }
1033
1130
  /**
1034
- * Registers post-fixers that add or remove the `ckboxLinkId` and `ckboxImageId` attributes.
1035
- */ _initFixers() {
1131
+ * Registers post-fixers that add or remove the `ckboxLinkId` and `ckboxImageId` attributes.
1132
+ */ _initFixers() {
1036
1133
  const editor = this.editor;
1037
1134
  const model = editor.model;
1038
1135
  const selection = model.document.selection;
@@ -1145,15 +1242,27 @@ class CKBoxEditing extends Plugin {
1145
1242
  return !!window.CKBox;
1146
1243
  }
1147
1244
 
1148
- class CKBox extends Plugin {
1245
+ /**
1246
+ * The CKBox feature, a bridge between the CKEditor 5 WYSIWYG editor and the CKBox file manager and uploader.
1247
+ *
1248
+ * This is a "glue" plugin which enables:
1249
+ *
1250
+ * * {@link module:ckbox/ckboxediting~CKBoxEditing},
1251
+ * * {@link module:ckbox/ckboxui~CKBoxUI},
1252
+ *
1253
+ * See the {@glink features/file-management/ckbox CKBox integration} guide to learn how to configure and use this feature.
1254
+ *
1255
+ * Check out the {@glink features/images/image-upload/image-upload Image upload} guide to learn about other ways to upload
1256
+ * images into CKEditor 5.
1257
+ */ class CKBox extends Plugin {
1149
1258
  /**
1150
- * @inheritDoc
1151
- */ static get pluginName() {
1259
+ * @inheritDoc
1260
+ */ static get pluginName() {
1152
1261
  return 'CKBox';
1153
1262
  }
1154
1263
  /**
1155
- * @inheritDoc
1156
- */ static get requires() {
1264
+ * @inheritDoc
1265
+ */ static get requires() {
1157
1266
  return [
1158
1267
  CKBoxEditing,
1159
1268
  CKBoxUI
@@ -1197,18 +1306,50 @@ function createUrlChecker(allowExternalImagesEditing) {
1197
1306
  return ()=>false;
1198
1307
  }
1199
1308
 
1200
- class CKBoxImageEditCommand extends Command {
1309
+ /**
1310
+ * The CKBox edit image command.
1311
+ *
1312
+ * Opens the CKBox dialog for editing the image.
1313
+ */ class CKBoxImageEditCommand extends Command {
1314
+ /**
1315
+ * The DOM element that acts as a mounting point for the CKBox Edit Image dialog.
1316
+ */ _wrapper = null;
1317
+ /**
1318
+ * The states of image processing in progress.
1319
+ */ _processInProgress = new Set();
1320
+ /**
1321
+ * Determines if the element can be edited.
1322
+ */ _canEdit;
1323
+ /**
1324
+ * A wrapper function to prepare mount options. Ensures that at most one preparation is in-flight.
1325
+ */ _prepareOptions;
1326
+ /**
1327
+ * CKBox's onClose function runs before the final cleanup, potentially causing
1328
+ * page layout changes after it finishes. To address this, we use a setTimeout hack
1329
+ * to ensure that floating elements on the page maintain their correct position.
1330
+ *
1331
+ * See: https://github.com/ckeditor/ckeditor5/issues/16153.
1332
+ */ _updateUiDelayed = delay(()=>this.editor.ui.update(), 0);
1333
+ /**
1334
+ * @inheritDoc
1335
+ */ constructor(editor){
1336
+ super(editor);
1337
+ this.value = false;
1338
+ this._canEdit = createEditabilityChecker(editor.config.get('ckbox.allowExternalImagesEditing'));
1339
+ this._prepareOptions = abortableDebounce((signal, state)=>this._prepareOptionsAbortable(signal, state));
1340
+ this._prepareListeners();
1341
+ }
1201
1342
  /**
1202
- * @inheritDoc
1203
- */ refresh() {
1343
+ * @inheritDoc
1344
+ */ refresh() {
1204
1345
  const editor = this.editor;
1205
1346
  this.value = this._getValue();
1206
1347
  const selectedElement = editor.model.document.selection.getSelectedElement();
1207
1348
  this.isEnabled = !!selectedElement && this._canEdit(selectedElement) && !this._checkIfElementIsBeingProcessed(selectedElement);
1208
1349
  }
1209
1350
  /**
1210
- * Opens the CKBox Image Editor dialog for editing the image.
1211
- */ execute() {
1351
+ * Opens the CKBox Image Editor dialog for editing the image.
1352
+ */ execute() {
1212
1353
  if (this._getValue()) {
1213
1354
  return;
1214
1355
  }
@@ -1235,23 +1376,24 @@ class CKBoxImageEditCommand extends Command {
1235
1376
  });
1236
1377
  }
1237
1378
  /**
1238
- * @inheritDoc
1239
- */ destroy() {
1379
+ * @inheritDoc
1380
+ */ destroy() {
1240
1381
  this._handleImageEditorClose();
1241
1382
  this._prepareOptions.abort();
1383
+ this._updateUiDelayed.cancel();
1242
1384
  for (const state of this._processInProgress.values()){
1243
1385
  state.controller.abort();
1244
1386
  }
1245
1387
  super.destroy();
1246
1388
  }
1247
1389
  /**
1248
- * Indicates if the CKBox Image Editor dialog is already opened.
1249
- */ _getValue() {
1390
+ * Indicates if the CKBox Image Editor dialog is already opened.
1391
+ */ _getValue() {
1250
1392
  return this._wrapper !== null;
1251
1393
  }
1252
1394
  /**
1253
- * Creates the options object for the CKBox Image Editor dialog.
1254
- */ async _prepareOptionsAbortable(signal, state) {
1395
+ * Creates the options object for the CKBox Image Editor dialog.
1396
+ */ async _prepareOptionsAbortable(signal, state) {
1255
1397
  const editor = this.editor;
1256
1398
  const ckboxConfig = editor.config.get('ckbox');
1257
1399
  const ckboxUtils = editor.plugins.get(CKBoxUtils);
@@ -1286,8 +1428,8 @@ class CKBoxImageEditCommand extends Command {
1286
1428
  };
1287
1429
  }
1288
1430
  /**
1289
- * Initializes event lister for an event of removing an image.
1290
- */ _prepareListeners() {
1431
+ * Initializes event lister for an event of removing an image.
1432
+ */ _prepareListeners() {
1291
1433
  // Abort editing processing when the image has been removed.
1292
1434
  this.listenTo(this.editor.model.document, 'change:data', ()=>{
1293
1435
  const processingStates = this._getProcessingStatesOfDeletedImages();
@@ -1297,8 +1439,8 @@ class CKBoxImageEditCommand extends Command {
1297
1439
  });
1298
1440
  }
1299
1441
  /**
1300
- * Gets processing states of images that have been deleted in the mean time.
1301
- */ _getProcessingStatesOfDeletedImages() {
1442
+ * Gets processing states of images that have been deleted in the mean time.
1443
+ */ _getProcessingStatesOfDeletedImages() {
1302
1444
  const states = [];
1303
1445
  for (const state of this._processInProgress.values()){
1304
1446
  if (state.element.root.rootName == '$graveyard') {
@@ -1316,20 +1458,21 @@ class CKBoxImageEditCommand extends Command {
1316
1458
  return false;
1317
1459
  }
1318
1460
  /**
1319
- * Closes the CKBox Image Editor dialog.
1320
- */ _handleImageEditorClose() {
1461
+ * Closes the CKBox Image Editor dialog.
1462
+ */ _handleImageEditorClose() {
1321
1463
  if (!this._wrapper) {
1322
1464
  return;
1323
1465
  }
1324
1466
  this._wrapper.remove();
1325
1467
  this._wrapper = null;
1326
1468
  this.editor.editing.view.focus();
1469
+ this._updateUiDelayed();
1327
1470
  this.refresh();
1328
1471
  }
1329
1472
  /**
1330
- * Save edited image. In case server respond with "success" replace with edited image,
1331
- * otherwise show notification error.
1332
- */ _handleImageEditorSave(state, asset) {
1473
+ * Save edited image. In case server respond with "success" replace with edited image,
1474
+ * otherwise show notification error.
1475
+ */ _handleImageEditorSave(state, asset) {
1333
1476
  const t = this.editor.locale.t;
1334
1477
  const notification = this.editor.plugins.get(Notification);
1335
1478
  const pendingActions = this.editor.plugins.get(PendingActions);
@@ -1359,9 +1502,9 @@ class CKBoxImageEditCommand extends Command {
1359
1502
  });
1360
1503
  }
1361
1504
  /**
1362
- * Get asset's status on server. If server responds with "success" status then
1363
- * image is already proceeded and ready for saving.
1364
- */ async _getAssetStatusFromServer(id, signal) {
1505
+ * Get asset's status on server. If server responds with "success" status then
1506
+ * image is already proceeded and ready for saving.
1507
+ */ async _getAssetStatusFromServer(id, signal) {
1365
1508
  const ckboxUtils = this.editor.plugins.get(CKBoxUtils);
1366
1509
  const url = new URL('assets/' + id, this.editor.config.get('ckbox.serviceOrigin'));
1367
1510
  const response = await sendHttpRequest({
@@ -1372,10 +1515,10 @@ class CKBoxImageEditCommand extends Command {
1372
1515
  const status = response.metadata.metadataProcessingStatus;
1373
1516
  if (!status || status == 'queued') {
1374
1517
  /**
1375
- * Image has not been processed yet.
1376
- *
1377
- * @error ckbox-image-not-processed
1378
- */ throw new CKEditorError('ckbox-image-not-processed');
1518
+ * Image has not been processed yet.
1519
+ *
1520
+ * @error ckbox-image-not-processed
1521
+ */ throw new CKEditorError('ckbox-image-not-processed');
1379
1522
  }
1380
1523
  return {
1381
1524
  data: {
@@ -1384,27 +1527,27 @@ class CKBoxImageEditCommand extends Command {
1384
1527
  };
1385
1528
  }
1386
1529
  /**
1387
- * Waits for an asset to be processed.
1388
- * It retries retrieving asset status from the server in case of failure.
1389
- */ async _waitForAssetProcessed(id, signal) {
1530
+ * Waits for an asset to be processed.
1531
+ * It retries retrieving asset status from the server in case of failure.
1532
+ */ async _waitForAssetProcessed(id, signal) {
1390
1533
  const result = await retry(()=>this._getAssetStatusFromServer(id, signal), {
1391
1534
  signal,
1392
1535
  maxAttempts: 5
1393
1536
  });
1394
1537
  if (result.data.metadata.metadataProcessingStatus != 'success') {
1395
1538
  /**
1396
- * The image processing failed.
1397
- *
1398
- * @error ckbox-image-processing-failed
1399
- */ throw new CKEditorError('ckbox-image-processing-failed');
1539
+ * The image processing failed.
1540
+ *
1541
+ * @error ckbox-image-processing-failed
1542
+ */ throw new CKEditorError('ckbox-image-processing-failed');
1400
1543
  }
1401
1544
  return result;
1402
1545
  }
1403
1546
  /**
1404
- * Shows processing indicator while image is processing.
1405
- *
1406
- * @param asset Data about certain asset.
1407
- */ _showImageProcessingIndicator(element, asset) {
1547
+ * Shows processing indicator while image is processing.
1548
+ *
1549
+ * @param asset Data about certain asset.
1550
+ */ _showImageProcessingIndicator(element, asset) {
1408
1551
  const editor = this.editor;
1409
1552
  editor.editing.view.change((writer)=>{
1410
1553
  const imageElementView = editor.editing.mapper.toViewElement(element);
@@ -1419,8 +1562,8 @@ class CKBoxImageEditCommand extends Command {
1419
1562
  });
1420
1563
  }
1421
1564
  /**
1422
- * Replace the edited image with the new one.
1423
- */ _replaceImage(element, asset) {
1565
+ * Replace the edited image with the new one.
1566
+ */ _replaceImage(element, asset) {
1424
1567
  const editor = this.editor;
1425
1568
  const { imageFallbackUrl, imageSources, imageWidth, imageHeight, imagePlaceholder } = prepareImageAssetAttributes(asset);
1426
1569
  const previousSelectionRanges = Array.from(editor.model.document.selection.getRanges());
@@ -1449,32 +1592,19 @@ class CKBoxImageEditCommand extends Command {
1449
1592
  writer.setSelection(previousSelectionRanges);
1450
1593
  });
1451
1594
  }
1452
- /**
1453
- * @inheritDoc
1454
- */ constructor(editor){
1455
- super(editor);
1456
- /**
1457
- * The DOM element that acts as a mounting point for the CKBox Edit Image dialog.
1458
- */ this._wrapper = null;
1459
- /**
1460
- * The states of image processing in progress.
1461
- */ this._processInProgress = new Set();
1462
- this.value = false;
1463
- this._canEdit = createEditabilityChecker(editor.config.get('ckbox.allowExternalImagesEditing'));
1464
- this._prepareOptions = abortableDebounce((signal, state)=>this._prepareOptionsAbortable(signal, state));
1465
- this._prepareListeners();
1466
- }
1467
1595
  }
1468
1596
 
1469
- class CKBoxImageEditEditing extends Plugin {
1597
+ /**
1598
+ * The CKBox image edit editing plugin.
1599
+ */ class CKBoxImageEditEditing extends Plugin {
1470
1600
  /**
1471
- * @inheritDoc
1472
- */ static get pluginName() {
1601
+ * @inheritDoc
1602
+ */ static get pluginName() {
1473
1603
  return 'CKBoxImageEditEditing';
1474
1604
  }
1475
1605
  /**
1476
- * @inheritDoc
1477
- */ static get requires() {
1606
+ * @inheritDoc
1607
+ */ static get requires() {
1478
1608
  return [
1479
1609
  CKBoxEditing,
1480
1610
  CKBoxUtils,
@@ -1485,8 +1615,8 @@ class CKBoxImageEditEditing extends Plugin {
1485
1615
  ];
1486
1616
  }
1487
1617
  /**
1488
- * @inheritDoc
1489
- */ init() {
1618
+ * @inheritDoc
1619
+ */ init() {
1490
1620
  const { editor } = this;
1491
1621
  editor.commands.add('ckboxImageEdit', new CKBoxImageEditCommand(editor));
1492
1622
  }
@@ -1494,15 +1624,20 @@ class CKBoxImageEditEditing extends Plugin {
1494
1624
 
1495
1625
  var ckboxImageEditIcon = "<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.1v14.363c0 .64.534 1.037 1.186 1.037H5.06l5.058-5.078L6.617 9.15a.696.696 0 0 0-.957-.033L1.5 13.6V2.5h15v4.354a3.478 3.478 0 0 1 1.5.049V2.1c0-.63-.547-1.1-1.2-1.1H1.202Zm11.713 2.803a2.147 2.147 0 0 0-2.049 1.992 2.14 2.14 0 0 0 1.28 2.096 2.13 2.13 0 0 0 2.642-3.11 2.129 2.129 0 0 0-1.873-.978ZM8.089 17.635v2.388h2.389l7.046-7.046-2.39-2.39-7.045 7.048Zm11.282-6.507a.637.637 0 0 0 .139-.692.603.603 0 0 0-.139-.205l-1.49-1.488a.63.63 0 0 0-.899 0l-1.166 1.163 2.39 2.39 1.165-1.168Z\"/></svg>";
1496
1626
 
1497
- class CKBoxImageEditUI extends Plugin {
1627
+ /**
1628
+ * The UI plugin of the CKBox image edit feature.
1629
+ *
1630
+ * It registers the `'ckboxImageEdit'` UI button in the editor's {@link module:ui/componentfactory~ComponentFactory component factory}
1631
+ * that allows you to open the CKBox dialog and edit the image.
1632
+ */ class CKBoxImageEditUI extends Plugin {
1498
1633
  /**
1499
- * @inheritDoc
1500
- */ static get pluginName() {
1634
+ * @inheritDoc
1635
+ */ static get pluginName() {
1501
1636
  return 'CKBoxImageEditUI';
1502
1637
  }
1503
1638
  /**
1504
- * @inheritDoc
1505
- */ init() {
1639
+ * @inheritDoc
1640
+ */ init() {
1506
1641
  const editor = this.editor;
1507
1642
  editor.ui.componentFactory.add('ckboxImageEdit', (locale)=>{
1508
1643
  const command = editor.commands.get('ckboxImageEdit');
@@ -1525,15 +1660,17 @@ class CKBoxImageEditUI extends Plugin {
1525
1660
  }
1526
1661
  }
1527
1662
 
1528
- class CKBoxImageEdit extends Plugin {
1663
+ /**
1664
+ * The CKBox image edit feature.
1665
+ */ class CKBoxImageEdit extends Plugin {
1529
1666
  /**
1530
- * @inheritDoc
1531
- */ static get pluginName() {
1667
+ * @inheritDoc
1668
+ */ static get pluginName() {
1532
1669
  return 'CKBoxImageEdit';
1533
1670
  }
1534
1671
  /**
1535
- * @inheritDoc
1536
- */ static get requires() {
1672
+ * @inheritDoc
1673
+ */ static get requires() {
1537
1674
  return [
1538
1675
  CKBoxImageEditEditing,
1539
1676
  CKBoxImageEditUI