@ckeditor/ckeditor5-emoji 44.2.0-alpha.9 → 44.2.1-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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-emoji",
3
- "version": "44.2.0-alpha.9",
3
+ "version": "44.2.1-alpha.0",
4
4
  "description": "Emoji feature for CKEditor 5.",
5
5
  "keywords": [
6
6
  "ckeditor",
@@ -13,13 +13,13 @@
13
13
  "type": "module",
14
14
  "main": "src/index.js",
15
15
  "dependencies": {
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
- "fuse.js": "7.0.0",
16
+ "@ckeditor/ckeditor5-core": "44.2.1-alpha.0",
17
+ "@ckeditor/ckeditor5-mention": "44.2.1-alpha.0",
18
+ "@ckeditor/ckeditor5-typing": "44.2.1-alpha.0",
19
+ "@ckeditor/ckeditor5-ui": "44.2.1-alpha.0",
20
+ "@ckeditor/ckeditor5-utils": "44.2.1-alpha.0",
21
+ "ckeditor5": "44.2.1-alpha.0",
22
+ "fuzzysort": "3.1.0",
23
23
  "lodash-es": "4.17.21"
24
24
  },
25
25
  "author": "CKSource (http://cksource.com/)",
@@ -41,6 +41,11 @@
41
41
  "ckeditor5-metadata.json",
42
42
  "CHANGELOG.md"
43
43
  ],
44
+ "sideEffects": [
45
+ "*.css",
46
+ "build/**/*.js",
47
+ "dist/translations/*.umd.js"
48
+ ],
44
49
  "types": "src/index.d.ts",
