@ckeditor/ckeditor5-emoji 44.2.0-alpha.8 → 44.2.0-alpha.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-emoji",
3
- "version": "44.2.0-alpha.8",
3
+ "version": "44.2.0-alpha.9",
4
4
  "description": "Emoji feature for CKEditor 5.",
5
5
  "keywords": [
6
6
  "ckeditor",
@@ -13,12 +13,12 @@
13
13
  "type": "module",
14
14
  "main": "src/index.js",
15
15
  "dependencies": {
16
- "@ckeditor/ckeditor5-core": "44.2.0-alpha.8",
17
- "@ckeditor/ckeditor5-mention": "44.2.0-alpha.8",
18
- "@ckeditor/ckeditor5-typing": "44.2.0-alpha.8",
19
- "@ckeditor/ckeditor5-ui": "44.2.0-alpha.8",
20
- "@ckeditor/ckeditor5-utils": "44.2.0-alpha.8",
21
- "ckeditor5": "44.2.0-alpha.8",
16
+ "@ckeditor/ckeditor5-core": "44.2.0-alpha.9",
17
+ "@ckeditor/ckeditor5-mention": "44.2.0-alpha.9",
18
+ "@ckeditor/ckeditor5-typing": "44.2.0-alpha.9",
19
+ "@ckeditor/ckeditor5-ui": "44.2.0-alpha.9",
20
+ "@ckeditor/ckeditor5-utils": "44.2.0-alpha.9",
21
+ "ckeditor5": "44.2.0-alpha.9",
22
22
  "fuse.js": "7.0.0",
23
23
  "lodash-es": "4.17.21"
24
24
  },
@@ -75,6 +75,7 @@ export interface EmojiConfig {
75
75
  *
76
76
  * @default 16
77
77
  */
78
- version?: 15 | 16;
78
+ version?: EmojiVersion;
79
79
  }
80
80
  export type SkinToneId = 'default' | 'light' | 'medium-light' | 'medium' | 'medium-dark' | 'dark';
81
+ export type EmojiVersion = 15 | 16;
@@ -8,26 +8,26 @@ import type { SkinToneId } from './emojiconfig.js';
8
8
  /**
9
9
  * The emoji repository plugin.
10
10
  *
11
- * Loads the emoji database from URL during plugin initialization and provides utility methods to search it.
11
+ * Loads the emoji repository from URL during plugin initialization and provides utility methods to search it.
12
12
  */
13
13
  export default class EmojiRepository extends Plugin {
14
14
  /**
15
- * A callback to resolve the {@link #_databasePromise} to control the return value of this promise.
15
+ * A callback to resolve the {@link #_repositoryPromise} to control the return value of this promise.
16
16
  */
17
- private _databasePromiseResolveCallback;
17
+ private _repositoryPromiseResolveCallback;
18
18
  /**
19
19
  * An instance of the [Fuse.js](https://www.fusejs.io/) library.
20
20
  */
21
21
  private _fuseSearch;
22
22
  /**
23
- * Emoji database.
23
+ * The emoji version that is used to prepare the emoji repository.
24
24
  */
25
- private _database;
25
+ private readonly _version;
26
26
  /**
27
- * A promise resolved after downloading the emoji database.
28
- * The promise resolves with `true` when the database is successfully downloaded or `false` otherwise.
27
+ * A promise resolved after downloading the emoji collection.
28
+ * The promise resolves with `true` when the repository is successfully downloaded or `false` otherwise.
29
29
  */
30
- private readonly _databasePromise;
30
+ private readonly _repositoryPromise;
31
31
  /**
32
32
  * @inheritDoc
33
33
  */
@@ -50,7 +50,7 @@ export default class EmojiRepository extends Plugin {
50
50
  init(): Promise<void>;
51
51
  /**
52
52
  * Returns an array of emoji entries that match the search query.
53
- * If the emoji database is not loaded, the [Fuse.js](https://www.fusejs.io/) instance is not created,
53
+ * If the emoji repository is not loaded, the [Fuse.js](https://www.fusejs.io/) instance is not created,
54
54
  * hence this method returns an empty array.
55
55
  *
56
56
  * @param searchQuery A search query to match emoji.
@@ -59,7 +59,7 @@ export default class EmojiRepository extends Plugin {
59
59
  getEmojiByQuery(searchQuery: string): Array<EmojiEntry>;
60
60
  /**
61
61
  * Groups all emojis by categories.
62
- * If the emoji database is not loaded, it returns an empty array.
62
+ * If the emoji repository is not loaded, it returns an empty array.
63
63
  *
64
64
  * @returns An array of emoji entries grouped by categories.
65
65
  */
@@ -69,9 +69,28 @@ export default class EmojiRepository extends Plugin {
69
69
  */
70
70
  getSkinTones(): Array<SkinTone>;
71
71
  /**
72
- * Indicates whether the emoji database has been successfully downloaded and the plugin is operational.
72
+ * Indicates whether the emoji repository has been successfully downloaded and the plugin is operational.
73
73
  */
74
74
  isReady(): Promise<boolean>;
75
+ /**
76
+ * Returns the emoji repository in a configured version if it is a non-empty array. Returns `null` otherwise.
77
+ */
78
+ private _getItems;
79
+ /**
80
+ * Makes the HTTP request to download the emoji repository in a configured version.
81
+ */
82
+ private _loadItemsFromCdn;
83
+ /**
84
+ * Normalizes the raw data fetched from CDN. By normalization, we meant:
85
+ *
86
+ * * Filter out unsupported emoji (these that will not render correctly),
87
+ * * Prepare skin tone variants if an emoji defines them.
88
+ */
89
+ private _normalizeEmoji;
90
+ /**
91
+ * Versioned emoji repository.
92
+ */
93
+ private static _results;
75
94
  }
76
95
  /**
77
96
  * Represents a single group of the emoji category, e.g., "Smileys & Expressions".
@@ -8,17 +8,17 @@
8
8
  import Fuse from 'fuse.js';
9
9
  import { groupBy } from 'lodash-es';
10
10
  import { Plugin } from 'ckeditor5/src/core.js';
11
- import { logWarning } from 'ckeditor5/src/utils.js';
11
+ import { logWarning, version } from 'ckeditor5/src/utils.js';
12
12
  import EmojiUtils from './emojiutils.js';
13
- // An endpoint from which the emoji database will be downloaded during plugin initialization.
13
+ // An endpoint from which the emoji data will be downloaded during plugin initialization.
14
14
  // The `{version}` placeholder is replaced with the value from editor config.
15
15
  const EMOJI_DATABASE_URL = 'https://cdn.ckeditor.com/ckeditor5/data/emoji/{version}/en.json';
16
16
  /**
17
17
  * The emoji repository plugin.
18
18
  *
19
- * Loads the emoji database from URL during plugin initialization and provides utility methods to search it.
19
+ * Loads the emoji repository from URL during plugin initialization and provides utility methods to search it.
20
20
  */
21
- export default class EmojiRepository extends Plugin {
21
+ class EmojiRepository extends Plugin {
22
22
  /**
23
23
  * @inheritDoc
24
24
  */
@@ -42,13 +42,13 @@ export default class EmojiRepository extends Plugin {
42
42
  */
43
43
  constructor(editor) {
44
44
  super(editor);
45
- this.editor.config.define('emoji', {
45
+ editor.config.define('emoji', {
46
46
  version: 16,
47
47
  skinTone: 'default'
48
48
  });
49
- this._database = [];
50
- this._databasePromise = new Promise(resolve => {
51
- this._databasePromiseResolveCallback = resolve;
49
+ this._version = editor.config.get('emoji.version');
50
+ this._repositoryPromise = new Promise(resolve => {
51
+ this._repositoryPromiseResolveCallback = resolve;
52
52
  });
53
53
  this._fuseSearch = null;
54
54
  }
@@ -56,26 +56,18 @@ export default class EmojiRepository extends Plugin {
56
56
  * @inheritDoc
57
57
  */
58
58
  async init() {
59
- const emojiUtils = this.editor.plugins.get('EmojiUtils');
60
- const emojiVersion = this.editor.config.get('emoji.version');
61
- const emojiDatabaseUrl = EMOJI_DATABASE_URL.replace('{version}', `${emojiVersion}`);
62
- const emojiDatabase = await loadEmojiDatabase(emojiDatabaseUrl);
63
- const emojiSupportedVersionByOs = emojiUtils.getEmojiSupportedVersionByOs();
64
- // Skip the initialization if the emoji database download has failed.
65
- // An empty database prevents the initialization of other dependent plugins, such as `EmojiMention` and `EmojiPicker`.
66
- if (!emojiDatabase.length) {
67
- return this._databasePromiseResolveCallback(false);
59
+ if (!(this._version in EmojiRepository._results)) {
60
+ const cdnResult = await this._loadItemsFromCdn();
61
+ EmojiRepository._results[this._version] = this._normalizeEmoji(cdnResult);
62
+ }
63
+ const items = this._getItems();
64
+ // Skip plugin initialization if the emoji repository is not available.
65
+ // The initialization of other dependent plugins, such as `EmojiMention` and `EmojiPicker`, is prevented as well.
66
+ if (!items) {
67
+ return this._repositoryPromiseResolveCallback(false);
68
68
  }
69
- const container = emojiUtils.createEmojiWidthTestingContainer();
70
- document.body.appendChild(container);
71
- // Store the emoji database after normalizing the raw data.
72
- this._database = emojiDatabase
73
- .filter(item => emojiUtils.isEmojiCategoryAllowed(item))
74
- .filter(item => emojiUtils.isEmojiSupported(item, emojiSupportedVersionByOs, container))
75
- .map(item => emojiUtils.normalizeEmojiSkinTone(item));
76
- container.remove();
77
69
  // Create instance of the Fuse.js library with configured weighted search keys and disabled fuzzy search.
78
- this._fuseSearch = new Fuse(this._database, {
70
+ this._fuseSearch = new Fuse(items, {
79
71
  keys: [
80
72
  { name: 'emoticon', weight: 5 },
81
73
  { name: 'annotation', weight: 3 },
@@ -85,11 +77,11 @@ export default class EmojiRepository extends Plugin {
85
77
  threshold: 0,
86
78
  ignoreLocation: true
87
79
  });
88
- return this._databasePromiseResolveCallback(true);
80
+ return this._repositoryPromiseResolveCallback(true);
89
81
  }
90
82
  /**
91
83
  * Returns an array of emoji entries that match the search query.
92
- * If the emoji database is not loaded, the [Fuse.js](https://www.fusejs.io/) instance is not created,
84
+ * If the emoji repository is not loaded, the [Fuse.js](https://www.fusejs.io/) instance is not created,
93
85
  * hence this method returns an empty array.
94
86
  *
95
87
  * @param searchQuery A search query to match emoji.
@@ -123,12 +115,13 @@ export default class EmojiRepository extends Plugin {
123
115
  }
124
116
  /**
125
117
  * Groups all emojis by categories.
126
- * If the emoji database is not loaded, it returns an empty array.
118
+ * If the emoji repository is not loaded, it returns an empty array.
127
119
  *
128
120
  * @returns An array of emoji entries grouped by categories.
129
121
  */
130
122
  getEmojiCategories() {
131
- if (!this._database.length) {
123
+ const repository = this._getItems();
124
+ if (!repository) {
132
125
  return [];
133
126
  }
134
127
  const { t } = this.editor.locale;
@@ -143,7 +136,7 @@ export default class EmojiRepository extends Plugin {
143
136
  { title: t('Symbols'), icon: '🟢', groupId: 8 },
144
137
  { title: t('Flags'), icon: '🏁', groupId: 9 }
145
138
  ];
146
- const groups = groupBy(this._database, 'group');
139
+ const groups = groupBy(repository, 'group');
147
140
  return categories.map(category => {
148
141
  return {
149
142
  ...category,
@@ -166,37 +159,69 @@ export default class EmojiRepository extends Plugin {
166
159
  ];
167
160
  }
168
161
  /**
169
- * Indicates whether the emoji database has been successfully downloaded and the plugin is operational.
162
+ * Indicates whether the emoji repository has been successfully downloaded and the plugin is operational.
170
163
  */
171
164
  isReady() {
172
- return this._databasePromise;
165
+ return this._repositoryPromise;
173
166
  }
174
- }
175
- /**
176
- * Makes the HTTP request to download the emoji database.
177
- */
178
- async function loadEmojiDatabase(emojiDatabaseUrl) {
179
- const result = await fetch(emojiDatabaseUrl)
180
- .then(response => {
181
- if (!response.ok) {
167
+ /**
168
+ * Returns the emoji repository in a configured version if it is a non-empty array. Returns `null` otherwise.
169
+ */
170
+ _getItems() {
171
+ const repository = EmojiRepository._results[this._version];
172
+ return repository && repository.length ? repository : null;
173
+ }
174
+ /**
175
+ * Makes the HTTP request to download the emoji repository in a configured version.
176
+ */
177
+ async _loadItemsFromCdn() {
178
+ const repositoryUrl = new URL(EMOJI_DATABASE_URL.replace('{version}', `${this._version}`));
179
+ repositoryUrl.searchParams.set('editorVersion', version);
180
+ const result = await fetch(repositoryUrl, { cache: 'force-cache' })
181
+ .then(response => {
182
+ if (!response.ok) {
183
+ return [];
184
+ }
185
+ return response.json();
186
+ })
187
+ .catch(() => {
182
188
  return [];
189
+ });
190
+ if (!result.length) {
191
+ /**
192
+ * Unable to load the emoji repository from CDN.
193
+ *
194
+ * If the CDN works properly and there is no disruption of communication, please check your
195
+ * {@glink getting-started/setup/csp Content Security Policy (CSP)} setting and make sure
196
+ * the CDN connection is allowed by the editor.
197
+ *
198
+ * @error emoji-repository-load-failed
199
+ */
200
+ logWarning('emoji-repository-load-failed');
183
201
  }
184
- return response.json();
185
- })
186
- .catch(() => {
187
- return [];
188
- });
189
- if (!result.length) {
190
- /**
191
- * Unable to load the emoji database from CDN.
192
- *
193
- * If the CDN works properly and there is no disruption of communication, please check your
194
- * {@glink getting-started/setup/csp Content Security Policy (CSP)} setting and make sure
195
- * the CDN connection is allowed by the editor.
196
- *
197
- * @error emoji-database-load-failed
198
- */
199
- logWarning('emoji-database-load-failed');
202
+ return result;
203
+ }
204
+ /**
205
+ * Normalizes the raw data fetched from CDN. By normalization, we meant:
206
+ *
207
+ * * Filter out unsupported emoji (these that will not render correctly),
208
+ * * Prepare skin tone variants if an emoji defines them.
209
+ */
210
+ _normalizeEmoji(data) {
211
+ const emojiUtils = this.editor.plugins.get('EmojiUtils');
212
+ const emojiSupportedVersionByOs = emojiUtils.getEmojiSupportedVersionByOs();
213
+ const container = emojiUtils.createEmojiWidthTestingContainer();
214
+ document.body.appendChild(container);
215
+ const results = data
216
+ .filter(item => emojiUtils.isEmojiCategoryAllowed(item))
217
+ .filter(item => emojiUtils.isEmojiSupported(item, emojiSupportedVersionByOs, container))
218
+ .map(item => emojiUtils.normalizeEmojiSkinTone(item));
219
+ container.remove();
220
+ return results;
200
221
  }
201
- return result;
202
222
  }
223
+ /**
224
+ * Versioned emoji repository.
225
+ */
226
+ EmojiRepository._results = {};
227
+ export default EmojiRepository;
@@ -59,7 +59,7 @@ export default function isEmojiSupported(unicode) {
59
59
  ;
60
60
  function getCanvas() {
61
61
  try {
62
- return document.createElement('canvas').getContext('2d');
62
+ return document.createElement('canvas').getContext('2d', { willReadFrequently: true });
63
63
  }
64
64
  catch {
65
65
  /* istanbul ignore next -- @preserve */