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

304 lines
8.0 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*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;