From 99b0c6ff01f4a5f27f8f88957a6139a122d2a0f7 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Thu, 1 Feb 2024 00:37:17 +0100 Subject: [PATCH] Fix URL not replaced with link node (fixes #258), beautify text via regex without rendering it, and fix execution order --- public/scripts/ui.js | 89 +++++++++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 26 deletions(-) diff --git a/public/scripts/ui.js b/public/scripts/ui.js index 04c70bca..5d679de3 100644 --- a/public/scripts/ui.js +++ b/public/scripts/ui.js @@ -2035,12 +2035,22 @@ class ReceiveTextDialog extends Dialog { window.blop.play(); this._receiveTextQueue.push({text: text, peerId: peerId}); this._setDocumentTitleMessages(); + changeFavicon("images/favicon-96x96-notification.png"); + if (this.isShown()) return; + this._dequeueRequests(); } _dequeueRequests() { - if (!this._receiveTextQueue.length) return; + if (!this._receiveTextQueue.length) { + this.$text.innerHTML = ""; + return; + } + + this._setDocumentTitleMessages(); + changeFavicon("images/favicon-96x96-notification.png"); + let {text, peerId} = this._receiveTextQueue.shift(); this._showReceiveTextDialog(text, peerId); } @@ -2051,41 +2061,68 @@ class ReceiveTextDialog extends Dialog { this.$displayName.classList.add($(peerId).ui._badgeClassName()); this.$text.innerText = text; - this.$text.classList.remove('text-center'); - - // Beautify text if text is short - if (text.length < 2000) { - // replace URLs with actual links - this.$text.innerHTML = this.$text.innerHTML - .replace(/(^|
|\s|")((https?:\/\/|www.)(([a-z]|[A-Z]|[0-9]|[\-_~:\/?#\[\]@!$&'()*+,;=%]){2,}\.)(([a-z]|[A-Z]|[0-9]|[\-_~:\/?#\[\]@!$&'()*+,;=%.]){2,}))/g, - (match, whitespace, url) => { - let link = url; - - // prefix www.example.com with http protocol to prevent it from being a relative link - if (link.startsWith('www')) { - link = "http://" + link - } - // Check if link is valid - if (isUrlValid(link)) { - return `${whitespace}${url}`; - } - else { - return match; + // Beautify text if text is not too long + if (this.$text.innerText.length <= 300000) { + // Hacky workaround to replace URLs with link nodes in all cases + // 1. Use text variable, find all valid URLs via regex and replace URLs with placeholder + // 2. Use html variable, find placeholders with regex and replace them with link nodes + + let $textShadow = document.createElement('div'); + $textShadow.innerText = text; + + let linkNodes = {}; + let searchHTML = $textShadow.innerHTML; + const p = "@"; + const pRgx = new RegExp(`${p}\\d+`, 'g'); + let occP = searchHTML.match(pRgx) || []; + + let m = 0; + + const allowedDomainChars = "a-zA-Z0-9áàäčçđéèêŋńñóòôöšŧüžæøåëìíîïðùúýþćěłřśţźǎǐǒǔǥǧǩǯəʒâûœÿãõāēīōūăąĉċďĕėęĝğġģĥħĩĭįıĵķĸĺļľņňŏőŕŗŝşťũŭůűųŵŷżאבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ"; + const urlRgx = new RegExp(`(^|\\n|\\s|["><\\-_~:\\/?#\\[\\]@!$&'()*+,;=%.])(((https?:\\/\\/)?(?:[${allowedDomainChars}](?:[${allowedDomainChars}-]{0,61}[${allowedDomainChars}])?\\.)+[${allowedDomainChars}][${allowedDomainChars}-]{0,61}[${allowedDomainChars}])(:?\\d*)\\/?([${allowedDomainChars}_\\/\\-#.]*)(\\?([${allowedDomainChars}\\-_~:\\/?#\\[\\]@!$&'()*+,;=%.]*))?)`, 'g'); + + $textShadow.innerText = text.replace(urlRgx, + (match, whitespaceOrSpecial, url, g3, scheme) => { + let link = url; + + // prefix www.example.com with http protocol to prevent it from being a relative link + if (!scheme && link.startsWith('www')) { + link = "http://" + link + } + + if (isUrlValid(link)) { + // link is valid -> replace with link node placeholder + + // find linkNodePlaceholder that is not yet present in text node + m++; + while (occP.includes(`${p}${m}`)) { + m++; } + let linkNodePlaceholder = `${p}${m}`; + + // add linkNodePlaceholder to text node and save a reference to linkNodes object + linkNodes[linkNodePlaceholder] = `${url}`; + return `${whitespaceOrSpecial}${linkNodePlaceholder}`; + } + // link is not valid -> do not replace + return match; }); - } - this._evaluateOverflowing(this.$text); - this._setDocumentTitleMessages(); + this.$text.innerHTML = $textShadow.innerHTML.replace(pRgx, + (m) => { + let urlNode = linkNodes[m]; + return urlNode ? urlNode : m; + }); + } - changeFavicon("images/favicon-96x96-notification.png"); + this._evaluateOverflowing(this.$text); this.show(); } _setDocumentTitleMessages() { - document.title = !this._receiveTextQueue.length + document.title = this._receiveTextQueue.length <= 1 ? `${ Localization.getTranslation("document-titles.message-received") } - PairDrop` : `${ Localization.getTranslation("document-titles.message-received-plural", null, {count: this._receiveTextQueue.length + 1}) } - PairDrop`; }