diff --git a/app/javascript/flavours/glitch/features/emoji/emoji.js b/app/javascript/flavours/glitch/features/emoji/emoji.js index 24c5814c4..d4431d6ba 100644 --- a/app/javascript/flavours/glitch/features/emoji/emoji.js +++ b/app/javascript/flavours/glitch/features/emoji/emoji.js @@ -20,13 +20,18 @@ const emojiFilename = (filename) => { }; const emojifyTextNode = (node, customEmojis) => { + const VS15 = 0xFE0E; + const VS16 = 0xFE0F; + let str = node.textContent; const fragment = new DocumentFragment(); + let i = 0; for (;;) { - let match, i = 0; + let match; + // Skip to the next potential emoji to replace if (customEmojis === null) { while (i < str.length && (useSystemEmojiFont || !(match = trie.search(str.slice(i))))) { i += str.codePointAt(i) < 65536 ? 1 : 2; @@ -37,51 +42,58 @@ const emojifyTextNode = (node, customEmojis) => { } } - let rend, replacement = null; + // We reached the end of the string, nothing to replace if (i === str.length) { break; - } else if (str[i] === ':') { - if (!(() => { - rend = str.indexOf(':', i + 1) + 1; - if (!rend) return false; // no pair of ':' - const shortname = str.slice(i, rend); - // now got a replacee as ':shortname:' - // if you want additional emoji handler, add statements below which set replacement and return true. - if (shortname in customEmojis) { - const filename = autoPlayGif ? customEmojis[shortname].url : customEmojis[shortname].static_url; - replacement = document.createElement('img'); - replacement.setAttribute('draggable', 'false'); - replacement.setAttribute('class', 'emojione custom-emoji'); - replacement.setAttribute('alt', shortname); - replacement.setAttribute('title', shortname); - replacement.setAttribute('src', filename); - replacement.setAttribute('data-original', customEmojis[shortname].url); - replacement.setAttribute('data-static', customEmojis[shortname].static_url); - return true; - } - return false; - })()) rend = ++i; + } + + let rend, replacement = null; + if (str[i] === ':') { // Potentially the start of a custom emoji shortcode + if (!(rend = str.indexOf(':', i + 1) + 1)) { + continue; // no pair of ':' + } + + const shortname = str.slice(i, rend); + // now got a replacee as ':shortname:' + // if you want additional emoji handler, add statements below which set replacement and return true. + if (shortname in customEmojis) { + const filename = autoPlayGif ? customEmojis[shortname].url : customEmojis[shortname].static_url; + replacement = document.createElement('img'); + replacement.setAttribute('draggable', 'false'); + replacement.setAttribute('class', 'emojione custom-emoji'); + replacement.setAttribute('alt', shortname); + replacement.setAttribute('title', shortname); + replacement.setAttribute('src', filename); + replacement.setAttribute('data-original', customEmojis[shortname].url); + replacement.setAttribute('data-static', customEmojis[shortname].static_url); + } else { + continue; + } } else if (!useSystemEmojiFont) { // matched to unicode emoji + rend = i + match.length; + + // If the matched character was followed by VS15 (for selecting text presentation), skip it. + if (str.codePointAt(rend - 1) !== VS16 && str.codePointAt(rend) === VS15) { + i = rend + 1; + continue; + } + const { filename, shortCode } = unicodeMapping[match]; const title = shortCode ? `:${shortCode}:` : ''; + replacement = document.createElement('img'); replacement.setAttribute('draggable', 'false'); replacement.setAttribute('class', 'emojione'); replacement.setAttribute('alt', match); replacement.setAttribute('title', title); replacement.setAttribute('src', `${assetHost}/emoji/${emojiFilename(filename)}.svg`); - rend = i + match.length; - // If the matched character was followed by VS15 (for selecting text presentation), skip it. - if (str.codePointAt(rend) === 65038) { - rend += 1; - } } + // Add the processed-up-to-now string and the emoji replacement fragment.append(document.createTextNode(str.slice(0, i))); - if (replacement) { - fragment.append(replacement); - } + fragment.append(replacement); str = str.slice(rend); + i = 0; } fragment.append(document.createTextNode(str));