diff --git a/app/javascript/mastodon/emoji.js b/app/javascript/mastodon/emoji.js index 865b85b61..e586a3442 100644 --- a/app/javascript/mastodon/emoji.js +++ b/app/javascript/mastodon/emoji.js @@ -4,47 +4,39 @@ import Trie from 'substring-trie'; const trie = new Trie(Object.keys(unicodeMapping)); const emojify = (str, customEmojis = {}) => { - // This walks through the string from start to end, ignoring any tags (

,
, etc.) - // and replacing valid unicode strings - // that _aren't_ within tags with an version. - // The goal is to be the same as an emojione.regUnicode replacement, but faster. - let i = -1; - let insideTag = false; - let insideShortname = false; - let shortnameStartIndex = -1; - let match; - while (++i < str.length) { - const char = str.charAt(i); - if (insideShortname && char === ':') { - const shortname = str.substring(shortnameStartIndex, i + 1); - if (shortname in customEmojis) { - const replacement = `${shortname}`; - str = str.substring(0, shortnameStartIndex) + replacement + str.substring(i + 1); - i += (replacement.length - shortname.length - 1); // jump ahead the length we've added to the string + let rtn = ''; + for (;;) { + let match, i = 0, tag; + while (i < str.length && (tag = '<&:'.indexOf(str[i])) === -1 && !(match = trie.search(str.slice(i)))) { + i += str.codePointAt(i) < 65536 ? 1 : 2; + } + if (i === str.length) + break; + else if (tag >= 0) { + let tagend = str.indexOf('>;:'[tag], i + 1) + 1; + if (!tagend) + break; + if (str[i] === ':') { + const shortname = str.slice(i, tagend); + const lt = str.indexOf('<', i + 1); + if ((lt === -1 || lt >= tagend) && shortname in customEmojis) { + rtn += str.slice(0, i) + `${shortname}`; + str = str.slice(tagend); + } else { + rtn += str.slice(0, i + 1); + str = str.slice(i + 1); + } } else { - i--; - } - insideShortname = false; - } else if (insideTag && char === '>') { - insideTag = false; - } else if (char === '<') { - insideTag = true; - insideShortname = false; - } else if (!insideTag && char === ':') { - insideShortname = true; - shortnameStartIndex = i; - } else if (!insideTag && (match = trie.search(str.substring(i)))) { - const unicodeStr = match; - if (unicodeStr in unicodeMapping) { - const [filename, shortCode] = unicodeMapping[unicodeStr]; - const alt = unicodeStr; - const replacement = `${alt}`; - str = str.substring(0, i) + replacement + str.substring(i + unicodeStr.length); - i += (replacement.length - unicodeStr.length); // jump ahead the length we've added to the string + rtn += str.slice(0, tagend); + str = str.slice(tagend); } + } else { + const [filename, shortCode] = unicodeMapping[match]; + rtn += str.slice(0, i) + `${match}`; + str = str.slice(i + match.length); } } - return str; + return rtn + str; }; export default emojify;