about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--app/javascript/mastodon/emoji.js66
1 files changed, 29 insertions, 37 deletions
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 (<p>, <br>, etc.)
-  // and replacing valid unicode strings
-  // that _aren't_ within tags with an <img> 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 = `<img draggable="false" class="emojione" alt="${shortname}" title="${shortname}" src="${customEmojis[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) + `<img draggable="false" class="emojione" alt="${shortname}" title="${shortname}" src="${customEmojis[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 =  `<img draggable="false" class="emojione" alt="${alt}" title=":${shortCode}:" src="/emoji/${filename}.svg" />`;
-        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) + `<img draggable="false" class="emojione" alt="${match}" title=":${shortCode}:" src="/emoji/${filename}.svg" />`;
+      str = str.slice(i + match.length);
     }
   }
-  return str;
+  return rtn + str;
 };
 
 export default emojify;