forked from rDrama/rDrama
1
0
Fork 0
rDrama/node_modules/node-emoji/lib/emoji.js

304 lines
8.0 KiB
JavaScript
Raw Normal View History

2021-12-13 22:28:32 +00:00
/*jslint node: true*/
var toArray = require('lodash/toArray');
var emojiByName = require('./emoji.json');
"use strict";
/**
* regex to parse emoji in a string - finds emoji, e.g. :coffee:
*/
var emojiNameRegex = /:([a-zA-Z0-9_\-\+]+):/g;
/**
* regex to trim whitespace
* use instead of String.prototype.trim() for IE8 support
*/
var trimSpaceRegex = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
/**
* Removes colons on either side
* of the string if present
* @param {string} str
* @return {string}
*/
function stripColons (str) {
var colonIndex = str.indexOf(':');
if (colonIndex > -1) {
// :emoji: (http://www.emoji-cheat-sheet.com/)
if (colonIndex === str.length - 1) {
str = str.substring(0, colonIndex);
return stripColons(str);
} else {
str = str.substr(colonIndex + 1);
return stripColons(str);
}
}
return str;
}
/**
* Adds colons to either side
* of the string
* @param {string} str
* @return {string}
*/
function wrapColons (str) {
return (typeof str === 'string' && str.length > 0) ? ':' + str + ':' : str;
}
/**
* Ensure that the word is wrapped in colons
* by only adding them, if they are not there.
* @param {string} str
* @return {string}
*/
function ensureColons (str) {
return (typeof str === 'string' && str[0] !== ':') ? wrapColons(str) : str;
}
// Non spacing mark, some emoticons have them. It's the 'Variant Form',
// which provides more information so that emoticons can be rendered as
// more colorful graphics. FE0E is a unicode text version, where as FE0F
// should be rendered as a graphical version. The code gracefully degrades.
var NON_SPACING_MARK = String.fromCharCode(65039); // 65039 - '' - 0xFE0F;
var nonSpacingRegex = new RegExp(NON_SPACING_MARK, 'g')
// Remove the non-spacing-mark from the code, never send a stripped version
// to the client, as it kills graphical emoticons.
function stripNSB (code) {
return code.replace(nonSpacingRegex, '');
};
// Reversed hash table, where as emojiByName contains a { heart: '❤' }
// dictionary emojiByCode contains { ❤: 'heart' }. The codes are normalized
// to the text version.
var emojiByCode = Object.keys(emojiByName).reduce(function(h,k) {
h[stripNSB(emojiByName[k])] = k;
return h;
}, {});
/**
* Emoji namespace
*/
var Emoji = {
emoji: emojiByName,
};
/**
* get emoji code from name. return emoji code back if code is passed in.
* @param {string} emoji
* @return {string}
*/
Emoji._get = function _get (emoji) {
if (emojiByCode[stripNSB(emoji)]) {
return emoji;
} else if (emojiByName.hasOwnProperty(emoji)) {
return emojiByName[emoji];
}
return ensureColons(emoji);
};
/**
* get emoji code from :emoji: string or name
* @param {string} emoji
* @return {string}
*/
Emoji.get = function get (emoji) {
emoji = stripColons(emoji);
return Emoji._get(emoji);
};
/**
* find the emoji by either code or name
* @param {string} nameOrCode The emoji to find, either `coffee`, `:coffee:` or ``;
* @return {object}
*/
Emoji.find = function find (nameOrCode) {
return Emoji.findByName(nameOrCode) || Emoji.findByCode(nameOrCode);
};
/**
* find the emoji by name
* @param {string} name The emoji to find either `coffee` or `:coffee:`;
* @return {object}
*/
Emoji.findByName = function findByName (name) {
var stripped = stripColons(name);
var emoji = emojiByName[stripped];
return emoji ? ({ emoji: emoji, key: stripped }) : undefined;
};
/**
* find the emoji by code (emoji)
* @param {string} code The emoji to find; for example `` or ``
* @return {object}
*/
Emoji.findByCode = function findByCode (code) {
var stripped = stripNSB(code);
var name = emojiByCode[stripped];
// lookup emoji to ensure the Variant Form is returned
return name ? ({ emoji: emojiByName[name], key: name }) : undefined;
};
/**
* Check if an emoji is known by this library
* @param {string} nameOrCode The emoji to validate, either `coffee`, `:coffee:` or ``;
* @return {object}
*/
Emoji.hasEmoji = function hasEmoji (nameOrCode) {
return Emoji.hasEmojiByName(nameOrCode) || Emoji.hasEmojiByCode(nameOrCode);
};
/**
* Check if an emoji with given name is known by this library
* @param {string} name The emoji to validate either `coffee` or `:coffee:`;
* @return {object}
*/
Emoji.hasEmojiByName = function hasEmojiByName (name) {
var result = Emoji.findByName(name);
return !!result && result.key === stripColons(name);
};
/**
* Check if a given emoji is known by this library
* @param {string} code The emoji to validate; for example `` or ``
* @return {object}
*/
Emoji.hasEmojiByCode = function hasEmojiByCode (code) {
var result = Emoji.findByCode(code);
return !!result && stripNSB(result.emoji) === stripNSB(code);
};
/**
* get emoji name from code
* @param {string} emoji
* @param {boolean} includeColons should the result include the ::
* @return {string}
*/
Emoji.which = function which (emoji_code, includeColons) {
var code = stripNSB(emoji_code);
var word = emojiByCode[code];
return includeColons ? wrapColons(word) : word;
};
/**
* emojify a string (replace :emoji: with an emoji)
* @param {string} str
* @param {function} on_missing (gets emoji name without :: and returns a proper emoji if no emoji was found)
* @param {function} format (wrap the returned emoji in a custom element)
* @return {string}
*/
Emoji.emojify = function emojify (str, on_missing, format) {
if (!str) return '';
return str.split(emojiNameRegex) // parse emoji via regex
.map(function parseEmoji(s, i) {
// every second element is an emoji, e.g. "test :fast_forward:" -> [ "test ", "fast_forward" ]
if (i % 2 === 0) return s;
var emoji = Emoji._get(s);
var isMissing = emoji.indexOf(':') > -1;
if (isMissing && typeof on_missing === 'function') {
return on_missing(s);
}
if (!isMissing && typeof format === 'function') {
return format(emoji, s);
}
return emoji;
})
.join('') // convert back to string
;
};
/**
* return a random emoji
* @return {string}
*/
Emoji.random = function random () {
var emojiKeys = Object.keys(emojiByName);
var randomIndex = Math.floor(Math.random() * emojiKeys.length);
var key = emojiKeys[randomIndex];
var emoji = Emoji._get(key);
return { key: key, emoji: emoji };
}
/**
* return an collection of potential emoji matches
* @param {string} str
* @return {Array.<Object>}
*/
Emoji.search = function search (str) {
var emojiKeys = Object.keys(emojiByName);
var matcher = stripColons(str)
var matchingKeys = emojiKeys.filter(function(key) {
return key.toString().indexOf(matcher) === 0;
});
return matchingKeys.map(function(key) {
return {
key: key,
emoji: Emoji._get(key),
};
});
}
/**
* unemojify a string (replace emoji with :emoji:)
* @param {string} str
* @return {string}
*/
Emoji.unemojify = function unemojify (str) {
if (!str) return '';
var words = toArray(str);
return words.map(function(word) {
return Emoji.which(word, true) || word;
}).join('');
};
/**
* replace emojis with replacement value
* @param {string} str
* @param {function|string} the string or callback function to replace the emoji with
* @param {boolean} should trailing whitespaces be cleaned? Defaults false
* @return {string}
*/
Emoji.replace = function replace (str, replacement, cleanSpaces) {
if (!str) return '';
var replace = typeof replacement === 'function' ? replacement : function() { return replacement; };
var words = toArray(str);
var replaced = words.map(function(word, idx) {
var emoji = Emoji.findByCode(word);
if (emoji && cleanSpaces && words[idx + 1] === ' ') {
words[idx + 1] = '';
}
return emoji ? replace(emoji) : word;
}).join('');
return cleanSpaces ? replaced.replace(trimSpaceRegex, '') : replaced;
};
/**
* remove all emojis from a string
* @param {string} str
* @return {string}
*/
Emoji.strip = function strip (str) {
return Emoji.replace(str, '', true);
};
module.exports = Emoji;