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); });