140 lines
5.2 KiB
JavaScript
140 lines
5.2 KiB
JavaScript
// On Windows, flags render as two-letter country codes, see http://emojipedia.org/flags/
|
|
const flagACodePoint = 127462; // parseInt('1F1E6', 16)
|
|
const flagZCodePoint = 127487; // parseInt('1F1FF', 16)
|
|
function isFlagEmoji(emojiUnicode) {
|
|
const cp = emojiUnicode.codePointAt(0);
|
|
// Length 4 because flags are made of 2 characters which are surrogate pairs
|
|
return emojiUnicode.length === 4 && cp >= flagACodePoint && cp <= flagZCodePoint;
|
|
}
|
|
|
|
// Tested on mac OS 10.12.6 and Windows 10 FCU, it renders as two separate characters
|
|
const baseFlagCodePoint = 127987; // parseInt('1F3F3', 16)
|
|
const rainbowCodePoint = 127752; // parseInt('1F308', 16)
|
|
function isRainbowFlagEmoji(emojiUnicode) {
|
|
const characters = Array.from(emojiUnicode);
|
|
// Length 4 because flags are made of 2 characters which are surrogate pairs
|
|
return (
|
|
emojiUnicode.length === 4 &&
|
|
characters[0].codePointAt(0) === baseFlagCodePoint &&
|
|
characters[1].codePointAt(0) === rainbowCodePoint
|
|
);
|
|
}
|
|
|
|
// Chrome <57 renders keycaps oddly
|
|
// See https://bugs.chromium.org/p/chromium/issues/detail?id=632294
|
|
// Same issue on Windows also fixed in Chrome 57, http://i.imgur.com/rQF7woO.png
|
|
function isKeycapEmoji(emojiUnicode) {
|
|
return emojiUnicode.length === 3 && emojiUnicode[2] === '\u20E3';
|
|
}
|
|
|
|
// Check for a skin tone variation emoji which aren't always supported
|
|
const tone1 = 127995; // parseInt('1F3FB', 16)
|
|
const tone5 = 127999; // parseInt('1F3FF', 16)
|
|
function isSkinToneComboEmoji(emojiUnicode) {
|
|
return (
|
|
emojiUnicode.length > 2 &&
|
|
Array.from(emojiUnicode).some(char => {
|
|
const cp = char.codePointAt(0);
|
|
return cp >= tone1 && cp <= tone5;
|
|
})
|
|
);
|
|
}
|
|
|
|
// macOS supports most skin tone emoji's but
|
|
// doesn't support the skin tone versions of horse racing
|
|
const horseRacingCodePoint = 127943; // parseInt('1F3C7', 16)
|
|
function isHorceRacingSkinToneComboEmoji(emojiUnicode) {
|
|
const firstCharacter = Array.from(emojiUnicode)[0];
|
|
return (
|
|
firstCharacter &&
|
|
firstCharacter.codePointAt(0) === horseRacingCodePoint &&
|
|
isSkinToneComboEmoji(emojiUnicode)
|
|
);
|
|
}
|
|
|
|
// Check for `family_*`, `kiss_*`, `couple_*`
|
|
// For ex. Windows 8.1 Firefox 51.0.1, doesn't support these
|
|
const zwj = 8205; // parseInt('200D', 16)
|
|
const personStartCodePoint = 128102; // parseInt('1F466', 16)
|
|
const personEndCodePoint = 128105; // parseInt('1F469', 16)
|
|
function isPersonZwjEmoji(emojiUnicode) {
|
|
let hasPersonEmoji = false;
|
|
let hasZwj = false;
|
|
Array.from(emojiUnicode).forEach(character => {
|
|
const cp = character.codePointAt(0);
|
|
if (cp === zwj) {
|
|
hasZwj = true;
|
|
} else if (cp >= personStartCodePoint && cp <= personEndCodePoint) {
|
|
hasPersonEmoji = true;
|
|
}
|
|
});
|
|
|
|
return hasPersonEmoji && hasZwj;
|
|
}
|
|
|
|
// Helper so we don't have to run `isFlagEmoji` twice
|
|
// in `isEmojiUnicodeSupported` logic
|
|
function checkFlagEmojiSupport(unicodeSupportMap, emojiUnicode) {
|
|
const isFlagResult = isFlagEmoji(emojiUnicode);
|
|
const isRainbowFlagResult = isRainbowFlagEmoji(emojiUnicode);
|
|
return (
|
|
(unicodeSupportMap.flag && isFlagResult) ||
|
|
(unicodeSupportMap.rainbowFlag && isRainbowFlagResult) ||
|
|
(!isFlagResult && !isRainbowFlagResult)
|
|
);
|
|
}
|
|
|
|
// Helper so we don't have to run `isSkinToneComboEmoji` twice
|
|
// in `isEmojiUnicodeSupported` logic
|
|
function checkSkinToneModifierSupport(unicodeSupportMap, emojiUnicode) {
|
|
const isSkinToneResult = isSkinToneComboEmoji(emojiUnicode);
|
|
return (unicodeSupportMap.skinToneModifier && isSkinToneResult) || !isSkinToneResult;
|
|
}
|
|
|
|
// Helper func so we don't have to run `isHorceRacingSkinToneComboEmoji` twice
|
|
// in `isEmojiUnicodeSupported` logic
|
|
function checkHorseRacingSkinToneComboEmojiSupport(unicodeSupportMap, emojiUnicode) {
|
|
const isHorseRacingSkinToneResult = isHorceRacingSkinToneComboEmoji(emojiUnicode);
|
|
return (
|
|
(unicodeSupportMap.horseRacing && isHorseRacingSkinToneResult) || !isHorseRacingSkinToneResult
|
|
);
|
|
}
|
|
|
|
// Helper so we don't have to run `isPersonZwjEmoji` twice
|
|
// in `isEmojiUnicodeSupported` logic
|
|
function checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode) {
|
|
const isPersonZwjResult = isPersonZwjEmoji(emojiUnicode);
|
|
return (unicodeSupportMap.personZwj && isPersonZwjResult) || !isPersonZwjResult;
|
|
}
|
|
|
|
// Takes in a support map and determines whether
|
|
// the given unicode emoji is supported on the platform.
|
|
//
|
|
// Combines all the edge case tests into a one-stop shop method
|
|
function isEmojiUnicodeSupported(unicodeSupportMap = {}, emojiUnicode, unicodeVersion) {
|
|
const isOlderThanChrome57 =
|
|
unicodeSupportMap.meta &&
|
|
unicodeSupportMap.meta.isChrome &&
|
|
unicodeSupportMap.meta.chromeVersion < 57;
|
|
|
|
// For comments about each scenario, see the comments above each individual respective function
|
|
return (
|
|
unicodeSupportMap[unicodeVersion] &&
|
|
!(isOlderThanChrome57 && isKeycapEmoji(emojiUnicode)) &&
|
|
checkFlagEmojiSupport(unicodeSupportMap, emojiUnicode) &&
|
|
checkSkinToneModifierSupport(unicodeSupportMap, emojiUnicode) &&
|
|
checkHorseRacingSkinToneComboEmojiSupport(unicodeSupportMap, emojiUnicode) &&
|
|
checkPersonEmojiSupport(unicodeSupportMap, emojiUnicode)
|
|
);
|
|
}
|
|
|
|
export {
|
|
isEmojiUnicodeSupported as default,
|
|
isFlagEmoji,
|
|
isRainbowFlagEmoji,
|
|
isKeycapEmoji,
|
|
isSkinToneComboEmoji,
|
|
isHorceRacingSkinToneComboEmoji,
|
|
isPersonZwjEmoji,
|
|
};
|