From e2f0ca6e4588b91355e44b3da67925b97621b07a Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Fri, 14 Feb 2025 19:13:39 +0100 Subject: [PATCH] Fix links in messages cut if ! in path; make regex more readable --- public/scripts/ui.js | 61 +++++++++++++++++++++++++----------------- public/scripts/util.js | 2 +- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/public/scripts/ui.js b/public/scripts/ui.js index c988db7..a4f17af 100644 --- a/public/scripts/ui.js +++ b/public/scripts/ui.js @@ -2077,35 +2077,48 @@ class ReceiveTextDialog extends Dialog { 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'); + const chrs = `a-zA-Z0-9áàäčçđéèêŋńñóòôöšŧüžæøåëìíîïðùúýþćěłřśţźǎǐǒǔǥǧǩǯəʒâûœÿãõāēīōūăąĉċďĕėęĝğġģĥħĩĭįıĵķĸĺļľņňŏőŕŗŝşťũŭůűųŵŷżאבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ`; // allowed chars in domain names + const rgxWhitespace = `(^|\\n|\\s)`; + const rgxScheme = `(https?:\\/\\/)` + const rgxSchemeMail = `(mailto:)` + const rgxUserinfo = `(?:(?:[${chrs}.%]*(?::[${chrs}.%]*)?)@)`; + const rgxHost = `(?:(?:[${chrs}](?:[${chrs}-]{0,61}[${chrs}])?\\.)+[${chrs}][${chrs}-]{0,61}[${chrs}])`; + const rgxPort = `(:\\d*)`; + const rgxPath = `(?:(?:\\/[${chrs}\\-\\._~!$&'\\(\\)\\*\\+,;=:@%]*)*)`; + const rgxQueryAndFragment = `(\\?[${chrs}\\-_~:\\/#\\[\\]@!$&'\\(\\)*+,;=%.]*)`; + const rgxUrl = `(${rgxScheme}?${rgxHost}${rgxPort}?${rgxPath}${rgxQueryAndFragment}?)`; + const rgxMail = `(${rgxSchemeMail}${rgxUserinfo}${rgxHost})`; + const rgxUrlAll = new RegExp(`${rgxWhitespace}${rgxUrl}`, 'g'); + const rgxMailAll = new RegExp(`${rgxWhitespace}${rgxMail}`, 'g'); - $textShadow.innerText = text.replace(urlRgx, - (match, whitespaceOrSpecial, url, g3, scheme) => { - let link = url; + const replaceMatchWithPlaceholder = function(match, whitespace, url, 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 - } + // prefix www.example.com with http scheme 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}`; - } + if (!isUrlValid(link)) { // link is not valid -> do not replace return match; - }); + } + + // 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 `${whitespace}${linkNodePlaceholder}`; + } + + text = text.replace(rgxUrlAll, replaceMatchWithPlaceholder); + $textShadow.innerText = text.replace(rgxMailAll, replaceMatchWithPlaceholder); this.$text.innerHTML = $textShadow.innerHTML.replace(pRgx, diff --git a/public/scripts/util.js b/public/scripts/util.js index 08a790f..1dcc388 100644 --- a/public/scripts/util.js +++ b/public/scripts/util.js @@ -590,7 +590,7 @@ async function decodeBase64Text(base64) { function isUrlValid(url) { try { - let urlObj = new URL(url); + new URL(url); return true; } catch (e) {