diff options
-rw-r--r-- | app/javascript/mastodon/features/emoji/__tests__/emoji-test.js | 42 | ||||
-rw-r--r-- | app/javascript/mastodon/features/emoji/emoji.js | 15 |
2 files changed, 32 insertions, 25 deletions
diff --git a/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js b/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js index 07b3d8c53..2f19aab7e 100644 --- a/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js +++ b/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js @@ -11,8 +11,8 @@ describe('emoji', () => { }); it('works with unclosed tags', () => { - expect(emojify('hello>')).toEqual('hello>'); - expect(emojify('<hello')).toEqual('<hello'); + expect(emojify('hello>')).toEqual('hello>'); + expect(emojify('<hello')).toEqual(''); }); it('works with unclosed shortcodes', () => { @@ -22,23 +22,23 @@ describe('emoji', () => { it('does unicode', () => { expect(emojify('\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66')).toEqual( - '<img draggable="false" class="emojione" alt="๐ฉโ๐ฉโ๐ฆโ๐ฆ" title=":woman-woman-boy-boy:" src="/emoji/1f469-200d-1f469-200d-1f466-200d-1f466.svg" />'); + '<img draggable="false" class="emojione" alt="๐ฉโ๐ฉโ๐ฆโ๐ฆ" title=":woman-woman-boy-boy:" src="/emoji/1f469-200d-1f469-200d-1f466-200d-1f466.svg">'); expect(emojify('๐จโ๐ฉโ๐งโ๐ง')).toEqual( - '<img draggable="false" class="emojione" alt="๐จโ๐ฉโ๐งโ๐ง" title=":man-woman-girl-girl:" src="/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg" />'); - expect(emojify('๐ฉโ๐ฉโ๐ฆ')).toEqual('<img draggable="false" class="emojione" alt="๐ฉโ๐ฉโ๐ฆ" title=":woman-woman-boy:" src="/emoji/1f469-200d-1f469-200d-1f466.svg" />'); + '<img draggable="false" class="emojione" alt="๐จโ๐ฉโ๐งโ๐ง" title=":man-woman-girl-girl:" src="/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg">'); + expect(emojify('๐ฉโ๐ฉโ๐ฆ')).toEqual('<img draggable="false" class="emojione" alt="๐ฉโ๐ฉโ๐ฆ" title=":woman-woman-boy:" src="/emoji/1f469-200d-1f469-200d-1f466.svg">'); expect(emojify('\u2757')).toEqual( - '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg" />'); + '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg">'); }); it('does multiple unicode', () => { expect(emojify('\u2757 #\uFE0F\u20E3')).toEqual( - '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg" />'); + '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg"> <img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg">'); expect(emojify('\u2757#\uFE0F\u20E3')).toEqual( - '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg" /><img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg" />'); + '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg"><img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg">'); expect(emojify('\u2757 #\uFE0F\u20E3 \u2757')).toEqual( - '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg" /> <img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg" />'); + '<img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg"> <img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg"> <img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg">'); expect(emojify('foo \u2757 #\uFE0F\u20E3 bar')).toEqual( - 'foo <img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg" /> bar'); + 'foo <img draggable="false" class="emojione" alt="โ" title=":exclamation:" src="/emoji/2757.svg"> <img draggable="false" class="emojione" alt="#๏ธโฃ" title=":hash:" src="/emoji/23-20e3.svg"> bar'); }); it('ignores unicode inside of tags', () => { @@ -46,16 +46,16 @@ describe('emoji', () => { }); it('does multiple emoji properly (issue 5188)', () => { - expect(emojify('๐๐๐')).toEqual('<img draggable="false" class="emojione" alt="๐" title=":ok_hand:" src="/emoji/1f44c.svg" /><img draggable="false" class="emojione" alt="๐" title=":rainbow:" src="/emoji/1f308.svg" /><img draggable="false" class="emojione" alt="๐" title=":two_hearts:" src="/emoji/1f495.svg" />'); - expect(emojify('๐ ๐ ๐')).toEqual('<img draggable="false" class="emojione" alt="๐" title=":ok_hand:" src="/emoji/1f44c.svg" /> <img draggable="false" class="emojione" alt="๐" title=":rainbow:" src="/emoji/1f308.svg" /> <img draggable="false" class="emojione" alt="๐" title=":two_hearts:" src="/emoji/1f495.svg" />'); + expect(emojify('๐๐๐')).toEqual('<img draggable="false" class="emojione" alt="๐" title=":ok_hand:" src="/emoji/1f44c.svg"><img draggable="false" class="emojione" alt="๐" title=":rainbow:" src="/emoji/1f308.svg"><img draggable="false" class="emojione" alt="๐" title=":two_hearts:" src="/emoji/1f495.svg">'); + expect(emojify('๐ ๐ ๐')).toEqual('<img draggable="false" class="emojione" alt="๐" title=":ok_hand:" src="/emoji/1f44c.svg"> <img draggable="false" class="emojione" alt="๐" title=":rainbow:" src="/emoji/1f308.svg"> <img draggable="false" class="emojione" alt="๐" title=":two_hearts:" src="/emoji/1f495.svg">'); }); it('does an emoji that has no shortcode', () => { - expect(emojify('๐โ๐จ')).toEqual('<img draggable="false" class="emojione" alt="๐โ๐จ" title="" src="/emoji/1f441-200d-1f5e8.svg" />'); + expect(emojify('๐โ๐จ')).toEqual('<img draggable="false" class="emojione" alt="๐โ๐จ" title="" src="/emoji/1f441-200d-1f5e8.svg">'); }); it('does an emoji whose filename is irregular', () => { - expect(emojify('โ๏ธ')).toEqual('<img draggable="false" class="emojione" alt="โ๏ธ" title=":arrow_lower_left:" src="/emoji/2199.svg" />'); + expect(emojify('โ๏ธ')).toEqual('<img draggable="false" class="emojione" alt="โ๏ธ" title=":arrow_lower_left:" src="/emoji/2199.svg">'); }); it('avoid emojifying on invisible text', () => { @@ -67,26 +67,26 @@ describe('emoji', () => { it('avoid emojifying on invisible text with nested tags', () => { expect(emojify('<span class="invisible">๐<span class="foo">bar</span>๐ด</span>๐')) - .toEqual('<span class="invisible">๐<span class="foo">bar</span>๐ด</span><img draggable="false" class="emojione" alt="๐" title=":innocent:" src="/emoji/1f607.svg" />'); + .toEqual('<span class="invisible">๐<span class="foo">bar</span>๐ด</span><img draggable="false" class="emojione" alt="๐" title=":innocent:" src="/emoji/1f607.svg">'); expect(emojify('<span class="invisible">๐<span class="invisible">๐</span>๐ด</span>๐')) - .toEqual('<span class="invisible">๐<span class="invisible">๐</span>๐ด</span><img draggable="false" class="emojione" alt="๐" title=":innocent:" src="/emoji/1f607.svg" />'); - expect(emojify('<span class="invisible">๐<br/>๐ด</span>๐')) - .toEqual('<span class="invisible">๐<br/>๐ด</span><img draggable="false" class="emojione" alt="๐" title=":innocent:" src="/emoji/1f607.svg" />'); + .toEqual('<span class="invisible">๐<span class="invisible">๐</span>๐ด</span><img draggable="false" class="emojione" alt="๐" title=":innocent:" src="/emoji/1f607.svg">'); + expect(emojify('<span class="invisible">๐<br>๐ด</span>๐')) + .toEqual('<span class="invisible">๐<br>๐ด</span><img draggable="false" class="emojione" alt="๐" title=":innocent:" src="/emoji/1f607.svg">'); }); it('skips the textual presentation VS15 character', () => { expect(emojify('โด๏ธ')) // This is U+2734 EIGHT POINTED BLACK STAR then U+FE0E VARIATION SELECTOR-15 - .toEqual('<img draggable="false" class="emojione" alt="โด" title=":eight_pointed_black_star:" src="/emoji/2734_border.svg" />'); + .toEqual('<img draggable="false" class="emojione" alt="โด" title=":eight_pointed_black_star:" src="/emoji/2734_border.svg">'); }); it('does an simple emoji properly', () => { expect(emojify('โโ')) - .toEqual('<img draggable="false" class="emojione" alt="โ" title=":female_sign:" src="/emoji/2640.svg" /><img draggable="false" class="emojione" alt="โ" title=":male_sign:" src="/emoji/2642.svg" />'); + .toEqual('<img draggable="false" class="emojione" alt="โ" title=":female_sign:" src="/emoji/2640.svg"><img draggable="false" class="emojione" alt="โ" title=":male_sign:" src="/emoji/2642.svg">'); }); it('does an emoji containing ZWJ properly', () => { expect(emojify('๐โโ๏ธ๐โโ๏ธ')) - .toEqual('<img draggable="false" class="emojione" alt="๐\u200Dโ๏ธ" title=":female-guard:" src="/emoji/1f482-200d-2640-fe0f_border.svg" /><img draggable="false" class="emojione" alt="๐\u200Dโ๏ธ" title=":male-guard:" src="/emoji/1f482-200d-2642-fe0f_border.svg" />'); + .toEqual('<img draggable="false" class="emojione" alt="๐\u200Dโ๏ธ" title=":female-guard:" src="/emoji/1f482-200d-2640-fe0f_border.svg"><img draggable="false" class="emojione" alt="๐\u200Dโ๏ธ" title=":male-guard:" src="/emoji/1f482-200d-2642-fe0f_border.svg">'); }); }); }); diff --git a/app/javascript/mastodon/features/emoji/emoji.js b/app/javascript/mastodon/features/emoji/emoji.js index 0ab32767a..52a8458fb 100644 --- a/app/javascript/mastodon/features/emoji/emoji.js +++ b/app/javascript/mastodon/features/emoji/emoji.js @@ -19,10 +19,13 @@ const emojiFilename = (filename) => { return borderedEmoji.includes(filename) ? (filename + '_border') : filename; }; +const domParser = new DOMParser(); + const emojifyTextNode = (node, customEmojis) => { - const parentElement = node.parentElement; let str = node.textContent; + const fragment = new DocumentFragment(); + for (;;) { let match, i = 0; @@ -64,12 +67,16 @@ const emojifyTextNode = (node, customEmojis) => { } } + fragment.append(document.createTextNode(str.slice(0, i))); + if (replacement) { + fragment.append(domParser.parseFromString(replacement, 'text/html').documentElement.getElementsByTagName('img')[0]); + } node.textContent = str.slice(0, i); - parentElement.insertAdjacentHTML('beforeend', replacement); str = str.slice(rend); - node = document.createTextNode(str); - parentElement.append(node); } + + fragment.append(document.createTextNode(str)); + node.parentElement.replaceChild(fragment, node); }; const emojifyNode = (node, customEmojis) => { |