From 35db01d31665d6893b645cdfca7e5cdc465d9f1d Mon Sep 17 00:00:00 2001 From: JC Brand Date: Fri, 27 Nov 2020 10:58:38 +0100 Subject: [PATCH] Styling: Take offset into consideration when adding templates --- spec/emojis.js | 2 +- spec/styling.js | 30 ++++++++++++++++ src/shared/message/text.js | 74 ++++++++++++++++++++++++++------------ 3 files changed, 83 insertions(+), 23 deletions(-) diff --git a/spec/emojis.js b/spec/emojis.js index 283333958..c8a39e452 100644 --- a/spec/emojis.js +++ b/spec/emojis.js @@ -5,7 +5,7 @@ const u = converse.env.utils; const original_timeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; -fdescribe("Emojis", function () { +describe("Emojis", function () { describe("The emoji picker", function () { beforeEach(() => (jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000)); diff --git a/spec/styling.js b/spec/styling.js index 7df07c0e0..ce023b0fd 100644 --- a/spec/styling.js +++ b/spec/styling.js @@ -300,6 +300,36 @@ describe("An incoming chat Message", function () { await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === '
```\n (println "Hello, world!")
\n'+ 'The entire blockquote is a preformatted text block, but this line is plaintext!'); + + msg_text = '> Also, icons.js is loaded from /dist, instead of dist.\nhttps://conversejs.org/docs/html/configuration.html#assets-path' + msg = mock.createChatMessage(_converse, contact_jid, msg_text) + await _converse.handleMessageStanza(msg); + await new Promise(resolve => view.model.messages.once('rendered', resolve)); + msg_el = Array.from(view.el.querySelectorAll('converse-chat-message-body')).pop(); + expect(msg_el.innerText).toBe(msg_text); + await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === + '
Also, icons.js is loaded from /dist, instead of dist.
'+ + 'https://conversejs.org/docs/html/configuration.html#assets-path'); + + msg_text = '> Where is it located?\ngeo:37.786971,-122.399677'; + msg = mock.createChatMessage(_converse, contact_jid, msg_text) + await _converse.handleMessageStanza(msg); + await new Promise(resolve => view.model.messages.once('rendered', resolve)); + msg_el = Array.from(view.el.querySelectorAll('converse-chat-message-body')).pop(); + expect(msg_el.innerText).toBe(msg_text); + await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === + '
Where is it located?
'+ + 'https://www.openstreetmap.org/?mlat=37.786971&mlon=-122.399677#map=18/37.786971/-122.399677'); + + msg_text = '> What do you think of it?\n :poop:'; + msg = mock.createChatMessage(_converse, contact_jid, msg_text) + await _converse.handleMessageStanza(msg); + await new Promise(resolve => view.model.messages.once('rendered', resolve)); + msg_el = Array.from(view.el.querySelectorAll('converse-chat-message-body')).pop(); + expect(msg_el.innerText).toBe(msg_text); + await u.waitUntil(() => msg_el.innerHTML.replace(//g, '') === + '
What do you think of it?
💩'); done(); })); }); diff --git a/src/shared/message/text.js b/src/shared/message/text.js index 9ccab3c6a..c49af3fc1 100644 --- a/src/shared/message/text.js +++ b/src/shared/message/text.js @@ -57,7 +57,13 @@ export class MessageText extends String { this.payload = []; } - addHyperlinks (text) { + /** + * Look for `http` URIs and return templates that render them as URL links + * @param { String } text + * @param { Integer } offset - The index of the passed in text relative to + * the start of the message body text. + */ + addHyperlinks (text, offset) { const objs = []; try { const parse_options = { 'start': /\b(?:([a-z][a-z0-9.+-]*:\/\/)|xmpp:|mailto:|www\.)/gi }; @@ -73,8 +79,8 @@ export class MessageText extends String { const url_text = text.slice(url_obj.start, url_obj.end); const filtered_url = u.filterQueryParamsFromURL(url_text); this.addTemplateResult( - url_obj.start, - url_obj.end, + url_obj.start+offset, + url_obj.end+offset, this.show_images && u.isImageURL(url_text) && u.isImageDomainAllowed(url_text) ? u.convertToImageTag(filtered_url, this.onImgLoad, this.onImgClick) : u.convertUrlToHyperlink(filtered_url), @@ -82,35 +88,54 @@ export class MessageText extends String { }); } - addMapURLs (text) { + /** + * Look for `geo` URIs and return templates that render them as URL links + * @param { String } text + * @param { Integer } offset - The index of the passed in text relative to + * the start of the message body text. + */ + addMapURLs (text, offset) { const regex = /geo:([\-0-9.]+),([\-0-9.]+)(?:,([\-0-9.]+))?(?:\?(.*))?/g; const matches = text.matchAll(regex); for (const m of matches) { this.addTemplateResult( - m.index, - m.index+m.input.length, + m.index+offset, + m.index+m.input.length+offset, u.convertUrlToHyperlink(m.input.replace(regex, _converse.geouri_replacement)) ); } } - async addEmojis (text) { + /** + * Look for emojis (shortnames or unicode) and add templates for rendering them. + * @param { String } text + * @param { Integer } offset - The index of the passed in text relative to + * the start of the message body text. + */ + async addEmojis (text, offset) { await api.emojis.initialize(); const references = [...getShortnameReferences(text.toString()), ...getCodePointReferences(text.toString())]; references.forEach(e => { this.addTemplateResult( - e.begin, - e.end, + e.begin+offset, + e.end+offset, getEmojiMarkup(e, {'add_title_wrapper': true}) ); }); } - addMentionReferences (text, offset) { + /** + * Look for mentions included as XEP-0372 references and add templates for + * rendering them. + * @param { String } text + * @param { Integer } offset - The index of the passed in text relative to + * the start of the message body text. + */ + addMentions (text, offset) { if (!this.model.collection) { // This model doesn't belong to a collection anymore, so it must be // have been removed in the meantime and can be ignored. - log.debug('addMentionReferences: ignoring dangling model'); + log.debug('addMentions: ignoring dangling model'); return; } const nick = this.model.collection.chatbox.get('nick'); @@ -129,7 +154,11 @@ export class MessageText extends String { }); } - addStylingReferences () { + /** + * Look for XEP-0393 styling directives and add templates for rendering + * them. + */ + addStyling () { if (this.model.get('is_unstyled') || !api.settings.get('allow_message_styling')) { return; } @@ -184,19 +213,20 @@ export class MessageText extends String { */ await api.trigger('beforeMessageBodyTransformed', this, {'Synchronous': true}); - this.addStylingReferences(); + this.addStyling(); const payload = this.marshall(); - - let offset = this.offset; + let idx = 0; // The text index of the element in the payload for (const text of payload) { - if (isString(text)) { - this.addHyperlinks(text); - this.addMapURLs(text); - await this.addEmojis(text); - this.addMentionReferences(text, offset); - offset += text.length; + if (!text) { + continue + } else if (isString(text)) { + this.addHyperlinks(text, idx); + this.addMapURLs(text, idx); + await this.addEmojis(text, idx); + this.addMentions(text, this.offset+idx); + idx += text.length; } else { - offset += text.begin; + idx += text.end; } }