import { registerOption } from 'pretty-text/pretty-text'; import { buildEmojiUrl, isCustomEmoji } from 'pretty-text/emoji'; import { translations } from 'pretty-text/emoji/data'; let _unicodeReplacements; let _unicodeRegexp; export function setUnicodeReplacements(replacements) { _unicodeReplacements = replacements; if (replacements) { // We sort and reverse to match longer emoji sequences first _unicodeRegexp = new RegExp(Object.keys(replacements).sort().reverse().join("|"), "g"); } }; function escapeRegExp(s) { return s.replace(/[-/\\^$*+?.()|[\]{}]/gi, '\\$&'); } function checkPrev(prev) { if (prev && prev.length) { const lastToken = prev[prev.length-1]; if (lastToken && lastToken.charAt) { const lastChar = lastToken.charAt(lastToken.length-1); if (!/\W/.test(lastChar)) return false; } } return true; } registerOption((siteSettings, opts, state) => { opts.features.emoji = !!siteSettings.enable_emoji; opts.emojiSet = siteSettings.emoji_set || ""; opts.customEmoji = state.customEmoji; }); export function setup(helper) { if (helper.markdownIt) { return; } helper.whiteList('img.emoji'); function imageFor(code) { code = code.toLowerCase(); const opts = helper.getOptions(); const url = buildEmojiUrl(code, opts); if (url) { const title = `:${code}:`; const classes = isCustomEmoji(code, opts) ? "emoji emoji-custom" : "emoji"; return ['img', { href: url, title, 'class': classes, alt: title }]; } } const translationsWithColon = {}; Object.keys(translations).forEach(t => { if (t[0] === ':') { translationsWithColon[t] = translations[t]; } else { const replacement = translations[t]; helper.inlineReplace(t, (token, match, prev) => { return checkPrev(prev) ? imageFor(replacement) : token; }); } }); const translationColonRegexp = new RegExp(Object.keys(translationsWithColon).map(t => `(${escapeRegExp(t)})`).join("|")); helper.registerInline(':', (text, match, prev) => { const endPos = text.indexOf(':', 1); const firstSpace = text.search(/\s/); if (!checkPrev(prev)) { return; } // If there is no trailing colon, check our translations that begin with colons if (endPos === -1 || (firstSpace !== -1 && endPos > firstSpace)) { translationColonRegexp.lastIndex = 0; const m = translationColonRegexp.exec(text); if (m && m[0] && text.indexOf(m[0]) === 0) { // Check outer edge const lastChar = text.charAt(m[0].length); if (lastChar && !/\s/.test(lastChar)) return; const contents = imageFor(translationsWithColon[m[0]]); if (contents) { return [m[0].length, contents]; } } return; } let between; const emojiNameMatch = text.match(/(?:.*?)(:(?!:).?[\w-]*(?::t\d)?:)/); if (emojiNameMatch) { between = emojiNameMatch[0].slice(1, -1); } else { between = text.slice(1, -1); } const contents = imageFor(between); if (contents) { return [text.indexOf(between, 1) + between.length + 1, contents]; } }); helper.addPreProcessor(text => { if (_unicodeReplacements) { _unicodeRegexp.lastIndex = 0; let m; while ((m = _unicodeRegexp.exec(text)) !== null) { let replacement = ":" + _unicodeReplacements[m[0]] + ":"; const before = text.charAt(m.index-1); if (!/\B/.test(before)) { replacement = "\u200b" + replacement; } text = text.replace(m[0], replacement); } } return text; }); }