45
50
  "exports": {
46
51
  ".": {
@@ -58,6 +58,22 @@ export interface EmojiConfig {
58
58
  * @default 'default'
59
59
  */
60
60
  skinTone?: SkinToneId;
61
+ /**
62
+ * The URL from which the emoji definitions should be loaded.
63
+ *
64
+ * ```ts
65
+ * ClassicEditor
66
+ * .create( editorElement, {
67
+ * plugins: [ Emoji, ... ],
68
+ * emoji: {
69
+ * definitionsUrl: ''
70
+ * }
71
+ * } )
72
+ * .then( ... )
73
+ * .catch( ... );
74
+ * ```
75
+ */
76
+ definitionsUrl?: string;
61
77
  /**
62
78
  * The emoji database version.
63
79
  *
@@ -73,7 +89,8 @@ export interface EmojiConfig {
73
89
  * .catch( ... );
74
90
  * ```
75
91
  *
76
- * @default 16
92
+ * If the {@link module:emoji/emojiconfig~EmojiConfig#definitionsUrl `emoji.definitionsUrl`}
93
+ * option is provided, `version` is ignored as the defined URL takes precedence over the `version`.
77
94
  */
78
95
  version?: EmojiVersion;
79
96
  }
@@ -176,6 +176,10 @@ export default class EmojiMention extends Plugin {
176
176
  if (searchQuery.startsWith(' ')) {
177
177
  return [];
178
178
  }
179
+ // Do not show anything when a query starts with a marker character.
180
+ if (searchQuery.startsWith(EMOJI_MENTION_MARKER)) {
181
+ return [];
182
+ }
179
183
  // If the repository plugin is not available, return an empty feed to avoid confusion. See: #17842.
180
184
  if (!this._isEmojiRepositoryAvailable) {
181
185
  return [];
@@ -16,13 +16,13 @@ export default class EmojiRepository extends Plugin {
16
16
  */
17
17
  private _repositoryPromiseResolveCallback;
18
18
  /**
19
- * An instance of the [Fuse.js](https://www.fusejs.io/) library.
19
+ * Emoji repository in a configured version.
20
20
  */
21
- private _fuseSearch;
21
+ private _items;
22
22
  /**
23
- * The emoji version that is used to prepare the emoji repository.
23
+ * The resolved URL from which the emoji repository is downloaded.
24
24
  */
25
- private readonly _version;
25
+ private readonly _url;
26
26
  /**
27
27
  * A promise resolved after downloading the emoji collection.
28
28
  * The promise resolves with `true` when the repository is successfully downloaded or `false` otherwise.
@@ -50,8 +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 repository is not loaded, the [Fuse.js](https://www.fusejs.io/) instance is not created,
54
- * hence this method returns an empty array.
53
+ * If the emoji repository is not loaded this method returns an empty array.
55
54
  *
56
55
  * @param searchQuery A search query to match emoji.
57
56
  * @returns An array of emoji entries that match the search query.
@@ -72,14 +71,26 @@ export default class EmojiRepository extends Plugin {
72
71
  * Indicates whether the emoji repository has been successfully downloaded and the plugin is operational.
73
72
  */
74
73
  isReady(): Promise<boolean>;
74
+ /**
75
+ * Returns the URL from which the emoji repository is downloaded. If the URL is not provided
76
+ * in the configuration, the default URL is used with the version from the configuration.
77
+ *
78
+ * If both the URL and version are provided, a warning is logged.
79
+ */
80
+ private _getUrl;
81
+ /**
82
+ * Warn users on self-hosted installations that this plugin uses a CDN to fetch the emoji repository.
83
+ */
84
+ private _warnAboutCdnUse;
75
85
  /**
76
86
  * Returns the emoji repository in a configured version if it is a non-empty array. Returns `null` otherwise.
77
87
  */
78
88
  private _getItems;
79
89
  /**
80
- * Makes the HTTP request to download the emoji repository in a configured version.
90
+ * Loads the emoji repository. If the repository is already loaded, it returns the cached result.
91
+ * Otherwise, it fetches the repository from the URL and adds it to the cache.
81
92
  */
82
- private _loadItemsFromCdn;
93
+ private _loadAndCacheEmoji;
83
94
  /**
84
95
  * Normalizes the raw data fetched from CDN. By normalization, we meant:
85
96
  *
@@ -5,14 +5,15 @@
5
5
  /**
6
6
  * @module emoji/emojirepository
7
7
  */
8
- import Fuse from 'fuse.js';
8
+ import fuzzysort from 'fuzzysort';
9
9
  import { groupBy } from 'lodash-es';
10
10
  import { Plugin } from 'ckeditor5/src/core.js';
11
- import { logWarning, version } from 'ckeditor5/src/utils.js';
11
+ import { logWarning, version as editorVersion } from 'ckeditor5/src/utils.js';
12
12
  import EmojiUtils from './emojiutils.js';
13
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
- const EMOJI_DATABASE_URL = 'https://cdn.ckeditor.com/ckeditor5/data/emoji/{version}/en.json';
15
+ const DEFAULT_EMOJI_DATABASE_URL = 'https://cdn.ckeditor.com/ckeditor5/data/emoji/{version}/en.json';
16
+ const DEFAULT_EMOJI_VERSION = 16;
16
17
  /**
17
18
  * The emoji repository plugin.
18
19
  *
@@ -43,52 +44,37 @@ class EmojiRepository extends Plugin {
43
44
  constructor(editor) {
44
45
  super(editor);
45
46
  editor.config.define('emoji', {
46
- version: 16,
47
- skinTone: 'default'
47
+ version: undefined,
48
+ skinTone: 'default',
49
+ definitionsUrl: undefined
48
50
  });
49
- this._version = editor.config.get('emoji.version');
51
+ this._url = this._getUrl();
50
52
  this._repositoryPromise = new Promise(resolve => {
51
53
  this._repositoryPromiseResolveCallback = resolve;
52
54
  });
53
- this._fuseSearch = null;
55
+ this._items = null;
54
56
  }
55
57
  /**
56
58
  * @inheritDoc
57
59
  */
58
60
  async init() {
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) {
61
+ this._warnAboutCdnUse();
62
+ await this._loadAndCacheEmoji();
63
+ this._items = this._getItems();
64
+ if (!this._items) {
67
65
  return this._repositoryPromiseResolveCallback(false);
68
66
  }
69
- // Create instance of the Fuse.js library with configured weighted search keys and disabled fuzzy search.
70
- this._fuseSearch = new Fuse(items, {
71
- keys: [
72
- { name: 'emoticon', weight: 5 },
73
- { name: 'annotation', weight: 3 },
74
- { name: 'tags', weight: 1 }
75
- ],
76
- minMatchCharLength: 2,
77
- threshold: 0,
78
- ignoreLocation: true
79
- });
80
67
  return this._repositoryPromiseResolveCallback(true);
81
68
  }
82
69
  /**
83
70
  * Returns an array of emoji entries that match the search query.
84
- * If the emoji repository is not loaded, the [Fuse.js](https://www.fusejs.io/) instance is not created,
85
- * hence this method returns an empty array.
71
+ * If the emoji repository is not loaded this method returns an empty array.
86
72
  *
87
73
  * @param searchQuery A search query to match emoji.
88
74
  * @returns An array of emoji entries that match the search query.
89
75
  */
90
76
  getEmojiByQuery(searchQuery) {
91
- if (!this._fuseSearch) {
77
+ if (!this._items) {
92
78
  return [];
93
79
  }
94
80
  const searchQueryTokens = searchQuery.split(/\s/).filter(Boolean);
@@ -97,21 +83,25 @@ class EmojiRepository extends Plugin {
97
83
  if (!shouldSearch) {
98
84
  return [];
99
85
  }
100
- return this._fuseSearch
101
- .search({
102
- '$or': [
103
- {
104
- emoticon: searchQuery
105
- },
106
- {
107
- '$and': searchQueryTokens.map(token => ({ annotation: token }))
108
- },
109
- {
110
- '$and': searchQueryTokens.map(token => ({ tags: token }))
86
+ return fuzzysort
87
+ .go(searchQuery, this._items, {
88
+ threshold: 0.6,
89
+ keys: [
90
+ 'emoticon',
91
+ 'annotation',
92
+ (emojiEntry) => {
93
+ // Instead of searching over all tags, let's use only those that matches the query.
94
+ // It enables searching in tags with the space character in names.
95
+ const searchQueryTokens = searchQuery.split(/\s/).filter(Boolean);
96
+ const matchedTags = searchQueryTokens.flatMap(tok => {
97
+ var _a;
98
+ return (_a = emojiEntry.tags) === null || _a === void 0 ? void 0 : _a.filter(t => t.startsWith(tok));
99
+ });
100
+ return matchedTags.join();
111
101
  }
112
102
  ]
113
103
  })
114
- .map(result => result.item);
104
+ .map(result => result.obj);
115
105
  }
116
106
  /**
117
107
  * Groups all emojis by categories.
@@ -164,20 +154,83 @@ class EmojiRepository extends Plugin {
164
154
  isReady() {
165
155
  return this._repositoryPromise;
166
156
  }
157
+ /**
158
+ * Returns the URL from which the emoji repository is downloaded. If the URL is not provided
159
+ * in the configuration, the default URL is used with the version from the configuration.
160
+ *
161
+ * If both the URL and version are provided, a warning is logged.
162
+ */
163
+ _getUrl() {
164
+ const { definitionsUrl, version } = this.editor.config.get('emoji');
165
+ if (!definitionsUrl || definitionsUrl === 'cdn') {
166
+ // URL was not provided or is set to 'cdn', so we use the default CDN URL.
167
+ const urlVersion = version || DEFAULT_EMOJI_VERSION;
168
+ const url = new URL(DEFAULT_EMOJI_DATABASE_URL.replace('{version}', urlVersion.toString()));
169
+ url.searchParams.set('editorVersion', editorVersion);
170
+ return url;
171
+ }
172
+ if (version) {
173
+ /**
174
+ * Both {@link module:emoji/emojiconfig~EmojiConfig#definitionsUrl `emoji.definitionsUrl`} and
175
+ * {@link module:emoji/emojiconfig~EmojiConfig#version `emoji.version`} configuration options
176
+ * are set. Only the `emoji.definitionsUrl` option will be used.
177
+ *
178
+ * The `emoji.version` option will be ignored and should be removed from the configuration.
179
+ *
180
+ * @error emoji-repository-redundant-version
181
+ */
182
+ logWarning('emoji-repository-redundant-version');
183
+ }
184
+ return new URL(definitionsUrl);
185
+ }
186
+ /**
187
+ * Warn users on self-hosted installations that this plugin uses a CDN to fetch the emoji repository.
188
+ */
189
+ _warnAboutCdnUse() {
190
+ const editor = this.editor;
191
+ const config = editor.config.get('emoji');
192
+ const licenseKey = editor.config.get('licenseKey');
193
+ const distributionChannel = window[Symbol.for('cke distribution')];
194
+ if (licenseKey === 'GPL') {
195
+ // Don't warn GPL users.
196
+ return;
197
+ }
198
+ if (distributionChannel === 'cloud') {
199
+ // Don't warn cloud users, because they already use our CDN.
200
+ return;
201
+ }
202
+ if (config && config.definitionsUrl) {
203
+ // Don't warn users who have configured their own definitions URL.
204
+ return;
205
+ }
206
+ /**
207
+ * By default, the Emoji plugin fetches the emoji repository from CKEditor 5 CDN. To avoid this,
208
+ * you can use the {@link module:emoji/emojiconfig~EmojiConfig#definitionsUrl `emoji.definitionsUrl`}
209
+ * configuration option to provide a URL to your own emoji repository.
210
+ *
211
+ * If you only want to suppress this warning, set this configuration option to `cdn`.
212
+ *
213
+ * @error emoji-repository-cdn-use
214
+ */
215
+ logWarning('emoji-repository-cdn-use');
216
+ }
167
217
  /**
168
218
  * Returns the emoji repository in a configured version if it is a non-empty array. Returns `null` otherwise.
169
219
  */
170
220
  _getItems() {
171
- const repository = EmojiRepository._results[this._version];
221
+ const repository = EmojiRepository._results[this._url.href];
172
222
  return repository && repository.length ? repository : null;
173
223
  }
174
224
  /**
175
- * Makes the HTTP request to download the emoji repository in a configured version.
225
+ * Loads the emoji repository. If the repository is already loaded, it returns the cached result.
226
+ * Otherwise, it fetches the repository from the URL and adds it to the cache.
176
227
  */
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' })
228
+ async _loadAndCacheEmoji() {
229
+ if (EmojiRepository._results[this._url.href]) {
230
+ // The repository has already been downloaded.
231
+ return;
232
+ }
233
+ const result = await fetch(this._url, { cache: 'force-cache' })
181
234
  .then(response => {
182
235
  if (!response.ok) {
183
236
  return [];
@@ -189,17 +242,17 @@ class EmojiRepository extends Plugin {
189
242
  });
190
243
  if (!result.length) {
191
244
  /**
192
- * Unable to load the emoji repository from CDN.
245
+ * Unable to load the emoji repository from the URL.
193
246
  *
194
- * If the CDN works properly and there is no disruption of communication, please check your
247
+ * If the URL works properly and there is no disruption of communication, please check your
195
248
  * {@glink getting-started/setup/csp Content Security Policy (CSP)} setting and make sure
196
- * the CDN connection is allowed by the editor.
249
+ * the URL connection is allowed by the editor.
197
250
  *
198
251
  * @error emoji-repository-load-failed
199
252
  */
200
253
  logWarning('emoji-repository-load-failed');
201
254
  }
202
- return result;
255
+ EmojiRepository._results[this._url.href] = this._normalizeEmoji(result);
203
256
  }
204
257
  /**
205
258
  * Normalizes the raw data fetched from CDN. By normalization, we meant: