PageSourceSearch

https://www.mediatheque-lucien-herr.fr/vendors/decalog/event/js/register.js?v=1.1

js mediatheque-lucien-herr.fr collected 2026-09-26 18:57:01 UTC 24,104 bytes, 624 lines download raw bytes

1function updateLegalRepresentativeVisibility(eventAudience) {
2    var $section = $('#legal-representative-section');
3    if ($section.length === 0) {
4        return;
5    }
6
7    var hasAdult = false;
8    var hasChild = false;
9
10    $('select[name*="participants"], input[name*="participants"]').each(function() {
11        var name = $(this).attr('name') || '';
12        if (name.indexOf('type') !== -1) {
13            var value = $(this).val();
14            if (value === 'ADULT') {
15                hasAdult = true;
16            } else if (value === 'CHILD' ) {
17                hasChild = true;
18            }
19        }
20    });
21
22    // Afficher la section représentant légal uniquement si:
23    // - L'audience est CHILD_ONLY
24    // - OU il y a au moins un enfant ET aucun adulte parmi les participants
25    var shouldShow = eventAudience === 'CHILD_ONLY' || (hasChild && !hasAdult);
26
27    if (shouldShow) {
28        $section.find('input').prop('required', false).removeAttr('required');
29        $section.show();
30        $('#legal_representative_firstname, #legal_representative_lastname, #legal_representative_email, #legal_representative_phone_number').prop('required', true);
31    } else {
32        $section.find('input').prop('required', false).removeAttr('required')
33            .each(function() { this.setCustomValidity(''); });
34        $section.hide();
35    }
36}
37
38function updateParticipantFieldsVisibility($row, eventAudience) {
39    var $typeField = $row.find('select[name*="[type]"], input[name*="[type]"]');
40    var typeValue = $typeField.val();
41
42    var isChild = (typeValue === 'CHILD' || eventAudience === 'CHILD_ONLY');
43    var isAdult = (typeValue === 'ADULT' || eventAudience === 'ADULT_ONLY');
44
45    var $emailField = $row.find('.participant-email-field');
46    var $birthdateField = $row.find('.participant-birthdate-field');
47
48    if (isChild) {
49        // Enfant : cacher email, afficher date de naissance
50        $emailField.hide();
51        $emailField.find('input').prop('required', false).removeAttr('required')
52            .each(function() { this.setCustomValidity(''); });
53        $birthdateField.show();
54        $birthdateField.find('input').prop('required', true);
55    } else if (isAdult) {
56        // Adulte : afficher email, cacher date de naissance
57        $emailField.show();
58        $emailField.find('input').prop('required', true);
59        $birthdateField.hide();
60        $birthdateField.find('input').prop('required', false).removeAttr('required')
61            .each(function() { this.setCustomValidity(''); });
62    } else {
63        // Pas encore sélectionné (audience ALL) : afficher email, cacher date de naissance
64        $emailField.show();
65        $emailField.find('input').prop('required', true);
66        $birthdateField.hide();
67        $birthdateField.find('input').prop('required', false).removeAttr('required')
68            .each(function() { this.setCustomValidity(''); });
69    }
70}
71
72function updateAllParticipantsFieldsVisibility(eventAudience) {
73    $('.participants-row').each(function() {
74        updateParticipantFieldsVisibility($(this), eventAudience);
75    });
76}
77
78function updateRegisterButton(modal) {
79    const registerBtn = modal.querySelector(".btn-register-occurrences");
80    if (!registerBtn) return;
81    const anyChecked = modal.querySelectorAll(".occurrence-checkbox:checked").length > 0;
82    registerBtn.disabled = !anyChecked;
83    registerBtn.style.cursor = anyChecked ? "pointer" : "not-allowed";
84    registerBtn.style.pointerEvents = anyChecked ? "auto" : "none";
85}
86
87function setupBonusFiltering(modalDetails, allowWaitingList) {
88    const yesBtn = modalDetails.querySelector('#bonus-yes');
89    const noBtn = modalDetails.querySelector('#bonus-no');
90    const rows = modalDetails.querySelectorAll('.modal-event-row');
91    const checkboxes = modalDetails.querySelectorAll('.occurrence-checkbox');
92
93    const eventSubtitle = modalDetails.querySelector('.event-subtitle');
94    const checkAllContainer = modalDetails.querySelector('.check-all-container');
95
96    rows.forEach(row => row.style.cssText = "display:none !important;");
97
98    if (eventSubtitle) eventSubtitle.style.display = "none";
99    if (checkAllContainer) checkAllContainer.style.display = "none";
100
101    updateRegisterButton(modalDetails);
102
103   const handleChoice = (showBonusOnly) => {
104        modalDetails.dataset.bonusChoice = showBonusOnly ? "yes" : "no";
105
106        if (eventSubtitle) eventSubtitle.style.display = "";
107        if (checkAllContainer) checkAllContainer.style.display = "";
108
109        yesBtn.classList.toggle('bonus-selected', showBonusOnly);
110        noBtn.classList.toggle('bonus-selected', !showBonusOnly);
111
112        let hiddenRows = 0;
113
114        rows.forEach(row => {
115            const hasBonus = row.dataset.hasBonus === '1';
116            const checkbox = row.querySelector('.occurrence-checkbox');
117            const availableNormal = parseInt(row.dataset.availableNormal || "0");
118            const availableBonus = parseInt(row.dataset.availableBonus || "0");
119
120            const shouldShow = showBonusOnly ? hasBonus : true;
121            row.style.display = shouldShow ? "" : "none";
122            if (!shouldShow) {
123                hiddenRows++;
124            }
125
126            const normalDiv = row.querySelector('.normal-places');
127            const bonusDiv = row.querySelector('.bonus-places');
128
129            if (normalDiv) normalDiv.style.display = showBonusOnly ? "none" : "flex";
130            if (bonusDiv) bonusDiv.style.display = showBonusOnly ? "flex" : "none";
131
132            let shouldDisable;
133
134            if (showBonusOnly) {
135                shouldDisable = availableBonus <= 0;
136            } else {
137                shouldDisable = !allowWaitingList && availableNormal <= 0;
138            }
139
140            if (shouldDisable) {
141                row.classList.add('is-disabled');
142                if (checkbox) checkbox.disabled = true;
143            } else {
144                row.classList.remove('is-disabled');
145                if (checkbox) checkbox.disabled = false;
146            }
147
148        });
149
150        if (checkAllContainer) {
151            if (showBonusOnly && hiddenRows > 0) modalDetails.querySelector('.check-all-container label').style.display = 'none';
152            else modalDetails.querySelector('.check-all-container label').style.display = '';
153        }
154
155        if (rows.length > 1) {
156            checkboxes.forEach(cb => cb.checked = false);
157        }
158        const checkAll = checkAllContainer?.querySelector('.check-all-occurrences');
159        if (checkAll) {
160            checkAll.checked = false;
161            checkAll.indeterminate = false;
162        }
163
164        if (rows.length === 1) {
165            const singleRow = rows[0];
166            const singleCheckbox = singleRow.querySelector('.occurrence-checkbox');
167            if (singleCheckbox) {
168                singleCheckbox.checked = singleRow.style.display !== 'none' && !singleCheckbox.disabled;
169            }
170        }
171        updateRegisterButton(modalDetails);
172    }
173    yesBtn.addEventListener('click', () => handleChoice(true));
174    noBtn.addEventListener('click', () => handleChoice(false));
175}
176
177function getModals(eventId, occurrenceId, url, options = {}) {
178    const { autoCheckOccurrence = true, checkForBonusPlaces = false } = options;
179
180    fetch(window.location.origin + url, {
181        method: "POST",
182        headers: { "Content-Type": "application/json" },
183        body: JSON.stringify({ eventId, occurrenceId, checkForBonusPlaces }),
184    })
185    .then((response) => response.json())
186    .then(({event, modal}) => {
187        // Supprime les anciennes modals
188        const oldEventDetails = document.getElementById("event-details-" + occurrenceId);
189        const oldRegistration = document.getElementById("registration-modal-" + occurrenceId);
190        if (oldEventDetails) oldEventDetails.remove();
191        if (oldRegistration) oldRegistration.remove();
192
193        // Injecte les modals
194        document.body.insertAdjacentHTML("beforeend", modal);
195        const modalDetails = document.getElementById("event-details-" + occurrenceId);
196        if (modalDetails) {
197            modalDetails.style.overflowX = "hidden";
198            modalDetails.style.overflowY = "hidden";
199            updateOccurrencesScroll(occurrenceId);
200        }
201
202        setupEventDetailsModal(event, occurrenceId, autoCheckOccurrence);
203
204        if (event.hasBonusPlaces) {
205            setupBonusFiltering(modalDetails, event.allowWaitingList);
206        }
207
208        // Affiche la modal détails
209        $("#event-details-" + occurrenceId).modal("show");
210
211        const eventAudience = event.targetAudience || '';
212        const modal_form = document.getElementById("registration-modal-" + occurrenceId);
213        const add_participant_button = modal_form.querySelector("#add-participant");
214        add_participant_button.addEventListener('click', () => addParticipant(event, occurrenceId, modal_form, eventAudience));
215
216        const typeFields = modal_form.querySelectorAll('#type, select[name*="type"], input[name*="type"]');
217        typeFields.forEach(field => {
218            field.addEventListener('change', function() {
219                updateLegalRepresentativeVisibility(eventAudience);
220                const $row = $(this).closest('.participants-row');
221                updateParticipantFieldsVisibility($row, eventAudience);
222            });
223        });
224
225        updateLegalRepresentativeVisibility(eventAudience);
226        updateAllParticipantsFieldsVisibility(eventAudience);
227
228    })
229    .catch((err) => console.error(err));
230}
231
232function setupEventDetailsModal(event, occurrenceId, preCheckedId = false) {
233    const modal = document.getElementById("event-details-" + occurrenceId);
234    if (!modal) return;
235
236    const registerBtn = modal.querySelector(".btn-register-occurrences");
237    const checkboxes = modal.querySelectorAll(".occurrence-checkbox");
238    const checkAll = modal.querySelector(".check-all-occurrences");
239
240    if (preCheckedId) {
241        const checkboxToCheck = modal.querySelector(`.occurrence-checkbox[value="${occurrenceId}"]`);
242        if (checkboxToCheck && !checkboxToCheck.disabled) {
243            checkboxToCheck.checked = true;
244        }
245    }
246
247    updateRegisterButton(modal);
248    // Checkbox "check all"
249    if (checkAll) {
250        checkAll.addEventListener("change", function () {
251            checkboxes.forEach((cb) => {
252                if (!cb.disabled) cb.checked = checkAll.checked;
253            });
254            updateRegisterButton(modal);
255        });
256    }
257
258    // Checkbox individuelles
259    checkboxes.forEach((cb) =>
260        cb.addEventListener("change", () => updateRegisterButton(modal)),
261    );
262
263    // Bouton "S'inscrire"
264    registerBtn.addEventListener("click", function () {
265        const selected = modal.querySelectorAll(".occurrence-checkbox:checked");
266        const ids = Array.from(selected).map((c) => c.value);
267
268        const occurrences = getOccurrencesFromEvent(event, ids);
269        const bonusChoice = modal.dataset.bonusChoice;
270        const isBonus = bonusChoice === "yes" ? 1 : 0;
271
272        // Affiche le modal d'inscription
273        $("#event-details-" + occurrenceId).modal("hide");
274        $("#registration-modal-" + occurrenceId).modal("show");
275
276        // Une fois le modal d'inscription affiché, remplir l'input hidden et ajouter 1 participant
277        $("#registration-modal-" + occurrenceId)
278            .off("shown.bs.modal")
279            .on("shown.bs.modal", function () {
280
281                // clear the modal avoiding glitch to submit multiple participant when it's not allowed
282                document.querySelectorAll(".participants-row:not(:first-child)").forEach(p => p.remove());
283
284                setupRegistrationSubmit(occurrenceId, ids, isBonus);
285                updateModalScroll(occurrenceId);
286
287                const modal_form = document.getElementById("registration-modal-" + occurrenceId);
288                handleParticipantSuppression(modal_form, event, occurrenceId, occurrences);
289
290                // Ajouter l'action de suppression sur le bouton du premier participant
291                const participant = document.querySelector(".participants-row");
292                const suppress_button = participant.querySelector(".remove-participant");
293                suppress_button.addEventListener('click', () => {
294                    participant.remove();
295                    handleParticipantSuppression(modal_form, event, occurrenceId, occurrences);
296                });
297            });
298
299        // Quand le modal d'inscription se ferme, réaffiche le modal détails
300        $("#registration-modal-" + occurrenceId)
301            .off("hidden.bs.modal")
302            .on("hidden.bs.modal", function () {
303                $("#event-details-" + occurrenceId).modal("show");
304            });
305    });
306}
307
308// Scroll dynamique de la modal inscription
309function updateModalScroll(occurrenceId) {
310    const modalBody = document.querySelector(
311        "#registration-modal-" + occurrenceId + " .modal-body",
312    );
313
314    if (!modalBody) return;
315    const modalContent = modalBody.closest(".modal-content");
316    const headerHeight =
317        modalContent.querySelector(".modal-header").offsetHeight;
318    const footerHeight =
319        modalContent.querySelector(".modal-footer").offsetHeight;
320    modalBody.style.maxHeight = `calc(90vh - ${headerHeight + footerHeight + 40}px)`;
321    modalBody.style.overflowY = "auto";
322}
323
324function updateOccurrencesScroll(occurrenceId) {
325    const occurrencesList = document.querySelector(`#event-details-${occurrenceId} .occurences-list`);
326    const modalContent = occurrencesList?.closest(".modal-content");
327    if (!occurrencesList || !modalContent) return;
328
329    const headerHeight = modalContent.querySelector(".modal-header")?.offsetHeight || 0;
330    const footerHeight = modalContent.querySelector(".modal-footer")?.offsetHeight || 0;
331    const extraSpacing = 20;
332
333    occurrencesList.style.maxHeight = `calc(45vh - ${headerHeight + footerHeight + extraSpacing}px)`;
334    occurrencesList.style.overflowY = "auto";
335    occurrencesList.style.overflowX = "hidden";
336}
337
338function handleParticipantSuppression(modal, event, occurrenceId, occurrences) {
339    updateAddParticipantButtonState(event, occurrenceId, occurrences);
340    const participants = modal.querySelectorAll(".participants-row");
341    if (participants.length === 1) {
342        const suppress_button = participants[0].querySelector(
343            ".remove-participant",
344        );
345        suppress_button.style.display = "none";
346    } else if (participants.length > 1) {
347        participants.forEach((participant) => {
348            const suppress_button = participant.querySelector(
349                ".remove-participant",
350            );
351            suppress_button.style.display = "block";
352        })
353    }
354}
355
356function addParticipant(event, occurrenceId, modal, eventAudience) {
357    const container = modal.querySelector("#participants-container");
358    const rows = container.querySelectorAll(".participants-row");
359
360    if (rows.length === 0) return;
361
362    const lastRow = rows[rows.length - 1];
363    const copy = lastRow.cloneNode(true);
364
365    copy.querySelectorAll("input, select, textarea").forEach(field => {
366
367        if (field.type === "checkbox" || field.type === "radio") {
368            field.checked = false;
369        } else {
370            if (!field.name.endsWith("[type]")) {
371                field.value = "";
372            }
373        }
374
375        if (field.id) {
376            field.removeAttribute("id");
377        }
378    });
379
380    container.appendChild(copy);
381    reindexParticipants(modal);
382    const suppressButton = copy.querySelector(".remove-participant");
383    suppressButton.style.display = "block";
384    // Extraire l'eventId depuis l'id du modal
385    var eventId = event.id;
386
387    const modal_occurrences = document.getElementById('event-details-' + occurrenceId);
388    const selected = modal_occurrences.querySelectorAll(".occurrence-checkbox:checked");
389    const ids = Array.from(selected).map((c) => c.value);
390    const occurrences = getOccurrencesFromEvent(event, ids);
391    suppressButton.addEventListener("click", () => {
392        copy.remove();
393        reindexParticipants(modal);
394
395        handleParticipantSuppression(modal, event, occurrenceId, occurrences);
396
397        updateModalScroll(eventId);
398        // Mettre à jour la visibilité de la section représentant légal
399        updateLegalRepresentativeVisibility(eventAudience);
400    });
401
402    // Ajouter l'écouteur de changement de type sur le nouveau participant
403    copy.querySelectorAll('select[name*="[type]"], input[name*="[type]"]').forEach(field => {
404        field.addEventListener('change', function() {
405            updateLegalRepresentativeVisibility(eventAudience);
406            // Mettre à jour les champs du participant concerné
407            var $row = $(this).closest('.participants-row');
408            updateParticipantFieldsVisibility($row, eventAudience);
409        });
410    });
411
412    handleParticipantSuppression(modal, event, occurrenceId, occurrences);
413    updateModalScroll(eventId);
414    // Mettre à jour la visibilité de la section représentant légal
415    updateLegalRepresentativeVisibility(eventAudience);
416    // Initialiser la visibilité des champs pour le nouveau participant
417    updateParticipantFieldsVisibility($(copy), eventAudience);
418}
419
420function reindexParticipants(modal) {
421    const rows = modal.querySelectorAll(".participants-row");
422    rows.forEach((row, index) => {
423        row.querySelectorAll("[name]").forEach(field => {
424            field.name = field.name.replace(
425                /participants\[\d+]/,
426                `participants[${index}]`
427            );
428        });
429    });
430}
431
432function setupRegistrationSubmit(occurrenceId, ids, isBonus = 0) {
433    const form = document.getElementById(`registration-form-${occurrenceId}`);
434    const modal = document.getElementById(`registration-modal-${occurrenceId}`);
435
436    // A cause de la manière dont sont géré les évènements et le fait qu'on soit en vanilla, on doit faire ça
437    // pour éviter de créer plusieurs inscriptions à la fois
438    const oldBtn = modal.querySelector("#submit-registration");
439    const newBtn = oldBtn.cloneNode(true);
440    oldBtn.parentNode.replaceChild(newBtn, oldBtn);
441
442    const bonusField = modal.querySelector('#is-bonus-registration');
443    if (bonusField) bonusField.value = isBonus;
444
445    newBtn.addEventListener("click", function handler() {
446        if (!form.checkValidity()) {
447            form.reportValidity();
448            return;
449        }
450
451        newBtn.removeEventListener("click", handler);
452
453        const formData = new FormData(form);
454        ids.forEach(id => formData.append('occurrenceIds[]', id));
455
456        formData.set('isBonusRegistration', isBonus);
457
458        fetch('/registration/create', {
459            method: 'POST',
460            body: formData,
461            headers: { 'X-Requested-With': 'XMLHttpRequest' }
462        })
463        .then(response => response.json())
464        .then(json => {
465            window.location.reload();
466        })
467        .catch(json => {
468            window.location.reload();
469        });
470    });
471}
472
473function displayResponse(registrationResult) {
474    let icon = 'fa fa-check-square';
475    let msg = registrationResult.message;
476    let type = 'success';
477
478    if (! registrationResult.success) {
479        icon = 'fa fa-exclamation-triangle';
480        type = 'danger';
481    }
482
483    $.notify(
484        {
485            icon: icon,
486            message: msg,
487        },
488        {
489            type: type,
490            newest_on_top: true,
491            delay: 3000,
492        }
493    );
494}
495
496function updateAddParticipantButtonState(event, occurrenceId, occurrences) {
497    const modal = document.getElementById('registration-modal-' + occurrenceId);
498    const addParticipantButton = modal.querySelector('#add-participant');
499    const participantsNumber = modal.querySelectorAll('.participants-row').length;
500    const eventDetailModal = document.getElementById("event-details-" + occurrenceId);
501    const bonusChoice = eventDetailModal.dataset.bonusChoice;
502    const isBonus = bonusChoice === "yes" ? 1 : 0;
503    let occurrencesWithLessAvailablePlaces = occurrences.reduce((prev, curr) => prev.availableAllPlaces < curr.availableAllPlaces ? prev : curr).availableAllPlaces;
504
505    if (isBonus) {
506        const lessAvailableBonusPlaces = occurrences.reduce((prev, curr) => prev.availableBonusPlaces < curr.availableBonusPlaces ? prev : curr);
507        occurrencesWithLessAvailablePlaces = lessAvailableBonusPlaces.availableBonusPlaces;
508    }
509
510    const maxSimultaneousParticipantsAllowed = getMaxSimultaneousParticipantsForSelectedOccurrences(occurrences);
511
512    let maxAllowed;
513
514    if (event.allowWaitingList && !isBonus) {
515        maxAllowed = maxSimultaneousParticipantsAllowed;
516    } else {
517        maxAllowed = Math.min(occurrencesWithLessAvailablePlaces, maxSimultaneousParticipantsAllowed);
518    }
519
520    const addParticipantAllowed = participantsNumber < maxAllowed;
521
522    const errorDiv = modal.querySelector("#participant-error");
523    const maxAllowedParticipantContent = errorDiv.textContent;
524
525    if (addParticipantAllowed) {
526        addParticipantButton.style.display = "block";
527        errorDiv.style.display = "none";
528    } else {
529        addParticipantButton.style.display = "none";
530        errorDiv.style.display = "block";
531        errorDiv.textContent = maxAllowedParticipantContent.replace('{X}', maxAllowed);
532    }
533}
534
535function getOccurrencesFromEvent(event, ids = []) {
536    if (!Array.isArray(ids) || ids.length === 0) {
537        return event.occurrences;
538    } else {
539        let idsAsInt = [];
540        ids.forEach((id) => {
541            idsAsInt.push(parseInt(id));
542        });
543        let result = [];
544        event.occurrences.forEach((occurrence) => {
545            if (idsAsInt.includes(occurrence.id)) {
546                result.push(occurrence);
547            }
548        });
549        return result;
550    }
551}
552
553function getMaxSimultaneousParticipantsForSelectedOccurrences(occurrences) {
554    let lower = occurrences[0].nbSimultaneousParticipants;
555    occurrences.forEach((occurrence) => {
556        if (occurrence.nbSimultaneousParticipants < lower) {
557            lower = occurrence.nbSimultaneousParticipants;
558        }
559    });
560    return lower;
561}
562
563function updateRegistrationButtonsStatus() {
564    let eventIds = [];
565    document.querySelectorAll(".occurrences-block").forEach((div) => {
566        let eventId = div.dataset.apiid;
567        if (!window.updatedEvents.includes(eventId)) {
568            eventIds.push(eventId);
569            window.updatedEvents.push(eventId);
570        }
571    });
572
573    if (eventIds.length === 0) return;
574
575    fetch('/registration/buttons', {
576        method: "POST",
577        headers: { "Content-Type": "application/json" },
578        body: JSON.stringify({
579            'referer': window.location.href,
580            'eventIds': eventIds
581        })
582    })
583        .then(response => response.json())
584        .then(json => {
585            Object.entries(json.data).forEach(([id, html]) => {
586                document.querySelectorAll(`#occ-${id}`).forEach(c => {
587                    c.querySelector(".event-register").innerHTML = html;
588                });
589            })
590        }).catch(err => console.error(err));
591}
592
593document.addEventListener('DOMContentLoaded', function () {
594    window.updatedEvents = [];
595    updateRegistrationButtonsStatus();
596    window.addEventListener('scroll', updateRegistrationButtonsStatus);
597
598    document.body.addEventListener('click', function (e) {
599
600        const btn = e.target.closest('[id^="open-registration-"]');
601        if (!btn) return;
602
603        e.preventDefault();
604        e.stopImmediatePropagation();
605
606        const eventId = btn.dataset.eventId;
607        const url = btn.dataset.url;
608        const occurrenceId = btn.dataset.occurrenceId;
609
610        if (!eventId || !url || !occurrenceId) {
611            console.error("Données manquantes sur le bouton", btn);
612            return;
613        }
614
615        const existingModal = document.getElementById("event-details-" + occurrenceId);
616
617        if (existingModal) {
618            $("#event-details-" + occurrenceId).modal("show");
619            return;
620        }
621
622        getModals(eventId, occurrenceId, url);
623    });
624});

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.