PageSourceSearch

https://agenda-static.grandnancy.eu/scripts/S-TYPO3/details-modals.js?v=1

js grandnancy.eu collected 2026-10-01 12:11:19 UTC 19,351 bytes, 499 lines download raw bytes

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.