diff options
Diffstat (limited to 'app/javascript/mastodon/emoji.js')
-rw-r--r-- | app/javascript/mastodon/emoji.js | 100 |
1 files changed, 61 insertions, 39 deletions
diff --git a/app/javascript/mastodon/emoji.js b/app/javascript/mastodon/emoji.js index 865b85b61..d75f6f598 100644 --- a/app/javascript/mastodon/emoji.js +++ b/app/javascript/mastodon/emoji.js @@ -3,48 +3,70 @@ import Trie from 'substring-trie'; const trie = new Trie(Object.keys(unicodeMapping)); +const assetHost = process.env.CDN_HOST || ''; + 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 - } 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 - } + let rtn = ''; + for (;;) { + let match, i = 0, tag; + while (i < str.length && (tag = '<&'.indexOf(str[i])) === -1 && str[i] !== ':' && !(match = trie.search(str.slice(i)))) { + i += str.codePointAt(i) < 65536 ? 1 : 2; + } + if (i === str.length) + break; + else if (tag >= 0) { + const tagend = str.indexOf('>;'[tag], i + 1) + 1; + if (!tagend) + break; + rtn += str.slice(0, tagend); + str = str.slice(tagend); + } else if (str[i] === ':') { + try { + // if replacing :shortname: succeed, exit this block with "continue" + const closeColon = str.indexOf(':', i + 1) + 1; + if (!closeColon) throw null; // no pair of ':' + const lt = str.indexOf('<', i + 1); + if (!(lt === -1 || lt >= closeColon)) throw null; // tag appeared before closing ':' + const shortname = str.slice(i, closeColon); + if (shortname in customEmojis) { + rtn += str.slice(0, i) + `<img draggable="false" class="emojione" alt="${shortname}" title="${shortname}" src="${customEmojis[shortname]}" />`; + str = str.slice(closeColon); + continue; + } + } catch (e) {} + // replacing :shortname: failed + rtn += str.slice(0, i + 1); + str = str.slice(i + 1); + } else { + const [filename, shortCode] = unicodeMapping[match]; + rtn += str.slice(0, i) + `<img draggable="false" class="emojione" alt="${match}" title=":${shortCode}:" src="${assetHost}/emoji/${filename}.svg" />`; + str = str.slice(i + match.length); } } - return str; + return rtn + str; }; export default emojify; + +export const buildCustomEmojis = customEmojis => { + const emojis = []; + + customEmojis.forEach(emoji => { + const shortcode = emoji.get('shortcode'); + const url = emoji.get('url'); + const name = shortcode.replace(':', ''); + + emojis.push({ + id: name, + name, + short_names: [name], + text: '', + emoticons: [], + keywords: [name], + imageUrl: url, + custom: true, + }); + }); + + return emojis; +}; |