node-emoji
Advanced tools
Comparing version 1.7.0 to 1.8.0
185
lib/emoji.js
/*jslint node: true*/ | ||
require('string.prototype.codepointat'); | ||
var toArray = require('lodash.toarray'); | ||
var emojiByName = require('./emoji.json'); | ||
@@ -10,3 +10,3 @@ "use strict"; | ||
*/ | ||
var parser = /:([a-zA-Z0-9_\-\+]+):/g; | ||
var emojiNameRegex = /:([a-zA-Z0-9_\-\+]+):/g; | ||
@@ -19,3 +19,3 @@ /** | ||
*/ | ||
var trim = function(str) { | ||
function stripColons (str) { | ||
var colonIndex = str.indexOf(':'); | ||
@@ -26,15 +26,58 @@ if (colonIndex > -1) { | ||
str = str.substring(0, colonIndex); | ||
return trim(str); | ||
return stripColons(str); | ||
} else { | ||
str = str.substr(colonIndex + 1); | ||
return trim(str); | ||
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 = module.exports = { | ||
emoji: require('./emoji.json') | ||
var Emoji = { | ||
emoji: emojiByName, | ||
}; | ||
@@ -47,7 +90,8 @@ | ||
*/ | ||
Emoji._get = function _get(emoji) { | ||
if (Emoji.emoji.hasOwnProperty(emoji)) { | ||
return Emoji.emoji[emoji]; | ||
Emoji._get = function _get (emoji) { | ||
if (emojiByName.hasOwnProperty(emoji)) { | ||
return emojiByName[emoji]; | ||
} | ||
return ':' + emoji + ':'; | ||
return ensureColons(emoji); | ||
}; | ||
@@ -60,4 +104,4 @@ | ||
*/ | ||
Emoji.get = function get(emoji) { | ||
emoji = trim(emoji); | ||
Emoji.get = function get (emoji) { | ||
emoji = stripColons(emoji); | ||
@@ -68,14 +112,76 @@ 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_code | ||
* @param {string} emoji | ||
* @param {boolean} includeColons should the result include the :: | ||
* @return {string} | ||
*/ | ||
Emoji.which = function which(emoji_code) { | ||
for (var prop in Emoji.emoji) { | ||
if (Emoji.emoji.hasOwnProperty(prop)) { | ||
if (Emoji.emoji[prop].codePointAt() === emoji_code.codePointAt()) { | ||
return prop; | ||
} | ||
} | ||
} | ||
Emoji.which = function which (emoji_code, includeColons) { | ||
var code = stripNSB(emoji_code); | ||
var word = emojiByCode[code]; | ||
return includeColons ? wrapColons(word) : word; | ||
}; | ||
@@ -90,6 +196,6 @@ | ||
*/ | ||
Emoji.emojify = function emojify(str, on_missing, format) { | ||
Emoji.emojify = function emojify (str, on_missing, format) { | ||
if (!str) return ''; | ||
return str.split(parser) // parse emoji via regex | ||
return str.split(emojiNameRegex) // parse emoji via regex | ||
.map(function parseEmoji(s, i) { | ||
@@ -99,8 +205,9 @@ // every second element is an emoji, e.g. "test :fast_forward:" -> [ "test ", "fast_forward" ] | ||
var emoji = Emoji._get(s); | ||
var isMissing = emoji.indexOf(':') > -1; | ||
if (emoji.indexOf(':') > -1 && typeof on_missing === 'function') { | ||
if (isMissing && typeof on_missing === 'function') { | ||
return on_missing(emoji.substr(1, emoji.length-2)); | ||
} | ||
if (typeof format === 'function') { | ||
if (!isMissing && typeof format === 'function') { | ||
return format(emoji, s); | ||
@@ -119,8 +226,8 @@ } | ||
*/ | ||
Emoji.random = function random() { | ||
var emojiKeys = Object.keys(Emoji.emoji); | ||
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 { key: key, emoji: emoji }; | ||
} | ||
@@ -133,5 +240,5 @@ | ||
*/ | ||
Emoji.search = function search(str) { | ||
var emojiKeys = Object.keys(Emoji.emoji); | ||
var matcher = trim(str) | ||
Emoji.search = function search (str) { | ||
var emojiKeys = Object.keys(emojiByName); | ||
var matcher = stripColons(str) | ||
var matchingKeys = emojiKeys.filter(function(key) { | ||
@@ -148,6 +255,2 @@ return key.toString().indexOf(matcher) === 0; | ||
var emojiToCode = Object.keys(Emoji.emoji).reduce(function(h,k) { | ||
return h[Emoji.emoji[k]] = k, h; | ||
}, {}); | ||
/** | ||
@@ -158,3 +261,3 @@ * unemojify a string (replace emoji with :emoji:) | ||
*/ | ||
Emoji.unemojify = function unemojify(str) { | ||
Emoji.unemojify = function unemojify (str) { | ||
if (!str) return ''; | ||
@@ -164,8 +267,6 @@ var words = toArray(str); | ||
return words.map(function(word) { | ||
var emoji_text = emojiToCode[word]; | ||
if (emoji_text) { | ||
return ':'+emoji_text+':'; | ||
} | ||
return word; | ||
return Emoji.which(word, true) || word; | ||
}).join(''); | ||
}; | ||
module.exports = Emoji; |
{ | ||
"name": "node-emoji", | ||
"version": "1.7.0", | ||
"version": "1.8.0", | ||
"description": "simple emoji support for node.js projects", | ||
@@ -26,4 +26,3 @@ "author": "Daniel Bugl <daniel.bugl@touchlay.com>", | ||
"dependencies": { | ||
"lodash.toarray": "^4.4.0", | ||
"string.prototype.codepointat": "^0.2.0" | ||
"lodash.toarray": "^4.4.0" | ||
}, | ||
@@ -39,2 +38,3 @@ "devDependencies": { | ||
"test": "./node_modules/.bin/mocha --require should --bail --reporter spec test/*", | ||
"watch": "./node_modules/.bin/mocha --require should --bail --reporter spec test/* --watch", | ||
"prepublish": "npm run test" | ||
@@ -41,0 +41,0 @@ }, |
@@ -26,2 +26,6 @@ # node-emoji | ||
emoji.unemojify('I ❤️ 🍕') // replaces the actual emoji with :emoji:, in this case: returns "I :heart: :pizza:" | ||
emoji.find('🍕'); // Find the `pizza` emoji, and returns `({ emoji: '🍕', key: 'pizza' })`; | ||
emoji.find('pizza'); // Find the `pizza` emoji, and returns `({ emoji: '🍕', key: 'pizza' })`; | ||
emoji.hasEmoji('🍕'); // Validate if this library knows an emoji like `🍕` | ||
emoji.hasEmoji('pizza'); // Validate if this library knowns a emoji with the name `pizza` | ||
``` | ||
@@ -73,3 +77,1 @@ | ||
* Bitcoin: [1J5eKsrAcPPLv5gPxSjSUkXnbJpkhndFgA](bitcoin:1J5eKsrAcPPLv5gPxSjSUkXnbJpkhndFgA) | ||
![http://i.imgur.com/RgzXqGD.png](http://i.imgur.com/RgzXqGD.png) |
@@ -40,2 +40,3 @@ /*jslint node: true*/ | ||
}); | ||
it("should work for differently formed characters", function () { | ||
@@ -46,2 +47,3 @@ var umbrella = emoji.which('☔'); | ||
}); | ||
it("should return the same name for differently formed characters", function () { | ||
@@ -54,2 +56,15 @@ var umbrella1 = emoji.which('☔'); | ||
}); | ||
it("should work for flags", function() { | ||
var mexico = emoji.which('🇲🇽'); | ||
should.exists(mexico); | ||
mexico.should.be.exactly('flag-mx'); | ||
var marocco = emoji.which('🇲🇦'); | ||
should.exists(marocco); | ||
marocco.should.be.exactly('flag-ma'); | ||
// see issue #21 | ||
mexico.should.not.equal(marocco); | ||
}); | ||
}); | ||
@@ -63,2 +78,9 @@ | ||
}); | ||
it("should handle flags correctly", function() { | ||
var flags = emoji.emojify('Mexico :flag-mx: and Marocco :flag-ma: are not the same'); | ||
should.exists(flags); | ||
flags.should.be.exactly('Mexico 🇲🇽 and Marocco 🇲🇦 are not the same'); | ||
}); | ||
it("should leave unknown emoji", function () { | ||
@@ -78,3 +100,3 @@ var coffee = emoji.emojify('I :unknown_emoji: :star: :another_one:'); | ||
it("should wrap emoji using provided cb function", function () { | ||
it("should wrap emoji using provided format function", function () { | ||
var coffee = emoji.emojify('I :heart: :coffee:', null, function(code, name) { | ||
@@ -87,2 +109,25 @@ return '<img alt="' + code + '" src="' + name + '.png" />'; | ||
}); | ||
it("should not wrap unknown using provided format function", function () { | ||
var coffee = emoji.emojify('I :unknown_emoji: :coffee:', null, function(code, name) { | ||
return '<img alt="' + code + '" src="' + name + '.png" />'; | ||
}); | ||
should.exist(coffee); | ||
coffee.should.be.exactly('I :unknown_emoji: <img alt="☕️" src="coffee.png" />'); | ||
}); | ||
it("should replace unknown emojis and wrap known emojis using cb functions", function () { | ||
var coffee = emoji.emojify('I :unknown_emoji: :coffee:', | ||
function(name) { | ||
return name; | ||
}, | ||
function(code, name) { | ||
return '<img alt="' + code + '" src="' + name + '.png" />'; | ||
} | ||
); | ||
should.exist(coffee); | ||
coffee.should.be.exactly('I unknown_emoji <img alt="☕️" src="coffee.png" />'); | ||
}); | ||
}); | ||
@@ -146,4 +191,67 @@ | ||
coffee.should.be.exactly('I love :woman-kiss-woman:'); | ||
}); | ||
it("should parse flags correctly", function () { | ||
var flags = emoji.unemojify('The flags of 🇲🇽 and 🇲🇦 are not the same'); | ||
should.exists(flags); | ||
flags.should.be.exactly('The flags of :flag-mx: and :flag-ma: are not the same'); | ||
}); | ||
}); | ||
describe('find emoji', function() { | ||
it('Should be able to find a emoji by :name:', function() { | ||
var result = emoji.find(':heart:') | ||
should.exists(result); | ||
result.should.eql({ emoji: '❤️', key: 'heart' }); | ||
}); | ||
it('Should be able to find an emoji by name', function() { | ||
var result = emoji.find('heart'); | ||
should.exists(result); | ||
result.should.eql({ emoji: '❤️', key: 'heart' }); | ||
}); | ||
it('Should be able to find an emoji by code', function() { | ||
var result = emoji.find('❤'); | ||
should.exists(result); | ||
result.should.eql({ emoji: '❤️', key: 'heart' }); | ||
}); | ||
it('Should return `undefined` for unknown emojis', function() { | ||
var result = emoji.find('unknown_emoji'); | ||
should.not.exists(result); | ||
}) | ||
}); | ||
describe('hasEmoji', function() { | ||
it('Should be able to check a emoji by :name:', function() { | ||
var result = emoji.hasEmoji(':heart:'); | ||
result.should.equal(true) | ||
}); | ||
it('Should be able to check a emoji by name', function() { | ||
var result = emoji.hasEmoji('heart'); | ||
result.should.equal(true); | ||
}); | ||
it('Should be able to check a emoji by code text form)', function() { | ||
var result = emoji.hasEmoji('❤'); | ||
result.should.equal(true); | ||
}); | ||
it('Should be able to check a emoji by code in variant form', function() { | ||
var result = emoji.hasEmoji('❤️'); | ||
result.should.equal(true); | ||
}); | ||
it('Should return false for unknown emoji names', function() { | ||
var result = emoji.hasEmoji(':pizza-kiss-coffee:'); | ||
result.should.equal(false); | ||
}); | ||
it('Should return false for unknown emoji codes', function() { | ||
var result = emoji.hasEmoji('🍕❤️💋☕'); | ||
result.should.equal(false); | ||
}); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
157325
1
1762
76
- Removedstring.prototype.codepointat@^0.2.0
- Removedstring.prototype.codepointat@0.2.1(transitive)