FEATURE: Allow plugins to add custom emoji translations
FIX: buildTranslationTree was erroring when translations overlapped (ie. ":-)" and ":-))") FIX: emoji translations wasn't working properly when translations overlapped
This commit is contained in:
parent
88e861e895
commit
95e5f8380d
|
@ -1,32 +1,5 @@
|
|||
export const emojis = <%= Emoji.standard.map(&:name).flatten.inspect %>;
|
||||
export const tonableEmojis = <%= Emoji.tonable_emojis.flatten.inspect %>;
|
||||
export const aliases = <%= Emoji.aliases.inspect.gsub("=>", ":") %>;
|
||||
export const searchAliases = <%= Emoji.searchAliases.inspect.gsub("=>", ":") %>;
|
||||
export const translations = {
|
||||
':)' : 'slight_smile',
|
||||
':-)' : 'slight_smile',
|
||||
'^_^' : 'slight_smile',
|
||||
'^__^' : 'slight_smile',
|
||||
':(' : 'frowning',
|
||||
':-(' : 'frowning',
|
||||
';)' : 'wink',
|
||||
';-)' : 'wink',
|
||||
':\'(' : 'cry',
|
||||
':\'-(': 'cry',
|
||||
':-\'(': 'cry',
|
||||
':p' : 'stuck_out_tongue',
|
||||
':P' : 'stuck_out_tongue',
|
||||
':-P' : 'stuck_out_tongue',
|
||||
':O' : 'open_mouth',
|
||||
':-O' : 'open_mouth',
|
||||
':D' : 'smiley',
|
||||
':-D' : 'smiley',
|
||||
':|' : 'expressionless',
|
||||
':-|' : 'expressionless',
|
||||
':/' : 'confused',
|
||||
'8-)' : 'sunglasses',
|
||||
";P" : 'stuck_out_tongue_winking_eye',
|
||||
";-P" : 'stuck_out_tongue_winking_eye',
|
||||
":$" : 'blush',
|
||||
":-$" : 'blush'
|
||||
};
|
||||
export const searchAliases = <%= Emoji.search_aliases.inspect.gsub("=>", ":") %>;
|
||||
export const translations = <%= Emoji.translations.inspect.gsub("=>", ":") %>;
|
||||
|
|
|
@ -9,22 +9,18 @@ let translationTree = null;
|
|||
// We build a data structure that allows us to quickly
|
||||
// search through our N next chars to see if any match
|
||||
// one of our alias emojis.
|
||||
//
|
||||
function buildTranslationTree() {
|
||||
let tree = [];
|
||||
let lastNode;
|
||||
|
||||
Object.keys(translations).forEach(function(key) {
|
||||
let i;
|
||||
Object.keys(translations).forEach(key => {
|
||||
let node = tree;
|
||||
|
||||
for (i = 0; i < key.length; i++) {
|
||||
for (let i = 0; i < key.length; i++) {
|
||||
let code = key.charCodeAt(i);
|
||||
let j;
|
||||
|
||||
let found = false;
|
||||
|
||||
for (j = 0; j < node.length; j++) {
|
||||
for (let j = 0; j < node.length; j++) {
|
||||
if (node[j][0] === code) {
|
||||
node = node[j][1];
|
||||
found = true;
|
||||
|
@ -33,7 +29,7 @@ function buildTranslationTree() {
|
|||
}
|
||||
|
||||
if (!found) {
|
||||
// token, children, value
|
||||
// code, children, value
|
||||
let tmp = [code, []];
|
||||
node.push(tmp);
|
||||
lastNode = tmp;
|
||||
|
@ -41,7 +37,7 @@ function buildTranslationTree() {
|
|||
}
|
||||
}
|
||||
|
||||
lastNode[1] = translations[key];
|
||||
lastNode[2] = translations[key];
|
||||
});
|
||||
|
||||
return tree;
|
||||
|
@ -121,28 +117,22 @@ function getEmojiTokenByName(name, state) {
|
|||
function getEmojiTokenByTranslation(content, pos, state) {
|
||||
translationTree = translationTree || buildTranslationTree();
|
||||
|
||||
let currentTree = translationTree;
|
||||
|
||||
let i;
|
||||
let search = true;
|
||||
let found = false;
|
||||
let t = translationTree;
|
||||
let start = pos;
|
||||
let found = null;
|
||||
|
||||
while (search) {
|
||||
search = false;
|
||||
while (t.length > 0 && pos < content.length) {
|
||||
let code = content.charCodeAt(pos);
|
||||
|
||||
for (i = 0; i < currentTree.length; i++) {
|
||||
if (currentTree[i][0] === code) {
|
||||
currentTree = currentTree[i][1];
|
||||
pos++;
|
||||
search = true;
|
||||
if (typeof currentTree === "string") {
|
||||
found = currentTree;
|
||||
}
|
||||
for (let i = 0; i < t.length; i++) {
|
||||
if (t[i][0] === code) {
|
||||
found = t[i][2];
|
||||
t = t[i][1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
|
@ -174,17 +164,9 @@ function getEmojiTokenByTranslation(content, pos, state) {
|
|||
}
|
||||
}
|
||||
|
||||
function applyEmoji(
|
||||
content,
|
||||
state,
|
||||
emojiUnicodeReplacer,
|
||||
enableShortcuts,
|
||||
inlineEmoji
|
||||
) {
|
||||
let i;
|
||||
function applyEmoji(content, state, emojiUnicodeReplacer, enableShortcuts, inlineEmoji) {
|
||||
let result = null;
|
||||
let contentToken = null;
|
||||
|
||||
let start = 0;
|
||||
|
||||
if (emojiUnicodeReplacer) {
|
||||
|
@ -193,10 +175,10 @@ function applyEmoji(
|
|||
|
||||
let endToken = content.length;
|
||||
|
||||
for (i = 0; i < content.length - 1; i++) {
|
||||
for (let i = 0; i < content.length - 1; i++) {
|
||||
let offset = 0;
|
||||
const emojiName = getEmojiName(content, i, state, inlineEmoji);
|
||||
let token = null;
|
||||
const emojiName = getEmojiName(content, i, state, inlineEmoji);
|
||||
|
||||
if (emojiName) {
|
||||
token = getEmojiTokenByName(emojiName, state);
|
||||
|
@ -207,8 +189,7 @@ function applyEmoji(
|
|||
|
||||
if (enableShortcuts && !token) {
|
||||
// handle aliases (note: we can't do this in inline cause ; is not a split point)
|
||||
//
|
||||
let info = getEmojiTokenByTranslation(content, i, state);
|
||||
const info = getEmojiTokenByTranslation(content, i, state);
|
||||
|
||||
if (info) {
|
||||
offset = info.pos - i;
|
||||
|
@ -225,7 +206,8 @@ function applyEmoji(
|
|||
}
|
||||
|
||||
result.push(token);
|
||||
endToken = start = i + offset;
|
||||
i += offset;
|
||||
endToken = start = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,10 +25,14 @@ class Emoji
|
|||
Discourse.cache.fetch(cache_key("aliases_emojis")) { db['aliases'] }
|
||||
end
|
||||
|
||||
def self.searchAliases
|
||||
def self.search_aliases
|
||||
Discourse.cache.fetch(cache_key("search_aliases_emojis")) { db['searchAliases'] }
|
||||
end
|
||||
|
||||
def self.translations
|
||||
Discourse.cache.fetch(cache_key("translations_emojis")) { load_translations }
|
||||
end
|
||||
|
||||
def self.custom
|
||||
Discourse.cache.fetch(cache_key("custom_emojis")) { load_custom }
|
||||
end
|
||||
|
@ -63,13 +67,13 @@ class Emoji
|
|||
end
|
||||
|
||||
def self.clear_cache
|
||||
%w{custom standard aliases search_aliases all tonable}.each do |key|
|
||||
%w{custom standard aliases search_aliases translations all tonable}.each do |key|
|
||||
Discourse.cache.delete(cache_key("#{key}_emojis"))
|
||||
end
|
||||
end
|
||||
|
||||
def self.db_file
|
||||
"#{Rails.root}/lib/emoji/db.json"
|
||||
@db_file ||= "#{Rails.root}/lib/emoji/db.json"
|
||||
end
|
||||
|
||||
def self.db
|
||||
|
@ -101,6 +105,10 @@ class Emoji
|
|||
result
|
||||
end
|
||||
|
||||
def self.load_translations
|
||||
db["translations"].merge(Plugin::CustomEmoji.translations)
|
||||
end
|
||||
|
||||
def self.base_directory
|
||||
"public#{base_url}"
|
||||
end
|
||||
|
@ -117,35 +125,35 @@ class Emoji
|
|||
end
|
||||
|
||||
def self.unicode_replacements
|
||||
return @unicode_replacements if @unicode_replacements
|
||||
@unicode_replacements ||= begin
|
||||
replacements = {}
|
||||
is_tonable_emojis = Emoji.tonable_emojis
|
||||
fitzpatrick_scales = FITZPATRICK_SCALE.map { |scale| scale.to_i(16) }
|
||||
|
||||
@unicode_replacements = {}
|
||||
is_tonable_emojis = Emoji.tonable_emojis
|
||||
fitzpatrick_scales = FITZPATRICK_SCALE.map { |scale| scale.to_i(16) }
|
||||
db['emojis'].each do |e|
|
||||
name = e['name']
|
||||
next if name == 'tm'.freeze
|
||||
|
||||
db['emojis'].each do |e|
|
||||
name = e['name']
|
||||
next if name == 'tm'.freeze
|
||||
code = replacement_code(e['code'])
|
||||
next unless code
|
||||
|
||||
code = replacement_code(e['code'])
|
||||
next unless code
|
||||
|
||||
@unicode_replacements[code] = name
|
||||
if is_tonable_emojis.include?(name)
|
||||
fitzpatrick_scales.each_with_index do |scale, index|
|
||||
toned_code = code.codepoints.insert(1, scale).pack("U*".freeze)
|
||||
@unicode_replacements[toned_code] = "#{name}:t#{index + 2}"
|
||||
replacements[code] = name
|
||||
if is_tonable_emojis.include?(name)
|
||||
fitzpatrick_scales.each_with_index do |scale, index|
|
||||
toned_code = code.codepoints.insert(1, scale).pack("U*".freeze)
|
||||
replacements[toned_code] = "#{name}:t#{index + 2}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
replacements["\u{2639}"] = 'frowning'
|
||||
replacements["\u{263A}"] = 'slight_smile'
|
||||
replacements["\u{263B}"] = 'slight_smile'
|
||||
replacements["\u{2661}"] = 'heart'
|
||||
replacements["\u{2665}"] = 'heart'
|
||||
|
||||
replacements
|
||||
end
|
||||
|
||||
@unicode_replacements["\u{2639}"] = 'frowning'
|
||||
@unicode_replacements["\u{263A}"] = 'slight_smile'
|
||||
@unicode_replacements["\u{263B}"] = 'slight_smile'
|
||||
@unicode_replacements["\u{2661}"] = 'heart'
|
||||
@unicode_replacements["\u{2665}"] = 'heart'
|
||||
|
||||
@unicode_replacements
|
||||
end
|
||||
|
||||
def self.unicode_unescape(string)
|
||||
|
|
|
@ -7115,5 +7115,33 @@
|
|||
"cry": [
|
||||
"sob"
|
||||
]
|
||||
},
|
||||
"translations": {
|
||||
":)" : "slight_smile",
|
||||
":-)" : "slight_smile",
|
||||
"^_^" : "slight_smile",
|
||||
"^__^": "slight_smile",
|
||||
":(" : "frowning",
|
||||
":-(" : "frowning",
|
||||
";)" : "wink",
|
||||
";-)" : "wink",
|
||||
":'(" : "cry",
|
||||
":'-(": "cry",
|
||||
":-'(": "cry",
|
||||
":p" : "stuck_out_tongue",
|
||||
":P" : "stuck_out_tongue",
|
||||
":-P" : "stuck_out_tongue",
|
||||
":O" : "open_mouth",
|
||||
":-O" : "open_mouth",
|
||||
":D" : "smiley",
|
||||
":-D" : "smiley",
|
||||
":|" : "expressionless",
|
||||
":-|" : "expressionless",
|
||||
":/" : "confused",
|
||||
"8-)" : "sunglasses",
|
||||
";P" : "stuck_out_tongue_winking_eye",
|
||||
";-P" : "stuck_out_tongue_winking_eye",
|
||||
":$" : "blush",
|
||||
":-$" : "blush"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,15 @@ class Plugin::CustomEmoji
|
|||
@@cache_key = Digest::SHA1.hexdigest(cache_key + name)[0..10]
|
||||
emojis[name] = url
|
||||
end
|
||||
|
||||
def self.translations
|
||||
@@translations ||= {}
|
||||
end
|
||||
|
||||
def self.translate(from, to)
|
||||
@@cache_key = Digest::SHA1.hexdigest(cache_key + from)[0..10]
|
||||
translations[from] = to
|
||||
end
|
||||
end
|
||||
|
||||
class Plugin::Instance
|
||||
|
@ -429,6 +438,10 @@ class Plugin::Instance
|
|||
Plugin::CustomEmoji.register(name, url)
|
||||
end
|
||||
|
||||
def translate_emoji(from, to)
|
||||
Plugin::CustomEmoji.translate(from, to)
|
||||
end
|
||||
|
||||
def automatic_assets
|
||||
css = styles.join("\n")
|
||||
js = javascripts.join("\n")
|
||||
|
|
Loading…
Reference in New Issue