1let closePlaceModal = false; 2let closeEventModal = false; 3 4const mq = window.matchMedia("(max-width: 991px)"); 5let count = 1; // compteur de modales créées dans le DOM 6let openedEventDetails = []; // ensemble des événements actuellement ouverts par l'utilisateur 7let openedEventDetailsTitle = []; //PATCH JBT 8const openedEventDetailsModalsId = []; 9let basedUrl = ''; 10let canonicalLink = ''; 11 12document.addEventListener("DOMContentLoaded", function (event) { 13 history.pushState({}, "", window.location); //on génère un state avec l'URL courante 14 15 canonicalLink = document.querySelector('link[rel="canonical"]'); 16 17 if (canonicalLink != null) { 18 canonicalLink = canonicalLink.href; 19 } 20 basedUrl = document.getElementById("agenda").dataset.urleventslist; 21 22 changeAnchors(); 23 24 // Observer de modification dans le body afin de modifier les ancres lors de l'affichage de nouveaux événements dans le DOM (ex : en cliquant sur "Voir plus") 25 let observer = new MutationObserver((mutations) => { 26 mutations.forEach((mutation) => { 27 let oldValue = mutation.oldValue; 28 let newValue = mutation.target.textContent; 29 if (oldValue !== newValue) { 30 changeAnchors(); 31 } 32 }); 33 }); 34 35 let observableElement = document.querySelector('body'); 36 37 observer.observe(observableElement, { 38 characterDataOldValue: true, 39 subtree: true, 40 childList: true, 41 characterData: true 42 }); 43 44 window.addEventListener('popstate', function (e) { 45 46 const state = e.state; 47 let cloneOpenedEventDetailsModalsId; 48 49 if (state && closeEventModal && !closePlaceModal) { 50 if (openedEventDetailsModalsId.length == 0) { 51 52 forwardToEventList(state); 53 54 return; 55 } else { 56 57 if (parseInt(sessionStorage.modal) != state.eventModal || parseInt(sessionStorage.modal) == 0 && state.eventModal == 0) { 58 id = openedEventDetailsModalsId[openedEventDetailsModalsId.length - 1]; 59 closeEventDetails('eventDetailsModal' + id, 'eventDetailsModalBody' + id, id, true); 60 61 if (openedEventDetailsModalsId.length == 0) { 62 63 forwardToEventList(state); 64 65 } 66 } else { // forward d'un détail d'événement vers un lieu 67 cloneOpenedEventDetailsModalsId = openedEventDetailsModalsId.slice(); 68 69 cloneOpenedEventDetailsModalsId.forEach((element) => { 70 closeEventDetails('eventDetailsModal' + element, 'eventDetailsModalBody' + element, element, true); 71 }); 72 73 forwardToEventList(state); 74 } 75 } 76 77 } else if (state && !closeEventModal && closePlaceModal) { 78 closePlaceDetails(true); 79 closeEventModal = true; 80 closePlaceModal = false; 81 } 82 },); 83}); 84 85function forwardToEventList(state) { 86 state.eventModal = 0; 87 let modalId = parseInt(sessionStorage.modal); 88 89 window.history.replaceState(state, "", basedUrl); 90 document.title = ""; 91 document.title = "Agenda"; 92 93 if (modalId != 0) { 94 closeEventDetails('eventDetailsModal' + modalId, 'eventDetailsModalBody' + modalId, modalId, true); 95 } 96} 97 98/** 99 * Fonction permettant de changer les ancres pour ouvrir les fiches d'événements et de lieux dans des modales sur mobile 100 */ 101function changeAnchors() { 102 103 let eventUuid; 104 const eventsCardsLinks = document.getElementsByClassName("event-card-link"); 105 const accessCardsLinks = document.getElementsByClassName("access-card-link"); 106 const placeLinks = document.getElementsByClassName("place-link"); 107 108 if (mq.matches) { 109 110 // modifie les accès aux détails des événements depuis la vue liste 111 let eventCardLink; 112 for (eventCardLink of eventsCardsLinks) { 113 eventUuid = eventCardLink.dataset.uuid; 114 eventCardLink.href = "javascript:void(0);"; 115 eventCardLink.setAttribute("onclick", "openEventDetails('" + eventUuid + "')"); 116 } 117 118 // modifie les accès aux cartes d'accès à d'autres événements depuis la vue détail d'un événement 119 let accessCardsLink; 120 for (accessCardsLink of accessCardsLinks) { 121 eventUuid = accessCardsLink.dataset.uuid; 122 accessCardsLink.href = "javascript:void(0);"; 123 accessCardsLink.setAttribute("onclick", "openEventDetails('" + eventUuid + "')"); 124 } 125 126 // modifie les accès à la fiche de lieu depuis la vue détail d'un événement 127 let placeLink; 128 for (placeLink of placeLinks) { 129 const id = placeLink.dataset.id; 130 placeLink.href = "javascript:void(0);"; 131 placeLink.setAttribute("onclick", "openPlaceDetails('" + id + "')"); 132 } 133 } 134} 135 136 137/** 138 * Fonction permettant de créer et d'ouvrir des modales de détails d'événements 139 * @param {string} uuid identifiant de l'événement dont l'utilisateur souhaite ouvrir le détail 140 */ 141async function openEventDetails(uuid) { 142 143 const container = document.getElementById("agenda"); 144 145 const loadingModal = '<div class="modal fade eventDetailsModal" id="eventDetailsModal' + count + '" tabindex="-1" data-id="' + count + '" data-uuid="' + uuid + '" aria-hidden="true">' + 146 '<div class="modal-dialog modal-fullscreen" role="document">' + 147 '<div class="modal-content">' + 148 '<div class="modal-header custom-modal-header">' + 149 '<button type="button" class="btn-close custom-btn-close rounded-pill custom-input" onclick="closeEventDetails(\'eventDetailsModal' + count + '\', \'eventDetailsModalBody' + count + '\',' + count + ')" aria-label="Close"></button>' + 150 '</div>' + 151 '<div class="modal-body event-details-modal-body" id="eventDetailsModalBody' + count + '">' + 152 '<div class="container-fluid" id="agenda-event-details">' + 153 '<div class="col-12">' + 154 '<div class="row d-flex mb-2" style=" background-color: #e9ecef;">' + 155 '<span style="height: 270px; width: 100%;"></span>' + 156 '</div>' + 157 '<div class="row">' + 158 '<h1 class="event-details-name placeholder-glow ms-2">' + 159 '<span class="event-details-name placeholder col-10"></span>' + 160 '</h1>' + 161 '</div>' + 162 '<div class="row">' + 163 '<h2 class="event-details-categories placeholder-glow ms-2">' + 164 '<span class="event-details-categories placeholder col-4"></span>' + 165 '</h2>' + 166 '</div>' + 167 '<div>' +
168 '<p class="placeholder-glow mb-0 ms-2"><span class="placeholder col-6"></span></p>' + 169 '<p class="placeholder-glow mb-0 ms-2"><span class="placeholder col-6"></span></p>' + 170 '<p class="placeholder-glow mb-0 ms-2"><span class="placeholder col-6"></span></p>' + 171 '</div>' + 172 '</div>' + 173 '</div>' + 174 '</div>' + 175 '</div>' + 176 '</div>' + 177 '</div>'; 178 179 let modalDiv = document.getElementById("agenda-event-details-modals"); 180 modalDiv.insertAdjacentHTML("beforeend", loadingModal); 181 182 $("#eventDetailsModal" + count).modal('show'); 183 $('[data-bs-toggle="agenda-details-mobile-popover"][data-event=' + uuid + ']').popover({ 184 html: true, 185 sanitize: false, 186 content: function () { 187 return $('#agenda-details-mobile-popover-content[data-event=' + uuid + ']').html(); 188 } 189 }); 190 191 let response = await this.fetchEvent(uuid); 192 let modalBody = document.getElementById("eventDetailsModalBody" + count); 193 194 195 if (response !== "HTTP_500") { 196 let tempHTMLObject = document.createElement('div'); 197 tempHTMLObject.innerHTML = await response.text(); 198 199 let eventDetails = tempHTMLObject.querySelector("#agenda-event-details"); 200 201 202 if (eventDetails !== null) { 203 let currentState = window.history.state; 204 const urlEventDetails = document.getElementById("agenda").dataset.urleventdetails; 205 const ogTitle = new DOMParser().parseFromString(response.headers.get('OG-Title'), "text/html").documentElement.textContent; 206 const css = tempHTMLObject.querySelector("#event-details-stylesheet"); 207 208 modalBody.appendChild(css); 209 modalBody.replaceChild(eventDetails, document.getElementById("eventDetailsModalBody" + count).childNodes[0]); 210 211 openedEventDetails.push(uuid); 212 openedEventDetailsModalsId.push(count); 213 openedEventDetailsTitle.push("Agenda : " + ogTitle); 214 215 const stateUrl = urlEventDetails + "?uuid=" + openedEventDetails[openedEventDetails.length - 1]; 216 217 sessionStorage.setItem("modal", count); 218 219 window.history.pushState(currentState, "", stateUrl); 220 221 if (openedEventDetailsTitle[openedEventDetailsTitle.length - 1]) { //ogTitle; //PATCH JBT 2023-01-31 222 document.title = openedEventDetailsTitle[openedEventDetailsTitle.length - 1]; 223 } 224 225 setCanonicalLink(stateUrl); 226 227 count++; 228 } else { 229 const error = tempHTMLObject.querySelector("#error-div"); 230 modalBody.replaceChild(error, document.getElementById("eventDetailsModalBody" + count).childNodes[0]); 231 } 232 } else { 233 let tempHTMLObject = document.createElement('div'); 234 const iconsUrl = document.getElementById("agenda").dataset.iconsurl; 235 236 tempHTMLObject.innerHTML = '<div class="col" id="error-div">' + 237 '<div class="row">' + 238 '<img class="no-results-img" src="' + iconsUrl + 'warning.svg" alt="Illustation d\'un homme à côté d\'un panneau attention">' + 239 '</div>' + 240 '<div class="row text-center mt-2">' + 241 '<p class="fw-bold fs-3 mb-0">Oups !</p>' + 242 '<p>Une erreur est survenue...</p>' + 243 '</div>' + 244 '<div class="row d-flex mt-2">' + 245 '<a class="text-center" href="javascript:window.location.href=window.location.href;">' + 246 '<button type="button" class="btn border custom-component-border custom-input fit-content bg-transparent remove-filters-button">Recharger l\'agenda</button>' + 247 '</a>' + 248 '</div>' + 249 '</div>'; 250 251 const error = tempHTMLObject.querySelector("#error-div"); 252 253 modalBody.replaceChild(error, document.getElementById("eventDetailsModalBody" + count).childNodes[0]); 254 } 255 256 257 closePlaceModal = false; 258 closeEventModal = true; 259} 260 261/** 262 * Fonction permettant d'obtenir la vue détail d'un événement, à insérer dans une modale 263 * @param uuid 264 * @returns {Promise<Response> || Promise<string>} 265 */ 266async function fetchEvent(uuid) { 267 268 const maxAttempts = 3; // Définir le nombre maximal de tentatives 269 let attempts = 0; 270 271 const url = document.getElementById("agenda").dataset.host + "/public/" + document.getElementById("agenda").dataset.entity + "/events/" + uuid + "?urleventdetails=" + document.getElementById("agenda").dataset.urleventdetails + "&urlplacedetails=" + document.getElementById("agenda").dataset.placeurl; 272 273 while (attempts < maxAttempts) { 274 try { 275 const response = await fetch(url, { 276 method: 'GET' 277 }); 278 279 if (response.ok) { 280 return response; 281 } else { 282 await new Promise(resolve => setTimeout(resolve, 3000)); // Attendre 3 secondes avant la prochaine tentative 283 } 284 } catch (error) { 285 } 286 287 attempts++; 288 } 289 290 return "HTTP_500"; 291} 292 293/** 294 * Fonction permettant de fermer des modales de détails d'événements 295 * @param {string} modalId id de la modale 296 * @param {string} modalBodyId id du corps de la modale 297 */ 298function closeEventDetails(modalId, modalBodyId, id, fromHistory = false) { 299 300 $("#" + modalId).modal('hide'); 301 302 var currentState = window.history.state; // state courant créé par dans le script vuejs-filters 303 304 //var eventModalBody = document.getElementById(modalId);
305 //eventModalBody.innerHTML = ''; 306 307 openedEventDetails.pop(); 308 309 openedEventDetailsModalsId.pop(); 310 311 currentState.eventModal = openedEventDetailsModalsId[openedEventDetailsModalsId.length - 1]; 312 313 sessionStorage.modal = 0; 314 315 openedEventDetailsTitle.pop(); //PATCH JBT 316 317 let stateUrl; 318 if (openedEventDetails.length > 0) { 319 const urlEventDetails = document.getElementById("agenda").dataset.urleventdetails; //PATCH JBT 2023-01-31 320 stateUrl = urlEventDetails + "?uuid=" + openedEventDetails[openedEventDetails.length - 1]; //PATCH JBT 2023-01-31 321 322 if (fromHistory == false) { 323 window.history.replaceState(currentState, "", stateUrl); 324 //if(openedEventDetailsTitle[openedEventDetailsTitle.length - 1]) document.title = "Agenda : " + ogTitle; //PATCH JBT 2023-01-31 325 document.title = "" + openedEventDetailsTitle[openedEventDetailsTitle.length - 1]; //PATCH JBT 2023-01-31 326 327 } 328 329 setCanonicalLink(stateUrl); 330 331 } else { 332 333 if (fromHistory == false) { 334 window.history.replaceState(currentState, "", document.getElementById("agenda").dataset.urleventslist); 335 document.title = "Agenda"; 336 } 337 338 setCanonicalLink(canonicalLink); 339 } 340} 341 342/** 343 * Fonction permettant d'ouvrir la modale de détail d'un lieu 344 * @param {int} id identifiant du lieu 345 */ 346async function openPlaceDetails(id) { 347 348 const modalBody = document.getElementById("placeModalBody"); 349 350 const loadingModalContent = 351 '<div id="agenda-place-details">' + 352 '<div class="col">' + 353 '<div class="row d-flex" style=" background-color: #e9ecef;">' + 354 '<span style="height: 270px; width: 100%;"></span>' + 355 '</div>' + 356 '<div class="row">' + 357 '<h1 class="place-details-name placeholder-glow ms-2">' + 358 '<span class="place-details-name placeholder col-10"></span>' + 359 '</h1>' + 360 '</div>' + 361 '<div class="row">' + 362 '<h2 class="place-details-categories placeholder-glow ms-2">' + 363 '<span class="place-details-categories placeholder col-4"></span>' + 364 '</h2>' + 365 '</div>' + 366 '<div>' +
367 '<p class="placeholder-glow mb-0 ms-2"><span class="placeholder col-6"></span></p>' + 368 '<p class="placeholder-glow mb-0 ms-2"><span class="placeholder col-6"></span></p>' + 369 '<p class="placeholder-glow mb-0 ms-2"><span class="placeholder col-6"></span></p>' + 370 '</div>' + 371 '</div>' + 372 '</div>' + 373 '</div>' + 374 '</div>' + 375 '</div>' + 376 '</div>'; 377 378 modalBody.innerHTML = loadingModalContent; 379 380 $("#placeModal").modal('show'); 381 382 383 let response = await this.fetchPlace(id); 384 385 if (response !== 'HTTP_500') { 386 let tempHTMLObject = document.createElement('div'); 387 const placeDetails = await response.text(); 388 389 tempHTMLObject.innerHTML = placeDetails; 390 391 if(placeDetails !== null) { 392 const currentState = window.history.state; // state courant créé par dans le script vuejs-filters 393 const ogTitle = new DOMParser().parseFromString(response.headers.get('OG-Title'), "text/html").documentElement.textContent; 394 const stateUrl = window.location.href + "&place=" + id; 395 const css = tempHTMLObject.querySelector("#place-details-stylesheet"); 396 397 modalBody.appendChild(css); 398 modalBody.replaceChild(tempHTMLObject.querySelector("#agenda-place-details"), document.getElementById("agenda-place-details")); 399 400 window.history.pushState(currentState, "", stateUrl); 401 402 document.title = "Agenda : " + ogTitle; 403 404 setCanonicalLink(stateUrl); 405 } 406 else { 407 const error = tempHTMLObject.querySelector("#error-div"); 408 modalBody.replaceChild(error, document.getElementById("placeModalBody")); 409 } 410 } else { 411 let tempHTMLObject = document.createElement('div'); 412 const iconsUrl = document.getElementById("agenda").dataset.iconsurl; 413 414 tempHTMLObject.innerHTML = '<div class="col" id="error-div">' + 415 '<div class="row">' + 416 '<img class="no-results-img" src="' + iconsUrl + 'warning.svg" alt="Illustation d\'un homme à côté d\'un panneau attention">' + 417 '</div>' + 418 '<div class="row text-center mt-2">' + 419 '<p class="fw-bold fs-3 mb-0">Oups !</p>' + 420 '<p>Une erreur est survenue...</p>' + 421 '</div>' + 422 '<div class="row d-flex mt-2">' + 423 '<a class="text-center" href="javascript:window.location.href=window.location.href;">' + 424 '<button type="button" class="btn border custom-component-border custom-input fit-content bg-transparent remove-filters-button">Recharger l\'agenda</button>' + 425 '</a>' + 426 '</div>' + 427 '</div>'; 428 429 const error = tempHTMLObject.querySelector("#error-div"); 430 431 modalBody.replaceChild(error, document.getElementById("placeModalBody")); 432 } 433 434 closePlaceModal = true; 435 closeEventModal = false; 436 437} 438 439/** 440 * Fonction permettant d'obtenir la vue détail d'un événement, à insérer dans une modale 441 * @param id 442 * @returns {Promise<Response> || Promise<string>} 443 */ 444async function fetchPlace(id) { 445 446 const maxAttempts = 3; // Définir le nombre maximal de tentatives 447 let attempts = 0; 448 449 const url = document.getElementById("agenda").dataset.host + "/public/" + document.getElementById("agenda").dataset.entity + "/places/" + id; 450 451 while (attempts < maxAttempts) { 452 try { 453 const response = await fetch(url, { 454 method: 'GET' 455 }); 456 457 if (response.ok) { 458 return response; 459 } else { 460 await new Promise(resolve => setTimeout(resolve, 3000)); // Attendre 3 secondes avant la prochaine tentative 461 } 462 } catch (error) { 463 } 464 465 attempts++; 466 } 467 468 return "HTTP_500"; 469} 470 471/** 472 * Fonction permettant de fermer la modale de détail d'un lieu 473 */ 474function closePlaceDetails(fromHistory = false) { 475 $("#placeModal").modal('hide'); 476 var placeModalBody = document.getElementById('placeModalBody'); 477 placeModalBody.innerHTML = ''; 478 479 if (fromHistory == false) { 480 history.go(-1); 481 } 482 483 484 setCanonicalLink(canonicalLink); 485} 486 487function setCanonicalLink(url) { 488 const head = document.head;
489 490 if (head != null) { 491 const canonicalLinkElement = document.querySelector('link[rel="canonical"]'); 492 493 if (canonicalLinkElement != null) { 494 canonicalLinkElement.href = url; 495 } else { 496 head.insertAdjacentHTML("beforeend", '<link rel="canonical" href="' + url + '">'); 497 } 498 } 499}
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.