@ckeditor/ckeditor5-ckbox 0.0.0-nightly-20240508.0 → 0.0.0-nightly-20240510.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +334 -251
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
@@ -5,20 +5,22 @@
|
|
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,
|
8
|
+
import { createElement, toMap, CKEditorError, logError, global, 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
|
-
|
13
|
+
/**
|
14
|
+
* The CKBoxUI plugin. It introduces the `'ckbox'` toolbar button.
|
15
|
+
*/ class CKBoxUI extends Plugin {
|
14
16
|
/**
|
15
|
-
|
16
|
-
|
17
|
+
* @inheritDoc
|
18
|
+
*/ static get pluginName() {
|
17
19
|
return 'CKBoxUI';
|
18
20
|
}
|
19
21
|
/**
|
20
|
-
|
21
|
-
|
22
|
+
* @inheritDoc
|
23
|
+
*/ afterInit() {
|
22
24
|
const editor = this.editor;
|
23
25
|
// Do not register the `ckbox` button if the command does not exist.
|
24
26
|
// This might happen when CKBox library is not loaded on the page.
|
@@ -58,8 +60,8 @@ class CKBoxUI extends Plugin {
|
|
58
60
|
}
|
59
61
|
}
|
60
62
|
/**
|
61
|
-
|
62
|
-
|
63
|
+
* Creates a button for CKBox command to use either in toolbar or in menu bar.
|
64
|
+
*/ _createButton(ButtonClass) {
|
63
65
|
const editor = this.editor;
|
64
66
|
const locale = editor.locale;
|
65
67
|
const view = new ButtonClass(locale);
|
@@ -235,7 +237,7 @@ const MIME_TO_EXTENSION = {
|
|
235
237
|
* Returns an extension from the given value.
|
236
238
|
*/ function getFileExtension(file) {
|
237
239
|
const fileName = file.name;
|
238
|
-
const extensionRegExp =
|
240
|
+
const extensionRegExp = /\.(?<ext>[^.]+)$/;
|
239
241
|
const match = fileName.match(extensionRegExp);
|
240
242
|
return match.groups.ext.toLowerCase();
|
241
243
|
}
|
@@ -244,29 +246,68 @@ const MIME_TO_EXTENSION = {
|
|
244
246
|
// `CKBoxCommand#_chosenAssets` and it is removed from there automatically after this time. See `CKBoxCommand#_chosenAssets` for more
|
245
247
|
// details.
|
246
248
|
const ASSET_INSERTION_WAIT_TIMEOUT = 1000;
|
247
|
-
|
249
|
+
/**
|
250
|
+
* The CKBox command. It is used by the {@link module:ckbox/ckboxediting~CKBoxEditing CKBox editing feature} to open the CKBox file manager.
|
251
|
+
* The file manager allows inserting an image or a link to a file into the editor content.
|
252
|
+
*
|
253
|
+
* ```ts
|
254
|
+
* editor.execute( 'ckbox' );
|
255
|
+
* ```
|
256
|
+
*
|
257
|
+
* **Note:** This command uses other features to perform the following tasks:
|
258
|
+
* - To insert images it uses the {@link module:image/image/insertimagecommand~InsertImageCommand 'insertImage'} command from the
|
259
|
+
* {@link module:image/image~Image Image feature}.
|
260
|
+
* - To insert links to other files it uses the {@link module:link/linkcommand~LinkCommand 'link'} command from the
|
261
|
+
* {@link module:link/link~Link Link feature}.
|
262
|
+
*/ class CKBoxCommand extends Command {
|
263
|
+
/**
|
264
|
+
* A set of all chosen assets. They are stored temporarily and they are automatically removed 1 second after being chosen.
|
265
|
+
* Chosen assets have to be "remembered" for a while to be able to map the given asset with the element inserted into the model.
|
266
|
+
* This association map is then used to set the ID on the model element.
|
267
|
+
*
|
268
|
+
* All chosen assets are automatically removed after the timeout, because (theoretically) it may happen that they will never be
|
269
|
+
* inserted into the model, even if the {@link module:link/linkcommand~LinkCommand `'link'`} command or the
|
270
|
+
* {@link module:image/image/insertimagecommand~InsertImageCommand `'insertImage'`} command is enabled. Such a case may arise when
|
271
|
+
* another plugin blocks the command execution. Then, in order not to keep the chosen (but not inserted) assets forever, we delete
|
272
|
+
* them automatically to prevent memory leakage. The 1 second timeout is enough to insert the asset into the model and extract the
|
273
|
+
* ID from the chosen asset.
|
274
|
+
*
|
275
|
+
* The assets are stored only if
|
276
|
+
* the {@link module:ckbox/ckboxconfig~CKBoxConfig#ignoreDataId `config.ckbox.ignoreDataId`} option is set to `false` (by default).
|
277
|
+
*
|
278
|
+
* @internal
|
279
|
+
*/ _chosenAssets = new Set();
|
280
|
+
/**
|
281
|
+
* The DOM element that acts as a mounting point for the CKBox dialog.
|
282
|
+
*/ _wrapper = null;
|
283
|
+
/**
|
284
|
+
* @inheritDoc
|
285
|
+
*/ constructor(editor){
|
286
|
+
super(editor);
|
287
|
+
this._initListeners();
|
288
|
+
}
|
248
289
|
/**
|
249
|
-
|
250
|
-
|
290
|
+
* @inheritDoc
|
291
|
+
*/ refresh() {
|
251
292
|
this.value = this._getValue();
|
252
293
|
this.isEnabled = this._checkEnabled();
|
253
294
|
}
|
254
295
|
/**
|
255
|
-
|
256
|
-
|
296
|
+
* @inheritDoc
|
297
|
+
*/ execute() {
|
257
298
|
this.fire('ckbox:open');
|
258
299
|
}
|
259
300
|
/**
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
301
|
+
* Indicates if the CKBox dialog is already opened.
|
302
|
+
*
|
303
|
+
* @protected
|
304
|
+
* @returns {Boolean}
|
305
|
+
*/ _getValue() {
|
265
306
|
return this._wrapper !== null;
|
266
307
|
}
|
267
308
|
/**
|
268
|
-
|
269
|
-
|
309
|
+
* Checks whether the command can be enabled in the current context.
|
310
|
+
*/ _checkEnabled() {
|
270
311
|
const imageCommand = this.editor.commands.get('insertImage');
|
271
312
|
const linkCommand = this.editor.commands.get('link');
|
272
313
|
if (!imageCommand.isEnabled && !linkCommand.isEnabled) {
|
@@ -275,17 +316,17 @@ class CKBoxCommand extends Command {
|
|
275
316
|
return true;
|
276
317
|
}
|
277
318
|
/**
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
319
|
+
* Creates the options object for the CKBox dialog.
|
320
|
+
*
|
321
|
+
* @returns The object with properties:
|
322
|
+
* - theme The theme for CKBox dialog.
|
323
|
+
* - language The language for CKBox dialog.
|
324
|
+
* - tokenUrl The token endpoint URL.
|
325
|
+
* - serviceOrigin The base URL of the API service.
|
326
|
+
* - forceDemoLabel Whether to force "Powered by CKBox" link.
|
327
|
+
* - dialog.onClose The callback function invoked after closing the CKBox dialog.
|
328
|
+
* - assets.onChoose The callback function invoked after choosing the assets.
|
329
|
+
*/ _prepareOptions() {
|
289
330
|
const editor = this.editor;
|
290
331
|
const ckboxConfig = editor.config.get('ckbox');
|
291
332
|
return {
|
@@ -303,8 +344,8 @@ class CKBoxCommand extends Command {
|
|
303
344
|
};
|
304
345
|
}
|
305
346
|
/**
|
306
|
-
|
307
|
-
|
347
|
+
* Initializes various event listeners for the `ckbox:*` events, because all functionality of the `ckbox` command is event-based.
|
348
|
+
*/ _initListeners() {
|
308
349
|
const editor = this.editor;
|
309
350
|
const model = editor.model;
|
310
351
|
const shouldInsertDataId = !editor.config.get('ckbox.ignoreDataId');
|
@@ -373,13 +414,13 @@ class CKBoxCommand extends Command {
|
|
373
414
|
});
|
374
415
|
}
|
375
416
|
/**
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
417
|
+
* Inserts the asset into the model.
|
418
|
+
*
|
419
|
+
* @param asset The asset to be inserted.
|
420
|
+
* @param isLastAsset Indicates if the current asset is the last one from the chosen set.
|
421
|
+
* @param writer An instance of the model writer.
|
422
|
+
* @param isSingleAsset It's true when only one asset is processed.
|
423
|
+
*/ _insertAsset(asset, isLastAsset, writer, isSingleAsset) {
|
383
424
|
const editor = this.editor;
|
384
425
|
const model = editor.model;
|
385
426
|
const selection = model.document.selection;
|
@@ -397,10 +438,10 @@ class CKBoxCommand extends Command {
|
|
397
438
|
}
|
398
439
|
}
|
399
440
|
/**
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
441
|
+
* Inserts the image by calling the `insertImage` command.
|
442
|
+
*
|
443
|
+
* @param asset The asset to be inserted.
|
444
|
+
*/ _insertImage(asset) {
|
404
445
|
const editor = this.editor;
|
405
446
|
const { imageFallbackUrl, imageSources, imageTextAlternative, imageWidth, imageHeight, imagePlaceholder } = asset.attributes;
|
406
447
|
editor.execute('insertImage', {
|
@@ -417,12 +458,12 @@ class CKBoxCommand extends Command {
|
|
417
458
|
});
|
418
459
|
}
|
419
460
|
/**
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
461
|
+
* Inserts the link to the asset by calling the `link` command.
|
462
|
+
*
|
463
|
+
* @param asset The asset to be inserted.
|
464
|
+
* @param writer An instance of the model writer.
|
465
|
+
* @param isSingleAsset It's true when only one asset is processed.
|
466
|
+
*/ _insertLink(asset, writer, isSingleAsset) {
|
426
467
|
const editor = this.editor;
|
427
468
|
const model = editor.model;
|
428
469
|
const selection = model.document.selection;
|
@@ -450,32 +491,6 @@ class CKBoxCommand extends Command {
|
|
450
491
|
}
|
451
492
|
editor.execute('link', linkHref);
|
452
493
|
}
|
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
494
|
}
|
480
495
|
/**
|
481
496
|
* Parses the chosen assets into the internal data format. Filters out chosen assets that are not allowed.
|
@@ -539,22 +554,27 @@ class CKBoxCommand extends Command {
|
|
539
554
|
}
|
540
555
|
|
541
556
|
const DEFAULT_CKBOX_THEME_NAME = 'lark';
|
542
|
-
|
557
|
+
/**
|
558
|
+
* The CKBox utilities plugin.
|
559
|
+
*/ class CKBoxUtils extends Plugin {
|
560
|
+
/**
|
561
|
+
* CKEditor Cloud Services access token.
|
562
|
+
*/ _token;
|
543
563
|
/**
|
544
|
-
|
545
|
-
|
564
|
+
* @inheritDoc
|
565
|
+
*/ static get pluginName() {
|
546
566
|
return 'CKBoxUtils';
|
547
567
|
}
|
548
568
|
/**
|
549
|
-
|
550
|
-
|
569
|
+
* @inheritDoc
|
570
|
+
*/ static get requires() {
|
551
571
|
return [
|
552
572
|
'CloudServices'
|
553
573
|
];
|
554
574
|
}
|
555
575
|
/**
|
556
|
-
|
557
|
-
|
576
|
+
* @inheritDoc
|
577
|
+
*/ async init() {
|
558
578
|
const editor = this.editor;
|
559
579
|
const hasConfiguration = !!editor.config.get('ckbox');
|
560
580
|
const isLibraryLoaded = !!window.CKBox;
|
@@ -576,22 +596,22 @@ class CKBoxUtils extends Plugin {
|
|
576
596
|
const ckboxTokenUrl = editor.config.get('ckbox.tokenUrl');
|
577
597
|
if (!ckboxTokenUrl) {
|
578
598
|
/**
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
599
|
+
* The {@link module:ckbox/ckboxconfig~CKBoxConfig#tokenUrl `config.ckbox.tokenUrl`} or the
|
600
|
+
* {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl `config.cloudServices.tokenUrl`}
|
601
|
+
* configuration is required for the CKBox plugin.
|
602
|
+
*
|
603
|
+
* ```ts
|
604
|
+
* ClassicEditor.create( document.createElement( 'div' ), {
|
605
|
+
* ckbox: {
|
606
|
+
* tokenUrl: "YOUR_TOKEN_URL"
|
607
|
+
* // ...
|
608
|
+
* }
|
609
|
+
* // ...
|
610
|
+
* } );
|
611
|
+
* ```
|
612
|
+
*
|
613
|
+
* @error ckbox-plugin-missing-token-url
|
614
|
+
*/ throw new CKEditorError('ckbox-plugin-missing-token-url', this);
|
595
615
|
}
|
596
616
|
if (ckboxTokenUrl == cloudServicesTokenUrl) {
|
597
617
|
this._token = cloudServices.token;
|
@@ -600,30 +620,30 @@ class CKBoxUtils extends Plugin {
|
|
600
620
|
}
|
601
621
|
}
|
602
622
|
/**
|
603
|
-
|
604
|
-
|
623
|
+
* Returns a token used by the CKBox plugin for communication with the CKBox service.
|
624
|
+
*/ getToken() {
|
605
625
|
return this._token;
|
606
626
|
}
|
607
627
|
/**
|
608
|
-
|
609
|
-
|
628
|
+
* The ID of workspace to use when uploading an image.
|
629
|
+
*/ getWorkspaceId() {
|
610
630
|
const t = this.editor.t;
|
611
631
|
const cannotAccessDefaultWorkspaceError = t('Cannot access default workspace.');
|
612
632
|
const defaultWorkspaceId = this.editor.config.get('ckbox.defaultUploadWorkspaceId');
|
613
633
|
const workspaceId = getWorkspaceId(this._token, defaultWorkspaceId);
|
614
634
|
if (workspaceId == null) {
|
615
635
|
/**
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
636
|
+
* The user is not authorized to access the workspace defined in the`ckbox.defaultUploadWorkspaceId` configuration.
|
637
|
+
*
|
638
|
+
* @error ckbox-access-default-workspace-error
|
639
|
+
*/ logError('ckbox-access-default-workspace-error');
|
620
640
|
throw cannotAccessDefaultWorkspaceError;
|
621
641
|
}
|
622
642
|
return workspaceId;
|
623
643
|
}
|
624
644
|
/**
|
625
|
-
|
626
|
-
|
645
|
+
* Resolves a promise with an object containing a category with which the uploaded file is associated or an error code.
|
646
|
+
*/ async getCategoryIdForFile(fileOrUrl, options) {
|
627
647
|
const t = this.editor.t;
|
628
648
|
const cannotFindCategoryError = t('Cannot determine a category for the uploaded file.');
|
629
649
|
const defaultCategories = this.editor.config.get('ckbox.defaultUploadCategories');
|
@@ -656,10 +676,10 @@ class CKBoxUtils extends Plugin {
|
|
656
676
|
return category.id;
|
657
677
|
}
|
658
678
|
/**
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
679
|
+
* Resolves a promise with an array containing available categories with which the uploaded file can be associated.
|
680
|
+
*
|
681
|
+
* If the API returns limited results, the method will collect all items.
|
682
|
+
*/ async _getAvailableCategories(options) {
|
663
683
|
const ITEMS_PER_REQUEST = 50;
|
664
684
|
const editor = this.editor;
|
665
685
|
const token = this._token;
|
@@ -680,10 +700,10 @@ class CKBoxUtils extends Plugin {
|
|
680
700
|
} catch {
|
681
701
|
signal.throwIfAborted();
|
682
702
|
/**
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
703
|
+
* Fetching a list of available categories with which an uploaded file can be associated failed.
|
704
|
+
*
|
705
|
+
* @error ckbox-fetch-category-http-error
|
706
|
+
*/ logError('ckbox-fetch-category-http-error');
|
687
707
|
return undefined;
|
688
708
|
}
|
689
709
|
function fetchCategories(offset) {
|
@@ -700,10 +720,18 @@ class CKBoxUtils extends Plugin {
|
|
700
720
|
}
|
701
721
|
}
|
702
722
|
|
703
|
-
|
723
|
+
/**
|
724
|
+
* A plugin that enables file uploads in CKEditor 5 using the CKBox server–side connector.
|
725
|
+
* See the {@glink features/file-management/ckbox CKBox file manager integration} guide to learn how to configure
|
726
|
+
* and use this feature as well as find out more about the full integration with the file manager
|
727
|
+
* provided by the {@link module:ckbox/ckbox~CKBox} plugin.
|
728
|
+
*
|
729
|
+
* Check out the {@glink features/images/image-upload/image-upload Image upload overview} guide to learn about
|
730
|
+
* other ways to upload images into CKEditor 5.
|
731
|
+
*/ class CKBoxUploadAdapter extends Plugin {
|
704
732
|
/**
|
705
|
-
|
706
|
-
|
733
|
+
* @inheritDoc
|
734
|
+
*/ static get requires() {
|
707
735
|
return [
|
708
736
|
'ImageUploadEditing',
|
709
737
|
'ImageUploadProgress',
|
@@ -712,13 +740,13 @@ class CKBoxUploadAdapter extends Plugin {
|
|
712
740
|
];
|
713
741
|
}
|
714
742
|
/**
|
715
|
-
|
716
|
-
|
743
|
+
* @inheritDoc
|
744
|
+
*/ static get pluginName() {
|
717
745
|
return 'CKBoxUploadAdapter';
|
718
746
|
}
|
719
747
|
/**
|
720
|
-
|
721
|
-
|
748
|
+
* @inheritDoc
|
749
|
+
*/ async afterInit() {
|
722
750
|
const editor = this.editor;
|
723
751
|
const hasConfiguration = !!editor.config.get('ckbox');
|
724
752
|
const isLibraryLoaded = !!window.CKBox;
|
@@ -747,10 +775,38 @@ class CKBoxUploadAdapter extends Plugin {
|
|
747
775
|
* Upload adapter for CKBox.
|
748
776
|
*/ class Adapter {
|
749
777
|
/**
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
778
|
+
* FileLoader instance to use during the upload.
|
779
|
+
*/ loader;
|
780
|
+
/**
|
781
|
+
* CKEditor Cloud Services access token.
|
782
|
+
*/ token;
|
783
|
+
/**
|
784
|
+
* The editor instance.
|
785
|
+
*/ editor;
|
786
|
+
/**
|
787
|
+
* The abort controller for aborting asynchronous processes.
|
788
|
+
*/ controller;
|
789
|
+
/**
|
790
|
+
* The base URL where all requests should be sent.
|
791
|
+
*/ serviceOrigin;
|
792
|
+
/**
|
793
|
+
* The reference to CKBoxUtils plugin.
|
794
|
+
*/ ckboxUtils;
|
795
|
+
/**
|
796
|
+
* Creates a new adapter instance.
|
797
|
+
*/ constructor(loader, editor, ckboxUtils){
|
798
|
+
this.loader = loader;
|
799
|
+
this.token = ckboxUtils.getToken();
|
800
|
+
this.ckboxUtils = ckboxUtils;
|
801
|
+
this.editor = editor;
|
802
|
+
this.controller = new AbortController();
|
803
|
+
this.serviceOrigin = editor.config.get('ckbox.serviceOrigin');
|
804
|
+
}
|
805
|
+
/**
|
806
|
+
* Starts the upload process.
|
807
|
+
*
|
808
|
+
* @see module:upload/filerepository~UploadAdapter#upload
|
809
|
+
*/ async upload() {
|
754
810
|
const ckboxUtils = this.ckboxUtils;
|
755
811
|
const t = this.editor.t;
|
756
812
|
const file = await this.loader.file;
|
@@ -788,33 +844,29 @@ class CKBoxUploadAdapter extends Plugin {
|
|
788
844
|
});
|
789
845
|
}
|
790
846
|
/**
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
847
|
+
* Aborts the upload process.
|
848
|
+
*
|
849
|
+
* @see module:upload/filerepository~UploadAdapter#abort
|
850
|
+
*/ abort() {
|
795
851
|
this.controller.abort();
|
796
852
|
}
|
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
853
|
}
|
808
854
|
|
809
|
-
|
855
|
+
/**
|
856
|
+
* The CKBox editing feature. It introduces the {@link module:ckbox/ckboxcommand~CKBoxCommand CKBox command} and
|
857
|
+
* {@link module:ckbox/ckboxuploadadapter~CKBoxUploadAdapter CKBox upload adapter}.
|
858
|
+
*/ class CKBoxEditing extends Plugin {
|
810
859
|
/**
|
811
|
-
|
812
|
-
|
860
|
+
* CKEditor Cloud Services access token.
|
861
|
+
*/ _token;
|
862
|
+
/**
|
863
|
+
* @inheritDoc
|
864
|
+
*/ static get pluginName() {
|
813
865
|
return 'CKBoxEditing';
|
814
866
|
}
|
815
867
|
/**
|
816
|
-
|
817
|
-
|
868
|
+
* @inheritDoc
|
869
|
+
*/ static get requires() {
|
818
870
|
return [
|
819
871
|
'LinkEditing',
|
820
872
|
'PictureEditing',
|
@@ -823,8 +875,8 @@ class CKBoxEditing extends Plugin {
|
|
823
875
|
];
|
824
876
|
}
|
825
877
|
/**
|
826
|
-
|
827
|
-
|
878
|
+
* @inheritDoc
|
879
|
+
*/ init() {
|
828
880
|
const editor = this.editor;
|
829
881
|
if (!this._shouldBeInitialised()) {
|
830
882
|
return;
|
@@ -836,8 +888,8 @@ class CKBoxEditing extends Plugin {
|
|
836
888
|
}
|
837
889
|
}
|
838
890
|
/**
|
839
|
-
|
840
|
-
|
891
|
+
* @inheritDoc
|
892
|
+
*/ afterInit() {
|
841
893
|
const editor = this.editor;
|
842
894
|
if (!this._shouldBeInitialised()) {
|
843
895
|
return;
|
@@ -851,35 +903,35 @@ class CKBoxEditing extends Plugin {
|
|
851
903
|
}
|
852
904
|
}
|
853
905
|
/**
|
854
|
-
|
855
|
-
|
856
|
-
|
906
|
+
* Returns true only when the integrator intentionally wants to use the plugin, i.e. when the `config.ckbox` exists or
|
907
|
+
* the CKBox JavaScript library is loaded.
|
908
|
+
*/ _shouldBeInitialised() {
|
857
909
|
const editor = this.editor;
|
858
910
|
const hasConfiguration = !!editor.config.get('ckbox');
|
859
911
|
return hasConfiguration || isLibraryLoaded();
|
860
912
|
}
|
861
913
|
/**
|
862
|
-
|
863
|
-
|
914
|
+
* Checks if at least one image plugin is loaded.
|
915
|
+
*/ _checkImagePlugins() {
|
864
916
|
const editor = this.editor;
|
865
917
|
if (!editor.plugins.has('ImageBlockEditing') && !editor.plugins.has('ImageInlineEditing')) {
|
866
918
|
/**
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
919
|
+
* The CKBox feature requires one of the following plugins to be loaded to work correctly:
|
920
|
+
*
|
921
|
+
* * {@link module:image/imageblock~ImageBlock},
|
922
|
+
* * {@link module:image/imageinline~ImageInline},
|
923
|
+
* * {@link module:image/image~Image} (loads both `ImageBlock` and `ImageInline`)
|
924
|
+
*
|
925
|
+
* Please make sure your editor configuration is correct.
|
926
|
+
*
|
927
|
+
* @error ckbox-plugin-image-feature-missing
|
928
|
+
* @param {module:core/editor/editor~Editor} editor
|
929
|
+
*/ logError('ckbox-plugin-image-feature-missing', editor);
|
878
930
|
}
|
879
931
|
}
|
880
932
|
/**
|
881
|
-
|
882
|
-
|
933
|
+
* Extends the schema to allow the `ckboxImageId` and `ckboxLinkId` attributes for links and images.
|
934
|
+
*/ _initSchema() {
|
883
935
|
const editor = this.editor;
|
884
936
|
const schema = editor.model.schema;
|
885
937
|
schema.extend('$text', {
|
@@ -909,8 +961,8 @@ class CKBoxEditing extends Plugin {
|
|
909
961
|
});
|
910
962
|
}
|
911
963
|
/**
|
912
|
-
|
913
|
-
|
964
|
+
* Configures the upcast and downcast conversions for the `ckboxImageId` and `ckboxLinkId` attributes.
|
965
|
+
*/ _initConversion() {
|
914
966
|
const editor = this.editor;
|
915
967
|
// Convert `ckboxLinkId` => `data-ckbox-resource-id`.
|
916
968
|
editor.conversion.for('downcast').add((dispatcher)=>{
|
@@ -1031,8 +1083,8 @@ class CKBoxEditing extends Plugin {
|
|
1031
1083
|
}
|
1032
1084
|
}
|
1033
1085
|
/**
|
1034
|
-
|
1035
|
-
|
1086
|
+
* Registers post-fixers that add or remove the `ckboxLinkId` and `ckboxImageId` attributes.
|
1087
|
+
*/ _initFixers() {
|
1036
1088
|
const editor = this.editor;
|
1037
1089
|
const model = editor.model;
|
1038
1090
|
const selection = model.document.selection;
|
@@ -1145,15 +1197,27 @@ class CKBoxEditing extends Plugin {
|
|
1145
1197
|
return !!window.CKBox;
|
1146
1198
|
}
|
1147
1199
|
|
1148
|
-
|
1200
|
+
/**
|
1201
|
+
* The CKBox feature, a bridge between the CKEditor 5 WYSIWYG editor and the CKBox file manager and uploader.
|
1202
|
+
*
|
1203
|
+
* This is a "glue" plugin which enables:
|
1204
|
+
*
|
1205
|
+
* * {@link module:ckbox/ckboxediting~CKBoxEditing},
|
1206
|
+
* * {@link module:ckbox/ckboxui~CKBoxUI},
|
1207
|
+
*
|
1208
|
+
* See the {@glink features/file-management/ckbox CKBox integration} guide to learn how to configure and use this feature.
|
1209
|
+
*
|
1210
|
+
* Check out the {@glink features/images/image-upload/image-upload Image upload} guide to learn about other ways to upload
|
1211
|
+
* images into CKEditor 5.
|
1212
|
+
*/ class CKBox extends Plugin {
|
1149
1213
|
/**
|
1150
|
-
|
1151
|
-
|
1214
|
+
* @inheritDoc
|
1215
|
+
*/ static get pluginName() {
|
1152
1216
|
return 'CKBox';
|
1153
1217
|
}
|
1154
1218
|
/**
|
1155
|
-
|
1156
|
-
|
1219
|
+
* @inheritDoc
|
1220
|
+
*/ static get requires() {
|
1157
1221
|
return [
|
1158
1222
|
CKBoxEditing,
|
1159
1223
|
CKBoxUI
|
@@ -1197,18 +1261,43 @@ function createUrlChecker(allowExternalImagesEditing) {
|
|
1197
1261
|
return ()=>false;
|
1198
1262
|
}
|
1199
1263
|
|
1200
|
-
|
1264
|
+
/**
|
1265
|
+
* The CKBox edit image command.
|
1266
|
+
*
|
1267
|
+
* Opens the CKBox dialog for editing the image.
|
1268
|
+
*/ class CKBoxImageEditCommand extends Command {
|
1269
|
+
/**
|
1270
|
+
* The DOM element that acts as a mounting point for the CKBox Edit Image dialog.
|
1271
|
+
*/ _wrapper = null;
|
1201
1272
|
/**
|
1202
|
-
|
1203
|
-
|
1273
|
+
* The states of image processing in progress.
|
1274
|
+
*/ _processInProgress = new Set();
|
1275
|
+
/**
|
1276
|
+
* Determines if the element can be edited.
|
1277
|
+
*/ _canEdit;
|
1278
|
+
/**
|
1279
|
+
* A wrapper function to prepare mount options. Ensures that at most one preparation is in-flight.
|
1280
|
+
*/ _prepareOptions;
|
1281
|
+
/**
|
1282
|
+
* @inheritDoc
|
1283
|
+
*/ constructor(editor){
|
1284
|
+
super(editor);
|
1285
|
+
this.value = false;
|
1286
|
+
this._canEdit = createEditabilityChecker(editor.config.get('ckbox.allowExternalImagesEditing'));
|
1287
|
+
this._prepareOptions = abortableDebounce((signal, state)=>this._prepareOptionsAbortable(signal, state));
|
1288
|
+
this._prepareListeners();
|
1289
|
+
}
|
1290
|
+
/**
|
1291
|
+
* @inheritDoc
|
1292
|
+
*/ refresh() {
|
1204
1293
|
const editor = this.editor;
|
1205
1294
|
this.value = this._getValue();
|
1206
1295
|
const selectedElement = editor.model.document.selection.getSelectedElement();
|
1207
1296
|
this.isEnabled = !!selectedElement && this._canEdit(selectedElement) && !this._checkIfElementIsBeingProcessed(selectedElement);
|
1208
1297
|
}
|
1209
1298
|
/**
|
1210
|
-
|
1211
|
-
|
1299
|
+
* Opens the CKBox Image Editor dialog for editing the image.
|
1300
|
+
*/ execute() {
|
1212
1301
|
if (this._getValue()) {
|
1213
1302
|
return;
|
1214
1303
|
}
|
@@ -1235,8 +1324,8 @@ class CKBoxImageEditCommand extends Command {
|
|
1235
1324
|
});
|
1236
1325
|
}
|
1237
1326
|
/**
|
1238
|
-
|
1239
|
-
|
1327
|
+
* @inheritDoc
|
1328
|
+
*/ destroy() {
|
1240
1329
|
this._handleImageEditorClose();
|
1241
1330
|
this._prepareOptions.abort();
|
1242
1331
|
for (const state of this._processInProgress.values()){
|
@@ -1245,13 +1334,13 @@ class CKBoxImageEditCommand extends Command {
|
|
1245
1334
|
super.destroy();
|
1246
1335
|
}
|
1247
1336
|
/**
|
1248
|
-
|
1249
|
-
|
1337
|
+
* Indicates if the CKBox Image Editor dialog is already opened.
|
1338
|
+
*/ _getValue() {
|
1250
1339
|
return this._wrapper !== null;
|
1251
1340
|
}
|
1252
1341
|
/**
|
1253
|
-
|
1254
|
-
|
1342
|
+
* Creates the options object for the CKBox Image Editor dialog.
|
1343
|
+
*/ async _prepareOptionsAbortable(signal, state) {
|
1255
1344
|
const editor = this.editor;
|
1256
1345
|
const ckboxConfig = editor.config.get('ckbox');
|
1257
1346
|
const ckboxUtils = editor.plugins.get(CKBoxUtils);
|
@@ -1286,8 +1375,8 @@ class CKBoxImageEditCommand extends Command {
|
|
1286
1375
|
};
|
1287
1376
|
}
|
1288
1377
|
/**
|
1289
|
-
|
1290
|
-
|
1378
|
+
* Initializes event lister for an event of removing an image.
|
1379
|
+
*/ _prepareListeners() {
|
1291
1380
|
// Abort editing processing when the image has been removed.
|
1292
1381
|
this.listenTo(this.editor.model.document, 'change:data', ()=>{
|
1293
1382
|
const processingStates = this._getProcessingStatesOfDeletedImages();
|
@@ -1297,8 +1386,8 @@ class CKBoxImageEditCommand extends Command {
|
|
1297
1386
|
});
|
1298
1387
|
}
|
1299
1388
|
/**
|
1300
|
-
|
1301
|
-
|
1389
|
+
* Gets processing states of images that have been deleted in the mean time.
|
1390
|
+
*/ _getProcessingStatesOfDeletedImages() {
|
1302
1391
|
const states = [];
|
1303
1392
|
for (const state of this._processInProgress.values()){
|
1304
1393
|
if (state.element.root.rootName == '$graveyard') {
|
@@ -1316,8 +1405,8 @@ class CKBoxImageEditCommand extends Command {
|
|
1316
1405
|
return false;
|
1317
1406
|
}
|
1318
1407
|
/**
|
1319
|
-
|
1320
|
-
|
1408
|
+
* Closes the CKBox Image Editor dialog.
|
1409
|
+
*/ _handleImageEditorClose() {
|
1321
1410
|
if (!this._wrapper) {
|
1322
1411
|
return;
|
1323
1412
|
}
|
@@ -1327,9 +1416,9 @@ class CKBoxImageEditCommand extends Command {
|
|
1327
1416
|
this.refresh();
|
1328
1417
|
}
|
1329
1418
|
/**
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1419
|
+
* Save edited image. In case server respond with "success" replace with edited image,
|
1420
|
+
* otherwise show notification error.
|
1421
|
+
*/ _handleImageEditorSave(state, asset) {
|
1333
1422
|
const t = this.editor.locale.t;
|
1334
1423
|
const notification = this.editor.plugins.get(Notification);
|
1335
1424
|
const pendingActions = this.editor.plugins.get(PendingActions);
|
@@ -1359,9 +1448,9 @@ class CKBoxImageEditCommand extends Command {
|
|
1359
1448
|
});
|
1360
1449
|
}
|
1361
1450
|
/**
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1451
|
+
* Get asset's status on server. If server responds with "success" status then
|
1452
|
+
* image is already proceeded and ready for saving.
|
1453
|
+
*/ async _getAssetStatusFromServer(id, signal) {
|
1365
1454
|
const ckboxUtils = this.editor.plugins.get(CKBoxUtils);
|
1366
1455
|
const url = new URL('assets/' + id, this.editor.config.get('ckbox.serviceOrigin'));
|
1367
1456
|
const response = await sendHttpRequest({
|
@@ -1372,10 +1461,10 @@ class CKBoxImageEditCommand extends Command {
|
|
1372
1461
|
const status = response.metadata.metadataProcessingStatus;
|
1373
1462
|
if (!status || status == 'queued') {
|
1374
1463
|
/**
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1464
|
+
* Image has not been processed yet.
|
1465
|
+
*
|
1466
|
+
* @error ckbox-image-not-processed
|
1467
|
+
*/ throw new CKEditorError('ckbox-image-not-processed');
|
1379
1468
|
}
|
1380
1469
|
return {
|
1381
1470
|
data: {
|
@@ -1384,27 +1473,27 @@ class CKBoxImageEditCommand extends Command {
|
|
1384
1473
|
};
|
1385
1474
|
}
|
1386
1475
|
/**
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1476
|
+
* Waits for an asset to be processed.
|
1477
|
+
* It retries retrieving asset status from the server in case of failure.
|
1478
|
+
*/ async _waitForAssetProcessed(id, signal) {
|
1390
1479
|
const result = await retry(()=>this._getAssetStatusFromServer(id, signal), {
|
1391
1480
|
signal,
|
1392
1481
|
maxAttempts: 5
|
1393
1482
|
});
|
1394
1483
|
if (result.data.metadata.metadataProcessingStatus != 'success') {
|
1395
1484
|
/**
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1485
|
+
* The image processing failed.
|
1486
|
+
*
|
1487
|
+
* @error ckbox-image-processing-failed
|
1488
|
+
*/ throw new CKEditorError('ckbox-image-processing-failed');
|
1400
1489
|
}
|
1401
1490
|
return result;
|
1402
1491
|
}
|
1403
1492
|
/**
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1493
|
+
* Shows processing indicator while image is processing.
|
1494
|
+
*
|
1495
|
+
* @param asset Data about certain asset.
|
1496
|
+
*/ _showImageProcessingIndicator(element, asset) {
|
1408
1497
|
const editor = this.editor;
|
1409
1498
|
editor.editing.view.change((writer)=>{
|
1410
1499
|
const imageElementView = editor.editing.mapper.toViewElement(element);
|
@@ -1419,8 +1508,8 @@ class CKBoxImageEditCommand extends Command {
|
|
1419
1508
|
});
|
1420
1509
|
}
|
1421
1510
|
/**
|
1422
|
-
|
1423
|
-
|
1511
|
+
* Replace the edited image with the new one.
|
1512
|
+
*/ _replaceImage(element, asset) {
|
1424
1513
|
const editor = this.editor;
|
1425
1514
|
const { imageFallbackUrl, imageSources, imageWidth, imageHeight, imagePlaceholder } = prepareImageAssetAttributes(asset);
|
1426
1515
|
const previousSelectionRanges = Array.from(editor.model.document.selection.getRanges());
|
@@ -1449,32 +1538,19 @@ class CKBoxImageEditCommand extends Command {
|
|
1449
1538
|
writer.setSelection(previousSelectionRanges);
|
1450
1539
|
});
|
1451
1540
|
}
|
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
1541
|
}
|
1468
1542
|
|
1469
|
-
|
1543
|
+
/**
|
1544
|
+
* The CKBox image edit editing plugin.
|
1545
|
+
*/ class CKBoxImageEditEditing extends Plugin {
|
1470
1546
|
/**
|
1471
|
-
|
1472
|
-
|
1547
|
+
* @inheritDoc
|
1548
|
+
*/ static get pluginName() {
|
1473
1549
|
return 'CKBoxImageEditEditing';
|
1474
1550
|
}
|
1475
1551
|
/**
|
1476
|
-
|
1477
|
-
|
1552
|
+
* @inheritDoc
|
1553
|
+
*/ static get requires() {
|
1478
1554
|
return [
|
1479
1555
|
CKBoxEditing,
|
1480
1556
|
CKBoxUtils,
|
@@ -1485,8 +1561,8 @@ class CKBoxImageEditEditing extends Plugin {
|
|
1485
1561
|
];
|
1486
1562
|
}
|
1487
1563
|
/**
|
1488
|
-
|
1489
|
-
|
1564
|
+
* @inheritDoc
|
1565
|
+
*/ init() {
|
1490
1566
|
const { editor } = this;
|
1491
1567
|
editor.commands.add('ckboxImageEdit', new CKBoxImageEditCommand(editor));
|
1492
1568
|
}
|
@@ -1494,15 +1570,20 @@ class CKBoxImageEditEditing extends Plugin {
|
|
1494
1570
|
|
1495
1571
|
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
1572
|
|
1497
|
-
|
1573
|
+
/**
|
1574
|
+
* The UI plugin of the CKBox image edit feature.
|
1575
|
+
*
|
1576
|
+
* It registers the `'ckboxImageEdit'` UI button in the editor's {@link module:ui/componentfactory~ComponentFactory component factory}
|
1577
|
+
* that allows you to open the CKBox dialog and edit the image.
|
1578
|
+
*/ class CKBoxImageEditUI extends Plugin {
|
1498
1579
|
/**
|
1499
|
-
|
1500
|
-
|
1580
|
+
* @inheritDoc
|
1581
|
+
*/ static get pluginName() {
|
1501
1582
|
return 'CKBoxImageEditUI';
|
1502
1583
|
}
|
1503
1584
|
/**
|
1504
|
-
|
1505
|
-
|
1585
|
+
* @inheritDoc
|
1586
|
+
*/ init() {
|
1506
1587
|
const editor = this.editor;
|
1507
1588
|
editor.ui.componentFactory.add('ckboxImageEdit', (locale)=>{
|
1508
1589
|
const command = editor.commands.get('ckboxImageEdit');
|
@@ -1525,15 +1606,17 @@ class CKBoxImageEditUI extends Plugin {
|
|
1525
1606
|
}
|
1526
1607
|
}
|
1527
1608
|
|
1528
|
-
|
1609
|
+
/**
|
1610
|
+
* The CKBox image edit feature.
|
1611
|
+
*/ class CKBoxImageEdit extends Plugin {
|
1529
1612
|
/**
|
1530
|
-
|
1531
|
-
|
1613
|
+
* @inheritDoc
|
1614
|
+
*/ static get pluginName() {
|
1532
1615
|
return 'CKBoxImageEdit';
|
1533
1616
|
}
|
1534
1617
|
/**
|
1535
|
-
|
1536
|
-
|
1618
|
+
* @inheritDoc
|
1619
|
+
*/ static get requires() {
|
1537
1620
|
return [
|
1538
1621
|
CKBoxImageEditEditing,
|
1539
1622
|
CKBoxImageEditUI
|