1// ========================================================= 2// Affichage de l'agenda à partir de data/agenda-data.js 3// Alimente à la fois la page agenda.html (liste complète) et 4// le bloc "Prochains événements" de la page d'accueil (3 premiers). 5// ========================================================= 6 7(function () { 8 var MOIS = ['JANV.', 'FÃVR.', 'MARS', 'AVR.', 'MAI', 'JUIN', 'JUIL.', 'AOÃT', 'SEPT.', 'OCT.', 'NOV.', 'DÃC.']; 9 10 var TAG_CLASS = { 11 'vie associative': 'tag-event', 12 'conseil municipal': 'tag-event', 13 'sport': 'tag-event', 14 'culture': 'tag-culture', 15 'marché': 'tag-culture', 16 'environnement': 'tag-news', 17 'travaux': 'tag-travaux' 18 }; 19 20 function parseDate(str) { 21 var parts = String(str).split('-'); 22 return new Date(parseInt(parts[0], 10), parseInt(parts[1], 10) - 1, parseInt(parts[2], 10)); 23 } 24 25 function escapeHtml(str) { 26 return String(str || '').replace(/[&<>"']/g, function (c) { 27 return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; 28 }); 29 } 30 31 function getUpcomingEvents() { 32 var raw = window.AGENDA_EVENTS || []; 33 var today = new Date(); 34 today.setHours(0, 0, 0, 0); 35 36 return raw 37 .map(function (e) { 38 var d = parseDate(e.date); 39 return { 40 _date: d, 41 categorie: e.categorie || '', 42 titre: e.titre || '', 43 lieu: e.lieu || '', 44 horaire: e.horaire || '', 45 description: e.description || '', 46 points: Array.isArray(e.points) ? e.points : [], 47 lien: e.lien || '' 48 }; 49 }) 50 .filter(function (e) { return !isNaN(e._date.getTime()) && e._date >= today; }) 51 .sort(function (a, b) { return a._date - b._date; }); 52 } 53 54 function renderItem(e) { 55 var tagClass = TAG_CLASS[(e.categorie || '').toLowerCase()] || 'tag-event'; 56 var day = ('0' + e._date.getDate()).slice(-2); 57 var mon = MOIS[e._date.getMonth()]; 58 var meta = [e.lieu, e.horaire].filter(Boolean).join(' â '); 59 var descLine = [meta, e.description].filter(Boolean).join('. '); 60 var pointsHtml = ''; 61 if (e.points && e.points.length) { 62 pointsHtml = '<ul class="small" style="list-style:disc;margin:6px 0 0;padding-left:18px;line-height:1.6;">' + 63 e.points.map(function (p) { return '<li style="display:list-item;padding:0;border:none;">' + escapeHtml(p) + '</li>'; }).join('') + 64 '</ul>'; 65 } 66 // Si l'événement a un champ "lien" dans data/agenda-data.js (ex. un 67 // formulaire de réservation dédié), son titre devient cliquable. 68 var titreHtml = e.lien 69 ? '<a href="' + escapeHtml(e.lien) + '" style="color:inherit;text-decoration:underline;">' + escapeHtml(e.titre) + '</a>' 70 : escapeHtml(e.titre); 71 72 return ( 73 '<li>' + 74 '<div class="date-chip"><span class="day">' + day + '</span><span class="mon">' + mon + '</span></div>' + 75 '<div>' + 76 '<span class="tag ' + tagClass + '">' + escapeHtml(e.categorie) + '</span>' + 77 '<h3 style="margin:4px 0 4px;">' + titreHtml + '</h3>' + 78 '<p class="small">' + escapeHtml(descLine) + '</p>' + 79 pointsHtml + 80 '</div>' + 81 '</li>' 82 ); 83 } 84 85 function fillList(elementId, events, emptyMessage) { 86 var el = document.getElementById(elementId); 87 if (!el) return; 88 if (events.length) { 89 el.innerHTML = events.map(renderItem).join(''); 90 } else { 91 el.innerHTML = '<p class="small">' + escapeHtml(emptyMessage) + '</p>'; 92 } 93 } 94 95 // Filtre par catégorie sur la page agenda.html : les 3 cartes en haut de 96 // page ("Tous les événements", "Vie associative", "Culture & loisirs") 97 // portent chacune un attribut data-filter-keys listant des mots-clés 98 // (séparés par des virgules). Un clic ne garde que les événements dont 99 // la catégorie contient un de ces mots-clés (comparaison insensible à 100 // la casse) ; la carte "Tous les événements" (mots-clés vide) réaffiche 101 // tout. Ceci ne touche pas "Prochains événements" sur la page d'accueil, 102 // qui n'a pas ces cartes. 103 function setupFilters(events) { 104 var cards = document.querySelectorAll('.agenda-filter-card'); 105 if (!cards.length) return; 106 107 function applyFilter(card) { 108 cards.forEach(function (c) { 109 c.classList.remove('is-active'); 110 c.setAttribute('aria-pressed', 'false'); 111 }); 112 card.classList.add('is-active'); 113 card.setAttribute('aria-pressed', 'true'); 114 115 var keysAttr = card.getAttribute('data-filter-keys') || ''; 116 var keys = keysAttr.split(',').map(function (k) { return k.trim().toLowerCase(); }
116).filter(Boolean); 117 118 var filtered = !keys.length 119 ? events 120 : events.filter(function (e) { 121 var cat = (e.categorie || '').toLowerCase(); 122 return keys.some(function (k) { return cat.indexOf(k) !== -1; }); 123 }); 124 125 fillList('agenda-full-list', filtered, 'Aucun événement à venir dans cette catégorie pour le moment.'); 126 } 127 128 cards.forEach(function (card) { 129 card.addEventListener('click', function () { applyFilter(card); }); 130 card.addEventListener('keydown', function (evt) { 131 if (evt.key === 'Enter' || evt.key === ' ') { 132 evt.preventDefault(); 133 applyFilter(card); 134 } 135 }); 136 }); 137 } 138 139 document.addEventListener('DOMContentLoaded', function () { 140 if (typeof window.AGENDA_EVENTS === 'undefined') { 141 // data/agenda-data.js n'a pas pu être chargé (fichier manquant ou 142 // déplacé) : on laisse le contenu existant de la page tel quel. 143 return; 144 } 145 146 var events = getUpcomingEvents(); 147 fillList('agenda-full-list', events, 'Aucun événement à venir pour le moment.'); 148 fillList('agenda-preview-list', events.slice(0, 3), 'Aucun événement à venir pour le moment.'); 149 setupFilters(events); 150 }); 151})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.