@ckeditor/ckeditor5-ckbox 41.4.2 → 42.0.0-alpha.0

Sign up to get free protection for your applications and to get access to all the features.
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