PageSourceSearch

https://clemont.fr/js/agenda-render.js

js clemont.fr collected 2026-10-02 20:03:20 UTC 5,871 bytes, 151 lines download raw bytes

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 { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[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.