@ckeditor/ckeditor5-emoji 44.2.0-alpha.7 → 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/LICENSE.md +1 -0
- package/build/emoji.js +1 -1
- package/ckeditor5-metadata.json +15 -2
- package/dist/index.js +284 -139
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/augmentation.d.ts +2 -1
- package/src/emojiconfig.d.ts +2 -1
- package/src/emojimention.d.ts +3 -2
- package/src/emojimention.js +8 -8
- package/src/emojipicker.d.ts +3 -3
- package/src/emojipicker.js +14 -14
- package/src/emojirepository.d.ts +35 -17
- package/src/emojirepository.js +86 -127
- package/src/emojiutils.d.ts +58 -0
- package/src/emojiutils.js +141 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/utils/isemojisupported.d.ts +11 -0
- package/src/utils/isemojisupported.js +68 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ckeditor/ckeditor5-emoji",
|
|
3
|
-
"version": "44.2.0-alpha.
|
|
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.
|
|
17
|
-
"@ckeditor/ckeditor5-mention": "44.2.0-alpha.
|
|
18
|
-
"@ckeditor/ckeditor5-typing": "44.2.0-alpha.
|
|
19
|
-
"@ckeditor/ckeditor5-ui": "44.2.0-alpha.
|
|
20
|
-
"@ckeditor/ckeditor5-utils": "44.2.0-alpha.
|
|
21
|
-
"ckeditor5": "44.2.0-alpha.
|
|
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
|
},
|
package/src/augmentation.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
4
|
*/
|
|
5
|
-
import type { Emoji, EmojiConfig, EmojiMention, EmojiPicker, EmojiRepository, EmojiCommand } from './index.js';
|
|
5
|
+
import type { Emoji, EmojiConfig, EmojiMention, EmojiPicker, EmojiRepository, EmojiUtils, EmojiCommand } from './index.js';
|
|
6
6
|
declare module '@ckeditor/ckeditor5-core' {
|
|
7
7
|
interface EditorConfig {
|
|
8
8
|
/**
|
|
@@ -17,6 +17,7 @@ declare module '@ckeditor/ckeditor5-core' {
|
|
|
17
17
|
[EmojiMention.pluginName]: EmojiMention;
|
|
18
18
|
[EmojiPicker.pluginName]: EmojiPicker;
|
|
19
19
|
[EmojiRepository.pluginName]: EmojiRepository;
|
|
20
|
+
[EmojiUtils.pluginName]: EmojiUtils;
|
|
20
21
|
}
|
|
21
22
|
interface CommandsMap {
|
|
22
23
|
emoji: EmojiCommand;
|
package/src/emojiconfig.d.ts
CHANGED
package/src/emojimention.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { Plugin, type Editor } from 'ckeditor5/src/core.js';
|
|
6
6
|
import { Typing } from 'ckeditor5/src/typing.js';
|
|
7
7
|
import EmojiRepository from './emojirepository.js';
|
|
8
|
+
import type EmojiPicker from './emojipicker.js';
|
|
8
9
|
/**
|
|
9
10
|
* The emoji mention plugin.
|
|
10
11
|
*
|
|
@@ -14,11 +15,11 @@ export default class EmojiMention extends Plugin {
|
|
|
14
15
|
/**
|
|
15
16
|
* An instance of the {@link module:emoji/emojipicker~EmojiPicker} plugin if it is loaded in the editor.
|
|
16
17
|
*/
|
|
17
|
-
|
|
18
|
+
emojiPickerPlugin: EmojiPicker | null;
|
|
18
19
|
/**
|
|
19
20
|
* An instance of the {@link module:emoji/emojirepository~EmojiRepository} plugin.
|
|
20
21
|
*/
|
|
21
|
-
|
|
22
|
+
emojiRepositoryPlugin: EmojiRepository;
|
|
22
23
|
/**
|
|
23
24
|
* A flag that informs if the {@link module:emoji/emojirepository~EmojiRepository} plugin is loaded correctly.
|
|
24
25
|
*/
|
package/src/emojimention.js
CHANGED
|
@@ -88,9 +88,9 @@ export default class EmojiMention extends Plugin {
|
|
|
88
88
|
*/
|
|
89
89
|
async init() {
|
|
90
90
|
const editor = this.editor;
|
|
91
|
-
this.
|
|
92
|
-
this.
|
|
93
|
-
this._isEmojiRepositoryAvailable = await this.
|
|
91
|
+
this.emojiPickerPlugin = editor.plugins.has('EmojiPicker') ? editor.plugins.get('EmojiPicker') : null;
|
|
92
|
+
this.emojiRepositoryPlugin = editor.plugins.get('EmojiRepository');
|
|
93
|
+
this._isEmojiRepositoryAvailable = await this.emojiRepositoryPlugin.isReady();
|
|
94
94
|
// Override the `mention` command listener if the emoji repository is ready.
|
|
95
95
|
if (this._isEmojiRepositoryAvailable) {
|
|
96
96
|
editor.once('ready', this._overrideMentionExecuteListener.bind(this));
|
|
@@ -152,7 +152,7 @@ export default class EmojiMention extends Plugin {
|
|
|
152
152
|
editor.model.change(writer => {
|
|
153
153
|
editor.model.deleteContent(writer.createSelection(eventData.range));
|
|
154
154
|
});
|
|
155
|
-
const emojiPickerPlugin = this.
|
|
155
|
+
const emojiPickerPlugin = this.emojiPickerPlugin;
|
|
156
156
|
emojiPickerPlugin.showUI(text.slice(1));
|
|
157
157
|
setTimeout(() => {
|
|
158
158
|
emojiPickerPlugin.emojiPickerView.focus();
|
|
@@ -180,18 +180,18 @@ export default class EmojiMention extends Plugin {
|
|
|
180
180
|
if (!this._isEmojiRepositoryAvailable) {
|
|
181
181
|
return [];
|
|
182
182
|
}
|
|
183
|
-
const emojis = this.
|
|
183
|
+
const emojis = this.emojiRepositoryPlugin.getEmojiByQuery(searchQuery)
|
|
184
184
|
.map(emoji => {
|
|
185
185
|
let text = emoji.skins[this._skinTone] || emoji.skins.default;
|
|
186
|
-
if (this.
|
|
187
|
-
text = emoji.skins[this.
|
|
186
|
+
if (this.emojiPickerPlugin) {
|
|
187
|
+
text = emoji.skins[this.emojiPickerPlugin.skinTone] || emoji.skins.default;
|
|
188
188
|
}
|
|
189
189
|
return {
|
|
190
190
|
id: `:${emoji.annotation}:`,
|
|
191
191
|
text
|
|
192
192
|
};
|
|
193
193
|
});
|
|
194
|
-
if (!this.
|
|
194
|
+
if (!this.emojiPickerPlugin) {
|
|
195
195
|
return emojis.slice(0, this._emojiDropdownLimit);
|
|
196
196
|
}
|
|
197
197
|
const actionItem = {
|
package/src/emojipicker.d.ts
CHANGED
|
@@ -25,11 +25,11 @@ export default class EmojiPicker extends Plugin {
|
|
|
25
25
|
/**
|
|
26
26
|
* The contextual balloon plugin instance.
|
|
27
27
|
*/
|
|
28
|
-
|
|
28
|
+
balloonPlugin: ContextualBalloon;
|
|
29
29
|
/**
|
|
30
30
|
* An instance of the {@link module:emoji/emojirepository~EmojiRepository} plugin.
|
|
31
31
|
*/
|
|
32
|
-
|
|
32
|
+
emojiRepositoryPlugin: EmojiRepository;
|
|
33
33
|
/**
|
|
34
34
|
* @inheritDoc
|
|
35
35
|
*/
|
|
@@ -80,7 +80,7 @@ export default class EmojiPicker extends Plugin {
|
|
|
80
80
|
*/
|
|
81
81
|
private _setupConversion;
|
|
82
82
|
/**
|
|
83
|
-
* Returns positioning options for the {@link #
|
|
83
|
+
* Returns positioning options for the {@link #balloonPlugin}. They control the way the balloon is attached
|
|
84
84
|
* to the target element or selection.
|
|
85
85
|
*/
|
|
86
86
|
private _getBalloonPositionData;
|
package/src/emojipicker.js
CHANGED
|
@@ -42,10 +42,10 @@ export default class EmojiPicker extends Plugin {
|
|
|
42
42
|
*/
|
|
43
43
|
async init() {
|
|
44
44
|
const editor = this.editor;
|
|
45
|
-
this.
|
|
46
|
-
this.
|
|
45
|
+
this.balloonPlugin = editor.plugins.get('ContextualBalloon');
|
|
46
|
+
this.emojiRepositoryPlugin = editor.plugins.get('EmojiRepository');
|
|
47
47
|
// Skip registering a button in the toolbar and list item in the menu bar if the emoji repository is not ready.
|
|
48
|
-
if (!await this.
|
|
48
|
+
if (!await this.emojiRepositoryPlugin.isReady()) {
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
51
|
const command = new EmojiCommand(editor);
|
|
@@ -99,8 +99,8 @@ export default class EmojiPicker extends Plugin {
|
|
|
99
99
|
this.emojiPickerView.searchView.setInputValue(searchValue);
|
|
100
100
|
}
|
|
101
101
|
this.emojiPickerView.searchView.search(searchValue);
|
|
102
|
-
if (!this.
|
|
103
|
-
this.
|
|
102
|
+
if (!this.balloonPlugin.hasView(this.emojiPickerView)) {
|
|
103
|
+
this.balloonPlugin.add({
|
|
104
104
|
view: this.emojiPickerView,
|
|
105
105
|
position: this._getBalloonPositionData()
|
|
106
106
|
});
|
|
@@ -129,11 +129,11 @@ export default class EmojiPicker extends Plugin {
|
|
|
129
129
|
*/
|
|
130
130
|
_createEmojiPickerView() {
|
|
131
131
|
const emojiPickerView = new EmojiPickerView(this.editor.locale, {
|
|
132
|
-
emojiCategories: this.
|
|
132
|
+
emojiCategories: this.emojiRepositoryPlugin.getEmojiCategories(),
|
|
133
133
|
skinTone: this.editor.config.get('emoji.skinTone'),
|
|
134
|
-
skinTones: this.
|
|
134
|
+
skinTones: this.emojiRepositoryPlugin.getSkinTones(),
|
|
135
135
|
getEmojiByQuery: (query) => {
|
|
136
|
-
return this.
|
|
136
|
+
return this.emojiRepositoryPlugin.getEmojiByQuery(query);
|
|
137
137
|
}
|
|
138
138
|
});
|
|
139
139
|
// Insert an emoji on a tile click.
|
|
@@ -145,8 +145,8 @@ export default class EmojiPicker extends Plugin {
|
|
|
145
145
|
});
|
|
146
146
|
// Update the balloon position when layout is changed.
|
|
147
147
|
this.listenTo(emojiPickerView, 'update', () => {
|
|
148
|
-
if (this.
|
|
149
|
-
this.
|
|
148
|
+
if (this.balloonPlugin.visibleView === emojiPickerView) {
|
|
149
|
+
this.balloonPlugin.updatePosition();
|
|
150
150
|
}
|
|
151
151
|
});
|
|
152
152
|
// Close the panel on `Esc` key press when the **actions have focus**.
|
|
@@ -157,9 +157,9 @@ export default class EmojiPicker extends Plugin {
|
|
|
157
157
|
// Close the dialog when clicking outside of it.
|
|
158
158
|
clickOutsideHandler({
|
|
159
159
|
emitter: emojiPickerView,
|
|
160
|
-
contextElements: [this.
|
|
160
|
+
contextElements: [this.balloonPlugin.view.element],
|
|
161
161
|
callback: () => this._hideUI(),
|
|
162
|
-
activator: () => this.
|
|
162
|
+
activator: () => this.balloonPlugin.visibleView === emojiPickerView
|
|
163
163
|
});
|
|
164
164
|
return emojiPickerView;
|
|
165
165
|
}
|
|
@@ -167,7 +167,7 @@ export default class EmojiPicker extends Plugin {
|
|
|
167
167
|
* Hides the balloon with the emoji picker.
|
|
168
168
|
*/
|
|
169
169
|
_hideUI() {
|
|
170
|
-
this.
|
|
170
|
+
this.balloonPlugin.remove(this.emojiPickerView);
|
|
171
171
|
this.emojiPickerView.searchView.setInputValue('');
|
|
172
172
|
this.editor.editing.view.focus();
|
|
173
173
|
this._hideFakeVisualSelection();
|
|
@@ -198,7 +198,7 @@ export default class EmojiPicker extends Plugin {
|
|
|
198
198
|
});
|
|
199
199
|
}
|
|
200
200
|
/**
|
|
201
|
-
* Returns positioning options for the {@link #
|
|
201
|
+
* Returns positioning options for the {@link #balloonPlugin}. They control the way the balloon is attached
|
|
202
202
|
* to the target element or selection.
|
|
203
203
|
*/
|
|
204
204
|
_getBalloonPositionData() {
|
package/src/emojirepository.d.ts
CHANGED
|
@@ -2,31 +2,36 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { type Editor, Plugin } from 'ckeditor5/src/core.js';
|
|
6
|
+
import EmojiUtils from './emojiutils.js';
|
|
6
7
|
import type { SkinToneId } from './emojiconfig.js';
|
|
7
8
|
/**
|
|
8
9
|
* The emoji repository plugin.
|
|
9
10
|
*
|
|
10
|
-
* Loads the emoji
|
|
11
|
+
* Loads the emoji repository from URL during plugin initialization and provides utility methods to search it.
|
|
11
12
|
*/
|
|
12
13
|
export default class EmojiRepository extends Plugin {
|
|
13
14
|
/**
|
|
14
|
-
*
|
|
15
|
+
* A callback to resolve the {@link #_repositoryPromise} to control the return value of this promise.
|
|
15
16
|
*/
|
|
16
|
-
private
|
|
17
|
+
private _repositoryPromiseResolveCallback;
|
|
17
18
|
/**
|
|
18
|
-
*
|
|
19
|
-
* The promise resolves with `true` when the database is successfully downloaded or `false` otherwise.
|
|
19
|
+
* An instance of the [Fuse.js](https://www.fusejs.io/) library.
|
|
20
20
|
*/
|
|
21
|
-
private
|
|
21
|
+
private _fuseSearch;
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* The emoji version that is used to prepare the emoji repository.
|
|
24
24
|
*/
|
|
25
|
-
private
|
|
25
|
+
private readonly _version;
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* A promise resolved after downloading the emoji collection.
|
|
28
|
+
* The promise resolves with `true` when the repository is successfully downloaded or `false` otherwise.
|
|
28
29
|
*/
|
|
29
|
-
private
|
|
30
|
+
private readonly _repositoryPromise;
|
|
31
|
+
/**
|
|
32
|
+
* @inheritDoc
|
|
33
|
+
*/
|
|
34
|
+
static get requires(): readonly [typeof EmojiUtils];
|
|
30
35
|
/**
|
|
31
36
|
* @inheritDoc
|
|
32
37
|
*/
|
|
@@ -45,7 +50,7 @@ export default class EmojiRepository extends Plugin {
|
|
|
45
50
|
init(): Promise<void>;
|
|
46
51
|
/**
|
|
47
52
|
* Returns an array of emoji entries that match the search query.
|
|
48
|
-
* If the emoji
|
|
53
|
+
* If the emoji repository is not loaded, the [Fuse.js](https://www.fusejs.io/) instance is not created,
|
|
49
54
|
* hence this method returns an empty array.
|
|
50
55
|
*
|
|
51
56
|
* @param searchQuery A search query to match emoji.
|
|
@@ -54,7 +59,7 @@ export default class EmojiRepository extends Plugin {
|
|
|
54
59
|
getEmojiByQuery(searchQuery: string): Array<EmojiEntry>;
|
|
55
60
|
/**
|
|
56
61
|
* Groups all emojis by categories.
|
|
57
|
-
* If the emoji
|
|
62
|
+
* If the emoji repository is not loaded, it returns an empty array.
|
|
58
63
|
*
|
|
59
64
|
* @returns An array of emoji entries grouped by categories.
|
|
60
65
|
*/
|
|
@@ -64,15 +69,28 @@ export default class EmojiRepository extends Plugin {
|
|
|
64
69
|
*/
|
|
65
70
|
getSkinTones(): Array<SkinTone>;
|
|
66
71
|
/**
|
|
67
|
-
* Indicates whether the emoji
|
|
72
|
+
* Indicates whether the emoji repository has been successfully downloaded and the plugin is operational.
|
|
68
73
|
*/
|
|
69
74
|
isReady(): Promise<boolean>;
|
|
70
75
|
/**
|
|
71
|
-
*
|
|
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:
|
|
72
85
|
*
|
|
73
|
-
*
|
|
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.
|
|
74
92
|
*/
|
|
75
|
-
private static
|
|
93
|
+
private static _results;
|
|
76
94
|
}
|
|
77
95
|
/**
|
|
78
96
|
* Represents a single group of the emoji category, e.g., "Smileys & Expressions".
|
package/src/emojirepository.js
CHANGED
|
@@ -8,25 +8,23 @@
|
|
|
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';
|
|
12
|
-
|
|
11
|
+
import { logWarning, version } from 'ckeditor5/src/utils.js';
|
|
12
|
+
import EmojiUtils from './emojiutils.js';
|
|
13
|
+
// An endpoint from which the emoji data will be downloaded during plugin initialization.
|
|
13
14
|
// The `{version}` placeholder is replaced with the value from editor config.
|
|
14
15
|
const EMOJI_DATABASE_URL = 'https://cdn.ckeditor.com/ckeditor5/data/emoji/{version}/en.json';
|
|
15
|
-
const SKIN_TONE_MAP = {
|
|
16
|
-
0: 'default',
|
|
17
|
-
1: 'light',
|
|
18
|
-
2: 'medium-light',
|
|
19
|
-
3: 'medium',
|
|
20
|
-
4: 'medium-dark',
|
|
21
|
-
5: 'dark'
|
|
22
|
-
};
|
|
23
|
-
const BASELINE_EMOJI_WIDTH = 24;
|
|
24
16
|
/**
|
|
25
17
|
* The emoji repository plugin.
|
|
26
18
|
*
|
|
27
|
-
* Loads the emoji
|
|
19
|
+
* Loads the emoji repository from URL during plugin initialization and provides utility methods to search it.
|
|
28
20
|
*/
|
|
29
21
|
class EmojiRepository extends Plugin {
|
|
22
|
+
/**
|
|
23
|
+
* @inheritDoc
|
|
24
|
+
*/
|
|
25
|
+
static get requires() {
|
|
26
|
+
return [EmojiUtils];
|
|
27
|
+
}
|
|
30
28
|
/**
|
|
31
29
|
* @inheritDoc
|
|
32
30
|
*/
|
|
@@ -44,13 +42,13 @@ class EmojiRepository extends Plugin {
|
|
|
44
42
|
*/
|
|
45
43
|
constructor(editor) {
|
|
46
44
|
super(editor);
|
|
47
|
-
|
|
45
|
+
editor.config.define('emoji', {
|
|
48
46
|
version: 16,
|
|
49
47
|
skinTone: 'default'
|
|
50
48
|
});
|
|
51
|
-
this.
|
|
52
|
-
this.
|
|
53
|
-
this.
|
|
49
|
+
this._version = editor.config.get('emoji.version');
|
|
50
|
+
this._repositoryPromise = new Promise(resolve => {
|
|
51
|
+
this._repositoryPromiseResolveCallback = resolve;
|
|
54
52
|
});
|
|
55
53
|
this._fuseSearch = null;
|
|
56
54
|
}
|
|
@@ -58,23 +56,18 @@ class EmojiRepository extends Plugin {
|
|
|
58
56
|
* @inheritDoc
|
|
59
57
|
*/
|
|
60
58
|
async init() {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if
|
|
67
|
-
|
|
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 = createEmojiWidthTestingContainer();
|
|
70
|
-
// Store the emoji database after normalizing the raw data.
|
|
71
|
-
this._database = emojiDatabase
|
|
72
|
-
.filter(item => isEmojiCategoryAllowed(item))
|
|
73
|
-
.filter(item => EmojiRepository._isEmojiSupported(item, container))
|
|
74
|
-
.map(item => normalizeEmojiSkinTone(item));
|
|
75
|
-
container.remove();
|
|
76
69
|
// Create instance of the Fuse.js library with configured weighted search keys and disabled fuzzy search.
|
|
77
|
-
this._fuseSearch = new Fuse(
|
|
70
|
+
this._fuseSearch = new Fuse(items, {
|
|
78
71
|
keys: [
|
|
79
72
|
{ name: 'emoticon', weight: 5 },
|
|
80
73
|
{ name: 'annotation', weight: 3 },
|
|
@@ -84,11 +77,11 @@ class EmojiRepository extends Plugin {
|
|
|
84
77
|
threshold: 0,
|
|
85
78
|
ignoreLocation: true
|
|
86
79
|
});
|
|
87
|
-
return this.
|
|
80
|
+
return this._repositoryPromiseResolveCallback(true);
|
|
88
81
|
}
|
|
89
82
|
/**
|
|
90
83
|
* Returns an array of emoji entries that match the search query.
|
|
91
|
-
* If the emoji
|
|
84
|
+
* If the emoji repository is not loaded, the [Fuse.js](https://www.fusejs.io/) instance is not created,
|
|
92
85
|
* hence this method returns an empty array.
|
|
93
86
|
*
|
|
94
87
|
* @param searchQuery A search query to match emoji.
|
|
@@ -122,12 +115,13 @@ class EmojiRepository extends Plugin {
|
|
|
122
115
|
}
|
|
123
116
|
/**
|
|
124
117
|
* Groups all emojis by categories.
|
|
125
|
-
* If the emoji
|
|
118
|
+
* If the emoji repository is not loaded, it returns an empty array.
|
|
126
119
|
*
|
|
127
120
|
* @returns An array of emoji entries grouped by categories.
|
|
128
121
|
*/
|
|
129
122
|
getEmojiCategories() {
|
|
130
|
-
|
|
123
|
+
const repository = this._getItems();
|
|
124
|
+
if (!repository) {
|
|
131
125
|
return [];
|
|
132
126
|
}
|
|
133
127
|
const { t } = this.editor.locale;
|
|
@@ -142,7 +136,7 @@ class EmojiRepository extends Plugin {
|
|
|
142
136
|
{ title: t('Symbols'), icon: '🟢', groupId: 8 },
|
|
143
137
|
{ title: t('Flags'), icon: '🏁', groupId: 9 }
|
|
144
138
|
];
|
|
145
|
-
const groups = groupBy(
|
|
139
|
+
const groups = groupBy(repository, 'group');
|
|
146
140
|
return categories.map(category => {
|
|
147
141
|
return {
|
|
148
142
|
...category,
|
|
@@ -165,104 +159,69 @@ class EmojiRepository extends Plugin {
|
|
|
165
159
|
];
|
|
166
160
|
}
|
|
167
161
|
/**
|
|
168
|
-
* Indicates whether the emoji
|
|
162
|
+
* Indicates whether the emoji repository has been successfully downloaded and the plugin is operational.
|
|
169
163
|
*/
|
|
170
164
|
isReady() {
|
|
171
|
-
return this.
|
|
165
|
+
return this._repositoryPromise;
|
|
172
166
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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(() => {
|
|
188
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');
|
|
189
201
|
}
|
|
190
|
-
return
|
|
191
|
-
})
|
|
192
|
-
.catch(() => {
|
|
193
|
-
return [];
|
|
194
|
-
});
|
|
195
|
-
if (!result.length) {
|
|
196
|
-
/**
|
|
197
|
-
* Unable to load the emoji database from CDN.
|
|
198
|
-
*
|
|
199
|
-
* If the CDN works properly and there is no disruption of communication, please check your
|
|
200
|
-
* {@glink getting-started/setup/csp Content Security Policy (CSP)} setting and make sure
|
|
201
|
-
* the CDN connection is allowed by the editor.
|
|
202
|
-
*
|
|
203
|
-
* @error emoji-database-load-failed
|
|
204
|
-
*/
|
|
205
|
-
logWarning('emoji-database-load-failed');
|
|
202
|
+
return result;
|
|
206
203
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
*/
|
|
225
|
-
function getNodeWidth(container, node) {
|
|
226
|
-
const span = document.createElement('span');
|
|
227
|
-
span.textContent = node;
|
|
228
|
-
container.appendChild(span);
|
|
229
|
-
const nodeWidth = span.offsetWidth;
|
|
230
|
-
container.removeChild(span);
|
|
231
|
-
return nodeWidth;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Checks whether the emoji is supported in the operating system.
|
|
235
|
-
*/
|
|
236
|
-
function isEmojiSupported(item, container) {
|
|
237
|
-
const emojiWidth = getNodeWidth(container, item.emoji);
|
|
238
|
-
// On Windows, some supported emoji are ~50% bigger than the baseline emoji, but what we really want to guard
|
|
239
|
-
// against are the ones that are 2x the size, because those are truly broken (person with red hair = person with
|
|
240
|
-
// floating red wig, black cat = cat with black square, polar bear = bear with snowflake, etc.)
|
|
241
|
-
// So here we set the threshold at 1.8 times the size of the baseline emoji.
|
|
242
|
-
return (emojiWidth / 1.8 < BASELINE_EMOJI_WIDTH) && (emojiWidth >= BASELINE_EMOJI_WIDTH);
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Adds default skin tone property to each emoji. If emoji defines other skin tones, they are added as well.
|
|
246
|
-
*/
|
|
247
|
-
function normalizeEmojiSkinTone(item) {
|
|
248
|
-
const entry = {
|
|
249
|
-
...item,
|
|
250
|
-
skins: {
|
|
251
|
-
default: item.emoji
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
if (item.skins) {
|
|
255
|
-
item.skins.forEach(skin => {
|
|
256
|
-
const skinTone = SKIN_TONE_MAP[skin.tone];
|
|
257
|
-
entry.skins[skinTone] = skin.emoji;
|
|
258
|
-
});
|
|
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;
|
|
259
221
|
}
|
|
260
|
-
return entry;
|
|
261
222
|
}
|
|
262
223
|
/**
|
|
263
|
-
*
|
|
224
|
+
* Versioned emoji repository.
|
|
264
225
|
*/
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return item.group !== 2;
|
|
268
|
-
}
|
|
226
|
+
EmojiRepository._results = {};
|
|
227
|
+
export default EmojiRepository;
|