diff --git a/extensions/emoji/js/src/forum/renderEmoji.js b/extensions/emoji/js/src/forum/renderEmoji.js index deac78075..b09d1e41e 100644 --- a/extensions/emoji/js/src/forum/renderEmoji.js +++ b/extensions/emoji/js/src/forum/renderEmoji.js @@ -14,12 +14,39 @@ const options = { }), }; +/** + * Parses an HTML string into a `` node containing the HTML content. + * + * Vanilla JS implementation of jQuery's `$.parseHTML()`, + * sourced from http://youmightnotneedjquery.com/ + */ +function parseHTML(str) { + const tmp = document.implementation.createHTMLDocument(); + tmp.body.innerHTML = str; + + return tmp.body; +} + export default function renderEmoji() { override(Post.prototype, 'contentHtml', function (original) { const contentHtml = original(); if (this.oldContentHtml !== contentHtml) { - this.emojifiedContentHtml = twemoji.parse(contentHtml, options); + // We need to parse the HTML string into a DOM node, then give it to Twemoji. + // + // This prevents some issues with the default find-replace that would be performed + // on a string passed to `Twemoji.parse()`. + // + // The parse function can only handle a single DOM node provided, so we need to + // wrap it in a node. In our `parseHTML` implementation, we wrap it in a `` + // element. This gets stripped below. + // + // See https://github.com/flarum/core/issues/2958 + const emojifiedDom = twemoji.parse(parseHTML(contentHtml), options); + + // Steal the HTML string inside the emojified DOM `` tag. + this.emojifiedContentHtml = emojifiedDom.innerHTML; + this.oldContentHtml = contentHtml; }