1(function () { 2 'use strict'; 3 4 function initCollectionCalendar() { 5 var root = document.getElementById('vpc-collection-calendar'); 6 if (!root) { return; } 7 if (root.getAttribute('data-vpc-calendar-version') === '4.23.2') { return; } 8 9 var memberId = parseInt(root.getAttribute('data-member-id'), 10); 10 var locale = root.getAttribute('data-locale') || 'fr'; 11 var apiBase = root.getAttribute('data-api-base') || '/api/v2/calendar/member'; 12 var calendarBase = root.getAttribute('data-calendar-base') || ('/' + locale + '/calendrier-v2'); 13 14 var list = document.getElementById('vpc-collection-calendar-list'); 15 var loading = document.getElementById('vpc-collection-calendar-loading'); 16 var empty = document.getElementById('vpc-collection-calendar-empty'); 17 var error = document.getElementById('vpc-collection-calendar-error'); 18 var monthLabel = document.getElementById('vpc-collection-calendar-month'); 19 var prevButton = document.getElementById('vpc-collection-calendar-prev'); 20 var nextButton = document.getElementById('vpc-collection-calendar-next'); 21 var todayButton = document.getElementById('vpc-collection-calendar-today'); 22 23 if (!memberId) { 24 if (loading) { 25 loading.textContent = 'Calendrier indisponible : membre invalide.'; 26 } 27 return; 28 } 29 30 /* 31 * V4.23.3 32 * Le composant complète automatiquement les éléments manquants. 33 */ 34 if (!monthLabel) { 35 monthLabel = document.createElement('strong'); 36 monthLabel.id = 'vpc-collection-calendar-month'; 37 38 var nav = root.querySelector('.vpc-collection-calendar-nav'); 39 40 if (nav) { 41 if (nextButton) { 42 nav.insertBefore(monthLabel, nextButton); 43 } else { 44 nav.appendChild(monthLabel); 45 } 46 } else { 47 root.insertBefore(monthLabel, root.firstChild); 48 } 49 } 50 51 if (!loading) { 52 loading = document.createElement('div'); 53 loading.id = 'vpc-collection-calendar-loading'; 54 loading.className = 'vpc-collection-calendar-state'; 55 loading.textContent = 'Chargement du calendrierâ¦'; 56 root.appendChild(loading); 57 } 58 59 if (!error) { 60 error = document.createElement('div'); 61 error.id = 'vpc-collection-calendar-error'; 62 error.className = 'vpc-collection-calendar-state vpc-collection-calendar-error'; 63 error.hidden = true; 64 root.appendChild(error); 65 } 66 67 if (!empty) { 68 empty = document.createElement('div'); 69 empty.id = 'vpc-collection-calendar-empty'; 70 empty.className = 'vpc-collection-calendar-state'; 71 empty.textContent = 'Aucun événement visible pour cette période.'; 72 empty.hidden = true; 73 root.appendChild(empty); 74 } 75 76 if (!list) { 77 list = document.createElement('div'); 78 list.id = 'vpc-collection-calendar-list'; 79 list.className = 'vpc-collection-calendar-list'; 80 root.appendChild(list); 81 } 82 83 root.setAttribute('data-vpc-calendar-ready', '1'); 84 root.setAttribute('data-vpc-calendar-version', '4.23.3'); 85 86 var cursor = new Date(); 87 cursor = new Date(cursor.getFullYear(), cursor.getMonth(), 1); 88 89 function pad2(v) { return v < 10 ? '0' + v : String(v); } 90 function isoDate(d) { return d.getFullYear() + '-' + pad2(d.getMonth() + 1) + '-' + pad2(d.getDate()); } 91 function monthStart(d) { return new Date(d.getFullYear(), d.getMonth(), 1); } 92 function monthEnd(d) { return new Date(d.getFullYear(), d.getMonth() + 1, 0); } 93 94 function esc(v) { 95 return String(v == null ? '' : v) 96 .replace(/&/g, '&') 97 .replace(/</g, '<') 98 .replace(/>/g, '>') 99 .replace(/"/g, '"') 100 .replace(/'/g, '''); 101 } 102 103 function parseDate(v) { 104 if (!v) { return null; } 105 var d = new Date(v); 106 return isNaN(d.getTime()) ? null : d; 107 } 108 109 function formatMonth(d) { 110 try { 111 return new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(d); 112 } catch (e) { 113 return pad2(d.getMonth() + 1) + '/' + d.getFullYear(); 114 } 115 } 116 117 function formatSmallMonth(d) { 118 try { 119 return new Intl.DateTimeFormat(locale, { month: 'short' }).format(d).replace('.', ''); 120 } catch (e) {
121 return pad2(d.getMonth() + 1); 122 } 123 } 124 125 function formatTime(d) { 126 if (!d) { return ''; } 127 try { 128 return new Intl.DateTimeFormat(locale, { hour: '2-digit', minute: '2-digit' }).format(d); 129 } catch (e) { 130 return pad2(d.getHours()) + ':' + pad2(d.getMinutes()); 131 } 132 } 133 134 function statusLabel(s) { 135 if (s === 'IN_PROGRESS') { return 'En cours'; } 136 if (s === 'FINISHED') { return 'Terminé'; } 137 if (s === 'CANCELLED') { return 'Annulé'; } 138 if (s === 'POSTPONED') { return 'Reporté'; } 139 return 'Planifié'; 140 } 141 142 function visibilityLabel(v) { 143 if (v === 'PRIVATE') { return 'Privé'; } 144 if (v === 'FOLLOWERS') { return 'Abonnés'; } 145 return 'Public'; 146 } 147 148 function setState(s) { 149 loading.hidden = s !== 'loading'; 150 error.hidden = s !== 'error'; 151 if (s === 'loading') { 152 empty.hidden = true; 153 list.innerHTML = ''; 154 } 155 } 156 157 function render(items) { 158 list.innerHTML = ''; 159 if (!items || !items.length) { 160 empty.hidden = false; 161 return; 162 } 163 164 empty.hidden = true; 165 var now = new Date(); 166 var html = ''; 167 168 items.forEach(function (item) { 169 var start = parseDate(item.start_at); 170 var end = parseDate(item.end_at); 171 if (!start) { return; } 172 173 var isPast = end ? end < now : start < now; 174 var location = ''; 175 176 if (item.location) { 177 location = item.location.venue_name || item.location.address || ''; 178 } 179 180 var whenText = item.all_day ? 'Toute la journée' : formatTime(start); 181 182 if (!item.all_day && end) { 183 whenText += ' â ' + formatTime(end); 184 } 185 186 var statusClass = ''; 187 if (item.status === 'POSTPONED') { statusClass = ' is-postponed'; } 188 if (item.status === 'CANCELLED') { statusClass = ' is-cancelled'; } 189 190 html += 191 '<article class="vpc-collection-calendar-item' + (isPast ? ' is-past' : '') + '">' + 192 '<div class="vpc-collection-calendar-date">' + 193 '<span class="vpc-collection-calendar-day">' + esc(pad2(start.getDate())) + '</span>' + 194 '<span class="vpc-collection-calendar-month-small">' + esc(formatSmallMonth(start)) + '</span>' + 195 '</div>' + 196 '<div class="vpc-collection-calendar-content">' + 197 '<h4><a href="' + esc(calendarBase + '/evenement/' + item.event_id) + '">' + 198 esc(item.title || 'Ãvénement') + 199 '</a></h4>' + 200 '<div class="vpc-collection-calendar-meta">' + 201 esc(whenText) + (location ? ' · ' + esc(location) : '') + 202 '</div>' + 203 '<div class="vpc-collection-calendar-badges">' + 204 '<span class="vpc-collection-calendar-badge' + statusClass + '">' + 205 esc(statusLabel(item.status)) + 206 '</span>' + 207 '<span class="vpc-collection-calendar-badge">' + 208 esc(visibilityLabel(item.visibility)) + 209 '</span>' + 210 '</div>' + 211 '</div>' + 212 '</article>'; 213 }); 214 215 list.innerHTML = html; 216 if (!html) { empty.hidden = false; } 217 } 218 219 function loadMonth() { 220 var from = isoDate(monthStart(cursor)); 221 var to = isoDate(monthEnd(cursor)); 222 223 monthLabel.textContent = formatMonth(cursor); 224 setState('loading'); 225 226 fetch(apiBase + '/' + memberId + '/' + from + '/' + to, { 227 method: 'GET', 228 credentials: 'same-origin', 229 headers: { 'Accept': 'application/json' } 230 }) 231 .then(function (response) { 232 if (!response.ok) { throw new Error('HTTP ' + response.status); } 233 return response.json(); 234 }) 235 .then(function (payload) { 236 if (!payload || payload.success !== true) { 237 throw new Error('Réponse calendrier invalide'); 238 } 239 240 setState('ready'); 241 render(payload.data || []); 242 }) 243 .catch(function (err) { 244 setState('error');
245 error.textContent = 'Impossible de charger le calendrier.'; 246 if (window.console && console.error) { 247 console.error('Calendrier collection V4.23.2 :', err); 248 } 249 }); 250 } 251 252 if (prevButton) { 253 prevButton.addEventListener('click', function () { 254 cursor = new Date(cursor.getFullYear(), cursor.getMonth() - 1, 1); 255 loadMonth(); 256 }); 257 } 258 259 if (nextButton) { 260 nextButton.addEventListener('click', function () { 261 cursor = new Date(cursor.getFullYear(), cursor.getMonth() + 1, 1); 262 loadMonth(); 263 }); 264 } 265 266 if (todayButton) { 267 todayButton.addEventListener('click', function () { 268 var today = new Date(); 269 cursor = new Date(today.getFullYear(), today.getMonth(), 1); 270 loadMonth(); 271 }); 272 } 273 274 loadMonth(); 275 } 276 277 if (document.readyState === 'loading') { 278 document.addEventListener('DOMContentLoaded', initCollectionCalendar); 279 } else { 280 initCollectionCalendar(); 281 } 282})();
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.