@ckeditor/ckeditor5-utils 41.4.1 → 42.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7,9 +7,47 @@ import { isObject, isString, isPlainObject, cloneDeepWith, isElement, isFunction
7
7
  /**
8
8
  * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
9
9
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
10
- */ /* globals navigator:false */ /**
11
- * @module utils/env
10
+ */ /* globals window, document */ /**
11
+ * @module utils/dom/global
12
+ */ // This interface exists to make our API pages more readable.
13
+ /**
14
+ * A helper (module) giving an access to the global DOM objects such as `window` and `document`.
12
15
  */ /**
16
+ * A helper (module) giving an access to the global DOM objects such as `window` and
17
+ * `document`. Accessing these objects using this helper allows easy and bulletproof
18
+ * testing, i.e. stubbing native properties:
19
+ *
20
+ * ```ts
21
+ * import { global } from 'ckeditor5/utils';
22
+ *
23
+ * // This stub will work for any code using global module.
24
+ * testUtils.sinon.stub( global, 'window', {
25
+ * innerWidth: 10000
26
+ * } );
27
+ *
28
+ * console.log( global.window.innerWidth );
29
+ * ```
30
+ */ let globalVar; // named globalVar instead of global: https://github.com/ckeditor/ckeditor5/issues/12971
31
+ // In some environments window and document API might not be available.
32
+ try {
33
+ globalVar = {
34
+ window,
35
+ document
36
+ };
37
+ } catch (e) {
38
+ // It's not possible to mock a window object to simulate lack of a window object without writing extremely convoluted code.
39
+ /* istanbul ignore next -- @preserve */ // Let's cast it to not change module's API.
40
+ // We only handle this so loading editor in environments without window and document doesn't fail.
41
+ // For better DX we shouldn't introduce mixed types and require developers to check the type manually.
42
+ // This module should not be used on purpose in any environment outside browser.
43
+ globalVar = {
44
+ window: {},
45
+ document: {}
46
+ };
47
+ }
48
+ var global = globalVar;
49
+
50
+ /**
13
51
  * Safely returns `userAgent` from browser's navigator API in a lower case.
14
52
  * If navigator API is not available it will return an empty string.
15
53
  */ function getUserAgent() {
@@ -20,23 +58,25 @@ import { isObject, isString, isPlainObject, cloneDeepWith, isElement, isFunction
20
58
  return '';
21
59
  }
22
60
  }
23
- const userAgent = getUserAgent();
61
+ const userAgent = /* #__PURE__ */ getUserAgent();
24
62
  /**
25
63
  * A namespace containing environment and browser information.
26
64
  */ const env = {
27
- isMac: isMac(userAgent),
28
- isWindows: isWindows(userAgent),
29
- isGecko: isGecko(userAgent),
30
- isSafari: isSafari(userAgent),
31
- isiOS: isiOS(userAgent),
32
- isAndroid: isAndroid(userAgent),
33
- isBlink: isBlink(userAgent),
34
- isMediaForcedColors: isMediaForcedColors(),
65
+ isMac: /* #__PURE__ */ isMac(userAgent),
66
+ isWindows: /* #__PURE__ */ isWindows(userAgent),
67
+ isGecko: /* #__PURE__ */ isGecko(userAgent),
68
+ isSafari: /* #__PURE__ */ isSafari(userAgent),
69
+ isiOS: /* #__PURE__ */ isiOS(userAgent),
70
+ isAndroid: /* #__PURE__ */ isAndroid(userAgent),
71
+ isBlink: /* #__PURE__ */ isBlink(userAgent),
72
+ get isMediaForcedColors () {
73
+ return isMediaForcedColors();
74
+ },
35
75
  get isMotionReduced () {
36
76
  return isMotionReduced();
37
77
  },
38
78
  features: {
39
- isRegExpUnicodePropertySupported: isRegExpUnicodePropertySupported()
79
+ isRegExpUnicodePropertySupported: /* #__PURE__ */ isRegExpUnicodePropertySupported()
40
80
  }
41
81
  };
42
82
  /**
@@ -116,13 +156,17 @@ const userAgent = getUserAgent();
116
156
  }
117
157
  /**
118
158
  * Checks if the user agent has enabled a forced colors mode (e.g. Windows High Contrast mode).
159
+ *
160
+ * Returns `false` in environments where `window` global object is not available.
119
161
  */ function isMediaForcedColors() {
120
- return window.matchMedia('(forced-colors: active)').matches;
162
+ return global.window.matchMedia ? global.window.matchMedia('(forced-colors: active)').matches : false;
121
163
  }
122
164
  /**
123
- * Checks if user enabled "prefers reduced motion" setting in browser.
165
+ * Checks if the user enabled "prefers reduced motion" setting in browser.
166
+ *
167
+ * Returns `false` in environments where `window` global object is not available.
124
168
  */ function isMotionReduced() {
125
- return window.matchMedia('(prefers-reduced-motion)').matches;
169
+ return global.window.matchMedia ? global.window.matchMedia('(prefers-reduced-motion)').matches : false;
126
170
  }
127
171
 
128
172
  /**
@@ -620,11 +664,45 @@ diff.fastDiff = fastDiff;
620
664
  };
621
665
  }
622
666
 
623
- class EventInfo {
667
+ /**
668
+ * The event object passed to event callbacks. It is used to provide information about the event as well as a tool to
669
+ * manipulate it.
670
+ */ class EventInfo {
671
+ /**
672
+ * The object that fired the event.
673
+ */ source;
624
674
  /**
625
- * @param source The emitter.
626
- * @param name The event name.
627
- */ constructor(source, name){
675
+ * The event name.
676
+ */ name;
677
+ /**
678
+ * Path this event has followed. See {@link module:utils/emittermixin~Emitter#delegate}.
679
+ */ path;
680
+ /**
681
+ * Stops the event emitter to call further callbacks for this event interaction.
682
+ */ stop;
683
+ /**
684
+ * Removes the current callback from future interactions of this event.
685
+ */ off;
686
+ /**
687
+ * The value which will be returned by {@link module:utils/emittermixin~Emitter#fire}.
688
+ *
689
+ * It's `undefined` by default and can be changed by an event listener:
690
+ *
691
+ * ```ts
692
+ * dataController.fire( 'getSelectedContent', ( evt ) => {
693
+ * // This listener will make `dataController.fire( 'getSelectedContent' )`
694
+ * // always return an empty DocumentFragment.
695
+ * evt.return = new DocumentFragment();
696
+ *
697
+ * // Make sure no other listeners are executed.
698
+ * evt.stop();
699
+ * } );
700
+ * ```
701
+ */ return;
702
+ /**
703
+ * @param source The emitter.
704
+ * @param name The event name.
705
+ */ constructor(source, name){
628
706
  this.source = source;
629
707
  this.name = name;
630
708
  this.path = [];
@@ -675,6 +753,10 @@ class EventInfo {
675
753
  * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
676
754
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
677
755
  */ /**
756
+ * @module utils/priorities
757
+ */ /**
758
+ * String representing a priority value.
759
+ */ /**
678
760
  * Provides group of constants to use instead of hardcoding numeric priority values.
679
761
  */ const priorities = {
680
762
  get (priority = 'normal') {
@@ -715,57 +797,90 @@ class EventInfo {
715
797
  */ /* globals console */ /**
716
798
  * URL to the documentation with error codes.
717
799
  */ const DOCUMENTATION_URL = 'https://ckeditor.com/docs/ckeditor5/latest/support/error-codes.html';
718
- class CKEditorError extends Error {
800
+ /**
801
+ * The CKEditor error class.
802
+ *
803
+ * You should throw `CKEditorError` when:
804
+ *
805
+ * * An unexpected situation occurred and the editor (most probably) will not work properly. Such exception will be handled
806
+ * by the {@link module:watchdog/watchdog~Watchdog watchdog} (if it is integrated),
807
+ * * If the editor is incorrectly integrated or the editor API is used in the wrong way. This way you will give
808
+ * feedback to the developer as soon as possible. Keep in mind that for common integration issues which should not
809
+ * stop editor initialization (like missing upload adapter, wrong name of a toolbar component) we use
810
+ * {@link module:utils/ckeditorerror~logWarning `logWarning()`} and
811
+ * {@link module:utils/ckeditorerror~logError `logError()`}
812
+ * to improve developers experience and let them see the a working editor as soon as possible.
813
+ *
814
+ * ```ts
815
+ * /**
816
+ * * Error thrown when a plugin cannot be loaded due to JavaScript errors, lack of plugins with a given name, etc.
817
+ * *
818
+ * * @error plugin-load
819
+ * * @param pluginName The name of the plugin that could not be loaded.
820
+ * * @param moduleName The name of the module which tried to load this plugin.
821
+ * *\/
822
+ * throw new CKEditorError( 'plugin-load', {
823
+ * pluginName: 'foo',
824
+ * moduleName: 'bar'
825
+ * } );
826
+ * ```
827
+ */ class CKEditorError extends Error {
828
+ /**
829
+ * A context of the error by which the Watchdog is able to determine which editor crashed.
830
+ */ context;
831
+ /**
832
+ * The additional error data passed to the constructor. Undefined if none was passed.
833
+ */ data;
834
+ /**
835
+ * Creates an instance of the CKEditorError class.
836
+ *
837
+ * @param errorName The error id in an `error-name` format. A link to this error documentation page will be added
838
+ * to the thrown error's `message`.
839
+ * @param context A context of the error by which the {@link module:watchdog/watchdog~Watchdog watchdog}
840
+ * is able to determine which editor crashed. It should be an editor instance or a property connected to it. It can be also
841
+ * a `null` value if the editor should not be restarted in case of the error (e.g. during the editor initialization).
842
+ * The error context should be checked using the `areConnectedThroughProperties( editor, context )` utility
843
+ * to check if the object works as the context.
844
+ * @param data Additional data describing the error. A stringified version of this object
845
+ * will be appended to the error message, so the data are quickly visible in the console. The original
846
+ * data object will also be later available under the {@link #data} property.
847
+ */ constructor(errorName, context, data){
848
+ super(getErrorMessage(errorName, data));
849
+ this.name = 'CKEditorError';
850
+ this.context = context;
851
+ this.data = data;
852
+ }
719
853
  /**
720
- * Checks if the error is of the `CKEditorError` type.
721
- */ is(type) {
854
+ * Checks if the error is of the `CKEditorError` type.
855
+ */ is(type) {
722
856
  return type === 'CKEditorError';
723
857
  }
724
858
  /**
725
- * A utility that ensures that the thrown error is a {@link module:utils/ckeditorerror~CKEditorError} one.
726
- * It is useful when combined with the {@link module:watchdog/watchdog~Watchdog} feature, which can restart the editor in case
727
- * of a {@link module:utils/ckeditorerror~CKEditorError} error.
728
- *
729
- * @param err The error to rethrow.
730
- * @param context An object connected through properties with the editor instance. This context will be used
731
- * by the watchdog to verify which editor should be restarted.
732
- */ static rethrowUnexpectedError(err, context) {
859
+ * A utility that ensures that the thrown error is a {@link module:utils/ckeditorerror~CKEditorError} one.
860
+ * It is useful when combined with the {@link module:watchdog/watchdog~Watchdog} feature, which can restart the editor in case
861
+ * of a {@link module:utils/ckeditorerror~CKEditorError} error.
862
+ *
863
+ * @param err The error to rethrow.
864
+ * @param context An object connected through properties with the editor instance. This context will be used
865
+ * by the watchdog to verify which editor should be restarted.
866
+ */ static rethrowUnexpectedError(err, context) {
733
867
  if (err.is && err.is('CKEditorError')) {
734
868
  throw err;
735
869
  }
736
870
  /**
737
- * An unexpected error occurred inside the CKEditor 5 codebase. This error will look like the original one
738
- * to make the debugging easier.
739
- *
740
- * This error is only useful when the editor is initialized using the {@link module:watchdog/watchdog~Watchdog} feature.
741
- * In case of such error (or any {@link module:utils/ckeditorerror~CKEditorError} error) the watchdog should restart the editor.
742
- *
743
- * @error unexpected-error
744
- */ const error = new CKEditorError(err.message, context);
871
+ * An unexpected error occurred inside the CKEditor 5 codebase. This error will look like the original one
872
+ * to make the debugging easier.
873
+ *
874
+ * This error is only useful when the editor is initialized using the {@link module:watchdog/watchdog~Watchdog} feature.
875
+ * In case of such error (or any {@link module:utils/ckeditorerror~CKEditorError} error) the watchdog should restart the editor.
876
+ *
877
+ * @error unexpected-error
878
+ */ const error = new CKEditorError(err.message, context);
745
879
  // Restore the original stack trace to make the error look like the original one.
746
880
  // See https://github.com/ckeditor/ckeditor5/issues/5595 for more details.
747
881
  error.stack = err.stack;
748
882
  throw error;
749
883
  }
750
- /**
751
- * Creates an instance of the CKEditorError class.
752
- *
753
- * @param errorName The error id in an `error-name` format. A link to this error documentation page will be added
754
- * to the thrown error's `message`.
755
- * @param context A context of the error by which the {@link module:watchdog/watchdog~Watchdog watchdog}
756
- * is able to determine which editor crashed. It should be an editor instance or a property connected to it. It can be also
757
- * a `null` value if the editor should not be restarted in case of the error (e.g. during the editor initialization).
758
- * The error context should be checked using the `areConnectedThroughProperties( editor, context )` utility
759
- * to check if the object works as the context.
760
- * @param data Additional data describing the error. A stringified version of this object
761
- * will be appended to the error message, so the data are quickly visible in the console. The original
762
- * data object will also be later available under the {@link #data} property.
763
- */ constructor(errorName, context, data){
764
- super(getErrorMessage(errorName, data));
765
- this.name = 'CKEditorError';
766
- this.context = context;
767
- this.data = data;
768
- }
769
884
  }
770
885
  /**
771
886
  * Logs a warning to the console with a properly formatted message and adds a link to the documentation.
@@ -850,144 +965,146 @@ class CKEditorError extends Error {
850
965
  ];
851
966
  }
852
967
 
853
- const version = '41.4.1';
968
+ const version = '42.0.0-alpha.0';
854
969
  // The second argument is not a month. It is `monthIndex` and starts from `0`.
855
- const releaseDate = new Date(2024, 4, 16);
970
+ const releaseDate = new Date(2024, 5, 5);
856
971
  /* istanbul ignore next -- @preserve */ if (globalThis.CKEDITOR_VERSION) {
857
972
  /**
858
- * This error is thrown when due to a mistake in how CKEditor 5 was installed or initialized, some
859
- * of its modules were duplicated (evaluated and executed twice). Module duplication leads to inevitable runtime
860
- * errors.
861
- *
862
- * There are many situations in which some modules can be loaded twice. In the worst case scenario,
863
- * you may need to check your project for each of these issues and fix them all.
864
- *
865
- * # Trying to add a plugin to an existing build
866
- *
867
- * If you import an existing CKEditor 5 build and a plugin like this:
868
- *
869
- * ```ts
870
- * import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
871
- * import Highlight from '@ckeditor/ckeditor5-highlight/src/highlight';
872
- * ```
873
- *
874
- * Then your project loads some CKEditor 5 packages twice. How does it happen?
875
- *
876
- * The build package contains a file which is already compiled with webpack. This means
877
- * that it contains all the necessary code from e.g. `@ckeditor/ckeditor5-engine` and `@ckeditor/ckeditor5-utils`.
878
- *
879
- * However, the `Highlight` plugin imports some of the modules from these packages, too. If you ask webpack to
880
- * build such a project, you will end up with the modules being included (and run) twice – first, because they are
881
- * included inside the build package, and second, because they are required by the `Highlight` plugin.
882
- *
883
- * Therefore, **you must never add plugins to an existing build** unless your plugin has no dependencies.
884
- *
885
- * Adding plugins to a build is done by taking the source version of this build (so, before it was built with webpack)
886
- * and adding plugins there. In this situation, webpack will know that it only needs to load each plugin once.
887
- *
888
- * Read more in the {@glink installation/plugins/installing-plugins Installing plugins} guide.
889
- *
890
- * # Confused an editor build with an editor implementation
891
- *
892
- * This scenario is very similar to the previous one, but has a different origin.
893
- *
894
- * Let's assume that you wanted to use CKEditor 5 from source, as explained in the
895
- * {@glink installation/advanced/alternative-setups/integrating-from-source-webpack "Building from source"} section
896
- * or in the {@glink framework/quick-start "Quick start"} guide of CKEditor 5 Framework.
897
- *
898
- * The correct way to do so is to import an editor and plugins and run them together like this:
899
- *
900
- * ```ts
901
- * import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
902
- * import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
903
- * import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
904
- * import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
905
- * import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
906
- *
907
- * ClassicEditor
908
- * .create( document.querySelector( '#editor' ), {
909
- * plugins: [ Essentials, Paragraph, Bold, Italic ],
910
- * toolbar: [ 'bold', 'italic' ]
911
- * } )
912
- * .then( editor => {
913
- * console.log( 'Editor was initialized', editor );
914
- * } )
915
- * .catch( error => {
916
- * console.error( error.stack );
917
- * } );
918
- * ```
919
- *
920
- * However, you might have mistakenly imported a build instead of the source `ClassicEditor`. In this case
921
- * your imports will look like this:
922
- *
923
- * ```ts
924
- * import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
925
- * import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
926
- * import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
927
- * import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
928
- * import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
929
- * ```
930
- *
931
- * This creates the same situation as in the previous section because you use a build together with source plugins.
932
- *
933
- * Remember: `@ckeditor/ckeditor5-build-*` packages contain editor builds and `@ckeditor/ckeditor5-editor-*` contain source editors.
934
- *
935
- * # Loading two or more builds on one page
936
- *
937
- * If you use CKEditor 5 builds, you might have loaded two (or more) `ckeditor.js` files on one web page.
938
- * Check your web page for duplicated `<script>` elements or make sure your page builder/bundler includes CKEditor only once.
939
- *
940
- * If you want to use two different types of editors at once, see the
941
- * {@glink installation/advanced/using-two-editors "Using two different editors"}
942
- * section.
943
- *
944
- * # Using outdated packages
945
- *
946
- * Building CKEditor 5 from source requires using multiple npm packages. These packages have their dependencies
947
- * to other packages. If you use the latest version of, for example, `@ckeditor/ckeditor5-editor-classic` with
948
- * an outdated version of `@ckeditor/ckeditor5-image`, npm or yarn will need to install two different versions of
949
- * `@ckeditor/ckeditor5-core` because `@ckeditor/ckeditor5-editor-classic` and `@ckeditor/ckeditor5-image` may require
950
- * different versions of the core package.
951
- *
952
- * The solution to this issue is to update all packages to their latest version. We recommend
953
- * using tools like [`npm-check-updates`](https://www.npmjs.com/package/npm-check-updates) which simplify this process.
954
- *
955
- * # Conflicting version of dependencies
956
- *
957
- * This is a special case of the previous scenario. If you use CKEditor 5 with some third-party plugins,
958
- * it may happen that even if you use the latest versions of the official packages and the latest version of
959
- * these third-party packages, there will be a conflict between some of their dependencies.
960
- *
961
- * Such a problem can be resolved by either downgrading CKEditor 5 packages (which we do not recommend) or
962
- * asking the author of the third-party package to upgrade its depdendencies (or forking their project and doing this yourself).
963
- *
964
- * **Note:** All official CKEditor 5 packages (excluding integrations and `ckeditor5-dev-*` packages) are released in the
965
- * same major version. This means that in the `x.y.z` version, the `x` is the same for all packages. This is the simplest way to check
966
- * whether you use packages coming from the same CKEditor 5 version. You can read more about versioning in the
967
- * {@glink updating/versioning-policy Versioning policy} guide.
968
- *
969
- * # Packages were duplicated in `node_modules`
970
- *
971
- * In some situations, especially when calling `npm install` multiple times, it may happen
972
- * that npm will not correctly "deduplicate" packages.
973
- *
974
- * Normally, npm deduplicates all packages so, for example, `@ckeditor/ckeditor5-core` is installed only once in `node_modules/`.
975
- * However, it is known to fail to do so from time to time.
976
- *
977
- * We recommend checking if any of the steps listed below help:
978
- *
979
- * * `rm -rf node_modules && npm install` to make sure you have a clean `node_modules/` directory. This step
980
- * is known to help in most cases.
981
- * * If you use `yarn.lock` or `package-lock.json`, remove it before `npm install`.
982
- * * Check whether all CKEditor 5 packages are up to date and reinstall them
983
- * if you changed anything (`rm -rf node_modules && npm install`).
984
- *
985
- * If all packages are correct and compatible with each other, the steps above are known to help. If not, you may
986
- * try to check with `npm ls` how many times packages like `@ckeditor/ckeditor5-core`, `@ckeditor/ckeditor5-engine` and
987
- *`@ckeditor/ckeditor5-utils` are installed. If more than once, verify which package causes that.
988
- *
989
- * @error ckeditor-duplicated-modules
990
- */ throw new CKEditorError('ckeditor-duplicated-modules', null);
973
+ * The best solution to avoid this error is migrating your CKEditor&nbsp;5 instance to
974
+ * {@glink updating/new-installation-methods new installation methods}.
975
+ *
976
+ * Mentioned below are predefined builds, which are a deprecated installation method. The solutions
977
+ * provided are kept here for legacy support only.
978
+ *
979
+ * This error is thrown when due to a mistake in how CKEditor 5 was installed or initialized, some
980
+ * of its modules were duplicated (evaluated and executed twice). Module duplication leads to inevitable runtime
981
+ * errors.
982
+ *
983
+ * There are many situations in which some modules can be loaded twice. In the worst case scenario,
984
+ * you may need to check your project for each of these issues and fix them all.
985
+ *
986
+ * # Trying to add a plugin to an existing build
987
+ *
988
+ * If you import an existing CKEditor 5 build and a plugin like this:
989
+ *
990
+ * ```ts
991
+ * import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
992
+ * import Highlight from '@ckeditor/ckeditor5-highlight/src/highlight';
993
+ * ```
994
+ *
995
+ * Then your project loads some CKEditor 5 packages twice. How does it happen?
996
+ *
997
+ * The build package contains a file which is already compiled with webpack. This means
998
+ * that it contains all the necessary code from e.g. `@ckeditor/ckeditor5-engine` and `@ckeditor/ckeditor5-utils`.
999
+ *
1000
+ * However, the `Highlight` plugin imports some of the modules from these packages, too. If you ask webpack to
1001
+ * build such a project, you will end up with the modules being included (and run) twice &ndash; first, because they are
1002
+ * included inside the build package, and second, because they are required by the `Highlight` plugin.
1003
+ *
1004
+ * Therefore, **you must never add plugins to an existing build** unless your plugin has no dependencies.
1005
+ *
1006
+ * Adding plugins to a build is done by taking the source version of this build (so, before it was built with webpack)
1007
+ * and adding plugins there. In this situation, webpack will know that it only needs to load each plugin once.
1008
+ *
1009
+ * # Confused an editor build with an editor implementation
1010
+ *
1011
+ * This scenario is very similar to the previous one, but has a different origin.
1012
+ *
1013
+ * Let's assume that you wanted to use CKEditor 5 from source.
1014
+ *
1015
+ * The correct way to do so is to import an editor and plugins and run them together like this:
1016
+ *
1017
+ * ```ts
1018
+ * import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
1019
+ * import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
1020
+ * import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
1021
+ * import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
1022
+ * import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
1023
+ *
1024
+ * ClassicEditor
1025
+ * .create( document.querySelector( '#editor' ), {
1026
+ * plugins: [ Essentials, Paragraph, Bold, Italic ],
1027
+ * toolbar: [ 'bold', 'italic' ]
1028
+ * } )
1029
+ * .then( editor => {
1030
+ * console.log( 'Editor was initialized', editor );
1031
+ * } )
1032
+ * .catch( error => {
1033
+ * console.error( error.stack );
1034
+ * } );
1035
+ * ```
1036
+ *
1037
+ * However, you might have mistakenly imported a build instead of the source `ClassicEditor`. In this case
1038
+ * your imports will look like this:
1039
+ *
1040
+ * ```ts
1041
+ * import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
1042
+ * import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
1043
+ * import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
1044
+ * import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
1045
+ * import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
1046
+ * ```
1047
+ *
1048
+ * This creates the same situation as in the previous section because you use a build together with source plugins.
1049
+ *
1050
+ * Remember: `@ckeditor/ckeditor5-build-*` packages contain editor builds and `@ckeditor/ckeditor5-editor-*` contain source editors.
1051
+ *
1052
+ * # Loading two or more builds on one page
1053
+ *
1054
+ * If you use CKEditor 5 builds, you might have loaded two (or more) `ckeditor.js` files on one web page.
1055
+ * Check your web page for duplicated `<script>` elements or make sure your page builder/bundler includes CKEditor only once.
1056
+ *
1057
+ * If you want to use two different types of editors at once, see the
1058
+ * {@glink getting-started/legacy/advanced/using-two-editors "Using two different editors"}
1059
+ * section.
1060
+ *
1061
+ * # Using outdated packages
1062
+ *
1063
+ * Building CKEditor 5 from source requires using multiple npm packages. These packages have their dependencies
1064
+ * to other packages. If you use the latest version of, for example, `@ckeditor/ckeditor5-editor-classic` with
1065
+ * an outdated version of `@ckeditor/ckeditor5-image`, npm or yarn will need to install two different versions of
1066
+ * `@ckeditor/ckeditor5-core` because `@ckeditor/ckeditor5-editor-classic` and `@ckeditor/ckeditor5-image` may require
1067
+ * different versions of the core package.
1068
+ *
1069
+ * The solution to this issue is to update all packages to their latest version. We recommend
1070
+ * using tools like [`npm-check-updates`](https://www.npmjs.com/package/npm-check-updates) which simplify this process.
1071
+ *
1072
+ * # Conflicting version of dependencies
1073
+ *
1074
+ * This is a special case of the previous scenario. If you use CKEditor 5 with some third-party plugins,
1075
+ * it may happen that even if you use the latest versions of the official packages and the latest version of
1076
+ * these third-party packages, there will be a conflict between some of their dependencies.
1077
+ *
1078
+ * Such a problem can be resolved by either downgrading CKEditor 5 packages (which we do not recommend) or
1079
+ * asking the author of the third-party package to upgrade its depdendencies (or forking their project and doing this yourself).
1080
+ *
1081
+ * **Note:** All official CKEditor 5 packages (excluding integrations and `ckeditor5-dev-*` packages) are released in the
1082
+ * same major version. This means that in the `x.y.z` version, the `x` is the same for all packages. This is the simplest way to check
1083
+ * whether you use packages coming from the same CKEditor 5 version. You can read more about versioning in the
1084
+ * {@glink updating/versioning-policy Versioning policy} guide.
1085
+ *
1086
+ * # Packages were duplicated in `node_modules`
1087
+ *
1088
+ * In some situations, especially when calling `npm install` multiple times, it may happen
1089
+ * that npm will not correctly "deduplicate" packages.
1090
+ *
1091
+ * Normally, npm deduplicates all packages so, for example, `@ckeditor/ckeditor5-core` is installed only once in `node_modules/`.
1092
+ * However, it is known to fail to do so from time to time.
1093
+ *
1094
+ * We recommend checking if any of the steps listed below help:
1095
+ *
1096
+ * * `rm -rf node_modules && npm install` to make sure you have a clean `node_modules/` directory. This step
1097
+ * is known to help in most cases.
1098
+ * * If you use `yarn.lock` or `package-lock.json`, remove it before `npm install`.
1099
+ * * Check whether all CKEditor 5 packages are up to date and reinstall them
1100
+ * if you changed anything (`rm -rf node_modules && npm install`).
1101
+ *
1102
+ * If all packages are correct and compatible with each other, the steps above are known to help. If not, you may
1103
+ * try to check with `npm ls` how many times packages like `@ckeditor/ckeditor5-core`, `@ckeditor/ckeditor5-engine` and
1104
+ *`@ckeditor/ckeditor5-utils` are installed. If more than once, verify which package causes that.
1105
+ *
1106
+ * @error ckeditor-duplicated-modules
1107
+ */ throw new CKEditorError('ckeditor-duplicated-modules', null);
991
1108
  } else {
992
1109
  globalThis.CKEDITOR_VERSION = version;
993
1110
  }
@@ -995,7 +1112,7 @@ const releaseDate = new Date(2024, 4, 16);
995
1112
  const _listeningTo = Symbol('listeningTo');
996
1113
  const _emitterId = Symbol('emitterId');
997
1114
  const _delegations = Symbol('delegations');
998
- const defaultEmitterClass$1 = EmitterMixin(Object);
1115
+ const defaultEmitterClass$1 = /* #__PURE__ */ EmitterMixin(Object);
999
1116
  function EmitterMixin(base) {
1000
1117
  if (!base) {
1001
1118
  return defaultEmitterClass$1;
@@ -1426,7 +1543,7 @@ const boundObservablesSymbol = Symbol('boundObservables');
1426
1543
  const boundPropertiesSymbol = Symbol('boundProperties');
1427
1544
  const decoratedMethods = Symbol('decoratedMethods');
1428
1545
  const decoratedOriginal = Symbol('decoratedOriginal');
1429
- const defaultObservableClass = ObservableMixin(EmitterMixin());
1546
+ const defaultObservableClass = /* #__PURE__ */ ObservableMixin(/* #__PURE__ */ EmitterMixin());
1430
1547
  function ObservableMixin(base) {
1431
1548
  if (!base) {
1432
1549
  return defaultObservableClass;
@@ -1444,22 +1561,22 @@ function ObservableMixin(base) {
1444
1561
  const properties = this[observablePropertiesSymbol];
1445
1562
  if (name in this && !properties.has(name)) {
1446
1563
  /**
1447
- * Cannot override an existing property.
1448
- *
1449
- * This error is thrown when trying to {@link module:utils/observablemixin~Observable#set set} a property with
1450
- * a name of an already existing property. For example:
1451
- *
1452
- * ```ts
1453
- * let observable = new Model();
1454
- * observable.property = 1;
1455
- * observable.set( 'property', 2 ); // throws
1456
- *
1457
- * observable.set( 'property', 1 );
1458
- * observable.set( 'property', 2 ); // ok, because this is an existing property.
1459
- * ```
1460
- *
1461
- * @error observable-set-cannot-override
1462
- */ throw new CKEditorError('observable-set-cannot-override', this);
1564
+ * Cannot override an existing property.
1565
+ *
1566
+ * This error is thrown when trying to {@link module:utils/observablemixin~Observable#set set} a property with
1567
+ * a name of an already existing property. For example:
1568
+ *
1569
+ * ```ts
1570
+ * let observable = new Model();
1571
+ * observable.property = 1;
1572
+ * observable.set( 'property', 2 ); // throws
1573
+ *
1574
+ * observable.set( 'property', 1 );
1575
+ * observable.set( 'property', 2 ); // ok, because this is an existing property.
1576
+ * ```
1577
+ *
1578
+ * @error observable-set-cannot-override
1579
+ */ throw new CKEditorError('observable-set-cannot-override', this);
1463
1580
  }
1464
1581
  Object.defineProperty(this, name, {
1465
1582
  enumerable: true,
@@ -1489,27 +1606,27 @@ function ObservableMixin(base) {
1489
1606
  bind(...bindProperties) {
1490
1607
  if (!bindProperties.length || !isStringArray(bindProperties)) {
1491
1608
  /**
1492
- * All properties must be strings.
1493
- *
1494
- * @error observable-bind-wrong-properties
1495
- */ throw new CKEditorError('observable-bind-wrong-properties', this);
1609
+ * All properties must be strings.
1610
+ *
1611
+ * @error observable-bind-wrong-properties
1612
+ */ throw new CKEditorError('observable-bind-wrong-properties', this);
1496
1613
  }
1497
1614
  if (new Set(bindProperties).size !== bindProperties.length) {
1498
1615
  /**
1499
- * Properties must be unique.
1500
- *
1501
- * @error observable-bind-duplicate-properties
1502
- */ throw new CKEditorError('observable-bind-duplicate-properties', this);
1616
+ * Properties must be unique.
1617
+ *
1618
+ * @error observable-bind-duplicate-properties
1619
+ */ throw new CKEditorError('observable-bind-duplicate-properties', this);
1503
1620
  }
1504
1621
  initObservable(this);
1505
1622
  const boundProperties = this[boundPropertiesSymbol];
1506
1623
  bindProperties.forEach((propertyName)=>{
1507
1624
  if (boundProperties.has(propertyName)) {
1508
1625
  /**
1509
- * Cannot bind the same property more than once.
1510
- *
1511
- * @error observable-bind-rebind
1512
- */ throw new CKEditorError('observable-bind-rebind', this);
1626
+ * Cannot bind the same property more than once.
1627
+ *
1628
+ * @error observable-bind-rebind
1629
+ */ throw new CKEditorError('observable-bind-rebind', this);
1513
1630
  }
1514
1631
  });
1515
1632
  const bindings = new Map();
@@ -1540,10 +1657,10 @@ function ObservableMixin(base) {
1540
1657
  if (unbindProperties.length) {
1541
1658
  if (!isStringArray(unbindProperties)) {
1542
1659
  /**
1543
- * Properties must be strings.
1544
- *
1545
- * @error observable-unbind-wrong-properties
1546
- */ throw new CKEditorError('observable-unbind-wrong-properties', this);
1660
+ * Properties must be strings.
1661
+ *
1662
+ * @error observable-unbind-wrong-properties
1663
+ */ throw new CKEditorError('observable-unbind-wrong-properties', this);
1547
1664
  }
1548
1665
  unbindProperties.forEach((propertyName)=>{
1549
1666
  const binding = boundProperties.get(propertyName);
@@ -1578,12 +1695,12 @@ function ObservableMixin(base) {
1578
1695
  const originalMethod = this[methodName];
1579
1696
  if (!originalMethod) {
1580
1697
  /**
1581
- * Cannot decorate an undefined method.
1582
- *
1583
- * @error observablemixin-cannot-decorate-undefined
1584
- * @param {Object} object The object which method should be decorated.
1585
- * @param {String} methodName Name of the method which does not exist.
1586
- */ throw new CKEditorError('observablemixin-cannot-decorate-undefined', this, {
1698
+ * Cannot decorate an undefined method.
1699
+ *
1700
+ * @error observablemixin-cannot-decorate-undefined
1701
+ * @param {Object} object The object which method should be decorated.
1702
+ * @param {String} methodName Name of the method which does not exist.
1703
+ */ throw new CKEditorError('observablemixin-cannot-decorate-undefined', this, {
1587
1704
  object: this,
1588
1705
  methodName
1589
1706
  });
@@ -1615,6 +1732,10 @@ function ObservableMixin(base) {
1615
1732
  }
1616
1733
  super.stopListening(emitter, event, callback);
1617
1734
  }
1735
+ [observablePropertiesSymbol];
1736
+ [decoratedMethods];
1737
+ [boundPropertiesSymbol];
1738
+ [boundObservablesSymbol];
1618
1739
  }
1619
1740
  return Mixin;
1620
1741
  }
@@ -1734,27 +1855,27 @@ function initObservable(observable) {
1734
1855
  // Eliminate A.bind( 'x' ).to( B, C )
1735
1856
  if (!parsedArgs.callback && parsedArgs.to.length > 1) {
1736
1857
  /**
1737
- * Binding multiple observables only possible with callback.
1738
- *
1739
- * @error observable-bind-to-no-callback
1740
- */ throw new CKEditorError('observable-bind-to-no-callback', this);
1858
+ * Binding multiple observables only possible with callback.
1859
+ *
1860
+ * @error observable-bind-to-no-callback
1861
+ */ throw new CKEditorError('observable-bind-to-no-callback', this);
1741
1862
  }
1742
1863
  // Eliminate A.bind( 'x', 'y' ).to( B, callback )
1743
1864
  if (numberOfBindings > 1 && parsedArgs.callback) {
1744
1865
  /**
1745
- * Cannot bind multiple properties and use a callback in one binding.
1746
- *
1747
- * @error observable-bind-to-extra-callback
1748
- */ throw new CKEditorError('observable-bind-to-extra-callback', this);
1866
+ * Cannot bind multiple properties and use a callback in one binding.
1867
+ *
1868
+ * @error observable-bind-to-extra-callback
1869
+ */ throw new CKEditorError('observable-bind-to-extra-callback', this);
1749
1870
  }
1750
1871
  parsedArgs.to.forEach((to)=>{
1751
1872
  // Eliminate A.bind( 'x', 'y' ).to( B, 'a' )
1752
1873
  if (to.properties.length && to.properties.length !== numberOfBindings) {
1753
1874
  /**
1754
- * The number of properties must match.
1755
- *
1756
- * @error observable-bind-to-properties-length
1757
- */ throw new CKEditorError('observable-bind-to-properties-length', this);
1875
+ * The number of properties must match.
1876
+ *
1877
+ * @error observable-bind-to-properties-length
1878
+ */ throw new CKEditorError('observable-bind-to-properties-length', this);
1758
1879
  }
1759
1880
  // When no to.properties specified, observing source properties instead i.e.
1760
1881
  // A.bind( 'x', 'y' ).to( B ) -> Observe B.x and B.y
@@ -1780,10 +1901,10 @@ function initObservable(observable) {
1780
1901
  */ function bindToMany(observables, attribute, callback) {
1781
1902
  if (this._bindings.size > 1) {
1782
1903
  /**
1783
- * Binding one attribute to many observables only possible with one attribute.
1784
- *
1785
- * @error observable-bind-to-many-not-one-binding
1786
- */ throw new CKEditorError('observable-bind-to-many-not-one-binding', this);
1904
+ * Binding one attribute to many observables only possible with one attribute.
1905
+ *
1906
+ * @error observable-bind-to-many-not-one-binding
1907
+ */ throw new CKEditorError('observable-bind-to-many-not-one-binding', this);
1787
1908
  }
1788
1909
  this.to(// Bind to #attribute of each observable...
1789
1910
  ...getBindingTargets(observables, attribute), // ...using given callback to parse attribute values.
@@ -1829,10 +1950,10 @@ function initObservable(observable) {
1829
1950
  // Eliminate A.bind( 'x' ).to()
1830
1951
  if (!args.length) {
1831
1952
  /**
1832
- * Invalid argument syntax in `to()`.
1833
- *
1834
- * @error observable-bind-to-parse-error
1835
- */ throw new CKEditorError('observable-bind-to-parse-error', null);
1953
+ * Invalid argument syntax in `to()`.
1954
+ *
1955
+ * @error observable-bind-to-parse-error
1956
+ */ throw new CKEditorError('observable-bind-to-parse-error', null);
1836
1957
  }
1837
1958
  const parsed = {
1838
1959
  to: []
@@ -2001,13 +2122,19 @@ function initObservable(observable) {
2001
2122
  * the original elements from the DOM.
2002
2123
  */ class ElementReplacer {
2003
2124
  /**
2004
- * Hides the `element` and, if specified, inserts the the given element next to it.
2005
- *
2006
- * The effect of this method can be reverted by {@link #restore}.
2007
- *
2008
- * @param element The element to replace.
2009
- * @param newElement The replacement element. If not passed, then the `element` will just be hidden.
2010
- */ replace(element, newElement) {
2125
+ * The elements replaced by {@link #replace} and their replacements.
2126
+ */ _replacedElements;
2127
+ constructor(){
2128
+ this._replacedElements = [];
2129
+ }
2130
+ /**
2131
+ * Hides the `element` and, if specified, inserts the the given element next to it.
2132
+ *
2133
+ * The effect of this method can be reverted by {@link #restore}.
2134
+ *
2135
+ * @param element The element to replace.
2136
+ * @param newElement The replacement element. If not passed, then the `element` will just be hidden.
2137
+ */ replace(element, newElement) {
2011
2138
  this._replacedElements.push({
2012
2139
  element,
2013
2140
  newElement
@@ -2018,8 +2145,8 @@ function initObservable(observable) {
2018
2145
  }
2019
2146
  }
2020
2147
  /**
2021
- * Restores what {@link #replace} did.
2022
- */ restore() {
2148
+ * Restores what {@link #replace} did.
2149
+ */ restore() {
2023
2150
  this._replacedElements.forEach(({ element, newElement })=>{
2024
2151
  element.style.display = '';
2025
2152
  if (newElement) {
@@ -2028,9 +2155,6 @@ function initObservable(observable) {
2028
2155
  });
2029
2156
  this._replacedElements = [];
2030
2157
  }
2031
- constructor(){
2032
- this._replacedElements = [];
2033
- }
2034
2158
  }
2035
2159
 
2036
2160
  /**
@@ -2165,7 +2289,32 @@ function initObservable(observable) {
2165
2289
  return element;
2166
2290
  }
2167
2291
 
2168
- class Config {
2292
+ /**
2293
+ * Handles a configuration dictionary.
2294
+ *
2295
+ * @typeParam Cfg A type of the configuration dictionary.
2296
+ */ class Config {
2297
+ /**
2298
+ * Store for the whole configuration.
2299
+ */ _config;
2300
+ /**
2301
+ * Creates an instance of the {@link ~Config} class.
2302
+ *
2303
+ * @param configurations The initial configurations to be set. Usually, provided by the user.
2304
+ * @param defaultConfigurations The default configurations. Usually, provided by the system.
2305
+ */ constructor(configurations, defaultConfigurations){
2306
+ this._config = {};
2307
+ // Set default configuration.
2308
+ if (defaultConfigurations) {
2309
+ // Clone the configuration to make sure that the properties will not be shared
2310
+ // between editors and make the watchdog feature work correctly.
2311
+ this.define(cloneConfig(defaultConfigurations));
2312
+ }
2313
+ // Set initial configuration.
2314
+ if (configurations) {
2315
+ this._setObjectToTarget(this._config, configurations);
2316
+ }
2317
+ }
2169
2318
  set(name, value) {
2170
2319
  this._setToTarget(this._config, name, value);
2171
2320
  }
@@ -2174,39 +2323,39 @@ class Config {
2174
2323
  this._setToTarget(this._config, name, value, isDefine);
2175
2324
  }
2176
2325
  /**
2177
- * Gets the value for a configuration entry.
2178
- *
2179
- * ```ts
2180
- * config.get( 'name' );
2181
- * ```
2182
- *
2183
- * Deep configurations can be retrieved by separating each part with a dot.
2184
- *
2185
- * ```ts
2186
- * config.get( 'toolbar.collapsed' );
2187
- * ```
2188
- *
2189
- * @param name The configuration name. Configuration names are case-sensitive.
2190
- * @returns The configuration value or `undefined` if the configuration entry was not found.
2191
- */ get(name) {
2326
+ * Gets the value for a configuration entry.
2327
+ *
2328
+ * ```ts
2329
+ * config.get( 'name' );
2330
+ * ```
2331
+ *
2332
+ * Deep configurations can be retrieved by separating each part with a dot.
2333
+ *
2334
+ * ```ts
2335
+ * config.get( 'toolbar.collapsed' );
2336
+ * ```
2337
+ *
2338
+ * @param name The configuration name. Configuration names are case-sensitive.
2339
+ * @returns The configuration value or `undefined` if the configuration entry was not found.
2340
+ */ get(name) {
2192
2341
  return this._getFromSource(this._config, name);
2193
2342
  }
2194
2343
  /**
2195
- * Iterates over all top level configuration names.
2196
- */ *names() {
2344
+ * Iterates over all top level configuration names.
2345
+ */ *names() {
2197
2346
  for (const name of Object.keys(this._config)){
2198
2347
  yield name;
2199
2348
  }
2200
2349
  }
2201
2350
  /**
2202
- * Saves passed configuration to the specified target (nested object).
2203
- *
2204
- * @param target Nested config object.
2205
- * @param name The configuration name or an object from which take properties as
2206
- * configuration entries. Configuration names are case-sensitive.
2207
- * @param value The configuration value. Used if a name is passed.
2208
- * @param isDefine Define if passed configuration should overwrite existing one.
2209
- */ _setToTarget(target, name, value, isDefine = false) {
2351
+ * Saves passed configuration to the specified target (nested object).
2352
+ *
2353
+ * @param target Nested config object.
2354
+ * @param name The configuration name or an object from which take properties as
2355
+ * configuration entries. Configuration names are case-sensitive.
2356
+ * @param value The configuration value. Used if a name is passed.
2357
+ * @param isDefine Define if passed configuration should overwrite existing one.
2358
+ */ _setToTarget(target, name, value, isDefine = false) {
2210
2359
  // In case of an object, iterate through it and call `_setToTarget` again for each property.
2211
2360
  if (isPlainObject(name)) {
2212
2361
  this._setObjectToTarget(target, name, isDefine);
@@ -2243,12 +2392,12 @@ class Config {
2243
2392
  target[name] = value;
2244
2393
  }
2245
2394
  /**
2246
- * Get specified configuration from specified source (nested object).
2247
- *
2248
- * @param source level of nested object.
2249
- * @param name The configuration name. Configuration names are case-sensitive.
2250
- * @returns The configuration value or `undefined` if the configuration entry was not found.
2251
- */ _getFromSource(source, name) {
2395
+ * Get specified configuration from specified source (nested object).
2396
+ *
2397
+ * @param source level of nested object.
2398
+ * @param name The configuration name. Configuration names are case-sensitive.
2399
+ * @returns The configuration value or `undefined` if the configuration entry was not found.
2400
+ */ _getFromSource(source, name) {
2252
2401
  // The configuration name should be split into parts if it has dots. E.g. `resize.width` -> [`resize`, `width`].
2253
2402
  const parts = name.split('.');
2254
2403
  // Take the name of the configuration out of the parts. E.g. `resize.width` -> `width`.
@@ -2266,34 +2415,16 @@ class Config {
2266
2415
  return source ? cloneConfig(source[name]) : undefined;
2267
2416
  }
2268
2417
  /**
2269
- * Iterates through passed object and calls {@link #_setToTarget} method with object key and value for each property.
2270
- *
2271
- * @param target Nested config object.
2272
- * @param configuration Configuration data set
2273
- * @param isDefine Defines if passed configuration is default configuration or not.
2274
- */ _setObjectToTarget(target, configuration, isDefine) {
2418
+ * Iterates through passed object and calls {@link #_setToTarget} method with object key and value for each property.
2419
+ *
2420
+ * @param target Nested config object.
2421
+ * @param configuration Configuration data set
2422
+ * @param isDefine Defines if passed configuration is default configuration or not.
2423
+ */ _setObjectToTarget(target, configuration, isDefine) {
2275
2424
  Object.keys(configuration).forEach((key)=>{
2276
2425
  this._setToTarget(target, key, configuration[key], isDefine);
2277
2426
  });
2278
2427
  }
2279
- /**
2280
- * Creates an instance of the {@link ~Config} class.
2281
- *
2282
- * @param configurations The initial configurations to be set. Usually, provided by the user.
2283
- * @param defaultConfigurations The default configurations. Usually, provided by the system.
2284
- */ constructor(configurations, defaultConfigurations){
2285
- this._config = {};
2286
- // Set default configuration.
2287
- if (defaultConfigurations) {
2288
- // Clone the configuration to make sure that the properties will not be shared
2289
- // between editors and make the watchdog feature work correctly.
2290
- this.define(cloneConfig(defaultConfigurations));
2291
- }
2292
- // Set initial configuration.
2293
- if (configurations) {
2294
- this._setObjectToTarget(this._config, configurations);
2295
- }
2296
- }
2297
2428
  }
2298
2429
  /**
2299
2430
  * Clones configuration object or value.
@@ -2346,7 +2477,7 @@ class Config {
2346
2477
  return false;
2347
2478
  }
2348
2479
 
2349
- const defaultEmitterClass = DomEmitterMixin(EmitterMixin());
2480
+ const defaultEmitterClass = /* #__PURE__ */ DomEmitterMixin(/* #__PURE__ */ EmitterMixin());
2350
2481
  function DomEmitterMixin(base) {
2351
2482
  if (!base) {
2352
2483
  return defaultEmitterClass;
@@ -2379,23 +2510,23 @@ function DomEmitterMixin(base) {
2379
2510
  }
2380
2511
  }
2381
2512
  /**
2382
- * Retrieves ProxyEmitter instance for given DOM Node residing in this Host and given options.
2383
- *
2384
- * @param node DOM Node of the ProxyEmitter.
2385
- * @param options Additional options.
2386
- * @param options.useCapture Indicates that events of this type will be dispatched to the registered
2387
- * listener before being dispatched to any EventTarget beneath it in the DOM tree.
2388
- * @param options.usePassive Indicates that the function specified by listener will never call preventDefault()
2389
- * and prevents blocking browser's main thread by this event handler.
2390
- * @returns ProxyEmitter instance bound to the DOM Node.
2391
- */ _getProxyEmitter(node, options) {
2513
+ * Retrieves ProxyEmitter instance for given DOM Node residing in this Host and given options.
2514
+ *
2515
+ * @param node DOM Node of the ProxyEmitter.
2516
+ * @param options Additional options.
2517
+ * @param options.useCapture Indicates that events of this type will be dispatched to the registered
2518
+ * listener before being dispatched to any EventTarget beneath it in the DOM tree.
2519
+ * @param options.usePassive Indicates that the function specified by listener will never call preventDefault()
2520
+ * and prevents blocking browser's main thread by this event handler.
2521
+ * @returns ProxyEmitter instance bound to the DOM Node.
2522
+ */ _getProxyEmitter(node, options) {
2392
2523
  return _getEmitterListenedTo(this, getProxyEmitterId(node, options));
2393
2524
  }
2394
2525
  /**
2395
- * Retrieves all the ProxyEmitter instances for given DOM Node residing in this Host.
2396
- *
2397
- * @param node DOM Node of the ProxyEmitter.
2398
- */ _getAllProxyEmitters(node) {
2526
+ * Retrieves all the ProxyEmitter instances for given DOM Node residing in this Host.
2527
+ *
2528
+ * @param node DOM Node of the ProxyEmitter.
2529
+ */ _getAllProxyEmitters(node) {
2399
2530
  return [
2400
2531
  {
2401
2532
  capture: false,
@@ -2462,18 +2593,39 @@ function DomEmitterMixin(base) {
2462
2593
  * | | click (DOM Event)
2463
2594
  * +-----------------------------------------+
2464
2595
  * fire( click, DOM Event )
2465
- */ class ProxyEmitter extends EmitterMixin() {
2596
+ */ class ProxyEmitter extends /* #__PURE__ */ EmitterMixin() {
2597
+ _domNode;
2598
+ _options;
2466
2599
  /**
2467
- * Registers a callback function to be executed when an event is fired.
2468
- *
2469
- * It attaches a native DOM listener to the DOM Node. When fired,
2470
- * a corresponding Emitter event will also fire with DOM Event object as an argument.
2471
- *
2472
- * **Note**: This is automatically called by the
2473
- * {@link module:utils/emittermixin~Emitter#listenTo `Emitter#listenTo()`}.
2474
- *
2475
- * @param event The name of the event.
2476
- */ attach(event) {
2600
+ * @param node DOM Node that fires events.
2601
+ * @param options Additional options.
2602
+ * @param options.useCapture Indicates that events of this type will be dispatched to the registered
2603
+ * listener before being dispatched to any EventTarget beneath it in the DOM tree.
2604
+ * @param options.usePassive Indicates that the function specified by listener will never call preventDefault()
2605
+ * and prevents blocking browser's main thread by this event handler.
2606
+ */ constructor(node, options){
2607
+ super();
2608
+ // Set emitter ID to match DOM Node "expando" property.
2609
+ _setEmitterId(this, getProxyEmitterId(node, options));
2610
+ // Remember the DOM Node this ProxyEmitter is bound to.
2611
+ this._domNode = node;
2612
+ // And given options.
2613
+ this._options = options;
2614
+ }
2615
+ /**
2616
+ * Collection of native DOM listeners.
2617
+ */ _domListeners;
2618
+ /**
2619
+ * Registers a callback function to be executed when an event is fired.
2620
+ *
2621
+ * It attaches a native DOM listener to the DOM Node. When fired,
2622
+ * a corresponding Emitter event will also fire with DOM Event object as an argument.
2623
+ *
2624
+ * **Note**: This is automatically called by the
2625
+ * {@link module:utils/emittermixin~Emitter#listenTo `Emitter#listenTo()`}.
2626
+ *
2627
+ * @param event The name of the event.
2628
+ */ attach(event) {
2477
2629
  // If the DOM Listener for given event already exist it is pointless
2478
2630
  // to attach another one.
2479
2631
  if (this._domListeners && this._domListeners[event]) {
@@ -2490,13 +2642,13 @@ function DomEmitterMixin(base) {
2490
2642
  this._domListeners[event] = domListener;
2491
2643
  }
2492
2644
  /**
2493
- * Stops executing the callback on the given event.
2494
- *
2495
- * **Note**: This is automatically called by the
2496
- * {@link module:utils/emittermixin~Emitter#stopListening `Emitter#stopListening()`}.
2497
- *
2498
- * @param event The name of the event.
2499
- */ detach(event) {
2645
+ * Stops executing the callback on the given event.
2646
+ *
2647
+ * **Note**: This is automatically called by the
2648
+ * {@link module:utils/emittermixin~Emitter#stopListening `Emitter#stopListening()`}.
2649
+ *
2650
+ * @param event The name of the event.
2651
+ */ detach(event) {
2500
2652
  let events;
2501
2653
  // Remove native DOM listeners which are orphans. If no callbacks
2502
2654
  // are awaiting given event, detach native DOM listener from DOM Node.
@@ -2506,34 +2658,34 @@ function DomEmitterMixin(base) {
2506
2658
  }
2507
2659
  }
2508
2660
  /**
2509
- * Adds callback to emitter for given event.
2510
- *
2511
- * @internal
2512
- * @param event The name of the event.
2513
- * @param callback The function to be called on event.
2514
- * @param options Additional options.
2515
- */ _addEventListener(event, callback, options) {
2661
+ * Adds callback to emitter for given event.
2662
+ *
2663
+ * @internal
2664
+ * @param event The name of the event.
2665
+ * @param callback The function to be called on event.
2666
+ * @param options Additional options.
2667
+ */ _addEventListener(event, callback, options) {
2516
2668
  this.attach(event);
2517
2669
  EmitterMixin().prototype._addEventListener.call(this, event, callback, options);
2518
2670
  }
2519
2671
  /**
2520
- * Removes callback from emitter for given event.
2521
- *
2522
- * @internal
2523
- * @param event The name of the event.
2524
- * @param callback The function to stop being called.
2525
- */ _removeEventListener(event, callback) {
2672
+ * Removes callback from emitter for given event.
2673
+ *
2674
+ * @internal
2675
+ * @param event The name of the event.
2676
+ * @param callback The function to stop being called.
2677
+ */ _removeEventListener(event, callback) {
2526
2678
  EmitterMixin().prototype._removeEventListener.call(this, event, callback);
2527
2679
  this.detach(event);
2528
2680
  }
2529
2681
  /**
2530
- * Creates a native DOM listener callback. When the native DOM event
2531
- * is fired it will fire corresponding event on this ProxyEmitter.
2532
- * Note: A native DOM Event is passed as an argument.
2533
- *
2534
- * @param event The name of the event.
2535
- * @returns The DOM listener callback.
2536
- */ _createDomListener(event) {
2682
+ * Creates a native DOM listener callback. When the native DOM event
2683
+ * is fired it will fire corresponding event on this ProxyEmitter.
2684
+ * Note: A native DOM Event is passed as an argument.
2685
+ *
2686
+ * @param event The name of the event.
2687
+ * @returns The DOM listener callback.
2688
+ */ _createDomListener(event) {
2537
2689
  const domListener = (domEvt)=>{
2538
2690
  this.fire(event, domEvt);
2539
2691
  };
@@ -2546,22 +2698,6 @@ function DomEmitterMixin(base) {
2546
2698
  };
2547
2699
  return domListener;
2548
2700
  }
2549
- /**
2550
- * @param node DOM Node that fires events.
2551
- * @param options Additional options.
2552
- * @param options.useCapture Indicates that events of this type will be dispatched to the registered
2553
- * listener before being dispatched to any EventTarget beneath it in the DOM tree.
2554
- * @param options.usePassive Indicates that the function specified by listener will never call preventDefault()
2555
- * and prevents blocking browser's main thread by this event handler.
2556
- */ constructor(node, options){
2557
- super();
2558
- // Set emitter ID to match DOM Node "expando" property.
2559
- _setEmitterId(this, getProxyEmitterId(node, options));
2560
- // Remember the DOM Node this ProxyEmitter is bound to.
2561
- this._domNode = node;
2562
- // And given options.
2563
- this._options = options;
2564
- }
2565
2701
  }
2566
2702
  /**
2567
2703
  * Gets an unique DOM Node identifier. The identifier will be set if not defined.
@@ -2582,44 +2718,6 @@ function DomEmitterMixin(base) {
2582
2718
  return id;
2583
2719
  }
2584
2720
 
2585
- /**
2586
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
2587
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
2588
- */ /**
2589
- * A helper (module) giving an access to the global DOM objects such as `window` and
2590
- * `document`. Accessing these objects using this helper allows easy and bulletproof
2591
- * testing, i.e. stubbing native properties:
2592
- *
2593
- * ```ts
2594
- * import { global } from 'ckeditor5/utils';
2595
- *
2596
- * // This stub will work for any code using global module.
2597
- * testUtils.sinon.stub( global, 'window', {
2598
- * innerWidth: 10000
2599
- * } );
2600
- *
2601
- * console.log( global.window.innerWidth );
2602
- * ```
2603
- */ let globalVar; // named globalVar instead of global: https://github.com/ckeditor/ckeditor5/issues/12971
2604
- // In some environments window and document API might not be available.
2605
- try {
2606
- globalVar = {
2607
- window,
2608
- document
2609
- };
2610
- } catch (e) {
2611
- // It's not possible to mock a window object to simulate lack of a window object without writing extremely convoluted code.
2612
- /* istanbul ignore next -- @preserve */ // Let's cast it to not change module's API.
2613
- // We only handle this so loading editor in environments without window and document doesn't fail.
2614
- // For better DX we shouldn't introduce mixed types and require developers to check the type manually.
2615
- // This module should not be used on purpose in any environment outside browser.
2616
- globalVar = {
2617
- window: {},
2618
- document: {}
2619
- };
2620
- }
2621
- var global = globalVar;
2622
-
2623
2721
  /**
2624
2722
  * Returns the closest scrollable ancestor of a DOM element.
2625
2723
  *
@@ -2750,21 +2848,127 @@ const rectProperties = [
2750
2848
  'width',
2751
2849
  'height'
2752
2850
  ];
2753
- class Rect {
2851
+ /**
2852
+ * A helper class representing a `ClientRect` object, e.g. value returned by
2853
+ * the native `object.getBoundingClientRect()` method. Provides a set of methods
2854
+ * to manipulate the rect and compare it against other rect instances.
2855
+ */ class Rect {
2856
+ /**
2857
+ * The "top" value of the rect.
2858
+ *
2859
+ * @readonly
2860
+ */ top;
2861
+ /**
2862
+ * The "right" value of the rect.
2863
+ *
2864
+ * @readonly
2865
+ */ right;
2866
+ /**
2867
+ * The "bottom" value of the rect.
2868
+ *
2869
+ * @readonly
2870
+ */ bottom;
2871
+ /**
2872
+ * The "left" value of the rect.
2873
+ *
2874
+ * @readonly
2875
+ */ left;
2876
+ /**
2877
+ * The "width" value of the rect.
2878
+ *
2879
+ * @readonly
2880
+ */ width;
2881
+ /**
2882
+ * The "height" value of the rect.
2883
+ *
2884
+ * @readonly
2885
+ */ height;
2886
+ /**
2887
+ * The object this rect is for.
2888
+ *
2889
+ * @readonly
2890
+ */ _source;
2891
+ /**
2892
+ * Creates an instance of rect.
2893
+ *
2894
+ * ```ts
2895
+ * // Rect of an HTMLElement.
2896
+ * const rectA = new Rect( document.body );
2897
+ *
2898
+ * // Rect of a DOM Range.
2899
+ * const rectB = new Rect( document.getSelection().getRangeAt( 0 ) );
2900
+ *
2901
+ * // Rect of a window (web browser viewport).
2902
+ * const rectC = new Rect( window );
2903
+ *
2904
+ * // Rect out of an object.
2905
+ * const rectD = new Rect( { top: 0, right: 10, bottom: 10, left: 0, width: 10, height: 10 } );
2906
+ *
2907
+ * // Rect out of another Rect instance.
2908
+ * const rectE = new Rect( rectD );
2909
+ *
2910
+ * // Rect out of a ClientRect.
2911
+ * const rectF = new Rect( document.body.getClientRects().item( 0 ) );
2912
+ * ```
2913
+ *
2914
+ * **Note**: By default a rect of an HTML element includes its CSS borders and scrollbars (if any)
2915
+ * ant the rect of a `window` includes scrollbars too. Use {@link #excludeScrollbarsAndBorders}
2916
+ * to get the inner part of the rect.
2917
+ *
2918
+ * @param source A source object to create the rect.
2919
+ */ constructor(source){
2920
+ const isSourceRange = isRange(source);
2921
+ Object.defineProperty(this, '_source', {
2922
+ // If the source is a Rect instance, copy it's #_source.
2923
+ value: source._source || source,
2924
+ writable: true,
2925
+ enumerable: false
2926
+ });
2927
+ if (isDomElement(source) || isSourceRange) {
2928
+ // The `Rect` class depends on `getBoundingClientRect` and `getClientRects` DOM methods. If the source
2929
+ // of a rect in an HTML element or a DOM range but it does not belong to any rendered DOM tree, these methods
2930
+ // will fail to obtain the geometry and the rect instance makes little sense to the features using it.
2931
+ // To get rid of this warning make sure the source passed to the constructor is a descendant of `window.document.body`.
2932
+ // @if CK_DEBUG // const sourceNode = isSourceRange ? source.startContainer : source;
2933
+ // @if CK_DEBUG // if ( !sourceNode.ownerDocument || !sourceNode.ownerDocument.body.contains( sourceNode ) ) {
2934
+ // @if CK_DEBUG // console.warn(
2935
+ // @if CK_DEBUG // 'rect-source-not-in-dom: The source of this rect does not belong to any rendered DOM tree.',
2936
+ // @if CK_DEBUG // { source } );
2937
+ // @if CK_DEBUG // }
2938
+ if (isSourceRange) {
2939
+ const rangeRects = Rect.getDomRangeRects(source);
2940
+ copyRectProperties(this, Rect.getBoundingRect(rangeRects));
2941
+ } else {
2942
+ copyRectProperties(this, source.getBoundingClientRect());
2943
+ }
2944
+ } else if (isWindow(source)) {
2945
+ const { innerWidth, innerHeight } = source;
2946
+ copyRectProperties(this, {
2947
+ top: 0,
2948
+ right: innerWidth,
2949
+ bottom: innerHeight,
2950
+ left: 0,
2951
+ width: innerWidth,
2952
+ height: innerHeight
2953
+ });
2954
+ } else {
2955
+ copyRectProperties(this, source);
2956
+ }
2957
+ }
2754
2958
  /**
2755
- * Returns a clone of the rect.
2756
- *
2757
- * @returns A cloned rect.
2758
- */ clone() {
2959
+ * Returns a clone of the rect.
2960
+ *
2961
+ * @returns A cloned rect.
2962
+ */ clone() {
2759
2963
  return new Rect(this);
2760
2964
  }
2761
2965
  /**
2762
- * Moves the rect so that its upper–left corner lands in desired `[ x, y ]` location.
2763
- *
2764
- * @param x Desired horizontal location.
2765
- * @param y Desired vertical location.
2766
- * @returns A rect which has been moved.
2767
- */ moveTo(x, y) {
2966
+ * Moves the rect so that its upper–left corner lands in desired `[ x, y ]` location.
2967
+ *
2968
+ * @param x Desired horizontal location.
2969
+ * @param y Desired vertical location.
2970
+ * @returns A rect which has been moved.
2971
+ */ moveTo(x, y) {
2768
2972
  this.top = y;
2769
2973
  this.right = x + this.width;
2770
2974
  this.bottom = y + this.height;
@@ -2772,12 +2976,12 @@ class Rect {
2772
2976
  return this;
2773
2977
  }
2774
2978
  /**
2775
- * Moves the rect in–place by a dedicated offset.
2776
- *
2777
- * @param x A horizontal offset.
2778
- * @param y A vertical offset
2779
- * @returns A rect which has been moved.
2780
- */ moveBy(x, y) {
2979
+ * Moves the rect in–place by a dedicated offset.
2980
+ *
2981
+ * @param x A horizontal offset.
2982
+ * @param y A vertical offset
2983
+ * @returns A rect which has been moved.
2984
+ */ moveBy(x, y) {
2781
2985
  this.top += y;
2782
2986
  this.right += x;
2783
2987
  this.left += x;
@@ -2785,8 +2989,8 @@ class Rect {
2785
2989
  return this;
2786
2990
  }
2787
2991
  /**
2788
- * Returns a new rect a a result of intersection with another rect.
2789
- */ getIntersection(anotherRect) {
2992
+ * Returns a new rect a a result of intersection with another rect.
2993
+ */ getIntersection(anotherRect) {
2790
2994
  const rect = {
2791
2995
  top: Math.max(this.top, anotherRect.top),
2792
2996
  right: Math.min(this.right, anotherRect.right),
@@ -2806,10 +3010,10 @@ class Rect {
2806
3010
  }
2807
3011
  }
2808
3012
  /**
2809
- * Returns the area of intersection with another rect.
2810
- *
2811
- * @returns Area of intersection.
2812
- */ getIntersectionArea(anotherRect) {
3013
+ * Returns the area of intersection with another rect.
3014
+ *
3015
+ * @returns Area of intersection.
3016
+ */ getIntersectionArea(anotherRect) {
2813
3017
  const rect = this.getIntersection(anotherRect);
2814
3018
  if (rect) {
2815
3019
  return rect.getArea();
@@ -2818,27 +3022,27 @@ class Rect {
2818
3022
  }
2819
3023
  }
2820
3024
  /**
2821
- * Returns the area of the rect.
2822
- */ getArea() {
3025
+ * Returns the area of the rect.
3026
+ */ getArea() {
2823
3027
  return this.width * this.height;
2824
3028
  }
2825
3029
  /**
2826
- * Returns a new rect, a part of the original rect, which is actually visible to the user and is relative to the,`body`,
2827
- * e.g. an original rect cropped by parent element rects which have `overflow` set in CSS
2828
- * other than `"visible"`.
2829
- *
2830
- * If there's no such visible rect, which is when the rect is limited by one or many of
2831
- * the ancestors, `null` is returned.
2832
- *
2833
- * **Note**: This method does not consider the boundaries of the viewport (window).
2834
- * To get a rect cropped by all ancestors and the viewport, use an intersection such as:
2835
- *
2836
- * ```ts
2837
- * const visibleInViewportRect = new Rect( window ).getIntersection( new Rect( source ).getVisible() );
2838
- * ```
2839
- *
2840
- * @returns A visible rect instance or `null`, if there's none.
2841
- */ getVisible() {
3030
+ * Returns a new rect, a part of the original rect, which is actually visible to the user and is relative to the,`body`,
3031
+ * e.g. an original rect cropped by parent element rects which have `overflow` set in CSS
3032
+ * other than `"visible"`.
3033
+ *
3034
+ * If there's no such visible rect, which is when the rect is limited by one or many of
3035
+ * the ancestors, `null` is returned.
3036
+ *
3037
+ * **Note**: This method does not consider the boundaries of the viewport (window).
3038
+ * To get a rect cropped by all ancestors and the viewport, use an intersection such as:
3039
+ *
3040
+ * ```ts
3041
+ * const visibleInViewportRect = new Rect( window ).getIntersection( new Rect( source ).getVisible() );
3042
+ * ```
3043
+ *
3044
+ * @returns A visible rect instance or `null`, if there's none.
3045
+ */ getVisible() {
2842
3046
  const source = this._source;
2843
3047
  let visibleRect = this.clone();
2844
3048
  // There's no ancestor to crop <body> with the overflow.
@@ -2910,13 +3114,13 @@ class Rect {
2910
3114
  return visibleRect;
2911
3115
  }
2912
3116
  /**
2913
- * Checks if all property values ({@link #top}, {@link #left}, {@link #right},
2914
- * {@link #bottom}, {@link #width} and {@link #height}) are the equal in both rect
2915
- * instances.
2916
- *
2917
- * @param anotherRect A rect instance to compare with.
2918
- * @returns `true` when Rects are equal. `false` otherwise.
2919
- */ isEqual(anotherRect) {
3117
+ * Checks if all property values ({@link #top}, {@link #left}, {@link #right},
3118
+ * {@link #bottom}, {@link #width} and {@link #height}) are the equal in both rect
3119
+ * instances.
3120
+ *
3121
+ * @param anotherRect A rect instance to compare with.
3122
+ * @returns `true` when Rects are equal. `false` otherwise.
3123
+ */ isEqual(anotherRect) {
2920
3124
  for (const prop of rectProperties){
2921
3125
  if (this[prop] !== anotherRect[prop]) {
2922
3126
  return false;
@@ -2925,17 +3129,17 @@ class Rect {
2925
3129
  return true;
2926
3130
  }
2927
3131
  /**
2928
- * Checks whether a rect fully contains another rect instance.
2929
- *
2930
- * @param anotherRect
2931
- * @returns `true` if contains, `false` otherwise.
2932
- */ contains(anotherRect) {
3132
+ * Checks whether a rect fully contains another rect instance.
3133
+ *
3134
+ * @param anotherRect
3135
+ * @returns `true` if contains, `false` otherwise.
3136
+ */ contains(anotherRect) {
2933
3137
  const intersectRect = this.getIntersection(anotherRect);
2934
3138
  return !!(intersectRect && intersectRect.isEqual(anotherRect));
2935
3139
  }
2936
3140
  /**
2937
- * Recalculates screen coordinates to coordinates relative to the positioned ancestor offset.
2938
- */ toAbsoluteRect() {
3141
+ * Recalculates screen coordinates to coordinates relative to the positioned ancestor offset.
3142
+ */ toAbsoluteRect() {
2939
3143
  const { scrollX, scrollY } = global.window;
2940
3144
  const absoluteRect = this.clone().moveBy(scrollX, scrollY);
2941
3145
  if (isDomElement(absoluteRect._source)) {
@@ -2947,13 +3151,13 @@ class Rect {
2947
3151
  return absoluteRect;
2948
3152
  }
2949
3153
  /**
2950
- * Excludes scrollbars and CSS borders from the rect.
2951
- *
2952
- * * Borders are removed when {@link #_source} is an HTML element.
2953
- * * Scrollbars are excluded from HTML elements and the `window`.
2954
- *
2955
- * @returns A rect which has been updated.
2956
- */ excludeScrollbarsAndBorders() {
3154
+ * Excludes scrollbars and CSS borders from the rect.
3155
+ *
3156
+ * * Borders are removed when {@link #_source} is an HTML element.
3157
+ * * Scrollbars are excluded from HTML elements and the `window`.
3158
+ *
3159
+ * @returns A rect which has been updated.
3160
+ */ excludeScrollbarsAndBorders() {
2957
3161
  const source = this._source;
2958
3162
  let scrollBarWidth, scrollBarHeight, direction;
2959
3163
  if (isWindow(source)) {
@@ -2983,11 +3187,11 @@ class Rect {
2983
3187
  return this;
2984
3188
  }
2985
3189
  /**
2986
- * Returns an array of rects of the given native DOM Range.
2987
- *
2988
- * @param range A native DOM range.
2989
- * @returns DOM Range rects.
2990
- */ static getDomRangeRects(range) {
3190
+ * Returns an array of rects of the given native DOM Range.
3191
+ *
3192
+ * @param range A native DOM range.
3193
+ * @returns DOM Range rects.
3194
+ */ static getDomRangeRects(range) {
2991
3195
  const rects = [];
2992
3196
  // Safari does not iterate over ClientRectList using for...of loop.
2993
3197
  const clientRects = Array.from(range.getClientRects());
@@ -3008,11 +3212,11 @@ class Rect {
3008
3212
  return rects;
3009
3213
  }
3010
3214
  /**
3011
- * Returns a bounding rectangle that contains all the given `rects`.
3012
- *
3013
- * @param rects A list of rectangles that should be contained in the result rectangle.
3014
- * @returns Bounding rectangle or `null` if no `rects` were given.
3015
- */ static getBoundingRect(rects) {
3215
+ * Returns a bounding rectangle that contains all the given `rects`.
3216
+ *
3217
+ * @param rects A list of rectangles that should be contained in the result rectangle.
3218
+ * @returns Bounding rectangle or `null` if no `rects` were given.
3219
+ */ static getBoundingRect(rects) {
3016
3220
  const boundingRectData = {
3017
3221
  left: Number.POSITIVE_INFINITY,
3018
3222
  top: Number.POSITIVE_INFINITY,
@@ -3036,73 +3240,6 @@ class Rect {
3036
3240
  boundingRectData.height = boundingRectData.bottom - boundingRectData.top;
3037
3241
  return new Rect(boundingRectData);
3038
3242
  }
3039
- /**
3040
- * Creates an instance of rect.
3041
- *
3042
- * ```ts
3043
- * // Rect of an HTMLElement.
3044
- * const rectA = new Rect( document.body );
3045
- *
3046
- * // Rect of a DOM Range.
3047
- * const rectB = new Rect( document.getSelection().getRangeAt( 0 ) );
3048
- *
3049
- * // Rect of a window (web browser viewport).
3050
- * const rectC = new Rect( window );
3051
- *
3052
- * // Rect out of an object.
3053
- * const rectD = new Rect( { top: 0, right: 10, bottom: 10, left: 0, width: 10, height: 10 } );
3054
- *
3055
- * // Rect out of another Rect instance.
3056
- * const rectE = new Rect( rectD );
3057
- *
3058
- * // Rect out of a ClientRect.
3059
- * const rectF = new Rect( document.body.getClientRects().item( 0 ) );
3060
- * ```
3061
- *
3062
- * **Note**: By default a rect of an HTML element includes its CSS borders and scrollbars (if any)
3063
- * ant the rect of a `window` includes scrollbars too. Use {@link #excludeScrollbarsAndBorders}
3064
- * to get the inner part of the rect.
3065
- *
3066
- * @param source A source object to create the rect.
3067
- */ constructor(source){
3068
- const isSourceRange = isRange(source);
3069
- Object.defineProperty(this, '_source', {
3070
- // If the source is a Rect instance, copy it's #_source.
3071
- value: source._source || source,
3072
- writable: true,
3073
- enumerable: false
3074
- });
3075
- if (isDomElement(source) || isSourceRange) {
3076
- // The `Rect` class depends on `getBoundingClientRect` and `getClientRects` DOM methods. If the source
3077
- // of a rect in an HTML element or a DOM range but it does not belong to any rendered DOM tree, these methods
3078
- // will fail to obtain the geometry and the rect instance makes little sense to the features using it.
3079
- // To get rid of this warning make sure the source passed to the constructor is a descendant of `window.document.body`.
3080
- // @if CK_DEBUG // const sourceNode = isSourceRange ? source.startContainer : source;
3081
- // @if CK_DEBUG // if ( !sourceNode.ownerDocument || !sourceNode.ownerDocument.body.contains( sourceNode ) ) {
3082
- // @if CK_DEBUG // console.warn(
3083
- // @if CK_DEBUG // 'rect-source-not-in-dom: The source of this rect does not belong to any rendered DOM tree.',
3084
- // @if CK_DEBUG // { source } );
3085
- // @if CK_DEBUG // }
3086
- if (isSourceRange) {
3087
- const rangeRects = Rect.getDomRangeRects(source);
3088
- copyRectProperties(this, Rect.getBoundingRect(rangeRects));
3089
- } else {
3090
- copyRectProperties(this, source.getBoundingClientRect());
3091
- }
3092
- } else if (isWindow(source)) {
3093
- const { innerWidth, innerHeight } = source;
3094
- copyRectProperties(this, {
3095
- top: 0,
3096
- right: innerWidth,
3097
- bottom: innerHeight,
3098
- left: 0,
3099
- width: innerWidth,
3100
- height: innerHeight
3101
- });
3102
- } else {
3103
- copyRectProperties(this, source);
3104
- }
3105
- }
3106
3243
  }
3107
3244
  /**
3108
3245
  * Acquires all the rect properties from the passed source.
@@ -3188,18 +3325,50 @@ class Rect {
3188
3325
  * under the hood.
3189
3326
  */ class ResizeObserver {
3190
3327
  /**
3191
- * The element observed by this observer.
3192
- */ get element() {
3328
+ * The element observed by this observer.
3329
+ */ _element;
3330
+ /**
3331
+ * The callback executed each time {@link #_element} is resized.
3332
+ */ _callback;
3333
+ /**
3334
+ * The single native observer instance shared across all {@link module:utils/dom/resizeobserver~ResizeObserver} instances.
3335
+ */ static _observerInstance = null;
3336
+ /**
3337
+ * A mapping of native DOM elements and their callbacks shared across all
3338
+ * {@link module:utils/dom/resizeobserver~ResizeObserver} instances.
3339
+ */ static _elementCallbacks = null;
3340
+ /**
3341
+ * Creates an instance of the `ResizeObserver` class.
3342
+ *
3343
+ * @param element A DOM element that is to be observed for resizing. Note that
3344
+ * the element must be visible (i.e. not detached from DOM) for the observer to work.
3345
+ * @param callback A function called when the observed element was resized. It passes
3346
+ * the [`ResizeObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
3347
+ * object with information about the resize event.
3348
+ */ constructor(element, callback){
3349
+ // **Note**: For the maximum performance, this class ensures only a single instance of the native
3350
+ // observer is used no matter how many instances of this class were created.
3351
+ if (!ResizeObserver._observerInstance) {
3352
+ ResizeObserver._createObserver();
3353
+ }
3354
+ this._element = element;
3355
+ this._callback = callback;
3356
+ ResizeObserver._addElementCallback(element, callback);
3357
+ ResizeObserver._observerInstance.observe(element);
3358
+ }
3359
+ /**
3360
+ * The element observed by this observer.
3361
+ */ get element() {
3193
3362
  return this._element;
3194
3363
  }
3195
3364
  /**
3196
- * Destroys the observer which disables the `callback` passed to the {@link #constructor}.
3197
- */ destroy() {
3365
+ * Destroys the observer which disables the `callback` passed to the {@link #constructor}.
3366
+ */ destroy() {
3198
3367
  ResizeObserver._deleteElementCallback(this._element, this._callback);
3199
3368
  }
3200
3369
  /**
3201
- * Registers a new resize callback for the DOM element.
3202
- */ static _addElementCallback(element, callback) {
3370
+ * Registers a new resize callback for the DOM element.
3371
+ */ static _addElementCallback(element, callback) {
3203
3372
  if (!ResizeObserver._elementCallbacks) {
3204
3373
  ResizeObserver._elementCallbacks = new Map();
3205
3374
  }
@@ -3211,9 +3380,9 @@ class Rect {
3211
3380
  callbacks.add(callback);
3212
3381
  }
3213
3382
  /**
3214
- * Removes a resize callback from the DOM element. If no callbacks are left
3215
- * for the element, it removes the element from the native observer.
3216
- */ static _deleteElementCallback(element, callback) {
3383
+ * Removes a resize callback from the DOM element. If no callbacks are left
3384
+ * for the element, it removes the element from the native observer.
3385
+ */ static _deleteElementCallback(element, callback) {
3217
3386
  const callbacks = ResizeObserver._getElementCallbacks(element);
3218
3387
  // Remove the element callback. Check if exist first in case someone
3219
3388
  // called destroy() twice.
@@ -3231,16 +3400,16 @@ class Rect {
3231
3400
  }
3232
3401
  }
3233
3402
  /**
3234
- * Returns are registered resize callbacks for the DOM element.
3235
- */ static _getElementCallbacks(element) {
3403
+ * Returns are registered resize callbacks for the DOM element.
3404
+ */ static _getElementCallbacks(element) {
3236
3405
  if (!ResizeObserver._elementCallbacks) {
3237
3406
  return null;
3238
3407
  }
3239
3408
  return ResizeObserver._elementCallbacks.get(element);
3240
3409
  }
3241
3410
  /**
3242
- * Creates the single native observer shared across all `ResizeObserver` instances.
3243
- */ static _createObserver() {
3411
+ * Creates the single native observer shared across all `ResizeObserver` instances.
3412
+ */ static _createObserver() {
3244
3413
  ResizeObserver._observerInstance = new global.window.ResizeObserver((entries)=>{
3245
3414
  for (const entry of entries){
3246
3415
  const callbacks = ResizeObserver._getElementCallbacks(entry.target);
@@ -3252,33 +3421,7 @@ class Rect {
3252
3421
  }
3253
3422
  });
3254
3423
  }
3255
- /**
3256
- * Creates an instance of the `ResizeObserver` class.
3257
- *
3258
- * @param element A DOM element that is to be observed for resizing. Note that
3259
- * the element must be visible (i.e. not detached from DOM) for the observer to work.
3260
- * @param callback A function called when the observed element was resized. It passes
3261
- * the [`ResizeObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
3262
- * object with information about the resize event.
3263
- */ constructor(element, callback){
3264
- // **Note**: For the maximum performance, this class ensures only a single instance of the native
3265
- // observer is used no matter how many instances of this class were created.
3266
- if (!ResizeObserver._observerInstance) {
3267
- ResizeObserver._createObserver();
3268
- }
3269
- this._element = element;
3270
- this._callback = callback;
3271
- ResizeObserver._addElementCallback(element, callback);
3272
- ResizeObserver._observerInstance.observe(element);
3273
- }
3274
3424
  }
3275
- /**
3276
- * The single native observer instance shared across all {@link module:utils/dom/resizeobserver~ResizeObserver} instances.
3277
- */ ResizeObserver._observerInstance = null;
3278
- /**
3279
- * A mapping of native DOM elements and their callbacks shared across all
3280
- * {@link module:utils/dom/resizeobserver~ResizeObserver} instances.
3281
- */ ResizeObserver._elementCallbacks = null;
3282
3425
 
3283
3426
  /**
3284
3427
  * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
@@ -3609,21 +3752,53 @@ class Rect {
3609
3752
  * translate directly to the `top` and `left` properties in CSS "`position: absolute` coordinate system". If set on the positioned element
3610
3753
  * in DOM, they will make it display it in the right place in the viewport.
3611
3754
  */ class PositionObject {
3755
+ name;
3756
+ config;
3757
+ _positioningFunctionCoordinates;
3758
+ _options;
3759
+ _cachedRect;
3760
+ _cachedAbsoluteRect;
3612
3761
  /**
3613
- * The left value in pixels in the CSS `position: absolute` coordinate system.
3614
- * Set it on the positioned element in DOM to move it to the position.
3615
- */ get left() {
3762
+ * Creates an instance of the {@link module:utils/dom/position~PositionObject} class.
3763
+ *
3764
+ * @param positioningFunction function The function that defines the expected
3765
+ * coordinates the positioned element should move to.
3766
+ * @param options options object.
3767
+ * @param options.elementRect The positioned element rect.
3768
+ * @param options.targetRect The target element rect.
3769
+ * @param options.viewportRect The viewport rect.
3770
+ * @param options.limiterRect The limiter rect.
3771
+ * @param options.positionedElementAncestor Nearest element ancestor element which CSS position is not "static".
3772
+ */ constructor(positioningFunction, options){
3773
+ const positioningFunctionOutput = positioningFunction(options.targetRect, options.elementRect, options.viewportRect, options.limiterRect);
3774
+ // Nameless position for a function that didn't participate.
3775
+ if (!positioningFunctionOutput) {
3776
+ return;
3777
+ }
3778
+ const { left, top, name, config } = positioningFunctionOutput;
3779
+ this.name = name;
3780
+ this.config = config;
3781
+ this._positioningFunctionCoordinates = {
3782
+ left,
3783
+ top
3784
+ };
3785
+ this._options = options;
3786
+ }
3787
+ /**
3788
+ * The left value in pixels in the CSS `position: absolute` coordinate system.
3789
+ * Set it on the positioned element in DOM to move it to the position.
3790
+ */ get left() {
3616
3791
  return this._absoluteRect.left;
3617
3792
  }
3618
3793
  /**
3619
- * The top value in pixels in the CSS `position: absolute` coordinate system.
3620
- * Set it on the positioned element in DOM to move it to the position.
3621
- */ get top() {
3794
+ * The top value in pixels in the CSS `position: absolute` coordinate system.
3795
+ * Set it on the positioned element in DOM to move it to the position.
3796
+ */ get top() {
3622
3797
  return this._absoluteRect.top;
3623
3798
  }
3624
3799
  /**
3625
- * An intersection area between positioned element and limiter within viewport constraints.
3626
- */ get limiterIntersectionArea() {
3800
+ * An intersection area between positioned element and limiter within viewport constraints.
3801
+ */ get limiterIntersectionArea() {
3627
3802
  const limiterRect = this._options.limiterRect;
3628
3803
  if (limiterRect) {
3629
3804
  return limiterRect.getIntersectionArea(this._rect);
@@ -3631,15 +3806,15 @@ class Rect {
3631
3806
  return 0;
3632
3807
  }
3633
3808
  /**
3634
- * An intersection area between positioned element and viewport.
3635
- */ get viewportIntersectionArea() {
3809
+ * An intersection area between positioned element and viewport.
3810
+ */ get viewportIntersectionArea() {
3636
3811
  const viewportRect = this._options.viewportRect;
3637
3812
  return viewportRect.getIntersectionArea(this._rect);
3638
3813
  }
3639
3814
  /**
3640
- * An already positioned element rect. A clone of the element rect passed to the constructor
3641
- * but placed in the viewport according to the positioning function.
3642
- */ get _rect() {
3815
+ * An already positioned element rect. A clone of the element rect passed to the constructor
3816
+ * but placed in the viewport according to the positioning function.
3817
+ */ get _rect() {
3643
3818
  if (this._cachedRect) {
3644
3819
  return this._cachedRect;
3645
3820
  }
@@ -3647,40 +3822,14 @@ class Rect {
3647
3822
  return this._cachedRect;
3648
3823
  }
3649
3824
  /**
3650
- * An already absolutely positioned element rect. See ({@link #_rect}).
3651
- */ get _absoluteRect() {
3825
+ * An already absolutely positioned element rect. See ({@link #_rect}).
3826
+ */ get _absoluteRect() {
3652
3827
  if (this._cachedAbsoluteRect) {
3653
3828
  return this._cachedAbsoluteRect;
3654
3829
  }
3655
3830
  this._cachedAbsoluteRect = this._rect.toAbsoluteRect();
3656
3831
  return this._cachedAbsoluteRect;
3657
3832
  }
3658
- /**
3659
- * Creates an instance of the {@link module:utils/dom/position~PositionObject} class.
3660
- *
3661
- * @param positioningFunction function The function that defines the expected
3662
- * coordinates the positioned element should move to.
3663
- * @param options options object.
3664
- * @param options.elementRect The positioned element rect.
3665
- * @param options.targetRect The target element rect.
3666
- * @param options.viewportRect The viewport rect.
3667
- * @param options.limiterRect The limiter rect.
3668
- * @param options.positionedElementAncestor Nearest element ancestor element which CSS position is not "static".
3669
- */ constructor(positioningFunction, options){
3670
- const positioningFunctionOutput = positioningFunction(options.targetRect, options.elementRect, options.viewportRect, options.limiterRect);
3671
- // Nameless position for a function that didn't participate.
3672
- if (!positioningFunctionOutput) {
3673
- return;
3674
- }
3675
- const { left, top, name, config } = positioningFunctionOutput;
3676
- this.name = name;
3677
- this.config = config;
3678
- this._positioningFunctionCoordinates = {
3679
- left,
3680
- top
3681
- };
3682
- this._options = options;
3683
- }
3684
3833
  }
3685
3834
 
3686
3835
  /**
@@ -4083,8 +4232,8 @@ const keyCodesToGlyphs = {
4083
4232
  * * `arrow(left|up|right|bottom)`,
4084
4233
  * * `backspace`, `delete`, `enter`, `esc`, `tab`,
4085
4234
  * * `ctrl`, `cmd`, `shift`, `alt`.
4086
- */ const keyCodes = generateKnownKeyCodes();
4087
- const keyCodeNames = Object.fromEntries(Object.entries(keyCodes).map(([name, code])=>{
4235
+ */ const keyCodes = /* #__PURE__ */ generateKnownKeyCodes();
4236
+ const keyCodeNames = /* #__PURE__ */ Object.fromEntries(/* #__PURE__ */ Object.entries(keyCodes).map(([name, code])=>{
4088
4237
  let prettyKeyName;
4089
4238
  if (code in keyCodesToGlyphs) {
4090
4239
  prettyKeyName = keyCodesToGlyphs[code];
@@ -4109,11 +4258,11 @@ const keyCodeNames = Object.fromEntries(Object.entries(keyCodes).map(([name, cod
4109
4258
  keyCode = keyCodes[key.toLowerCase()];
4110
4259
  if (!keyCode) {
4111
4260
  /**
4112
- * Unknown key name. Only key names included in the {@link module:utils/keyboard#keyCodes} can be used.
4113
- *
4114
- * @error keyboard-unknown-key
4115
- * @param {String} key
4116
- */ throw new CKEditorError('keyboard-unknown-key', null, {
4261
+ * Unknown key name. Only key names included in the {@link module:utils/keyboard#keyCodes} can be used.
4262
+ *
4263
+ * @error keyboard-unknown-key
4264
+ * @param {String} key
4265
+ */ throw new CKEditorError('keyboard-unknown-key', null, {
4117
4266
  key
4118
4267
  });
4119
4268
  }
@@ -4285,6 +4434,10 @@ function splitKeystrokeText(keystroke) {
4285
4434
  /**
4286
4435
  * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
4287
4436
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4437
+ */ /**
4438
+ * @module utils/language
4439
+ */ /**
4440
+ * String representing a language direction.
4288
4441
  */ const RTL_LANGUAGE_CODES = [
4289
4442
  'ar',
4290
4443
  'ara',
@@ -4311,6 +4464,14 @@ function splitKeystrokeText(keystroke) {
4311
4464
  /**
4312
4465
  * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
4313
4466
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4467
+ */ /**
4468
+ * @module utils/toarray
4469
+ */ /**
4470
+ * Transforms any value to an array. If the provided value is already an array, it is returned unchanged.
4471
+ *
4472
+ * @label MUTABLE
4473
+ * @param data The value to transform to an array.
4474
+ * @returns An array created from data.
4314
4475
  */ function toArray(data) {
4315
4476
  return Array.isArray(data) ? data : [
4316
4477
  data
@@ -4360,12 +4521,12 @@ function splitKeystrokeText(keystroke) {
4360
4521
  */ function _translate(language, message, quantity = 1, translations) {
4361
4522
  if (typeof quantity !== 'number') {
4362
4523
  /**
4363
- * An incorrect value was passed to the translation function. This was probably caused
4364
- * by an incorrect message interpolation of a plural form. Note that for messages supporting plural forms
4365
- * the second argument of the `t()` function should always be a number or an array with a number as the first element.
4366
- *
4367
- * @error translation-service-quantity-not-a-number
4368
- */ throw new CKEditorError('translation-service-quantity-not-a-number', null, {
4524
+ * An incorrect value was passed to the translation function. This was probably caused
4525
+ * by an incorrect message interpolation of a plural form. Note that for messages supporting plural forms
4526
+ * the second argument of the `t()` function should always be a number or an array with a number as the first element.
4527
+ *
4528
+ * @error translation-service-quantity-not-a-number
4529
+ */ throw new CKEditorError('translation-service-quantity-not-a-number', null, {
4369
4530
  quantity
4370
4531
  });
4371
4532
  }
@@ -4411,27 +4572,121 @@ function getNumberOfLanguages(translations) {
4411
4572
  return Object.keys(translations).length;
4412
4573
  }
4413
4574
 
4414
- class Locale {
4575
+ /**
4576
+ * Represents the localization services.
4577
+ */ class Locale {
4578
+ /**
4579
+ * The editor UI language code in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
4580
+ *
4581
+ * If the {@link #contentLanguage content language} was not specified in the `Locale` constructor,
4582
+ * it also defines the language of the content.
4583
+ */ uiLanguage;
4584
+ /**
4585
+ * Text direction of the {@link #uiLanguage editor UI language}. Either `'ltr'` or `'rtl'`.
4586
+ */ uiLanguageDirection;
4587
+ /**
4588
+ * The editor content language code in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
4589
+ *
4590
+ * Usually the same as the {@link #uiLanguage editor language}, it can be customized by passing an optional
4591
+ * argument to the `Locale` constructor.
4592
+ */ contentLanguage;
4415
4593
  /**
4416
- * The editor UI language code in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
4417
- *
4418
- * **Note**: This property was deprecated. Please use {@link #uiLanguage} and {@link #contentLanguage}
4419
- * properties instead.
4420
- *
4421
- * @deprecated
4422
- */ get language() {
4594
+ * Text direction of the {@link #contentLanguage editor content language}.
4595
+ *
4596
+ * If the content language was passed directly to the `Locale` constructor, this property represents the
4597
+ * direction of that language.
4598
+ *
4599
+ * If the {@link #contentLanguage editor content language} was derived from the {@link #uiLanguage editor language},
4600
+ * the content language direction is the same as the {@link #uiLanguageDirection UI language direction}.
4601
+ *
4602
+ * The value is either `'ltr'` or `'rtl'`.
4603
+ */ contentLanguageDirection;
4604
+ /**
4605
+ * Translates the given message to the {@link #uiLanguage}. This method is also available in
4606
+ * {@link module:core/editor/editor~Editor#t `Editor`} and {@link module:ui/view~View#t `View`}.
4607
+ *
4608
+ * This method's context is statically bound to the `Locale` instance and **should always be called as a function**:
4609
+ *
4610
+ * ```ts
4611
+ * const t = locale.t;
4612
+ * t( 'Label' );
4613
+ * ```
4614
+ *
4615
+ * The message can be either a string or an object implementing the {@link module:utils/translation-service~Message} interface.
4616
+ *
4617
+ * The message may contain placeholders (`%<index>`) for value(s) that are passed as a `values` parameter.
4618
+ * For an array of values, the `%<index>` will be changed to an element of that array at the given index.
4619
+ * For a single value passed as the second argument, only the `%0` placeholders will be changed to the provided value.
4620
+ *
4621
+ * ```ts
4622
+ * t( 'Created file "%0" in %1ms.', [ fileName, timeTaken ] );
4623
+ * t( 'Created file "%0", fileName );
4624
+ * ```
4625
+ *
4626
+ * The message supports plural forms. To specify the plural form, use the `plural` property. Singular or plural form
4627
+ * will be chosen depending on the first value from the passed `values`. The value of the `plural` property is used
4628
+ * as a default plural translation when the translation for the target language is missing.
4629
+ *
4630
+ * ```ts
4631
+ * t( { string: 'Add a space', plural: 'Add %0 spaces' }, 1 ); // 'Add a space' for the English language.
4632
+ * t( { string: 'Add a space', plural: 'Add %0 spaces' }, 5 ); // 'Add 5 spaces' for the English language.
4633
+ * t( { string: '%1 a space', plural: '%1 %0 spaces' }, [ 2, 'Add' ] ); // 'Add 2 spaces' for the English language.
4634
+ *
4635
+ * t( { string: 'Add a space', plural: 'Add %0 spaces' }, 1 ); // 'Dodaj spację' for the Polish language.
4636
+ * t( { string: 'Add a space', plural: 'Add %0 spaces' }, 5 ); // 'Dodaj 5 spacji' for the Polish language.
4637
+ * t( { string: '%1 a space', plural: '%1 %0 spaces' }, [ 2, 'Add' ] ); // 'Dodaj 2 spacje' for the Polish language.
4638
+ * ```
4639
+ *
4640
+ * * The message should provide an ID using the `id` property when the message strings are not unique and their
4641
+ * translations should be different.
4642
+ *
4643
+ * ```ts
4644
+ * translate( 'en', { string: 'image', id: 'ADD_IMAGE' } );
4645
+ * translate( 'en', { string: 'image', id: 'AN_IMAGE' } );
4646
+ * ```
4647
+ */ t;
4648
+ /**
4649
+ * Object that contains translations.
4650
+ */ translations;
4651
+ /**
4652
+ * Creates a new instance of the locale class. Learn more about
4653
+ * {@glink getting-started/setup/ui-language configuring the language of the editor}.
4654
+ *
4655
+ * @param options Locale configuration.
4656
+ * @param options.uiLanguage The editor UI language code in the
4657
+ * [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format. See {@link #uiLanguage}.
4658
+ * @param options.contentLanguage The editor content language code in the
4659
+ * [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format. If not specified, the same as `options.language`.
4660
+ * See {@link #contentLanguage}.
4661
+ * @param translations Translations passed as a editor config parameter.
4662
+ */ constructor({ uiLanguage = 'en', contentLanguage, translations } = {}){
4663
+ this.uiLanguage = uiLanguage;
4664
+ this.contentLanguage = contentLanguage || this.uiLanguage;
4665
+ this.uiLanguageDirection = getLanguageDirection(this.uiLanguage);
4666
+ this.contentLanguageDirection = getLanguageDirection(this.contentLanguage);
4667
+ this.translations = _unifyTranslations(translations);
4668
+ this.t = (message, values)=>this._t(message, values);
4669
+ }
4670
+ /**
4671
+ * The editor UI language code in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
4672
+ *
4673
+ * **Note**: This property was deprecated. Please use {@link #uiLanguage} and {@link #contentLanguage}
4674
+ * properties instead.
4675
+ *
4676
+ * @deprecated
4677
+ */ get language() {
4423
4678
  /**
4424
- * The {@link module:utils/locale~Locale#language `Locale#language`} property was deprecated and will
4425
- * be removed in the near future. Please use the {@link module:utils/locale~Locale#uiLanguage `Locale#uiLanguage`} and
4426
- * {@link module:utils/locale~Locale#contentLanguage `Locale#contentLanguage`} properties instead.
4427
- *
4428
- * @error locale-deprecated-language-property
4429
- */ console.warn('locale-deprecated-language-property: ' + 'The Locale#language property has been deprecated and will be removed in the near future. ' + 'Please use #uiLanguage and #contentLanguage properties instead.');
4679
+ * The {@link module:utils/locale~Locale#language `Locale#language`} property was deprecated and will
4680
+ * be removed in the near future. Please use the {@link module:utils/locale~Locale#uiLanguage `Locale#uiLanguage`} and
4681
+ * {@link module:utils/locale~Locale#contentLanguage `Locale#contentLanguage`} properties instead.
4682
+ *
4683
+ * @error locale-deprecated-language-property
4684
+ */ console.warn('locale-deprecated-language-property: ' + 'The Locale#language property has been deprecated and will be removed in the near future. ' + 'Please use #uiLanguage and #contentLanguage properties instead.');
4430
4685
  return this.uiLanguage;
4431
4686
  }
4432
4687
  /**
4433
- * An unbound version of the {@link #t} method.
4434
- */ _t(message, values = []) {
4688
+ * An unbound version of the {@link #t} method.
4689
+ */ _t(message, values = []) {
4435
4690
  values = toArray(values);
4436
4691
  if (typeof message === 'string') {
4437
4692
  message = {
@@ -4443,25 +4698,6 @@ class Locale {
4443
4698
  const translatedString = _translate(this.uiLanguage, message, quantity, this.translations);
4444
4699
  return interpolateString(translatedString, values);
4445
4700
  }
4446
- /**
4447
- * Creates a new instance of the locale class. Learn more about
4448
- * {@glink features/ui-language configuring the language of the editor}.
4449
- *
4450
- * @param options Locale configuration.
4451
- * @param options.uiLanguage The editor UI language code in the
4452
- * [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format. See {@link #uiLanguage}.
4453
- * @param options.contentLanguage The editor content language code in the
4454
- * [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format. If not specified, the same as `options.language`.
4455
- * See {@link #contentLanguage}.
4456
- * @param translations Translations passed as a editor config parameter.
4457
- */ constructor({ uiLanguage = 'en', contentLanguage, translations } = {}){
4458
- this.uiLanguage = uiLanguage;
4459
- this.contentLanguage = contentLanguage || this.uiLanguage;
4460
- this.uiLanguageDirection = getLanguageDirection(this.uiLanguage);
4461
- this.contentLanguageDirection = getLanguageDirection(this.contentLanguage);
4462
- this.translations = _unifyTranslations(translations);
4463
- this.t = (message, values)=>this._t(message, values);
4464
- }
4465
4701
  }
4466
4702
  /**
4467
4703
  * Fills the `%0, %1, ...` string placeholders with values.
@@ -4471,56 +4707,117 @@ class Locale {
4471
4707
  });
4472
4708
  }
4473
4709
 
4474
- class Collection extends EmitterMixin() {
4710
+ /**
4711
+ * Collections are ordered sets of objects. Items in the collection can be retrieved by their indexes
4712
+ * in the collection (like in an array) or by their ids.
4713
+ *
4714
+ * If an object without an `id` property is being added to the collection, the `id` property will be generated
4715
+ * automatically. Note that the automatically generated id is unique only within this single collection instance.
4716
+ *
4717
+ * By default an item in the collection is identified by its `id` property. The name of the identifier can be
4718
+ * configured through the constructor of the collection.
4719
+ *
4720
+ * @typeParam T The type of the collection element.
4721
+ */ class Collection extends /* #__PURE__ */ EmitterMixin() {
4722
+ /**
4723
+ * The internal list of items in the collection.
4724
+ */ _items;
4725
+ /**
4726
+ * The internal map of items in the collection.
4727
+ */ _itemMap;
4728
+ /**
4729
+ * The name of the property which is considered to identify an item.
4730
+ */ _idProperty;
4731
+ /**
4732
+ * A collection instance this collection is bound to as a result
4733
+ * of calling {@link #bindTo} method.
4734
+ */ _bindToCollection;
4735
+ /**
4736
+ * A helper mapping external items of a bound collection ({@link #bindTo})
4737
+ * and actual items of this collection. It provides information
4738
+ * necessary to properly remove items bound to another collection.
4739
+ *
4740
+ * See {@link #_bindToInternalToExternalMap}.
4741
+ */ _bindToExternalToInternalMap;
4742
+ /**
4743
+ * A helper mapping items of this collection to external items of a bound collection
4744
+ * ({@link #bindTo}). It provides information necessary to manage the bindings, e.g.
4745
+ * to avoid loops in two–way bindings.
4746
+ *
4747
+ * See {@link #_bindToExternalToInternalMap}.
4748
+ */ _bindToInternalToExternalMap;
4749
+ /**
4750
+ * Stores indexes of skipped items from bound external collection.
4751
+ */ _skippedIndexesFromExternal;
4752
+ constructor(initialItemsOrOptions = {}, options = {}){
4753
+ super();
4754
+ const hasInitialItems = isIterable(initialItemsOrOptions);
4755
+ if (!hasInitialItems) {
4756
+ options = initialItemsOrOptions;
4757
+ }
4758
+ this._items = [];
4759
+ this._itemMap = new Map();
4760
+ this._idProperty = options.idProperty || 'id';
4761
+ this._bindToExternalToInternalMap = new WeakMap();
4762
+ this._bindToInternalToExternalMap = new WeakMap();
4763
+ this._skippedIndexesFromExternal = [];
4764
+ // Set the initial content of the collection (if provided in the constructor).
4765
+ if (hasInitialItems) {
4766
+ for (const item of initialItemsOrOptions){
4767
+ this._items.push(item);
4768
+ this._itemMap.set(this._getItemIdBeforeAdding(item), item);
4769
+ }
4770
+ }
4771
+ }
4475
4772
  /**
4476
- * The number of items available in the collection.
4477
- */ get length() {
4773
+ * The number of items available in the collection.
4774
+ */ get length() {
4478
4775
  return this._items.length;
4479
4776
  }
4480
4777
  /**
4481
- * Returns the first item from the collection or null when collection is empty.
4482
- */ get first() {
4778
+ * Returns the first item from the collection or null when collection is empty.
4779
+ */ get first() {
4483
4780
  return this._items[0] || null;
4484
4781
  }
4485
4782
  /**
4486
- * Returns the last item from the collection or null when collection is empty.
4487
- */ get last() {
4783
+ * Returns the last item from the collection or null when collection is empty.
4784
+ */ get last() {
4488
4785
  return this._items[this.length - 1] || null;
4489
4786
  }
4490
4787
  /**
4491
- * Adds an item into the collection.
4492
- *
4493
- * If the item does not have an id, then it will be automatically generated and set on the item.
4494
- *
4495
- * @param item
4496
- * @param index The position of the item in the collection. The item
4497
- * is pushed to the collection when `index` not specified.
4498
- * @fires add
4499
- * @fires change
4500
- */ add(item, index) {
4788
+ * Adds an item into the collection.
4789
+ *
4790
+ * If the item does not have an id, then it will be automatically generated and set on the item.
4791
+ *
4792
+ * @param item
4793
+ * @param index The position of the item in the collection. The item
4794
+ * is pushed to the collection when `index` not specified.
4795
+ * @fires add
4796
+ * @fires change
4797
+ */ add(item, index) {
4501
4798
  return this.addMany([
4502
4799
  item
4503
4800
  ], index);
4504
4801
  }
4505
4802
  /**
4506
- * Adds multiple items into the collection.
4507
- *
4508
- * Any item not containing an id will get an automatically generated one.
4509
- *
4510
- * @param items
4511
- * @param index The position of the insertion. Items will be appended if no `index` is specified.
4512
- * @fires add
4513
- * @fires change
4514
- */ addMany(items, index) {
4803
+ * Adds multiple items into the collection.
4804
+ *
4805
+ * Any item not containing an id will get an automatically generated one.
4806
+ *
4807
+ * @param items
4808
+ * @param index The position of the insertion. Items will be appended if no `index` is specified.
4809
+ * @fires add
4810
+ * @fires change
4811
+ */ addMany(items, index) {
4515
4812
  if (index === undefined) {
4516
4813
  index = this._items.length;
4517
4814
  } else if (index > this._items.length || index < 0) {
4518
4815
  /**
4519
- * The `index` passed to {@link module:utils/collection~Collection#addMany `Collection#addMany()`}
4520
- * is invalid. It must be a number between 0 and the collection's length.
4521
- *
4522
- * @error collection-add-item-invalid-index
4523
- */ throw new CKEditorError('collection-add-item-invalid-index', this);
4816
+ * The `index` passed to {@link module:utils/collection~Collection#addMany `Collection#addMany()`}
4817
+ * is invalid. It must be a number between 0 and the collection's length.
4818
+ *
4819
+ * @error collection-add-item-invalid-index
4820
+ */ throw new CKEditorError('collection-add-item-invalid-index', this);
4524
4821
  }
4525
4822
  let offset = 0;
4526
4823
  for (const item of items){
@@ -4539,11 +4836,11 @@ class Collection extends EmitterMixin() {
4539
4836
  return this;
4540
4837
  }
4541
4838
  /**
4542
- * Gets an item by its ID or index.
4543
- *
4544
- * @param idOrIndex The item ID or index in the collection.
4545
- * @returns The requested item or `null` if such item does not exist.
4546
- */ get(idOrIndex) {
4839
+ * Gets an item by its ID or index.
4840
+ *
4841
+ * @param idOrIndex The item ID or index in the collection.
4842
+ * @returns The requested item or `null` if such item does not exist.
4843
+ */ get(idOrIndex) {
4547
4844
  let item;
4548
4845
  if (typeof idOrIndex == 'string') {
4549
4846
  item = this._itemMap.get(idOrIndex);
@@ -4551,19 +4848,19 @@ class Collection extends EmitterMixin() {
4551
4848
  item = this._items[idOrIndex];
4552
4849
  } else {
4553
4850
  /**
4554
- * An index or ID must be given.
4555
- *
4556
- * @error collection-get-invalid-arg
4557
- */ throw new CKEditorError('collection-get-invalid-arg', this);
4851
+ * An index or ID must be given.
4852
+ *
4853
+ * @error collection-get-invalid-arg
4854
+ */ throw new CKEditorError('collection-get-invalid-arg', this);
4558
4855
  }
4559
4856
  return item || null;
4560
4857
  }
4561
4858
  /**
4562
- * Returns a Boolean indicating whether the collection contains an item.
4563
- *
4564
- * @param itemOrId The item or its ID in the collection.
4565
- * @returns `true` if the collection contains the item, `false` otherwise.
4566
- */ has(itemOrId) {
4859
+ * Returns a Boolean indicating whether the collection contains an item.
4860
+ *
4861
+ * @param itemOrId The item or its ID in the collection.
4862
+ * @returns `true` if the collection contains the item, `false` otherwise.
4863
+ */ has(itemOrId) {
4567
4864
  if (typeof itemOrId == 'string') {
4568
4865
  return this._itemMap.has(itemOrId);
4569
4866
  } else {
@@ -4573,12 +4870,12 @@ class Collection extends EmitterMixin() {
4573
4870
  }
4574
4871
  }
4575
4872
  /**
4576
- * Gets an index of an item in the collection.
4577
- * When an item is not defined in the collection, the index will equal -1.
4578
- *
4579
- * @param itemOrId The item or its ID in the collection.
4580
- * @returns The index of a given item.
4581
- */ getIndex(itemOrId) {
4873
+ * Gets an index of an item in the collection.
4874
+ * When an item is not defined in the collection, the index will equal -1.
4875
+ *
4876
+ * @param itemOrId The item or its ID in the collection.
4877
+ * @returns The index of a given item.
4878
+ */ getIndex(itemOrId) {
4582
4879
  let item;
4583
4880
  if (typeof itemOrId == 'string') {
4584
4881
  item = this._itemMap.get(itemOrId);
@@ -4588,13 +4885,13 @@ class Collection extends EmitterMixin() {
4588
4885
  return item ? this._items.indexOf(item) : -1;
4589
4886
  }
4590
4887
  /**
4591
- * Removes an item from the collection.
4592
- *
4593
- * @param subject The item to remove, its ID or index in the collection.
4594
- * @returns The removed item.
4595
- * @fires remove
4596
- * @fires change
4597
- */ remove(subject) {
4888
+ * Removes an item from the collection.
4889
+ *
4890
+ * @param subject The item to remove, its ID or index in the collection.
4891
+ * @returns The removed item.
4892
+ * @fires remove
4893
+ * @fires change
4894
+ */ remove(subject) {
4598
4895
  const [item, index] = this._remove(subject);
4599
4896
  this.fire('change', {
4600
4897
  added: [],
@@ -4606,47 +4903,47 @@ class Collection extends EmitterMixin() {
4606
4903
  return item;
4607
4904
  }
4608
4905
  /**
4609
- * Executes the callback for each item in the collection and composes an array or values returned by this callback.
4610
- *
4611
- * @typeParam U The result type of the callback.
4612
- * @param callback
4613
- * @param ctx Context in which the `callback` will be called.
4614
- * @returns The result of mapping.
4615
- */ map(callback, ctx) {
4906
+ * Executes the callback for each item in the collection and composes an array or values returned by this callback.
4907
+ *
4908
+ * @typeParam U The result type of the callback.
4909
+ * @param callback
4910
+ * @param ctx Context in which the `callback` will be called.
4911
+ * @returns The result of mapping.
4912
+ */ map(callback, ctx) {
4616
4913
  return this._items.map(callback, ctx);
4617
4914
  }
4618
4915
  /**
4619
- * Performs the specified action for each item in the collection.
4620
- *
4621
- * @param ctx Context in which the `callback` will be called.
4622
- */ forEach(callback, ctx) {
4916
+ * Performs the specified action for each item in the collection.
4917
+ *
4918
+ * @param ctx Context in which the `callback` will be called.
4919
+ */ forEach(callback, ctx) {
4623
4920
  this._items.forEach(callback, ctx);
4624
4921
  }
4625
4922
  /**
4626
- * Finds the first item in the collection for which the `callback` returns a true value.
4627
- *
4628
- * @param callback
4629
- * @param ctx Context in which the `callback` will be called.
4630
- * @returns The item for which `callback` returned a true value.
4631
- */ find(callback, ctx) {
4923
+ * Finds the first item in the collection for which the `callback` returns a true value.
4924
+ *
4925
+ * @param callback
4926
+ * @param ctx Context in which the `callback` will be called.
4927
+ * @returns The item for which `callback` returned a true value.
4928
+ */ find(callback, ctx) {
4632
4929
  return this._items.find(callback, ctx);
4633
4930
  }
4634
4931
  /**
4635
- * Returns an array with items for which the `callback` returned a true value.
4636
- *
4637
- * @param callback
4638
- * @param ctx Context in which the `callback` will be called.
4639
- * @returns The array with matching items.
4640
- */ filter(callback, ctx) {
4932
+ * Returns an array with items for which the `callback` returned a true value.
4933
+ *
4934
+ * @param callback
4935
+ * @param ctx Context in which the `callback` will be called.
4936
+ * @returns The array with matching items.
4937
+ */ filter(callback, ctx) {
4641
4938
  return this._items.filter(callback, ctx);
4642
4939
  }
4643
4940
  /**
4644
- * Removes all items from the collection and destroys the binding created using
4645
- * {@link #bindTo}.
4646
- *
4647
- * @fires remove
4648
- * @fires change
4649
- */ clear() {
4941
+ * Removes all items from the collection and destroys the binding created using
4942
+ * {@link #bindTo}.
4943
+ *
4944
+ * @fires remove
4945
+ * @fires change
4946
+ */ clear() {
4650
4947
  if (this._bindToCollection) {
4651
4948
  this.stopListening(this._bindToCollection);
4652
4949
  this._bindToCollection = null;
@@ -4662,122 +4959,122 @@ class Collection extends EmitterMixin() {
4662
4959
  });
4663
4960
  }
4664
4961
  /**
4665
- * Binds and synchronizes the collection with another one.
4666
- *
4667
- * The binding can be a simple factory:
4668
- *
4669
- * ```ts
4670
- * class FactoryClass {
4671
- * public label: string;
4672
- *
4673
- * constructor( data: { label: string } ) {
4674
- * this.label = data.label;
4675
- * }
4676
- * }
4677
- *
4678
- * const source = new Collection<{ label: string }>( { idProperty: 'label' } );
4679
- * const target = new Collection<FactoryClass>();
4680
- *
4681
- * target.bindTo( source ).as( FactoryClass );
4682
- *
4683
- * source.add( { label: 'foo' } );
4684
- * source.add( { label: 'bar' } );
4685
- *
4686
- * console.log( target.length ); // 2
4687
- * console.log( target.get( 1 ).label ); // 'bar'
4688
- *
4689
- * source.remove( 0 );
4690
- * console.log( target.length ); // 1
4691
- * console.log( target.get( 0 ).label ); // 'bar'
4692
- * ```
4693
- *
4694
- * or the factory driven by a custom callback:
4695
- *
4696
- * ```ts
4697
- * class FooClass {
4698
- * public label: string;
4699
- *
4700
- * constructor( data: { label: string } ) {
4701
- * this.label = data.label;
4702
- * }
4703
- * }
4704
- *
4705
- * class BarClass {
4706
- * public label: string;
4707
- *
4708
- * constructor( data: { label: string } ) {
4709
- * this.label = data.label;
4710
- * }
4711
- * }
4712
- *
4713
- * const source = new Collection<{ label: string }>( { idProperty: 'label' } );
4714
- * const target = new Collection<FooClass | BarClass>();
4715
- *
4716
- * target.bindTo( source ).using( ( item ) => {
4717
- * if ( item.label == 'foo' ) {
4718
- * return new FooClass( item );
4719
- * } else {
4720
- * return new BarClass( item );
4721
- * }
4722
- * } );
4723
- *
4724
- * source.add( { label: 'foo' } );
4725
- * source.add( { label: 'bar' } );
4726
- *
4727
- * console.log( target.length ); // 2
4728
- * console.log( target.get( 0 ) instanceof FooClass ); // true
4729
- * console.log( target.get( 1 ) instanceof BarClass ); // true
4730
- * ```
4731
- *
4732
- * or the factory out of property name:
4733
- *
4734
- * ```ts
4735
- * const source = new Collection<{ nested: { value: string } }>();
4736
- * const target = new Collection<{ value: string }>();
4737
- *
4738
- * target.bindTo( source ).using( 'nested' );
4739
- *
4740
- * source.add( { nested: { value: 'foo' } } );
4741
- * source.add( { nested: { value: 'bar' } } );
4742
- *
4743
- * console.log( target.length ); // 2
4744
- * console.log( target.get( 0 ).value ); // 'foo'
4745
- * console.log( target.get( 1 ).value ); // 'bar'
4746
- * ```
4747
- *
4748
- * It's possible to skip specified items by returning null value:
4749
- *
4750
- * ```ts
4751
- * const source = new Collection<{ hidden: boolean }>();
4752
- * const target = new Collection<{ hidden: boolean }>();
4753
- *
4754
- * target.bindTo( source ).using( item => {
4755
- * if ( item.hidden ) {
4756
- * return null;
4757
- * }
4758
- *
4759
- * return item;
4760
- * } );
4761
- *
4762
- * source.add( { hidden: true } );
4763
- * source.add( { hidden: false } );
4764
- *
4765
- * console.log( source.length ); // 2
4766
- * console.log( target.length ); // 1
4767
- * ```
4768
- *
4769
- * **Note**: {@link #clear} can be used to break the binding.
4770
- *
4771
- * @typeParam S The type of `externalCollection` element.
4772
- * @param externalCollection A collection to be bound.
4773
- * @returns The binding chain object.
4774
- */ bindTo(externalCollection) {
4962
+ * Binds and synchronizes the collection with another one.
4963
+ *
4964
+ * The binding can be a simple factory:
4965
+ *
4966
+ * ```ts
4967
+ * class FactoryClass {
4968
+ * public label: string;
4969
+ *
4970
+ * constructor( data: { label: string } ) {
4971
+ * this.label = data.label;
4972
+ * }
4973
+ * }
4974
+ *
4975
+ * const source = new Collection<{ label: string }>( { idProperty: 'label' } );
4976
+ * const target = new Collection<FactoryClass>();
4977
+ *
4978
+ * target.bindTo( source ).as( FactoryClass );
4979
+ *
4980
+ * source.add( { label: 'foo' } );
4981
+ * source.add( { label: 'bar' } );
4982
+ *
4983
+ * console.log( target.length ); // 2
4984
+ * console.log( target.get( 1 ).label ); // 'bar'
4985
+ *
4986
+ * source.remove( 0 );
4987
+ * console.log( target.length ); // 1
4988
+ * console.log( target.get( 0 ).label ); // 'bar'
4989
+ * ```
4990
+ *
4991
+ * or the factory driven by a custom callback:
4992
+ *
4993
+ * ```ts
4994
+ * class FooClass {
4995
+ * public label: string;
4996
+ *
4997
+ * constructor( data: { label: string } ) {
4998
+ * this.label = data.label;
4999
+ * }
5000
+ * }
5001
+ *
5002
+ * class BarClass {
5003
+ * public label: string;
5004
+ *
5005
+ * constructor( data: { label: string } ) {
5006
+ * this.label = data.label;
5007
+ * }
5008
+ * }
5009
+ *
5010
+ * const source = new Collection<{ label: string }>( { idProperty: 'label' } );
5011
+ * const target = new Collection<FooClass | BarClass>();
5012
+ *
5013
+ * target.bindTo( source ).using( ( item ) => {
5014
+ * if ( item.label == 'foo' ) {
5015
+ * return new FooClass( item );
5016
+ * } else {
5017
+ * return new BarClass( item );
5018
+ * }
5019
+ * } );
5020
+ *
5021
+ * source.add( { label: 'foo' } );
5022
+ * source.add( { label: 'bar' } );
5023
+ *
5024
+ * console.log( target.length ); // 2
5025
+ * console.log( target.get( 0 ) instanceof FooClass ); // true
5026
+ * console.log( target.get( 1 ) instanceof BarClass ); // true
5027
+ * ```
5028
+ *
5029
+ * or the factory out of property name:
5030
+ *
5031
+ * ```ts
5032
+ * const source = new Collection<{ nested: { value: string } }>();
5033
+ * const target = new Collection<{ value: string }>();
5034
+ *
5035
+ * target.bindTo( source ).using( 'nested' );
5036
+ *
5037
+ * source.add( { nested: { value: 'foo' } } );
5038
+ * source.add( { nested: { value: 'bar' } } );
5039
+ *
5040
+ * console.log( target.length ); // 2
5041
+ * console.log( target.get( 0 ).value ); // 'foo'
5042
+ * console.log( target.get( 1 ).value ); // 'bar'
5043
+ * ```
5044
+ *
5045
+ * It's possible to skip specified items by returning null value:
5046
+ *
5047
+ * ```ts
5048
+ * const source = new Collection<{ hidden: boolean }>();
5049
+ * const target = new Collection<{ hidden: boolean }>();
5050
+ *
5051
+ * target.bindTo( source ).using( item => {
5052
+ * if ( item.hidden ) {
5053
+ * return null;
5054
+ * }
5055
+ *
5056
+ * return item;
5057
+ * } );
5058
+ *
5059
+ * source.add( { hidden: true } );
5060
+ * source.add( { hidden: false } );
5061
+ *
5062
+ * console.log( source.length ); // 2
5063
+ * console.log( target.length ); // 1
5064
+ * ```
5065
+ *
5066
+ * **Note**: {@link #clear} can be used to break the binding.
5067
+ *
5068
+ * @typeParam S The type of `externalCollection` element.
5069
+ * @param externalCollection A collection to be bound.
5070
+ * @returns The binding chain object.
5071
+ */ bindTo(externalCollection) {
4775
5072
  if (this._bindToCollection) {
4776
5073
  /**
4777
- * The collection cannot be bound more than once.
4778
- *
4779
- * @error collection-bind-to-rebind
4780
- */ throw new CKEditorError('collection-bind-to-rebind', this);
5074
+ * The collection cannot be bound more than once.
5075
+ *
5076
+ * @error collection-bind-to-rebind
5077
+ */ throw new CKEditorError('collection-bind-to-rebind', this);
4781
5078
  }
4782
5079
  this._bindToCollection = externalCollection;
4783
5080
  return {
@@ -4794,10 +5091,10 @@ class Collection extends EmitterMixin() {
4794
5091
  };
4795
5092
  }
4796
5093
  /**
4797
- * Finalizes and activates a binding initiated by {@link #bindTo}.
4798
- *
4799
- * @param factory A function which produces collection items.
4800
- */ _setUpBindToBinding(factory) {
5094
+ * Finalizes and activates a binding initiated by {@link #bindTo}.
5095
+ *
5096
+ * @param factory A function which produces collection items.
5097
+ */ _setUpBindToBinding(factory) {
4801
5098
  const externalCollection = this._bindToCollection;
4802
5099
  // Adds the item to the collection once a change has been done to the external collection.
4803
5100
  const addItem = (evt, externalItem, index)=>{
@@ -4898,29 +5195,29 @@ class Collection extends EmitterMixin() {
4898
5195
  });
4899
5196
  }
4900
5197
  /**
4901
- * Returns an unique id property for a given `item`.
4902
- *
4903
- * The method will generate new id and assign it to the `item` if it doesn't have any.
4904
- *
4905
- * @param item Item to be added.
4906
- */ _getItemIdBeforeAdding(item) {
5198
+ * Returns an unique id property for a given `item`.
5199
+ *
5200
+ * The method will generate new id and assign it to the `item` if it doesn't have any.
5201
+ *
5202
+ * @param item Item to be added.
5203
+ */ _getItemIdBeforeAdding(item) {
4907
5204
  const idProperty = this._idProperty;
4908
5205
  let itemId;
4909
5206
  if (idProperty in item) {
4910
5207
  itemId = item[idProperty];
4911
5208
  if (typeof itemId != 'string') {
4912
5209
  /**
4913
- * This item's ID should be a string.
4914
- *
4915
- * @error collection-add-invalid-id
4916
- */ throw new CKEditorError('collection-add-invalid-id', this);
5210
+ * This item's ID should be a string.
5211
+ *
5212
+ * @error collection-add-invalid-id
5213
+ */ throw new CKEditorError('collection-add-invalid-id', this);
4917
5214
  }
4918
5215
  if (this.get(itemId)) {
4919
5216
  /**
4920
- * This item already exists in the collection.
4921
- *
4922
- * @error collection-add-item-already-exists
4923
- */ throw new CKEditorError('collection-add-item-already-exists', this);
5217
+ * This item already exists in the collection.
5218
+ *
5219
+ * @error collection-add-item-already-exists
5220
+ */ throw new CKEditorError('collection-add-item-already-exists', this);
4924
5221
  }
4925
5222
  } else {
4926
5223
  item[idProperty] = itemId = uid();
@@ -4928,14 +5225,14 @@ class Collection extends EmitterMixin() {
4928
5225
  return itemId;
4929
5226
  }
4930
5227
  /**
4931
- * Core {@link #remove} method implementation shared in other functions.
4932
- *
4933
- * In contrast this method **does not** fire the {@link #event:change} event.
4934
- *
4935
- * @param subject The item to remove, its id or index in the collection.
4936
- * @returns Returns an array with the removed item and its index.
4937
- * @fires remove
4938
- */ _remove(subject) {
5228
+ * Core {@link #remove} method implementation shared in other functions.
5229
+ *
5230
+ * In contrast this method **does not** fire the {@link #event:change} event.
5231
+ *
5232
+ * @param subject The item to remove, its id or index in the collection.
5233
+ * @returns Returns an array with the removed item and its index.
5234
+ * @fires remove
5235
+ */ _remove(subject) {
4939
5236
  let index, id, item;
4940
5237
  let itemDoesNotExist = false;
4941
5238
  const idProperty = this._idProperty;
@@ -4961,10 +5258,10 @@ class Collection extends EmitterMixin() {
4961
5258
  }
4962
5259
  if (itemDoesNotExist) {
4963
5260
  /**
4964
- * Item not found.
4965
- *
4966
- * @error collection-remove-404
4967
- */ throw new CKEditorError('collection-remove-404', this);
5261
+ * Item not found.
5262
+ *
5263
+ * @error collection-remove-404
5264
+ */ throw new CKEditorError('collection-remove-404', this);
4968
5265
  }
4969
5266
  this._items.splice(index, 1);
4970
5267
  this._itemMap.delete(id);
@@ -4978,30 +5275,10 @@ class Collection extends EmitterMixin() {
4978
5275
  ];
4979
5276
  }
4980
5277
  /**
4981
- * Iterable interface.
4982
- */ [Symbol.iterator]() {
5278
+ * Iterable interface.
5279
+ */ [Symbol.iterator]() {
4983
5280
  return this._items[Symbol.iterator]();
4984
5281
  }
4985
- constructor(initialItemsOrOptions = {}, options = {}){
4986
- super();
4987
- const hasInitialItems = isIterable(initialItemsOrOptions);
4988
- if (!hasInitialItems) {
4989
- options = initialItemsOrOptions;
4990
- }
4991
- this._items = [];
4992
- this._itemMap = new Map();
4993
- this._idProperty = options.idProperty || 'id';
4994
- this._bindToExternalToInternalMap = new WeakMap();
4995
- this._bindToInternalToExternalMap = new WeakMap();
4996
- this._skippedIndexesFromExternal = [];
4997
- // Set the initial content of the collection (if provided in the constructor).
4998
- if (hasInitialItems) {
4999
- for (const item of initialItemsOrOptions){
5000
- this._items.push(item);
5001
- this._itemMap.set(this._getItemIdBeforeAdding(item), item);
5002
- }
5003
- }
5004
- }
5005
5282
  }
5006
5283
 
5007
5284
  /**
@@ -5019,16 +5296,40 @@ class Collection extends EmitterMixin() {
5019
5296
  return iteratorItem.value;
5020
5297
  }
5021
5298
 
5022
- class FocusTracker extends DomEmitterMixin(ObservableMixin()) {
5299
+ /**
5300
+ * Allows observing a group of `Element`s whether at least one of them is focused.
5301
+ *
5302
+ * Used by the {@link module:core/editor/editor~Editor} in order to track whether the focus is still within the application,
5303
+ * or were used outside of its UI.
5304
+ *
5305
+ * **Note** `focus` and `blur` listeners use event capturing, so it is only needed to register wrapper `Element`
5306
+ * which contain other `focusable` elements. But note that this wrapper element has to be focusable too
5307
+ * (have e.g. `tabindex="-1"`).
5308
+ *
5309
+ * Check out the {@glink framework/deep-dive/ui/focus-tracking "Deep dive into focus tracking"} guide to learn more.
5310
+ */ class FocusTracker extends /* #__PURE__ */ DomEmitterMixin(/* #__PURE__ */ ObservableMixin()) {
5023
5311
  /**
5024
- * Starts tracking the specified element.
5025
- */ add(element) {
5312
+ * List of registered elements.
5313
+ *
5314
+ * @internal
5315
+ */ _elements = new Set();
5316
+ /**
5317
+ * Event loop timeout.
5318
+ */ _nextEventLoopTimeout = null;
5319
+ constructor(){
5320
+ super();
5321
+ this.set('isFocused', false);
5322
+ this.set('focusedElement', null);
5323
+ }
5324
+ /**
5325
+ * Starts tracking the specified element.
5326
+ */ add(element) {
5026
5327
  if (this._elements.has(element)) {
5027
5328
  /**
5028
- * This element is already tracked by {@link module:utils/focustracker~FocusTracker}.
5029
- *
5030
- * @error focustracker-add-element-already-exist
5031
- */ throw new CKEditorError('focustracker-add-element-already-exist', this);
5329
+ * This element is already tracked by {@link module:utils/focustracker~FocusTracker}.
5330
+ *
5331
+ * @error focustracker-add-element-already-exist
5332
+ */ throw new CKEditorError('focustracker-add-element-already-exist', this);
5032
5333
  }
5033
5334
  this.listenTo(element, 'focus', ()=>this._focus(element), {
5034
5335
  useCapture: true
@@ -5039,8 +5340,8 @@ class FocusTracker extends DomEmitterMixin(ObservableMixin()) {
5039
5340
  this._elements.add(element);
5040
5341
  }
5041
5342
  /**
5042
- * Stops tracking the specified element and stops listening on this element.
5043
- */ remove(element) {
5343
+ * Stops tracking the specified element and stops listening on this element.
5344
+ */ remove(element) {
5044
5345
  if (element === this.focusedElement) {
5045
5346
  this._blur();
5046
5347
  }
@@ -5050,48 +5351,77 @@ class FocusTracker extends DomEmitterMixin(ObservableMixin()) {
5050
5351
  }
5051
5352
  }
5052
5353
  /**
5053
- * Destroys the focus tracker by:
5054
- * - Disabling all event listeners attached to tracked elements.
5055
- * - Removing all tracked elements that were previously added.
5056
- */ destroy() {
5354
+ * Destroys the focus tracker by:
5355
+ * - Disabling all event listeners attached to tracked elements.
5356
+ * - Removing all tracked elements that were previously added.
5357
+ */ destroy() {
5057
5358
  this.stopListening();
5058
5359
  }
5059
5360
  /**
5060
- * Stores currently focused element and set {@link #isFocused} as `true`.
5061
- */ _focus(element) {
5361
+ * Stores currently focused element and set {@link #isFocused} as `true`.
5362
+ */ _focus(element) {
5062
5363
  clearTimeout(this._nextEventLoopTimeout);
5063
5364
  this.focusedElement = element;
5064
5365
  this.isFocused = true;
5065
5366
  }
5066
5367
  /**
5067
- * Clears currently focused element and set {@link #isFocused} as `false`.
5068
- * This method uses `setTimeout` to change order of fires `blur` and `focus` events.
5069
- */ _blur() {
5368
+ * Clears currently focused element and set {@link #isFocused} as `false`.
5369
+ * This method uses `setTimeout` to change order of fires `blur` and `focus` events.
5370
+ */ _blur() {
5070
5371
  clearTimeout(this._nextEventLoopTimeout);
5071
5372
  this._nextEventLoopTimeout = setTimeout(()=>{
5072
5373
  this.focusedElement = null;
5073
5374
  this.isFocused = false;
5074
5375
  }, 0);
5075
5376
  }
5076
- constructor(){
5077
- super();
5078
- /**
5079
- * List of registered elements.
5080
- *
5081
- * @internal
5082
- */ this._elements = new Set();
5083
- /**
5084
- * Event loop timeout.
5085
- */ this._nextEventLoopTimeout = null;
5086
- this.set('isFocused', false);
5087
- this.set('focusedElement', null);
5088
- }
5089
5377
  }
5090
5378
 
5091
- class KeystrokeHandler {
5379
+ /**
5380
+ * Keystroke handler allows registering callbacks for given keystrokes.
5381
+ *
5382
+ * The most frequent use of this class is through the {@link module:core/editor/editor~Editor#keystrokes `editor.keystrokes`}
5383
+ * property. It allows listening to keystrokes executed in the editing view:
5384
+ *
5385
+ * ```ts
5386
+ * editor.keystrokes.set( 'Ctrl+A', ( keyEvtData, cancel ) => {
5387
+ * console.log( 'Ctrl+A has been pressed' );
5388
+ * cancel();
5389
+ * } );
5390
+ * ```
5391
+ *
5392
+ * However, this utility class can be used in various part of the UI. For instance, a certain {@link module:ui/view~View}
5393
+ * can use it like this:
5394
+ *
5395
+ * ```ts
5396
+ * class MyView extends View {
5397
+ * constructor() {
5398
+ * this.keystrokes = new KeystrokeHandler();
5399
+ *
5400
+ * this.keystrokes.set( 'tab', handleTabKey );
5401
+ * }
5402
+ *
5403
+ * render() {
5404
+ * super.render();
5405
+ *
5406
+ * this.keystrokes.listenTo( this.element );
5407
+ * }
5408
+ * }
5409
+ * ```
5410
+ *
5411
+ * That keystroke handler will listen to `keydown` events fired in this view's main element.
5412
+ *
5413
+ */ class KeystrokeHandler {
5414
+ /**
5415
+ * Listener used to listen to events for easier keystroke handler destruction.
5416
+ */ _listener;
5092
5417
  /**
5093
- * Starts listening for `keydown` events from a given emitter.
5094
- */ listenTo(emitter) {
5418
+ * Creates an instance of the keystroke handler.
5419
+ */ constructor(){
5420
+ this._listener = new (DomEmitterMixin())();
5421
+ }
5422
+ /**
5423
+ * Starts listening for `keydown` events from a given emitter.
5424
+ */ listenTo(emitter) {
5095
5425
  // The #_listener works here as a kind of dispatcher. It groups the events coming from the same
5096
5426
  // keystroke so the listeners can be attached to them with different priorities.
5097
5427
  //
@@ -5106,18 +5436,18 @@ class KeystrokeHandler {
5106
5436
  });
5107
5437
  }
5108
5438
  /**
5109
- * Registers a handler for the specified keystroke.
5110
- *
5111
- * @param keystroke Keystroke defined in a format accepted by
5112
- * the {@link module:utils/keyboard~parseKeystroke} function.
5113
- * @param callback A function called with the
5114
- * {@link module:engine/view/observer/keyobserver~KeyEventData key event data} object and
5115
- * a helper function to call both `preventDefault()` and `stopPropagation()` on the underlying event.
5116
- * @param options Additional options.
5117
- * @param options.priority The priority of the keystroke
5118
- * callback. The higher the priority value the sooner the callback will be executed. Keystrokes having the same priority
5119
- * are called in the order they were added.
5120
- */ set(keystroke, callback, options = {}) {
5439
+ * Registers a handler for the specified keystroke.
5440
+ *
5441
+ * @param keystroke Keystroke defined in a format accepted by
5442
+ * the {@link module:utils/keyboard~parseKeystroke} function.
5443
+ * @param callback A function called with the
5444
+ * {@link module:engine/view/observer/keyobserver~KeyEventData key event data} object and
5445
+ * a helper function to call both `preventDefault()` and `stopPropagation()` on the underlying event.
5446
+ * @param options Additional options.
5447
+ * @param options.priority The priority of the keystroke
5448
+ * callback. The higher the priority value the sooner the callback will be executed. Keystrokes having the same priority
5449
+ * are called in the order they were added.
5450
+ */ set(keystroke, callback, options = {}) {
5121
5451
  const keyCode = parseKeystroke(keystroke);
5122
5452
  const priority = options.priority;
5123
5453
  // Execute the passed callback on KeystrokeHandler#_keydown.
@@ -5139,28 +5469,23 @@ class KeystrokeHandler {
5139
5469
  });
5140
5470
  }
5141
5471
  /**
5142
- * Triggers a keystroke handler for a specified key combination, if such a keystroke was {@link #set defined}.
5143
- *
5144
- * @param keyEvtData Key event data.
5145
- * @returns Whether the keystroke was handled.
5146
- */ press(keyEvtData) {
5472
+ * Triggers a keystroke handler for a specified key combination, if such a keystroke was {@link #set defined}.
5473
+ *
5474
+ * @param keyEvtData Key event data.
5475
+ * @returns Whether the keystroke was handled.
5476
+ */ press(keyEvtData) {
5147
5477
  return !!this._listener.fire('_keydown:' + getCode(keyEvtData), keyEvtData);
5148
5478
  }
5149
5479
  /**
5150
- * Stops listening to `keydown` events from the given emitter.
5151
- */ stopListening(emitter) {
5480
+ * Stops listening to `keydown` events from the given emitter.
5481
+ */ stopListening(emitter) {
5152
5482
  this._listener.stopListening(emitter);
5153
5483
  }
5154
5484
  /**
5155
- * Destroys the keystroke handler.
5156
- */ destroy() {
5485
+ * Destroys the keystroke handler.
5486
+ */ destroy() {
5157
5487
  this.stopListening();
5158
5488
  }
5159
- /**
5160
- * Creates an instance of the keystroke handler.
5161
- */ constructor(){
5162
- this._listener = new (DomEmitterMixin())();
5163
- }
5164
5489
  }
5165
5490
 
5166
5491
  /**
@@ -5460,7 +5785,7 @@ class KeystrokeHandler {
5460
5785
  */ function isInsideCombinedSymbol(string, offset) {
5461
5786
  return isCombiningMark(string.charAt(offset));
5462
5787
  }
5463
- const EMOJI_PATTERN = buildEmojiRegexp();
5788
+ const EMOJI_PATTERN = /* #__PURE__ */ buildEmojiRegexp();
5464
5789
  /**
5465
5790
  * Checks whether given offset in a string is inside multi-character emoji sequence.
5466
5791
  *
@@ -5473,15 +5798,15 @@ const EMOJI_PATTERN = buildEmojiRegexp();
5473
5798
  function buildEmojiRegexp() {
5474
5799
  const parts = [
5475
5800
  // Emoji Tag Sequence (ETS)
5476
- RegExp("\\p{Emoji}[\\u{E0020}-\\u{E007E}]+\\u{E007F}", "u"),
5801
+ /\p{Emoji}[\u{E0020}-\u{E007E}]+\u{E007F}/u,
5477
5802
  // Emoji Keycap Sequence
5478
- RegExp("\\p{Emoji}\\u{FE0F}?\\u{20E3}", "u"),
5803
+ /\p{Emoji}\u{FE0F}?\u{20E3}/u,
5479
5804
  // Emoji Presentation Sequence
5480
- RegExp("\\p{Emoji}\\u{FE0F}", "u"),
5805
+ /\p{Emoji}\u{FE0F}/u,
5481
5806
  // Single-Character Emoji / Emoji Modifier Sequence
5482
- RegExp("(?=\\p{General_Category=Other_Symbol})\\p{Emoji}\\p{Emoji_Modifier}*", "u")
5807
+ /(?=\p{General_Category=Other_Symbol})\p{Emoji}\p{Emoji_Modifier}*/u
5483
5808
  ];
5484
- const flagSequence = RegExp("\\p{Regional_Indicator}{2}", "u").source;
5809
+ const flagSequence = /\p{Regional_Indicator}{2}/u.source;
5485
5810
  const emoji = '(?:' + parts.map((part)=>part.source).join('|') + ')';
5486
5811
  const sequence = `${flagSequence}|${emoji}(?:\u{200D}${emoji})*`;
5487
5812
  return new RegExp(sequence, 'ug');