2019-12-18 16:40:19 +01:00
|
|
|
/*
|
|
|
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
import EMOJIBASE from 'emojibase-data/en/compact.json';
|
2021-07-11 04:40:30 +02:00
|
|
|
import SHORTCODES from 'emojibase-data/en/shortcodes/iamcal.json';
|
2019-12-18 16:40:19 +01:00
|
|
|
|
2020-04-20 20:17:58 +02:00
|
|
|
export interface IEmoji {
|
|
|
|
annotation: string;
|
2021-07-11 04:40:30 +02:00
|
|
|
group?: number;
|
2020-04-20 20:17:58 +02:00
|
|
|
hexcode: string;
|
2021-07-11 04:40:30 +02:00
|
|
|
order?: number;
|
2021-07-16 22:36:03 +02:00
|
|
|
shortcodes: string[];
|
2021-07-11 04:40:30 +02:00
|
|
|
tags?: string[];
|
2020-04-20 20:17:58 +02:00
|
|
|
unicode: string;
|
2021-07-20 13:46:54 +02:00
|
|
|
skins?: Omit<IEmoji, "shortcodes" | "tags">[]; // Currently unused
|
2020-04-20 20:17:58 +02:00
|
|
|
emoticon?: string;
|
|
|
|
}
|
|
|
|
|
2019-12-18 16:40:19 +01:00
|
|
|
// The unicode is stored without the variant selector
|
2021-07-16 22:36:03 +02:00
|
|
|
const UNICODE_TO_EMOJI = new Map<string, IEmoji>(); // not exported as gets for it are handled by getEmojiFromUnicode
|
|
|
|
export const EMOTICON_TO_EMOJI = new Map<string, IEmoji>();
|
2019-12-18 16:40:19 +01:00
|
|
|
|
2020-01-07 19:48:55 +01:00
|
|
|
export const getEmojiFromUnicode = unicode => UNICODE_TO_EMOJI.get(stripVariation(unicode));
|
2019-12-18 16:40:19 +01:00
|
|
|
|
2021-08-05 23:56:16 +02:00
|
|
|
const isRegionalIndicator = (x: string): boolean => {
|
|
|
|
// First verify that the string is a single character. We use Array.from
|
|
|
|
// to make sure we count by characters, not UTF-8 code units.
|
|
|
|
return Array.from(x).length === 1 &&
|
|
|
|
// Next verify that the character is within the code point range for
|
|
|
|
// regional indicators.
|
|
|
|
// http://unicode.org/charts/PDF/Unicode-6.0/U60-1F100.pdf
|
|
|
|
x >= '\u{1f1e6}' && x <= '\u{1f1ff}';
|
|
|
|
};
|
2021-07-27 23:22:49 +02:00
|
|
|
|
2019-12-18 16:40:19 +01:00
|
|
|
const EMOJIBASE_GROUP_ID_TO_CATEGORY = [
|
|
|
|
"people", // smileys
|
|
|
|
"people", // actually people
|
|
|
|
"control", // modifiers and such, not displayed in picker
|
|
|
|
"nature",
|
|
|
|
"foods",
|
|
|
|
"places",
|
|
|
|
"activity",
|
|
|
|
"objects",
|
|
|
|
"symbols",
|
|
|
|
"flags",
|
|
|
|
];
|
|
|
|
|
|
|
|
export const DATA_BY_CATEGORY = {
|
|
|
|
"people": [],
|
|
|
|
"nature": [],
|
|
|
|
"foods": [],
|
|
|
|
"places": [],
|
|
|
|
"activity": [],
|
|
|
|
"objects": [],
|
|
|
|
"symbols": [],
|
|
|
|
"flags": [],
|
|
|
|
};
|
|
|
|
|
|
|
|
// Store various mappings from unicode/emoticon/shortcode to the Emoji objects
|
2021-07-19 21:09:15 +02:00
|
|
|
export const EMOJI: IEmoji[] = EMOJIBASE.map((emojiData: Omit<IEmoji, "shortcodes">) => {
|
2021-07-20 13:47:31 +02:00
|
|
|
// If there's ever a gap in shortcode coverage, we fudge it by
|
|
|
|
// filling it in with the emoji's CLDR annotation
|
|
|
|
const shortcodeData = SHORTCODES[emojiData.hexcode] ??
|
|
|
|
[emojiData.annotation.toLowerCase().replace(/ /g, "_")];
|
|
|
|
|
2021-07-16 22:36:03 +02:00
|
|
|
const emoji: IEmoji = {
|
|
|
|
...emojiData,
|
2021-07-19 21:09:15 +02:00
|
|
|
// Homogenize shortcodes by ensuring that everything is an array
|
2021-07-20 13:47:31 +02:00
|
|
|
shortcodes: typeof shortcodeData === "string" ? [shortcodeData] : shortcodeData,
|
2021-07-16 22:36:03 +02:00
|
|
|
};
|
|
|
|
|
2021-07-27 23:22:49 +02:00
|
|
|
// We manually include regional indicators in the symbols group, since
|
|
|
|
// Emojibase intentionally leaves them uncategorized
|
|
|
|
const categoryId = EMOJIBASE_GROUP_ID_TO_CATEGORY[emoji.group] ??
|
|
|
|
(isRegionalIndicator(emoji.unicode) ? "symbols" : null);
|
|
|
|
|
2019-12-18 16:40:19 +01:00
|
|
|
if (DATA_BY_CATEGORY.hasOwnProperty(categoryId)) {
|
|
|
|
DATA_BY_CATEGORY[categoryId].push(emoji);
|
|
|
|
}
|
2021-07-11 04:40:30 +02:00
|
|
|
|
2019-12-18 16:40:19 +01:00
|
|
|
// Add mapping from unicode to Emoji object
|
2020-01-07 19:48:55 +01:00
|
|
|
// The 'unicode' field that we use in emojibase has either
|
|
|
|
// VS15 or VS16 appended to any characters that can take
|
|
|
|
// variation selectors. Which one it appends depends
|
|
|
|
// on whether emojibase considers their type to be 'text' or
|
|
|
|
// 'emoji'. We therefore strip any variation chars from strings
|
|
|
|
// both when building the map and when looking up.
|
|
|
|
UNICODE_TO_EMOJI.set(stripVariation(emoji.unicode), emoji);
|
2019-12-18 16:40:19 +01:00
|
|
|
|
|
|
|
if (emoji.emoticon) {
|
|
|
|
// Add mapping from emoticon to Emoji object
|
|
|
|
EMOTICON_TO_EMOJI.set(emoji.emoticon, emoji);
|
|
|
|
}
|
2021-07-16 22:36:03 +02:00
|
|
|
|
|
|
|
return emoji;
|
2019-12-18 16:40:19 +01:00
|
|
|
});
|
2020-01-07 19:48:55 +01:00
|
|
|
|
|
|
|
/**
|
2020-01-26 23:15:44 +01:00
|
|
|
* Strips variation selectors from the end of given string
|
|
|
|
* NB. Skin tone modifiers are not variation selectors:
|
2020-01-07 19:48:55 +01:00
|
|
|
* this function does not touch them. (Should it?)
|
|
|
|
*
|
|
|
|
* @param {string} str string to strip
|
|
|
|
* @returns {string} stripped string
|
|
|
|
*/
|
|
|
|
function stripVariation(str) {
|
2020-01-26 23:15:44 +01:00
|
|
|
return str.replace(/[\uFE00-\uFE0F]$/, "");
|
2020-01-07 19:48:55 +01:00
|
|
|
}
|