Refactor all emoji utility methods into window.Signal.Emoji
This commit is contained in:
parent
32e2c6dcb5
commit
a5416e42c4
14 changed files with 131 additions and 387 deletions
|
@ -934,7 +934,6 @@
|
||||||
<script type='text/javascript' src='js/registration.js'></script>
|
<script type='text/javascript' src='js/registration.js'></script>
|
||||||
<script type='text/javascript' src='js/expire.js'></script>
|
<script type='text/javascript' src='js/expire.js'></script>
|
||||||
<script type='text/javascript' src='js/conversation_controller.js'></script>
|
<script type='text/javascript' src='js/conversation_controller.js'></script>
|
||||||
<script type='text/javascript' src='js/emoji_util.js'></script>
|
|
||||||
|
|
||||||
<script type='text/javascript' src='js/views/react_wrapper_view.js'></script>
|
<script type='text/javascript' src='js/views/react_wrapper_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/whisper_view.js'></script>
|
<script type='text/javascript' src='js/views/whisper_view.js'></script>
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
(function() {
|
|
||||||
'use strict';
|
|
||||||
window.emoji_util = window.emoji_util || {};
|
|
||||||
|
|
||||||
// EmojiConverter overrides
|
|
||||||
EmojiConvertor.prototype.getCountOfAllMatches = function(str, regex) {
|
|
||||||
var match = regex.exec(str);
|
|
||||||
var count = 0;
|
|
||||||
|
|
||||||
if (!regex.global) {
|
|
||||||
return match ? 1 : 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';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var imgClass = /(<img [^>]+ class="emoji)(")/g;
|
|
||||||
EmojiConvertor.prototype.addClass = function(text, sizeClass) {
|
|
||||||
if (!sizeClass) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
return text.replace(imgClass, function(match, before, after) {
|
|
||||||
return before + ' ' + sizeClass + after;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var imgTitle = /(<img [^>]+ class="emoji[^>]+ title=")([^:">]+)(")/g;
|
|
||||||
EmojiConvertor.prototype.ensureTitlesHaveColons = function(text) {
|
|
||||||
return text.replace(imgTitle, function(match, before, title, after) {
|
|
||||||
return before + ':' + title + ':' + after;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
EmojiConvertor.prototype.signalReplace = function(str) {
|
|
||||||
var sizeClass = this.getSizeClass(str);
|
|
||||||
|
|
||||||
var text = this.replace_unified(str);
|
|
||||||
text = this.addClass(text, sizeClass);
|
|
||||||
|
|
||||||
return this.ensureTitlesHaveColons(text);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.emoji = new EmojiConvertor();
|
|
||||||
emoji.init_colons();
|
|
||||||
emoji.img_sets.apple.path =
|
|
||||||
'node_modules/emoji-datasource-apple/img/apple/64/';
|
|
||||||
emoji.include_title = true;
|
|
||||||
emoji.replace_mode = 'img';
|
|
||||||
emoji.supports_css = false; // needed to avoid spans with background-image
|
|
||||||
|
|
||||||
window.emoji_util.parse = function($el) {
|
|
||||||
if (!$el || !$el.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$el.html(emoji.signalReplace($el.html()));
|
|
||||||
};
|
|
||||||
})();
|
|
|
@ -3,6 +3,7 @@
|
||||||
const Backbone = require('../ts/backbone');
|
const Backbone = require('../ts/backbone');
|
||||||
const Crypto = require('./modules/crypto');
|
const Crypto = require('./modules/crypto');
|
||||||
const Database = require('./modules/database');
|
const Database = require('./modules/database');
|
||||||
|
const Emoji = require('../ts/util/emoji');
|
||||||
const HTML = require('../ts/html');
|
const HTML = require('../ts/html');
|
||||||
const Message = require('./modules/types/message');
|
const Message = require('./modules/types/message');
|
||||||
const Notifications = require('../ts/notifications');
|
const Notifications = require('../ts/notifications');
|
||||||
|
@ -117,6 +118,7 @@ exports.setup = (options = {}) => {
|
||||||
Components,
|
Components,
|
||||||
Crypto,
|
Crypto,
|
||||||
Database,
|
Database,
|
||||||
|
Emoji,
|
||||||
HTML,
|
HTML,
|
||||||
Migrations,
|
Migrations,
|
||||||
Notifications,
|
Notifications,
|
||||||
|
|
|
@ -75,7 +75,6 @@
|
||||||
this.timeStampView.setElement(this.$('.last-timestamp'));
|
this.timeStampView.setElement(this.$('.last-timestamp'));
|
||||||
this.timeStampView.update();
|
this.timeStampView.update();
|
||||||
|
|
||||||
emoji_util.parse(this.$('.name'));
|
|
||||||
|
|
||||||
if (lastMessage) {
|
if (lastMessage) {
|
||||||
if (this.bodyView) {
|
if (this.bodyView) {
|
||||||
|
|
|
@ -177,7 +177,6 @@
|
||||||
model: this.model,
|
model: this.model,
|
||||||
});
|
});
|
||||||
|
|
||||||
emoji_util.parse(this.$('.conversation-name'));
|
|
||||||
|
|
||||||
this.window = options.window;
|
this.window = options.window;
|
||||||
this.fileInput = new Whisper.FileInputView({
|
this.fileInput = new Whisper.FileInputView({
|
||||||
|
@ -1331,7 +1330,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const input = this.$messageField;
|
const input = this.$messageField;
|
||||||
const message = this.replace_colons(input.val()).trim();
|
const message = window.Signal.Emoji.replaceColons(input.val()).trim();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!message.length && !this.fileInput.hasFiles()) {
|
if (!message.length && !this.fileInput.hasFiles()) {
|
||||||
|
|
|
@ -408,7 +408,6 @@
|
||||||
this.$el.addClass('control');
|
this.$el.addClass('control');
|
||||||
const content = this.$('.content');
|
const content = this.$('.content');
|
||||||
content.text(this.model.getDescription());
|
content.text(this.model.getDescription());
|
||||||
emoji_util.parse(content);
|
|
||||||
} else {
|
} else {
|
||||||
this.$el.removeClass('control');
|
this.$el.removeClass('control');
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,6 @@ const { autoOrientImage } = require('./js/modules/auto_orient_image');
|
||||||
|
|
||||||
window.autoOrientImage = autoOrientImage;
|
window.autoOrientImage = autoOrientImage;
|
||||||
window.dataURLToBlobSync = require('blueimp-canvas-to-blob');
|
window.dataURLToBlobSync = require('blueimp-canvas-to-blob');
|
||||||
window.EmojiConvertor = require('emoji-js');
|
|
||||||
window.emojiData = require('emoji-datasource');
|
window.emojiData = require('emoji-datasource');
|
||||||
window.EmojiPanel = require('emoji-panel');
|
window.EmojiPanel = require('emoji-panel');
|
||||||
window.filesize = require('filesize');
|
window.filesize = require('filesize');
|
||||||
|
|
|
@ -124,9 +124,6 @@ module.exports = {
|
||||||
{
|
{
|
||||||
src: 'js/conversation_controller.js',
|
src: 'js/conversation_controller.js',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
src: 'js/emoji_util.js',
|
|
||||||
},
|
|
||||||
// Select Backbone views
|
// Select Backbone views
|
||||||
{
|
{
|
||||||
src: 'js/views/react_wrapper_view.js',
|
src: 'js/views/react_wrapper_view.js',
|
||||||
|
|
|
@ -1,179 +0,0 @@
|
||||||
'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);
|
|
||||||
});
|
|
||||||
it('returns zero for no match with non-global regular expression', function() {
|
|
||||||
var r = /s/g;
|
|
||||||
var str = 'no match';
|
|
||||||
var actual = emoji.getCountOfAllMatches(str, r);
|
|
||||||
assert.equal(actual, 0);
|
|
||||||
});
|
|
||||||
it('returns 1 for match with non-global regular expression', function() {
|
|
||||||
var r = /s/;
|
|
||||||
var str = 's + s';
|
|
||||||
var actual = emoji.getCountOfAllMatches(str, r);
|
|
||||||
assert.equal(actual, 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
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('addClass', function() {
|
|
||||||
it('returns original string if no emoji images', function() {
|
|
||||||
var start = 'no images. but there is some 🔥. <img src="random.jpg" />';
|
|
||||||
|
|
||||||
var expected = start;
|
|
||||||
var actual = emoji.addClass(start, 'jumbo');
|
|
||||||
|
|
||||||
assert.equal(expected, actual);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns original string if no sizeClass provided', function() {
|
|
||||||
var start =
|
|
||||||
'before <img src="node_modules/emoji-datasource-apple/img/apple/64/1f3e0.png" class="emoji" title="house"/> after';
|
|
||||||
|
|
||||||
var expected = start;
|
|
||||||
var actual = emoji.addClass(start);
|
|
||||||
|
|
||||||
assert.equal(expected, actual);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('adds provided class to image class', function() {
|
|
||||||
var start =
|
|
||||||
'before <img src="node_modules/emoji-datasource-apple/img/apple/64/1f3e0.png" class="emoji" title="house"/> after';
|
|
||||||
|
|
||||||
var expected =
|
|
||||||
'before <img src="node_modules/emoji-datasource-apple/img/apple/64/1f3e0.png" class="emoji jumbo" title="house"/> after';
|
|
||||||
var actual = emoji.addClass(start, 'jumbo');
|
|
||||||
|
|
||||||
assert.equal(expected, actual);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('ensureTitlesHaveColons', function() {
|
|
||||||
it('returns original string if no emoji images', function() {
|
|
||||||
var start = 'no images. but there is some 🔥. <img src="random.jpg" />';
|
|
||||||
|
|
||||||
var expected = start;
|
|
||||||
var actual = emoji.ensureTitlesHaveColons(start);
|
|
||||||
|
|
||||||
assert.equal(expected, actual);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns original string if image title already has colons', function() {
|
|
||||||
var start =
|
|
||||||
'before <img src="node_modules/emoji-datasource-apple/img/apple/64/1f3e0.png" class="emoji" title=":house:"/> after';
|
|
||||||
|
|
||||||
var expected = start;
|
|
||||||
var actual = emoji.ensureTitlesHaveColons(start);
|
|
||||||
|
|
||||||
assert.equal(expected, actual);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not change title for non-emoji image', function() {
|
|
||||||
var start =
|
|
||||||
'before <img src="random.png" title="my random title"/> after';
|
|
||||||
|
|
||||||
var expected = start;
|
|
||||||
var actual = emoji.ensureTitlesHaveColons(start);
|
|
||||||
|
|
||||||
assert.equal(expected, actual);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('adds colons to emoji image title', function() {
|
|
||||||
var start =
|
|
||||||
'before <img src="node_modules/emoji-datasource-apple/img/apple/64/1f3e0.png" class="emoji" title="house"/> after';
|
|
||||||
|
|
||||||
var expected =
|
|
||||||
'before <img src="node_modules/emoji-datasource-apple/img/apple/64/1f3e0.png" class="emoji" title=":house:"/> after';
|
|
||||||
var actual = emoji.ensureTitlesHaveColons(start);
|
|
||||||
|
|
||||||
assert.equal(expected, actual);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('signalReplace', function() {
|
|
||||||
it('returns images for every emoji', function() {
|
|
||||||
var actual = emoji.signalReplace('🏠 🔥');
|
|
||||||
var expected =
|
|
||||||
'<img src="node_modules/emoji-datasource-apple/img/apple/64/1f3e0.png" class="emoji jumbo" data-codepoints="1f3e0" title=":house:"/>' +
|
|
||||||
' <img src="node_modules/emoji-datasource-apple/img/apple/64/1f525.png" class="emoji jumbo" data-codepoints="1f525" title=":fire:"/>';
|
|
||||||
|
|
||||||
assert.equal(expected, actual);
|
|
||||||
});
|
|
||||||
it('properly hyphenates a variation', function() {
|
|
||||||
var actual = emoji.signalReplace('💪🏿'); // muscle with dark skin tone modifier
|
|
||||||
var expected =
|
|
||||||
'<img src="node_modules/emoji-datasource-apple/img/apple/64/1f4aa-1f3ff.png" class="emoji jumbo" data-codepoints="1f4aa-1f3ff" title=":muscle:"/>';
|
|
||||||
|
|
||||||
assert.equal(expected, actual);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -592,7 +592,6 @@
|
||||||
<script type="text/javascript" src="../js/models/conversations.js" data-cover></script>
|
<script type="text/javascript" src="../js/models/conversations.js" data-cover></script>
|
||||||
<script type="text/javascript" src="../js/models/blockedNumbers.js" data-cover></script>
|
<script type="text/javascript" src="../js/models/blockedNumbers.js" data-cover></script>
|
||||||
<script type="text/javascript" src="../js/conversation_controller.js" data-cover></script>
|
<script type="text/javascript" src="../js/conversation_controller.js" data-cover></script>
|
||||||
<script type='text/javascript' src='../js/emoji_util.js' data-cover></script>
|
|
||||||
<script type="text/javascript" src="../js/keychange_listener.js" data-cover></script>
|
<script type="text/javascript" src="../js/keychange_listener.js" data-cover></script>
|
||||||
<script type='text/javascript' src='../js/expiring_messages.js' data-cover></script>
|
<script type='text/javascript' src='../js/expiring_messages.js' data-cover></script>
|
||||||
<script type='text/javascript' src='../js/notifications.js' data-cover></script>
|
<script type='text/javascript' src='../js/notifications.js' data-cover></script>
|
||||||
|
@ -654,7 +653,6 @@
|
||||||
<script type="text/javascript" src="conversation_controller_test.js"></script>
|
<script type="text/javascript" src="conversation_controller_test.js"></script>
|
||||||
<script type="text/javascript" src="storage_test.js"></script>
|
<script type="text/javascript" src="storage_test.js"></script>
|
||||||
<script type="text/javascript" src="keychange_listener_test.js"></script>
|
<script type="text/javascript" src="keychange_listener_test.js"></script>
|
||||||
<script type="text/javascript" src="emoji_util_test.js"></script>
|
|
||||||
<script type="text/javascript" src="reliable_trigger_test.js"></script>
|
<script type="text/javascript" src="reliable_trigger_test.js"></script>
|
||||||
<script type="text/javascript" src="backup_test.js"></script>
|
<script type="text/javascript" src="backup_test.js"></script>
|
||||||
<script type="text/javascript" src="crypto_test.js"></script>
|
<script type="text/javascript" src="crypto_test.js"></script>
|
||||||
|
|
|
@ -63,14 +63,6 @@ window.Signal.Migrations = {
|
||||||
|
|
||||||
window.Signal.Components = {};
|
window.Signal.Components = {};
|
||||||
|
|
||||||
window.EmojiConvertor = function EmojiConvertor() {};
|
|
||||||
window.EmojiConvertor.prototype.init_colons = () => {};
|
|
||||||
window.EmojiConvertor.prototype.signalReplace = html => html;
|
|
||||||
window.EmojiConvertor.prototype.replace_unified = string => string;
|
|
||||||
window.EmojiConvertor.prototype.img_sets = {
|
|
||||||
apple: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
window.i18n = () => '';
|
window.i18n = () => '';
|
||||||
|
|
||||||
// Ideally we don't need to add things here. We want to add them in StyleGuideUtil, which
|
// Ideally we don't need to add things here. We want to add them in StyleGuideUtil, which
|
||||||
|
|
|
@ -3,90 +3,14 @@ import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import is from '@sindresorhus/is';
|
import is from '@sindresorhus/is';
|
||||||
|
|
||||||
// @ts-ignore
|
import {
|
||||||
import EmojiConvertor from 'emoji-js';
|
findImage,
|
||||||
|
getRegex,
|
||||||
|
getReplacementData,
|
||||||
|
getTitle,
|
||||||
|
} from '../../util/emoji';
|
||||||
import { AddNewLines } from './AddNewLines';
|
import { AddNewLines } from './AddNewLines';
|
||||||
|
|
||||||
function getCountOfAllMatches(str: string, regex: RegExp) {
|
|
||||||
let match = regex.exec(str);
|
|
||||||
let count = 0;
|
|
||||||
|
|
||||||
if (!regex.global) {
|
|
||||||
return match ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (match) {
|
|
||||||
count += 1;
|
|
||||||
match = regex.exec(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasNormalCharacters(str: string) {
|
|
||||||
const noEmoji = str.replace(instance.rx_unified, '').trim();
|
|
||||||
return noEmoji.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSizeClass(str: string) {
|
|
||||||
if (hasNormalCharacters(str)) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojiCount = getCountOfAllMatches(str, instance.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';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const VARIATION_LOOKUP: { [index: string]: string } = {
|
|
||||||
'\uD83C\uDFFB': '1f3fb',
|
|
||||||
'\uD83C\uDFFC': '1f3fc',
|
|
||||||
'\uD83C\uDFFD': '1f3fd',
|
|
||||||
'\uD83C\uDFFE': '1f3fe',
|
|
||||||
'\uD83C\uDFFF': '1f3ff',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Taken from emoji-js/replace_unified
|
|
||||||
function getEmojiReplacementData(
|
|
||||||
m: string,
|
|
||||||
p1: string | undefined,
|
|
||||||
p2: string | undefined
|
|
||||||
) {
|
|
||||||
const unified = instance.map.unified[p1];
|
|
||||||
if (unified) {
|
|
||||||
const variation = VARIATION_LOOKUP[p2 || ''];
|
|
||||||
if (variation) {
|
|
||||||
return {
|
|
||||||
value: unified,
|
|
||||||
variation,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
value: unified,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const unifiedVars = instance.map.unified_vars[p1];
|
|
||||||
if (unifiedVars) {
|
|
||||||
return {
|
|
||||||
value: unifiedVars[0],
|
|
||||||
variation: unifiedVars[1],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some of this logic taken from emoji-js/replacement
|
// Some of this logic taken from emoji-js/replacement
|
||||||
function getImageTag({
|
function getImageTag({
|
||||||
match,
|
match,
|
||||||
|
@ -97,14 +21,14 @@ function getImageTag({
|
||||||
sizeClass: string | undefined;
|
sizeClass: string | undefined;
|
||||||
key: string | number;
|
key: string | number;
|
||||||
}) {
|
}) {
|
||||||
const result = getEmojiReplacementData(match[0], match[1], match[2]);
|
const result = getReplacementData(match[0], match[1], match[2]);
|
||||||
|
|
||||||
if (is.string(result)) {
|
if (is.string(result)) {
|
||||||
return <span key={key}>{match[0]}</span>;
|
return <span key={key}>{match[0]}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const img = instance.find_image(result.value, result.variation);
|
const img = findImage(result.value, result.variation);
|
||||||
const title = instance.data[result.value][3][0];
|
const title = getTitle(result.value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
|
@ -117,15 +41,6 @@ function getImageTag({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const instance = new EmojiConvertor();
|
|
||||||
instance.init_unified();
|
|
||||||
instance.init_colons();
|
|
||||||
instance.img_sets.apple.path =
|
|
||||||
'node_modules/emoji-datasource-apple/img/apple/64/';
|
|
||||||
instance.include_title = true;
|
|
||||||
instance.replace_mode = 'img';
|
|
||||||
instance.supports_css = false; // needed to avoid spans with background-image
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
text: string;
|
text: string;
|
||||||
sizeClass?: string;
|
sizeClass?: string;
|
||||||
|
@ -135,8 +50,9 @@ export class Emojify extends React.Component<Props, {}> {
|
||||||
public render() {
|
public render() {
|
||||||
const { text, sizeClass } = this.props;
|
const { text, sizeClass } = this.props;
|
||||||
const results: Array<any> = [];
|
const results: Array<any> = [];
|
||||||
|
const regex = getRegex();
|
||||||
|
|
||||||
let match = instance.rx_unified.exec(text);
|
let match = regex.exec(text);
|
||||||
let last = 0;
|
let last = 0;
|
||||||
let count = 1;
|
let count = 1;
|
||||||
|
|
||||||
|
@ -152,8 +68,8 @@ export class Emojify extends React.Component<Props, {}> {
|
||||||
|
|
||||||
results.push(getImageTag({ match, sizeClass, key: count++ }));
|
results.push(getImageTag({ match, sizeClass, key: count++ }));
|
||||||
|
|
||||||
last = instance.rx_unified.lastIndex;
|
last = regex.lastIndex;
|
||||||
match = instance.rx_unified.exec(text);
|
match = regex.exec(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last < text.length) {
|
if (last < text.length) {
|
||||||
|
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
||||||
|
|
||||||
import createLinkify from 'linkify-it';
|
import createLinkify from 'linkify-it';
|
||||||
|
|
||||||
import { Emojify, getSizeClass } from './Emojify';
|
import { getSizeClass } from '../../util/emoji';
|
||||||
|
import { Emojify } from './Emojify';
|
||||||
|
|
||||||
const linkify = createLinkify();
|
const linkify = createLinkify();
|
||||||
|
|
||||||
|
|
113
ts/util/emoji.ts
Normal file
113
ts/util/emoji.ts
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
// @ts-ignore
|
||||||
|
import EmojiConvertor from 'emoji-js';
|
||||||
|
|
||||||
|
const instance = new EmojiConvertor();
|
||||||
|
instance.init_unified();
|
||||||
|
instance.init_colons();
|
||||||
|
instance.img_sets.apple.path =
|
||||||
|
'node_modules/emoji-datasource-apple/img/apple/64/';
|
||||||
|
instance.include_title = true;
|
||||||
|
instance.replace_mode = 'img';
|
||||||
|
instance.supports_css = false; // needed to avoid spans with background-image
|
||||||
|
|
||||||
|
export function getRegex(): RegExp {
|
||||||
|
return instance.rx_unified;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTitle(value: string): string | undefined {
|
||||||
|
return instance.data[value][3][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findImage(value: string, variation?: string) {
|
||||||
|
return instance.find_image(value, variation);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function replaceColons(str: string) {
|
||||||
|
return str.replace(instance.rx_colons, m => {
|
||||||
|
const name = m.substr(1, m.length - 2);
|
||||||
|
const code = instance.map.colons[name];
|
||||||
|
if (code) {
|
||||||
|
return instance.data[code][0][0];
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCountOfAllMatches(str: string, regex: RegExp) {
|
||||||
|
let match = regex.exec(str);
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
if (!regex.global) {
|
||||||
|
return match ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (match) {
|
||||||
|
count += 1;
|
||||||
|
match = regex.exec(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasNormalCharacters(str: string) {
|
||||||
|
const noEmoji = str.replace(instance.rx_unified, '').trim();
|
||||||
|
return noEmoji.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSizeClass(str: string) {
|
||||||
|
if (hasNormalCharacters(str)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const emojiCount = getCountOfAllMatches(str, instance.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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VARIATION_LOOKUP: { [index: string]: string } = {
|
||||||
|
'\uD83C\uDFFB': '1f3fb',
|
||||||
|
'\uD83C\uDFFC': '1f3fc',
|
||||||
|
'\uD83C\uDFFD': '1f3fd',
|
||||||
|
'\uD83C\uDFFE': '1f3fe',
|
||||||
|
'\uD83C\uDFFF': '1f3ff',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Taken from emoji-js/replace_unified
|
||||||
|
export function getReplacementData(
|
||||||
|
m: string,
|
||||||
|
p1: string | undefined,
|
||||||
|
p2: string | undefined
|
||||||
|
): string | { value: string; variation?: string } {
|
||||||
|
const unified = instance.map.unified[p1];
|
||||||
|
if (unified) {
|
||||||
|
const variation = VARIATION_LOOKUP[p2 || ''];
|
||||||
|
if (variation) {
|
||||||
|
return {
|
||||||
|
value: unified,
|
||||||
|
variation,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
value: unified,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const unifiedVars = instance.map.unified_vars[p1];
|
||||||
|
if (unifiedVars) {
|
||||||
|
return {
|
||||||
|
value: unifiedVars[0],
|
||||||
|
variation: unifiedVars[1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue