From ddf3f79e7aa214473095d81e65e07ca7f5c4694b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9drix?= Date: Wed, 23 Sep 2026 12:42:22 +0200 Subject: [PATCH] =?UTF-8?q?Rendre=20cliquables=20les=20adresses=20=C3=A9cr?= =?UTF-8?q?ites=20dans=20l'agenda?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit « infos et inscriptions sur https://installparty.alpinux.org » s'affichait en texte mort dans la fiche d'un événement : à recopier à la main. Les adresses du descriptif et du lieu deviennent des liens, la ponctuation qui termine la phrase restant en dehors. Le texte vient du calendrier, donc de quelqu'un qui écrit du texte et non du HTML : il est échappé avant d'être lié, et les titres et lieux des cartes le sont aussi, ce qui n'était pas le cas jusqu'ici. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JYfYUZRcJmmEWJwAxZb4o2 --- site/index.html | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/site/index.html b/site/index.html index 2264ee6..60e9c26 100644 --- a/site/index.html +++ b/site/index.html @@ -437,6 +437,8 @@ } .ev-modal-body { font-size: .92rem; line-height: 1.6; color: #444; } + .ev-modal-body a { color: #1a6bbf; word-break: break-word; } + .ev-modal-body a:hover { text-decoration: none; } .event-date { font-size: .82rem; @@ -1088,6 +1090,30 @@ } } + /* Le calendrier est saisi par des humains : son texte est du texte, jamais du HTML. */ + function echapper(texte) { + return String(texte == null ? '' : texte) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + } + + /* Rend cliquables les adresses écrites dans un descriptif. Le texte reçu est déjà + échappé — le motif s'arrête donc avant une balise — et la ponctuation qui termine + une phrase reste en dehors du lien. */ + function avecLiens(texteEchappe) { + return texteEchappe.replace(/https?:\/\/[^\s<]+/g, function(url) { + var queue = ''; + var fin = /[.,;:!?»)\]]+$/.exec(url); + if (fin) { + queue = fin[0]; + url = url.slice(0, url.length - queue.length); + } + return '' + url + '' + queue; + }); + } + function showDetail(date, evs, el) { el.hidden = false; el.innerHTML = '' + @@ -1097,9 +1123,9 @@ var time = e.allDay ? '' : ' ' + e.start.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' }) + ''; - return '
  • ' + e.title + time + + return '
  • ' + echapper(e.title) + time + (e.desc ? ' ℹ' : '') + - (e.location ? ' — ' + e.location + '' : '') + + (e.location ? ' — ' + echapper(e.location) + '' : '') + '
  • '; }).join('') + ''; el.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); @@ -1120,11 +1146,11 @@ var descHtml = ''; if (ev.desc) { descHtml = '

    ' + - ev.desc.replace(/&/g,'&').replace(//g,'>') + '

    '; + avecLiens(echapper(ev.desc)) + '

    '; } body.innerHTML = '

    📅 ' + dateStr + '

    ' + - (ev.location ? '

    📍 ' + ev.location + '

    ' : '') + + (ev.location ? '

    📍 ' + avecLiens(echapper(ev.location)) + '

    ' : '') + descHtml; modal.hidden = false; document.getElementById('ev-modal').querySelector('.ev-modal-close').focus(); @@ -1156,8 +1182,8 @@ : ev.start.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' }) + (ev.end ? ' – ' + ev.end.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' }) : '')) + '' + - '
    ' + ev.title + '
    ' + - (ev.location ? '
    📍 ' + ev.location + '
    ' : ''); + '
    ' + echapper(ev.title) + '
    ' + + (ev.location ? '
    📍 ' + echapper(ev.location) + '
    ' : ''); card.addEventListener('click', function() { openModal(ev); }); container.appendChild(card); }); @@ -1179,8 +1205,8 @@ card.innerHTML = (ev.desc ? 'ℹ' : '') + '
    ' + fmtDate(ev.start, ev.allDay) + '
    ' + - '
    ' + ev.title + '
    ' + - (ev.location ? '
    📍 ' + ev.location + '
    ' : ''); + '
    ' + echapper(ev.title) + '
    ' + + (ev.location ? '
    📍 ' + echapper(ev.location) + '
    ' : ''); card.addEventListener('click', function() { openModal(ev); }); container.appendChild(card); });