diff --git a/components/emojijs/demo/emoji.css b/components/emojijs/demo/emoji.css
index 4a334edaf..25b57c884 100644
--- a/components/emojijs/demo/emoji.css
+++ b/components/emojijs/demo/emoji.css
@@ -44,3 +44,20 @@ img.emoji {
width: 1em;
height: 1em;
}
+
+img.emoji.small {
+ width: 1.25em;
+ height: 1.25em;
+}
+img.emoji.medium {
+ width: 1.5em;
+ height: 1.5em;
+}
+img.emoji.large {
+ width: 1.75em;
+ height: 1.75em;
+}
+img.emoji.jumbo {
+ width: 2em;
+ height: 2em;
+}
diff --git a/js/emoji_util.js b/js/emoji_util.js
index 50702bc25..f4659732e 100644
--- a/js/emoji_util.js
+++ b/js/emoji_util.js
@@ -16,9 +16,83 @@
this.img_sets.apple.path = '/images/emoji/apple/';
this.replace_mode = 'img';
};
+
+ EmojiConvertor.prototype.getCountOfAllMatches = function(str, regex) {
+ var match = regex.exec(str);
+ var count = 0;
+
+ while (match) {
+ count += 1;
+ match = regex.exec(str);
+ }
+
+ return count;
+ };
+
+ EmojiConvertor.prototype.hasNormalCharacters = function(str) {
+ var self = this;
+ var noEmoji = str.replace(self.rx_unified, '').trim();
+ return noEmoji.length > 0;
+ };
+
+ EmojiConvertor.prototype.getSizeClass = function(str) {
+ var self = this;
+
+ if (self.hasNormalCharacters(str)) {
+ return '';
+ }
+
+ var emojiCount = self.getCountOfAllMatches(str, self.rx_unified);
+ if (emojiCount > 8) {
+ return '';
+ }
+ else if (emojiCount > 6) {
+ return 'small';
+ }
+ else if (emojiCount > 4) {
+ return 'medium';
+ }
+ else if (emojiCount > 2) {
+ return 'large';
+ }
+ else {
+ return 'jumbo';
+ }
+ };
+
+ // A stripped-down version of the original: https://github.com/WhisperSystems/Signal-Desktop/blob/aed573562018462fbacd8f2f715e9daeddcde0dd/components/emojijs/lib/emoji.js#L323-L396
+ // One primary change - we inject the second parameter as an additional class
+ EmojiConvertor.prototype.replacement = function(idx, sizeClass, actual, wrapper, variation) {
+ var self = this;
+ var img_set = self.img_set;
+
+ var extra = '';
+ var variation_idx = 0;
+ if (typeof variation === 'object') {
+ extra = self.replacement(variation.idx, null, variation.actual, variation.wrapper);
+ variation_idx = idx + '-' + variation.idx;
+ }
+
+ var img = self.data[idx][7] || self.img_sets[img_set].path + idx + '.png' + self.img_suffix;
+ var title = self.include_title ? ' title="' + (actual || self.data[idx][3][0]) + '"' : '';
+
+ if (variation_idx && self.variations_data[variation_idx] && self.variations_data[variation_idx][2] && !self.data[idx][7]) {
+ if (self.variations_data[variation_idx][2] & self.img_sets[self.img_set].mask) {
+ img = self.img_sets[self.img_set].path + variation_idx + '.png';
+ extra = '';
+ }
+ }
+
+ return '
';
+ };
+
+ // Modeled after the original: https://github.com/WhisperSystems/Signal-Desktop/blob/aed573562018462fbacd8f2f715e9daeddcde0dd/components/emojijs/lib/emoji.js#L265-L286
EmojiConvertor.prototype.replace_unified = function(str) {
var self = this;
self.init_unified();
+
+ var sizeClass = self.getSizeClass(str);
+
return str.replace(self.rx_unified, function(m, p1, p2) {
var val = self.map.unified[p1];
if (!val) { return m; }
@@ -29,14 +103,14 @@
if (p2 == '\uD83C\uDFFE') { idx = '1f3fe'; }
if (p2 == '\uD83C\uDFFF') { idx = '1f3ff'; }
if (idx) {
- return self.replacement(val, null, null, {
+ return self.replacement(val, sizeClass, null, null, {
idx : idx,
actual : p2,
wrapper : ':'
});
}
// wrap names in :'s
- return self.replacement(val, ':' + self.data[val][3][0] + ':');
+ return self.replacement(val, sizeClass, ':' + self.data[val][3][0] + ':');
});
};
window.emoji = new EmojiConvertor();
@@ -46,6 +120,7 @@
if (!$el || !$el.length) {
return;
}
+
$el.html(emoji.replace_unified($el.html()));
};
diff --git a/stylesheets/manifest.css b/stylesheets/manifest.css
index bfd594963..93abea79f 100644
--- a/stylesheets/manifest.css
+++ b/stylesheets/manifest.css
@@ -704,6 +704,22 @@ img.emoji {
width: 1em;
height: 1em; }
+img.emoji.small {
+ width: 1.25em;
+ height: 1.25em; }
+
+img.emoji.medium {
+ width: 1.5em;
+ height: 1.5em; }
+
+img.emoji.large {
+ width: 1.75em;
+ height: 1.75em; }
+
+img.emoji.jumbo {
+ width: 2em;
+ height: 2em; }
+
.settings.modal {
padding: 50px; }
.settings.modal .content {
diff --git a/test/emoji_util_test.js b/test/emoji_util_test.js
new file mode 100644
index 000000000..e306f4c00
--- /dev/null
+++ b/test/emoji_util_test.js
@@ -0,0 +1,102 @@
+'use strict';
+
+describe('EmojiUtil', function() {
+ describe('getCountOfAllMatches', function() {
+ it('returns zero for string with no matches', function() {
+ var r = /s/g;
+ var str = 'no match';
+ var actual = emoji.getCountOfAllMatches(str, r);
+ assert.equal(actual, 0);
+ });
+ it('returns 1 for one match', function() {
+ var r = /s/g;
+ var str = 'just one match';
+ var actual = emoji.getCountOfAllMatches(str, r);
+ assert.equal(actual, 1);
+ });
+ it('returns 2 for two matches', function() {
+ var r = /s/g;
+ var str = 's + s';
+ var actual = emoji.getCountOfAllMatches(str, r);
+ assert.equal(actual, 2);
+ });
+ });
+
+ describe('hasNormalCharacters', function() {
+ it('returns true for all normal text', function() {
+ var str = 'normal';
+ var actual = emoji.hasNormalCharacters(str);
+ assert.equal(actual, true);
+ });
+ it('returns false for all emoji text', function() {
+ var str = '🔥🔥🔥🔥';
+ var actual = emoji.hasNormalCharacters(str);
+ assert.equal(actual, false);
+ });
+ it('returns false for emojis mixed with spaces', function() {
+ var str = '🔥 🔥 🔥 🔥';
+ var actual = emoji.hasNormalCharacters(str);
+ assert.equal(actual, false);
+ });
+ it('returns true for emojis and text', function() {
+ var str = '🔥 normal 🔥 🔥 🔥';
+ var actual = emoji.hasNormalCharacters(str);
+ assert.equal(actual, true);
+ });
+ });
+
+ describe('getSizeClass', function() {
+ it('returns nothing for non-emoji text', function() {
+ assert.equal(emoji.getSizeClass('normal text'), '');
+ });
+ it('returns nothing for emojis mixed with text', function() {
+ assert.equal(emoji.getSizeClass('🔥 normal 🔥'), '');
+ });
+ it('returns nothing for more than 8 emojis', function() {
+ assert.equal(emoji.getSizeClass('🔥🔥 🔥🔥 🔥🔥 🔥🔥 🔥'), '');
+ });
+ it('returns "small" for 7-8 emojis', function() {
+ assert.equal(emoji.getSizeClass('🔥🔥 🔥🔥 🔥🔥 🔥🔥'), 'small');
+ assert.equal(emoji.getSizeClass('🔥🔥 🔥🔥 🔥🔥 🔥'), 'small');
+ });
+ it('returns "medium" for 5-6 emojis', function() {
+ assert.equal(emoji.getSizeClass('🔥🔥 🔥🔥 🔥🔥'), 'medium');
+ assert.equal(emoji.getSizeClass('🔥🔥 🔥🔥 🔥'), 'medium');
+ });
+ it('returns "large" for 3-4 emojis', function() {
+ assert.equal(emoji.getSizeClass('🔥🔥 🔥🔥'), 'large');
+ assert.equal(emoji.getSizeClass('🔥🔥 🔥'), 'large');
+ });
+ it('returns "jumbo" for 1-2 emojis', function() {
+ assert.equal(emoji.getSizeClass('🔥🔥'), 'jumbo');
+ assert.equal(emoji.getSizeClass('🔥'), 'jumbo');
+ });
+ });
+
+ describe('replacement', function() {
+ it('returns an
tag', function() {
+ var actual = emoji.replacement('1f525');
+ assert.equal(actual, '
');
+ });
+ it('returns an
tag with provided sizeClass', function() {
+ var actual = emoji.replacement('1f525', 'large');
+ assert.equal(actual, '
');
+ });
+ });
+
+ describe('replace_unified', function() {
+ it('returns images for every emoji', function() {
+ var actual = emoji.replace_unified('🏠 🔥');
+ var expected = '
'
+ + '
';
+
+ assert.equal(expected, actual);
+ });
+ it('properly hyphenates a variation', function() {
+ var actual = emoji.replace_unified('💪🏿'); // muscle with dark skin tone modifier
+ var expected = '
';
+
+ assert.equal(expected, actual);
+ });
+ });
+});
diff --git a/test/index.html b/test/index.html
index 4f72299d2..706eb60fc 100644
--- a/test/index.html
+++ b/test/index.html
@@ -576,6 +576,7 @@
+