PageSourceSearch

https://fhclm.ca/wp-content/plugins/AnWp_FormulairesDeDons/js/FormWizard/interactionManager.js?ver=1.0.0

js fhclm.ca collected 2026-10-08 03:28:28 UTC 64,364 bytes, 1,491 lines download raw bytes

1// Populate hidden fields with URL parameters
2    // const urlParams = new URLSearchParams(window.location.search);
3    // $('#utm-source').val(urlParams.get('utm_source') || '');
4    // $('#utm-medium').val(urlParams.get('utm_medium') || '');
5    // $('#utm-campaign').val(urlParams.get('utm_campaign') || '');
6
7(function($, FW) {
8    'use strict';
9
10    window.DonationFormData = window.DonationFormData || {};
11    let nonceManagerInitialized = false;
12
13    FW.InteractionManager = (function() {
14
15        function initializeNonceManager() {
16            const config = window.DonationFormData || {};
17            const ajaxUrl = config.ajaxUrl;
18            const formType = config.formType;
19            const $form = $('#donation-form');
20            const $nonceField = $('#dfp-nonce');
21
22            if (!$form.length || !$nonceField.length || !ajaxUrl || !formType) {
23                return;
24            }
25
26            if (nonceManagerInitialized) {
27                return;
28            }
29            nonceManagerInitialized = true;
30
31            const refreshIntervalSeconds = Number(config.nonceRefreshInterval);
32            const refreshIntervalMs = !Number.isNaN(refreshIntervalSeconds) && refreshIntervalSeconds > 0
33                ? refreshIntervalSeconds * 1000
34                : 180000;
35
36            let inFlightRequest = null;
37            let refreshTimer = null;
38            let pendingSubmit = false;
39            let allowSubmit = false;
40
41            function hasNonce() {
42                return Boolean($nonceField.val());
43            }
44
45            function resumePendingSubmit() {
46                if (!pendingSubmit || !hasNonce()) {
47                    return;
48                }
49                pendingSubmit = false;
50                allowSubmit = true;
51                $form.trigger('submit');
52            }
53
54            function scheduleRefresh(successful) {
55                clearTimeout(refreshTimer);
56                if (refreshIntervalMs <= 0) {
57                    return;
58                }
59                const retryDelay = Math.min(60000, refreshIntervalMs);
60                const delay = successful ? refreshIntervalMs : retryDelay;
61                refreshTimer = setTimeout(function () {
62                    fetchNonce('interval');
63                }, delay);
64            }
65
66            function fetchNonce(reason) {
67                if (inFlightRequest) {
68                    return inFlightRequest;
69                }
70
71                inFlightRequest = $.ajax({
72                    url: ajaxUrl,
73                    method: 'GET',
74                    dataType: 'json',
75                    cache: false,
76                    data: {
77                        action: 'dfp_get_nonce',
78                        form_type: formType
79                    }
80                }).done(function (response) {
81                    if (response && response.success && response.data && response.data.nonce) {
82                        $nonceField.val(response.data.nonce);
83                        scheduleRefresh(true);
84                        resumePendingSubmit();
85                    } else {
86                        console.error('Réponse inattendue lors du chargement du nonce.', response);
87                        scheduleRefresh(false);
88                        if (reason === 'submit') {
89                            pendingSubmit = false;
90                            window.alert('Nous n\'avons pas pu préparer le formulaire. Veuillez réessayer.');
91                        }
92                    }
93                }).fail(function (jqXHR, textStatus) {
94                    console.error('Impossible de récupérer un nonce frais :', textStatus);
95                    scheduleRefresh(false);
96                    if (reason === 'submit') {
97                        pendingSubmit = false;
98                        window.alert('Nous n\'avons pas pu préparer le formulaire. Veuillez vérifier votre connexion et réessayer.');
99                    }
100                }).always(function () {
101                    inFlightRequest = null;
102                });
103
104                return inFlightRequest;
105            }
106
107            $form.on('submit.dfpNonce', function (event) {
108                if (allowSubmit) {
109                    allowSubmit = false;
110                    return;
111                }
112
113                if (hasNonce()) {
114                    return;
115                }
116
117                event.preventDefault();
118                pendingSubmit = true;
119                fetchNonce('submit');
120            });
121
122            function onVisibilityChange() {
123                if (document.visibilityState === 'visible') {
124                    fetchNonce('visibility');
125                }
126            }
127
128            function onWindowFocus() {
129                fetchNonce('focus');
130            }
131
132            document.addEventListener('visibilitychange', onVisibilityChange);
133            $(window).on('focus.dfpNonce', onWindowFocus);
134            $(window).on('beforeunload.dfpNonce', function () {
135                clearTimeout(refreshTimer);
136                if (inFlightRequest && typeof inFlightRequest.abort === 'function') {
137                    inFlightRequest.abort();
138                }
139            });
140
141            fetchNonce('initial');
142        }
143
144        function init() {
145            initializeNonceManager();
146
147            const FormType = (window.DonationFormData && window.DonationFormData.formType) ? window.DonationFormData.formType : '';
148            if (FormType == "general") {
149                handleRecurrenceButtons();
150                handleOneTimeAmountButtons();
151                handleMonthlyButtons();
152                handleConvertMonthlyButtons();
153                handleTributeTypeInteractions();
154                handleEcardSection();
155                handleEcardImageSelection();
156                handleGeneralFeeCovering();
157                handleDonationTypeInteractions();
158            }
159            if (FormType == "monthly") {
160                handleRecurrenceButtons();
161                handleOneTimeAmountButtons();
162                handleMonthlyButtons();
163                handleConvertMonthlyButtons();
164                handleTributeTypeInteractions();
165                handleEcardSection();
166                handleEcardImageSelection();
167                handleGeneralFeeCovering();
168                handleDonationTypeInteractions();
169            }
170            if (FormType == "one-time") {
171                handleRecurrenceButtons();
172                handleOneTimeAmountButtons();
173                handleMonthlyButtons();
174                handleConvertMonthlyButtons();
175                handleTributeTypeInteractions();
176                handleEcardSection();
177                handleEcardImageSelection();
178                handleGeneralFeeCovering();
179                handleDonationTypeInteractions();
180            }
181            if (FormType == "golf") {
182                handleGolfSelections();
183                handleGolfFeeCovering();
184                handleDonationTypeInteractions();
185            }
186            if (FormType == "loto") {
187                handleProductSelections();
188                handleLotoFeeCovering();
189                handleDonationTypeInteractions();
190            }
191            if (FormType == "paulee") {
192                handlePauleeSelections();
193                handlePauleeFeeCovering();
194                handleDonationTypeInteractions();
195            }
196            if (FormType == "invoice") {
197                handleInvoiceSelections();
198                handleInvoiceFeeCovering();
199                handleDonationTypeInteractions();
200            }
201            if (FormType == "admin") {
202                
203            }
204            if (FormType == "encrypted") {
205                handleEncOneTimeAmountButtons();
206                handleGeneralFeeCovering();
207                handleEncChangeInfo();
208            }
209            handleCommentSection();
210        }
211
212        function syncAmount(amount, $targetElement, $inputField = null) {
213            const parsedAmount = parseFloat(amount);
214            if (isNaN(parsedAmount) || parsedAmount < 0) {
215                $targetElement.text('0');
216                if ($inputField) {
217                    $inputField.addClass('input-error');
218                }
219            } else {
220                $targetElement.text(parseFloat(parsedAmount.toFixed(2)));
221                if ($inputField) {
222                    $inputField.removeClass('input-error');
223                }
224            }
225        }
226
227        function calculateSuggestedMonthly(oneTimeAmount, tiers = null) {
228            const defaultDonationTiers = [
229                { min: 0, max: 100, suggestedMonthly: 10 },
230                { min: 100, max: 200, suggestedMonthly: 20 },
231                { min: 200, max: Infinity, suggestedMonthly: 50 }
232            ];
233            const donationTiers = (tiers && Array.isArray(tiers) && tiers.length > 0) ? tiers : defaultDonationTiers;
234
235            if (typeof oneTimeAmount !== 'number' || isNaN(oneTimeAmount) || oneTimeAmount <= 0) {
236                console.warn(`Montant de don ponctuel invalide: ${oneTimeAmount}`);
237                return 0;
238            }
239
240            for (let tier of donationTiers) {
241                if (oneTimeAmount >= tier.min && oneTimeAmount < tier.max) {
242                    if (typeof tier.suggestedMonthly !== 'number' || isNaN(tier.suggestedMonthly)) {
243                        console.error(`Valeur suggérée invalide dans le tier: ${JSON.stringify(tier)}`);
244                        continue;
245                    }
246                    return parseFloat(tier.suggestedMonthly.toFixed(2));
247                }
248            }
249            console.warn(`Aucun tier correspondant pour le montant: ${oneTimeAmount}. Suggestion par défaut: 10.`);
250            return 10;
251        }
252
253        function calculateProductTotalPrice(itemPrice, itemNumber) {
254            var price = parseFloat(itemPrice);
255            var number = parseFloat(itemNumber);
256            if (isNaN(price) || price <= 0) {
257                console.warn('Invalid product price:', price);
258                return 0;
259            }
260            if (isNaN(number) || number < 0) { // Allow zero tickets.
261                console.warn('Invalid product number:', number);
262                return 0;
263            }
264            return parseFloat((price * number).toFixed(2));
265        }
266
267        function sprintf(template /*, ...args */) {
268            var args = Array.prototype.slice.call(arguments, 1);
269            return template.replace(/%(\d+)\$s/g, function(match, index) {
270                return typeof args[index - 1] !== 'undefined' ? args[index - 1] : match;
271            });
272        }
273
274        function handleRecurrenceButtons() {
275            const $recurrenceButtons = $('.recurrence-btn');
276            const $recurrenceInput = $('#recurrence');
277            const $monthlyContainer = $('#monthly-amount-container');
278            const $oneTimeContainer = $('#one-time-amount-container');
279            const $oneTimeDescription = $('.recurrence-description--one-time');
280            const $monthlyDescription = $('.recurrence-description--monthly');
281            const $confirmationMessage = $('#recurrence-confirmation');
282            const $oneTimeDonationSummary = $('.one-time-donation-summary');
283            const $monthlyDonationSummary = $('.monthly-donation-summary');
284
285            function updateUI(recurrenceType) {
286                if (recurrenceType === 'one-time') {
287                    $monthlyContainer.hide();
288                    $oneTimeContainer.show();
289                    $monthlyDescription.hide();
290                    $oneTimeDescription.show();
291                    $oneTimeDonationSummary.show();
292                    $monthlyDonationSummary.hide();
293                    showConfirmation('Vous avez sélectionné un don ponctuel.');
294                    FW.StepManager.updateNavigation('step-giftChoice');
295                } else if (recurrenceType === 'monthly') {
296                    $oneTimeContainer.hide();
297                    $monthlyContainer.show();
298                    $oneTimeDescription.hide();
299                    $monthlyDescription.show();
300                    $oneTimeDonationSummary.hide();
301                    $monthlyDonationSummary.show();
302                    showConfirmation('Vous avez sélectionné un don mensuel.');
303                    FW.StepManager.updateNavigation('step-giftChoice');
304                } else {
305                    console.warn(`Type de don non géré: ${recurrenceType}`);
306                    showConfirmation('Type de don non reconnu.');
307                }
308            }
309
310            function setActiveButton($activeButton) {
311                $recurrenceButtons.removeClass('recurrence-btn--active').attr('aria-pressed', 'false');
312                $activeButton.addClass('recurrence-btn--active').attr('aria-pressed', 'true');
313            }
314
315            function showConfirmation(message) {
316                $confirmationMessage.text(message).fadeIn().delay(3000).fadeOut();
317            }
318
319            $recurrenceButtons.each(function () {
320                const $button = $(this);
321                const isActive = $button.hasClass('recurrence-btn--active');
322                $button.attr('aria-pressed', isActive ? 'true' : 'false');
323            });
324
325            $recurrenceButtons.on('click', function () {
326                const $button = $(this);
327                const recurrenceType = $button.data('recurrence');
328
329                if (!recurrenceType) {
330                    console.error('Type de don non défini pour ce bouton.');
331                    showConfirmation('Erreur: Type de don non défini.');
332                    return;
333                }
334
335                $recurrenceInput.val(recurrenceType);
336                setActiveButton($button);
337                updateUI(recurrenceType);
338            });
339
340            $recurrenceButtons.on('keypress', function (e) {
341                if (e.key === 'Enter' || e.key === ' ') {
342                    e.preventDefault();
343                    $(this).click();
344                }
345            });
346
347            const $defaultActiveButton = $recurrenceButtons.filter('.recurrence-btn--active').first();
348            if ($defaultActiveButton.length) {
349                updateUI($defaultActiveButton.data('recurrence'));
350            } else {
351                const defaultRecurrence = 'monthly';
352                $recurrenceButtons.filter(`[data-recurrence="${defaultRecurrence}"]`).addClass('recurrence-btn--active').attr('aria-pressed', 'true');
353                $recurrenceInput.val(defaultRecurrence);
354                updateUI(defaultRecurrence);
355            }
356        }
357
358        function handleOneTimeAmountButtons() {
359            const $oneTimeButtons = $('.amount-option--one-time');
360            const $oneTimeInput = $('#one-time-amount');
361            const $convertMonthlySuggestedAmount = $('.convert-monthly-suggested-amount');
362            const $confirmMonthly = $('#confirm-monthly');
363
364            if (!$oneTimeButtons.length || !$oneTimeInput.length || !$convertMonthlySuggestedAmount.length || !$confirmMonthly.length) {
365                console.warn('Eléments requis pour les boutons de don ponctuel manquants.');
366                return;
367            }
368
369            function updateActiveState($activeButton) {
370                $oneTimeButtons.attr('aria-pressed', 'false').removeClass('amount-option--active');
371                $activeButton.attr('aria-pressed', 'true').addClass('amount-option--active');
372            }
373
374            function validateAmount(amount) {
375                const parsedAmount = parseFloat(amount);
376                return !isNaN(parsedAmount) && parsedAmount > 0;
377            }
378
379            function debounce(func, delay) {
380                let debounceTimer;
381                return function () {
382                    const context = this;
383                    const args = arguments;
384                    clearTimeout(debounceTimer);
385                    debounceTimer = setTimeout(() => func.apply(context, args), delay);
386                };
387            }
388
389            $oneTimeButtons.on('click', function () {
390                const $button = $(this);
391                const oneTimeValue = $button.data('amount');
392
393                if (!validateAmount(oneTimeValue)) {
394                    alert('Veuillez sélectionner un montant de don valide.');
395                    return;
396                }
397
398                const suggestedMonthlyAmount = calculateSuggestedMonthly(Number(oneTimeValue));
399                $oneTimeInput.val(oneTimeValue).removeClass('input-error');
400                syncAmount(oneTimeValue, $('.sync-one-time-amount'), $button);
401                $convertMonthlySuggestedAmount.text(suggestedMonthlyAmount);
402                $confirmMonthly.attr('data-amount', suggestedMonthlyAmount);
403                updateActiveState($button);
404            });
405
406            $oneTimeInput.on('input', debounce(function () {
407                const inputVal = $(this).val();
408
409                if (!validateAmount(inputVal)) {
410                    syncAmount(0, $('.sync-one-time-amount'), $(this));
411                    $convertMonthlySuggestedAmount.text('0');
412                    $confirmMonthly.attr('data-amount', '0');
413                    $oneTimeButtons.removeClass('amount-option--active');
414                    $(this).addClass('input-error');
415                    return;
416                }
417
418                const suggestedMonthlyAmount = calculateSuggestedMonthly(Number(inputVal));
419                syncAmount(inputVal, $('.sync-one-time-amount'), $(this));
420                $convertMonthlySuggestedAmount.text(suggestedMonthlyAmount);
421                $confirmMonthly.attr('data-amount', suggestedMonthlyAmount);
422                $oneTimeButtons.removeClass('amount-option--active');
423                $(this).removeClass('input-error');
424            }, 300));
425
426            $oneTimeInput.on('blur', function () {
427                const inputVal = $(this).val();
428                if (!validateAmount(inputVal)) {
429                    $(this).val('');
430                    syncAmount(0, $('.sync-one-time-amount'), $(this));
431                    $convertMonthlySuggestedAmount.text('0');
432                    $confirmMonthly.attr('data-amount', '0');
433                    $oneTimeButtons.removeClass('amount-option--active');
434                }
435            });
436
437            $oneTimeButtons.on('keypress', function (e) {
438                if (e.key === 'Enter' || e.key === ' ') {
439                    e.preventDefault();
440                    $(this).click();
441                }
442            });
443        }
444
445        function handleEncOneTimeAmountButtons() {
446            const $oneTimeInput = $('#one-time-amount');
447
448            function validateAmount(amount) {
449                const parsedAmount = parseFloat(amount);
450                return !isNaN(parsedAmount) && parsedAmount > 0;
451            }
452
453            function debounce(func, delay) {
454                let debounceTimer;
455                return function () {
456                    const context = this;
457                    const args = arguments;
458                    clearTimeout(debounceTimer);
459                    debounceTimer = setTimeout(() =>
459 func.apply(context, args), delay);
460                };
461            }
462
463            $oneTimeInput.on('input', debounce(function () {
464                const inputVal = $(this).val();
465
466                if (!validateAmount(inputVal)) {
467                    syncAmount(0, $('.sync-one-time-amount'), $(this));
468                    $(this).addClass('input-error');
469                    return;
470                }
471
472                syncAmount(inputVal, $('.sync-one-time-amount'), $(this));
473
474            }, 300));
475
476            $oneTimeInput.on('blur', function () {
477                const inputVal = $(this).val();
478                if (!validateAmount(inputVal)) {
479                    $(this).val('');
480                    syncAmount(0, $('.sync-one-time-amount'), $(this));
481                }
482            });
483        }
484
485        function handleEncChangeInfo() {
486            const $changeEmail = $('#change-email');
487            const $changeAddress = $('#change-address');
488            const $newEmail = $('.new-email');
489            const $newAddress = $('.new-address');
490
491            const $donorEmail = $('#donor-email');
492            const $donorAddress = $('#donor-address');
493            const $donorAddressCity = $('#donor-address-city');
494            const $donorAddressZip = $('#donor-address-zip');
495            const $donorAddressState = $('#donor_address_state');
496
497            $changeEmail.on('change', function () {
498                if ($changeEmail.is(':checked')) {
499                    $newEmail.show();
500                    $donorEmail.attr('required', 'True');
501                } else {
502                    $newEmail.hide();
503                    $donorEmail.removeAttr('required');
504                }
505            });
506
507            $changeAddress.on('change', function () {
508                if ($changeAddress.is(':checked')) {
509                    $newAddress.show();
510                    $donorAddress.attr('required', 'True');
511                    $donorAddressCity.attr('required', 'True');
512                    $donorAddressZip.attr('required', 'True');
513                    $donorAddressState.attr('required', 'True');
514                } else {
515                    $newAddress.hide();
516                    $donorAddress.removeAttr('required');
517                    $donorAddressCity.removeAttr('required');
518                    $donorAddressZip.removeAttr('required');
519                    $donorAddressState.removeAttr('required');
520                }
521            });
522
523        }
524
525        function handleMonthlyButtons() {
526            const $monthlyButtons = $('.amount-option--monthly');
527            const $monthlyInput = $('#monthly-amount');
528            const $syncMonthlyAmount = $('.sync-monthly-amount');
529            const $confirmMonthly = $('#confirm-monthly');
530
531            function setActiveButton($activeButton) {
532                $monthlyButtons.removeClass('amount-option--active').attr('aria-pressed', 'false');
533                $activeButton.addClass('amount-option--active').attr('aria-pressed', 'true');
534            }
535
536            function validateAmount(amount) {
537                const parsedAmount = parseFloat(amount);
538                return !isNaN(parsedAmount) && parsedAmount > 0 && parsedAmount <= 10000;
539            }
540
541            function onMonthlyButtonClick() {
542                const $button = $(this);
543                const monthlyAmount = $button.data('amount');
544
545                if (!validateAmount(monthlyAmount)) {
546                    alert('Veuillez sélectionner un montant de don valide.');
547                    return;
548                }
549
550                $monthlyInput.val(monthlyAmount);
551                syncAmount(monthlyAmount, $syncMonthlyAmount, $(this));
552                $confirmMonthly.attr('data-amount', monthlyAmount);
553                setActiveButton($button);
554            }
555
556            function onMonthlyInputChange() {
557                const inputVal = $(this).val();
558                if (!validateAmount(inputVal)) {
559                    syncAmount('0', $syncMonthlyAmount, $(this));
560                    $confirmMonthly.attr('data-amount', '0');
561                    $monthlyButtons.removeClass('amount-option--active');
562                    return;
563                }
564                syncAmount(inputVal, $syncMonthlyAmount, $(this));
565                $confirmMonthly.attr('data-amount', inputVal);
566                $monthlyButtons.removeClass('amount-option--active');
567            }
568
569            function onMonthlyInputBlur() {
570                const inputVal = $(this).val();
571                if (!validateAmount(inputVal)) {
572                    $(this).val('');
573                    syncAmount('0', $syncMonthlyAmount, $(this));
574                    $confirmMonthly.attr('data-amount', '0');
575                    $monthlyButtons.removeClass('amount-option--active');
576                }
577            }
578
579            function onMonthlyButtonKeyPress(e) {
580                if (e.key === 'Enter' || e.key === ' ') {
581                    e.preventDefault();
582                    $(this).click();
583                }
584            }
585
586            $monthlyButtons.each(function () {
587                const $button = $(this);
588                $button.attr('aria-pressed', $button.hasClass('amount-option--active') ? 'true' : 'false');
589            });
590
591            $monthlyButtons.on('click', onMonthlyButtonClick);
592            $monthlyButtons.on('keypress', onMonthlyButtonKeyPress);
593            $monthlyInput.on('input', onMonthlyInputChange);
594            $monthlyInput.on('blur', onMonthlyInputBlur);
595
596            syncAmount($monthlyInput.val() || '0', $syncMonthlyAmount);
597        }
598
599        function handleConvertMonthlyButtons() {
600            const $confirmMonthly = $('#confirm-monthly');
601            const $recurrenceButtons = $('.recurrence-btn');
602            const $recurrenceInput = $('#recurrence');
603            const $monthlyButtons = $('.amount-option--monthly');
604            const $monthlyInput = $('#monthly-amount');
605            const $monthlyContainer = $('#monthly-amount-container');
606            const $oneTimeContainer = $('#one-time-amount-container');
607            const $monthlyButton = $('#monthly-btn');
608            const $monthlyConfirmation = $('#monthly-confirmation');
609            const $oneTimeDonationSummary = $('.one-time-donation-summary');
610            const $monthlyDonationSummary = $('.monthly-donation-summary');
611            const $syncMonthlyAmount = $('.sync-monthly-amount');
612
613            function validateAmount(amount) {
614                const parsedAmount = parseFloat(amount);
615                return !isNaN(parsedAmount) && parsedAmount > 0;
616            }
617
618            function setActiveButton($button) {
619                $recurrenceButtons.removeClass('recurrence-btn--active').attr('aria-pressed', 'false');
620                $monthlyButtons.removeClass('amount-option--active').attr('aria-pressed', 'false');
621                $button.addClass('amount-option--active').attr('aria-pressed', 'true');
622            }
623
624            function showConfirmation(message) {
625                if ($monthlyConfirmation.length) {
626                    $monthlyConfirmation.text(message).fadeIn().delay(3000).fadeOut();
627                } else {
628                    console.warn('Conteneur de confirmation introuvable.');
629                }
630            }
631
632            function onConfirmMonthlyClick() {
633                const suggestedAmount = $confirmMonthly.attr('data-amount');
634                if (!validateAmount(suggestedAmount)) {
635                    alert('Montant suggéré invalide. Veuillez réessayer.');
636                    return;
637                }
638                $recurrenceInput.val('monthly');
639                $monthlyInput.val(suggestedAmount);
640                syncAmount(suggestedAmount, $syncMonthlyAmount);
641                setActiveButton($monthlyButton);
642                $oneTimeContainer.hide();
643                $monthlyContainer.show();
644                $oneTimeDonationSummary.hide();
645                $monthlyDonationSummary.show();
646                showConfirmation(`Vous avez sélectionné un don mensuel de ${parseFloat(suggestedAmount).toFixed(2)} $.`);
647
648                if (FW && FW.ValidationManager && FW.StepManager) {
649                    FW.ValidationManager.validateCurrentStep(FW.StepManager.goToNextStep);
650                } else {
651                    console.error('FW.ValidationManager ou FW.StepManager non défini.');
652                }
653            }
654
655            function onMonthlyInputClick() {
656                $monthlyButtons.removeClass('amount-option--active').attr('aria-pressed', 'false');
657            }
658
659            function onConfirmMonthlyKeyPress(e) {
660                if (e.key === 'Enter' || e.key === ' ') {
661                    e.preventDefault();
662                    $(this).click();
663                }
664            }
665            
666            $confirmMonthly.on('click', onConfirmMonthlyClick);
667            $confirmMonthly.on('keypress', onConfirmMonthlyKeyPress);
668            $monthlyInput.on('click', onMonthlyInputClick);
669        }
670
671        function handleTributeTypeInteractions() {
672            const $dedicatedCheckbox = $('#dedicated-gift-checkbox');
673            const $honorCheckbox = $('#celebration-gift-checkbox');
674            const $memoriamCheckbox = $('#memoriam-gift-checkbox');
675            const $gratitudeCheckbox = $('#gratitude-gift-checkbox');
676            const $tributeOptions = $('.tribute-options');
677            const $tributeInfo = $('.tribute-info');
678            const $deptSelect = $('#tribute-dept-select');
679            const $honorDescription = $('.tribute-description--celebration');
680            const $memoriamDescription = $('.tribute-description--memoriam');
681            const $gratitudeDescription = $('.tribute-description--gratitude');
682            const $addEcardBtn = $('#addEcardBtn');
683            const $tributeNameInput = $('#tribute-name');
684            const $tributeDeptInput = $('#tribute-dept');
685            
686
687            function updateTributeChoiceUI($selectedCheckbox, $showOptions) {
688                if ($selectedCheckbox.is(':checked')) {
689                    $showOptions.show();
690                    $honorCheckbox.prop('checked', false).attr('aria-checked', 'false');
691                    $memoriamCheckbox.prop('checked', false).attr('aria-checked', 'false');
692                    $gratitudeCheckbox.prop('checked', false).attr('aria-checked', 'false');
693                    $tributeNameInput.val('');
694                } else {
695                    $showOptions.hide();
696                    $tributeInfo.hide();
697                }
698            }
699
700            // This function now handles all three options.
701            function updateTributeOptionUI($selectedCheckbox) {
702                if ($selectedCheckbox.is(':checked')) {
703                    // Uncheck the other checkboxes and update their aria attributes.
704                    [$honorCheckbox, $memoriamCheckbox, $gratitudeCheckbox].forEach(function($checkbox) {
705                        if ($checkbox.get(0) !== $selectedCheckbox.get(0)) {
706                            $checkbox.prop('checked', false)
707                                     .attr('aria-checked', 'false');
708                        } else {
709                            $checkbox.attr('aria-checked', 'true');
710                        }
711                    });
712                    // Show tribute info and ensure only the selected description is visible.
713                    $tributeInfo.show();
714                    // $honorDescription.hide();
715                    // $memoriamDescription.hide();
716                    // $gratitudeDescription.hide();
717                    if ($selectedCheckbox.is($honorCheckbox)) {
718                        // $honorDescription.show();
719                        $addEcardBtn.show();
720                        $deptSelect.hide();
721                        $tributeNameInput.val('');
722                        $tributeDeptInput.val('autre');
723                    } else if ($selectedCheckbox.is($memoriamCheckbox)) {
724                        // $memoriamDescription.show();
725                        $addEcardBtn.show();
726                        $deptSelect.hide();
727                        $tributeNameInput.val('');
728                        $tributeDeptInput.val('autre');
729                    } else if ($selectedCheckbox.is($gratitudeCheckbox)) {
730                        // $gratitudeDescription.show();
731                        $addEcardBtn.hide();
732                        $tributeNameInput.val('');
733                        $deptSelect.show();
734                    }
735                } else {
736                    // If unchecked, update aria and hide the info container and related description.
737                    $selectedCheckbox.attr('aria-checked', 'false');
738                    $tributeInfo.hide();
739                    $tributeNameInput.val('');
740                    $tributeDeptInput.val('autre');
741                    // if ($selectedCheckbox.is($honorCheckbox)) {
742                    //     $honorDescription.hide();
743                    // } else if ($selectedCheckbox.is($memoriamCheckbox)) {
744                    //     $memoriamDescription.hide();
745                    // } else if ($selectedCheckbox.is($gratitudeCheckbox)) {
746                    //     $gratitudeDescription.hide();
747                    // }
748                }
749            }
750
751            function initializeARIA() {
752                $dedicatedCheckbox.attr('aria-checked', $dedicatedCheckbox.is(':checked') ? 'true' : 'false');
753                $honorCheckbox.attr('aria-checked', $honorCheckbox.is(':checked') ? 'true' : 'false');
754                $memoriamCheckbox.attr('aria-checked', $memoriamCheckbox.is(':checked') ? 'true' : 'false');
755                $gratitudeCheckbox.attr('aria-checked', $gratitudeCheckbox.is(':checked') ? 'true' : 'false');
756            }
757
758            $dedicatedCheckbox.on('change', function () {
759                updateTributeChoiceUI($dedicatedCheckbox, $tributeOptions);
760            });
761
762            $honorCheckbox.on('change', function () {
763                updateTributeOptionUI($honorCheckbox);
764            });
765
766            $memoriamCheckbox.on('change', function () {
767                updateTributeOptionUI($memoriamCheckbox);
768            });
769            
770            $gratitudeCheckbox.on('change', function () {
771                updateTributeOptionUI($gratitudeCheckbox);
772            });
773
774            initializeARIA();
775        }
776
777        function handleDonationTypeInteractions() {
778            const $donationTypeButtons = $('.donation-type-btn');
779            const $donationTypeInput = $('#donation-type');
780            const $personalDonationFields = $('#personal-donation-fields');
781            const $companyDonationFields = $('#company-donation-fields');
782            const $companyNameFields = $('#company-name');
783            const $companyEmailFields = $('#company-email');
784
785            function updateUI(donationType) {
786                if (donationType === 'personal') {
787                    $companyDonationFields.hide();
788                    $companyNameFields.removeAttr('required');
789                    $companyEmailFields.removeAttr('required');
790                } else if (donationType === 'company') {
791                    $companyDonationFields.show();
792                    $companyNameFields.attr('required', 'True');
793                    $companyEmailFields.attr('required', 'True');
794                } else {
795                    console.warn(`Type de don non géré: ${donationType}`);
796                    
797                }
798            }
799
800            function setActiveButton($activeButton) {
801                $donationTypeButtons.removeClass('donation-type-btn--active').attr('aria-pressed', 'false');
802                $activeButton.addClass('donation-type-btn--active').attr('aria-pressed', 'true');
803            }
804
805            $donationTypeButtons.each(function () {
806                const $button = $(this);
807                const isActive = $button.hasClass('donation-type-btn--active');
808                $button.attr('aria-pressed', isActive ? 'true' : 'false');
809            });
810
811            $donationTypeButtons.on('click', function () {
812                const $button = $(this);
813                const donationType = $button.data('donation-type');
814
815                if (!donationType) {
816                    console.error('Type de don non défini pour ce bouton.');
817                    
818                    return;
819                }
820
821                $donationTypeInput.val(donationType);
822                setActiveButton($button);
823                updateUI(donationType);
824            });
825
826            $donationTypeButtons.on('keypress', function (e) {
827                if (e.key === 'Enter' || e.key === ' ') {
828                    e.preventDefault();
829                    $(this).click();
830                }
831            });
832
833            const $defaultActiveButton = $donationTypeButtons.filter('.donation-type-btn--active').first();
834            if ($defaultActiveButton.length) {
835                updateUI($defaultActiveButton.data('donation-type'));
836            } else {
837                const defaultDonationType = 'personal';
838                $recurrenceButtons.filter(`[data-donation-type="${defaultDonationType}"]`).addClass('donation-type-btn--active').attr('aria-pressed', 'true');
839                $recurrenceInput.val(defaultDonationType);
840                updateUI(defaultDonationType);
841            }
842        }
843
844        function handleGeneralFeeCovering() {
845            const $coverFeeCheckbox = $('#cover-transaction-fee');
846            if (!$coverFeeCheckbox.length) {
847                // If doesn't exist, exit the function.
848                return;
849            }
850            const $recurrenceInput = $('#recurrence');
851            const $donationTotal = $('#donation-total');
852            const $oneTimeAmountInput = $('#one-time-amount');
853            const $monthlyAmountInput = $('#monthly-amount');
854            const $oneTimeAmountPlusFee = $('#one-time-amount-plus-fee');
855            const $monthlyAmountPlusFee = $('#monthly-amount-plus-fee');
856            const $donationWithFees = $('.donation-with-fees');
857            const TRANSACTION_FEE_PERCENTAGE = 3;
858            const TRANSACTION_FEE_CAP = 20;
859
860            function calculateTransactionFee(amount) {
861                const fee = Math.round(((amount * TRANSACTION_FEE_PERCENTAGE) / 100) * 100) / 100;
862                return fee <= TRANSACTION_FEE_CAP ? fee : TRANSACTION_FEE_CAP;
863            }
864
865            function updateDonationTotal() {
866                const isChecked = $coverFeeCheckbox.is(':checked');
867                const donationType = $recurrenceInput.val();
868                if (isChecked) {
869                    let donationAmount = 0;
870                    if (donationType === 'one-time') {
871                        donationAmount = parseFloat($oneTimeAmountInput.val()) || 0;
872                    } else if (donationType === 'monthly') {
873                        donationAmount = parseFloat($monthlyAmountInput.val()) || 0;
874                    }
875                    const fee = calculateTransactionFee(donationAmount);
876                    const totalAmount = donationAmount + fee;
877                    $donationWithFees.text(totalAmount.toFixed(2));
878                    $donationTotal.show();
879                    if (donationType === 'one-time') {
880                        $oneTimeAmountPlusFee.val(totalAmount.toFixed(2));
881                        $monthlyAmountPlusFee.val('');
882                    } else if (donationType === 'monthly') {
883                        $oneTimeAmountPlusFee.val('');
884                        $monthlyAmountPlusFee.val(totalAmount.toFixed(2));
885                    }
886                } else {
887                    $donationTotal.hide();
888                    $oneTimeAmountPlusFee.val('');
889                    $monthlyAmountPlusFee.val('');
890                }
891            }
892
893            function initializeARIA() {
894                $coverFeeCheckbox.attr('aria-checked', $coverFeeCheckbox.is(':checked') ? 'true' : 'false');
895            }
896
897            $coverFeeCheckbox.on('change', function () {
898                updateDonationTotal();
899                $(this).attr('aria-checked', this.checked ? 'true' : 'false');
900            });
901
902            $oneTimeAmountInput.on('input', function () {
903                if ($coverFeeCheckbox.is(':checked')) {
904                    updateDonationTotal();
905                }
906            });
907
908            $monthlyAmountInput.on('input', function () {
909                if ($coverFeeCheckbox.is(':checked')) {
910                    updateDonationTotal();
911                }
912            });
913
914            $recurrenceInput.on('change', function () {
915                if ($coverFeeCheckbox.is(':checked')) {
916                    updateDonationTotal();
917                }
918            });
919
920            initializeARIA();
921            updateDonationTotal();
922        }
923
924        function handleLotoFeeCovering() {
925            const $coverFeeCheckbox = $('#cover-transaction-fee');
926            if (!$coverFeeCheckbox.length) {
927                // If doesn't exist, exit the function.
928                return;
929            }
930            const $lotoTotalField = $('#product-total--loto'); // Make sure this exists in your HTML
931            const $donationTotal = $('#donation-total');
932            const $lotoAmountPlusFee = $('#loto-plus-fee'); // Hidden field to store loto total + fee
933            const $donationWithFees = $('.donation-with-fees'); // Or a dedicated display for loto total if needed
934            const TRANSACTION_FEE_PERCENTAGE = 3;
935            const TRANSACTION_FEE_CAP = 20;
936
937            function calculateTransactionFee(amount) {
938                const fee = Math.round(((amount * TRANSACTION_FEE_PERCENTAGE) / 100) * 100) / 100;
939                return fee <= TRANSACTION_FEE_CAP ? fee : TRANSACTION_FEE_CAP;
940            }
941
942            function updateLotoDonationTotal() {
943                if ($coverFeeCheckbox.is(':checked')) {
944                    const donationAmount = parseFloat($lotoTotalField.val()) || 0;
945                    const fee = calculateTransactionFee(donationAmount);
946                    const totalAmount = donationAmount + fee;
947                    $donationWithFees.text(totalAmount.toFixed(2));
948                    $lotoAmountPlusFee.val(totalAmount.toFixed(2));
949                    $donationTotal.show();
950                } else {
951                    $lotoAmountPlusFee.val('');
952                }
953            }
954
955            $lotoTotalField.on('change', updateLotoDonationTotal);
956            $coverFeeCheckbox.on('change', updateLotoDonationTotal);
957            updateLotoDonationTotal();
958        }
959
960        function handleGolfSelections() {
961            const $fivesomeQty = $('#golf-fivesome-quantity');
962            const $fivesomeTotalDisplay = $('.sync-golf-total--fivesome');
963            const $saveFivesome = $('#product-number--golf-fivesome');
964            const $saveTotal = $('#product-total--golf');
965            const $golfSummary = $('.golf-summary');
966            const $golfSummaryText = $('.golf-summary .donation-summary');
967
968            function updateGolfTotals() {
969                const fivesomeQty = parseInt($fivesomeQty.val(), 10) || 0;
970                const fivesomeTotal = 8000 * fivesomeQty;
971                const allTotal = fivesomeTotal;
972
973                $fivesomeTotalDisplay.text(fivesomeTotal.toFixed(2));
974
975                $saveFivesome.val(fivesomeQty);
976                $saveTotal.val(allTotal.toFixed(2));
977                $saveTotal.trigger('change');
978
979                if (allTotal > 0) {
980                    $golfSummaryText.html(sprintf(DonationFormData.golfTemplate, 'Fivesome', fivesomeQty, fivesomeTotal.toFixed(2)));
981                    $golfSummary.show();
982                } else {
983                    $golfSummary.hide();
984                    $golfSummaryText.html('');
985                }
986            }
987
988            $fivesomeQty.on('change', updateGolfTotals);
989            updateGolfTotals();
990        }
991
992        function handleGolfFeeCovering() {
993            const $coverFeeCheckbox = $('#cover-transaction-fee');
994            if (!$coverFeeCheckbox.length) {
995                return;
996            }
997            const $golfTotalField = $('#product-total--golf');
998            const $donationTotal = $('#donation-total');
999            const $golfAmountPlusFee = $('#golf-plus-fee');
1000            const $donationWithFees = $('.donation-with-fees');
1001            const TRANSACTION_FEE_PERCENTAGE = 3;
1002
1003            function calculateTransactionFee(amount) {
1004                return Math.round(((amount * TRANSACTION_FEE_PERCENTAGE) / 100) * 100) / 100;
1005            }
1006
1007            function updateGolfDonationTotal() {
1008                if ($coverFeeCheckbox.is(':checked')) {
1009                    const donationAmount = parseFloat($golfTotalField.val()) || 0;
1010                    const fee = calculateTransactionFee(donationAmount);
1011                    const totalAmount = donationAmount + fee;
1012                    $donationWithFees.text(totalAmount.toFixed(2));
1013                    $golfAmountPlusFee.val(totalAmount.toFixed(2));
1014                    $donationTotal.show();
1015                } else {
1016                    $golfAmountPlusFee.val('');
1017                    $donationWithFees.text('');
1018                    $donationTotal.hide();
1019                }
1020            }
1021
1022            $golfTotalField.on('change', updateGolfDonationTotal);
1023            $coverFeeCheckbox.on('change', updateGolfDonationTotal);
1024            updateGolfDonationTotal();
1025        }
1026
1027        function handlePauleeSelections() {
1028            const $ticketQty = $('#paulee-ticket-quantity');
1029            const $ticketTotalDisplay = $('.sync-paulee-total');
1030            const $saveTicketQty = $('#product-number--paulee-ticket');
1031            const $saveTotal = $('#product-total--paulee');
1032            const $pauleeSummary = $('.paulee-summary');
1033            const $pauleeSummaryText = $('.paulee-summary .donation-summary');
1034
1035            function updatePauleeTotals() {
1036                const ticketQty = parseInt($ticketQty.val(), 10) || 0;
1037                const ticketTotal = 5000 * ticketQty;
1038
1039                $ticketTotalDisplay.text(ticketTotal.toFixed(2));
1040                $saveTicketQty.val(ticketQty);
1041                $saveTotal.val(ticketTotal.toFixed(2));
1042                $saveTotal.trigger('change');
1043
1044                if (ticketTotal > 0) {
1045                    $pauleeSummaryText.html(sprintf(DonationFormData.golfTemplate, 'Billet la Paulée', ticketQty, ticketTotal.toFixed(2)));
1046                    $pauleeSummary.show();
1047                } else {
1048                    $pauleeSummary.hide();
1049                    $pauleeSummaryText.html('');
1050                }
1051            }
1052
1053            $ticketQty.on('change', updatePauleeTotals);
1054            updatePauleeTotals();
1055        }
1056
1057        function handlePauleeFeeCovering() {
1058            const $coverFeeCheckbox = $('#cover-transaction-fee');
1059            if (!$coverFeeCheckbox.length) {
1060                return;
1061            }
1062            const $pauleeTotalField = $('#product-total--paulee');
1063            const $donationTotal = $('#donation-total');
1064            const $pauleeAmountPlusFee = $('#paulee-plus-fee');
1065            const $donationWithFees = $('.donation-with-fees');
1066            const TRANSACTION_FEE_PERCENTAGE = 3;
1067
1068            function calculateTransactionFee(amount) {
1069                return Math.round(((amount * TRANSACTION_FEE_PERCENTAGE) / 100) * 100) / 100;
1070            }
1071
1072            function updatePauleeDonationTotal() {
1073                if ($coverFeeCheckbox.is(':checked')) {
1074                    const donationAmount = parseFloat($pauleeTotalField.val()) || 0;
1075                    const fee = calculateTransactionFee(donationAmount);
1076                    const totalAmount = donationAmount + fee;
1077                    $donationWithFees.text(totalAmount.toFixed(2));
1078                    $pauleeAmountPlusFee.val(totalAmount.toFixed(2));
1079                    $donationTotal.show();
1080                } else {
1081                    $pauleeAmountPlusFee.val('');
1082                    $donationWithFees.text('');
1083                    $donationTotal.hide();
1084                }
1085            }
1086
1087            $pauleeTotalField.on('change', updatePauleeDonationTotal);
1088            $coverFeeCheckbox.on('change', updatePauleeDonationTotal);
1089            updatePauleeDonationTotal();
1090        }
1091
1092        function handleInvoiceSelections() {
1093            const $invoiceAmount = $('#invoice-amount');
1094            const $invoiceNumber = $('#invoice-number');
1095            const $invoiceSummary = $('.invoice-summary');
1096            const $invoiceSummaryText = $('.invoice-summary .donation-summary');
1097            const $invoiceAmountPlusFee = $('#invoice-amount-plus-fee');
1098
1099            function updateInvoiceSummary() {
1100                const amount = parseFloat($invoiceAmount.val()) || 0;
1101                const invoiceNumber = ($invoiceNumber.val() || '').trim();
1102
1103                if (amount > 0 && invoiceNumber) {
1104                    $invoiceSummaryText.html(`Paiement de facture ${invoiceNumber} : ${amount.toFixed(2)} $`);
1105                    $invoiceSummary.show();
1106                } else {
1107                    $invoiceSummaryText.html('');
1108                    $invoiceSummary.hide();
1109                }
1110
1111                if (!$('#cover-transaction-fee').is(':checked')) {
1112                    $invoiceAmountPlusFee.val('');
1113                }
1114            }
1115
1116            $invoiceAmount.on('input blur', updateInvoiceSummary);
1117            $invoiceNumber.on('input blur', updateInvoiceSummary);
1118            updateInvoiceSummary();
1119        }
1120
1121        function handleInvoiceFeeCovering() {
1122            const $coverFeeCheckbox = $('#cover-transaction-fee');
1123            if (!$coverFeeCheckbox.length) {
1124                return;
1125            }
1126            const $invoiceAmount = $('#invoice-amount');
1127            const $donationTotal = $('#donation-total');
1128            const $invoiceAmountPlusFee = $('#invoice-amount-plus-fee');
1129            const $donationWithFees = $('.donation-with-fees');
1130            const TRANSACTION_FEE_PERCENTAGE = 3;
1131
1132            function calculateTransactionFee(amount) {
1133                return Math.round(((amount * TRANSACTION_FEE_PERCENTAGE) / 100) * 100) / 100;
1134            }
1135
1136            function updateInvoiceDonationTotal() {
1137                const invoiceAmount = parseFloat($invoiceAmount.val()) || 0;
1138
1139                if ($coverFeeCheckbox.is(':checked') && invoiceAmount > 0) {
1140                    const fee = calculateTransactionFee(invoiceAmount);
1141                    const totalAmount = invoiceAmount + fee;
1142                    $donationWithFees.text(totalAmount.toFixed(2));
1143                    $invoiceAmountPlusFee.val(totalAmount.toFixed(2));
1144                    $donationTotal.show();
1145                } else {
1146                    $invoiceAmountPlusFee.val('');
1147                    $donationWithFees.text('');
1148                    $donationTotal.hide();
1149                }
1150            }
1151
1152            $invoiceAmount.on('input blur', updateInvoiceDonationTotal);
1153            $coverFeeCheckbox.on('change', updateInvoiceDonationTotal);
1154            updateInvoiceDonationTotal();
1155        }
1156
1157        function handleCommentSection() {
1158            const $addCommentBtn = $('#addCommentBtn');
1159            const $stepsSection = $('#stepsSection');
1160
1161            const $commentSection = $('#commentSection');
1162            const $backCommentBtn = $('#commentSection-backBtn');
1163            const $saveCommentBtn = $('#commentSection-saveCommentBtn');
1164
1165            const $userComment = $('#commentSection-userComment');
1166            const $sharingConsentInputs = $('input[name="sharingConsent"]');
1167
1168            const $savedUserComment = $('#commentSection-savedUserComment');
1169            const $savedSharingConsent = $('#commentSection-savedSharingConsent');
1170
1171            // const $commentExist = $('#hiddenUserComment');
1172
1173            const MAX_COMMENT_DISPLAY_LENGTH = 20;
1174
1175            const $errorConsentOptions = $('#error-consent-options');
1176            const $errorUserComment = $('#error-user-comment');
1177
1178            function initializeARIA() {
1179                $addCommentBtn.attr('aria-expanded', 'false');
1180                $backCommentBtn.attr('aria-label', 'Retour à la section précédente');
1181                $saveCommentBtn.attr('aria-label', 'Sauvegarder le message');
1182            }
1183
1184            function showCommentSection() {
1185                clearErrors();
1186                $stepsSection.hide();
1187                $commentSection.show();
1188                $addCommentBtn.attr('aria-expanded', 'true');
1189                $userComment.val($savedUserComment.val());
1190                if ($savedSharingConsent.val()) {
1191                    $("input[name=sharingConsent][value=" + $savedSharingConsent.val() + "]").prop('checked', true);
1192                }
1193
1194            }
1195
1196            function hideCommentSection() {
1197                clearErrors();
1198                $commentSection.hide();
1199                $stepsSection.show();
1200                $addCommentBtn.attr('aria-expanded', 'false');
1201                const commentText = $userComment.val().trim();
1202                if (commentText === '') {
1203                    $addCommentBtn.text('Ajoutez un message');
1204                    // $commentExist.val(false);
1205                    $sharingConsentInputs.prop('checked', false);
1206                    $savedUserComment.val("")
1207                    $savedSharingConsent.val("")
1208                }
1209            }
1210
1211            // function displayError($element, message) {
1212            //     // $('.error-message').remove();
1213            //     $('.error-message').val("");
1214            //     const $error = $('.error-message').text(message);
1215            //     $element.after($error);
1216            // }
1217
1218            function showError($field, message) {
1219                $field.addClass('error');
1220                $field.next('.error-message').show();
1221                $field.next('.error-message').text(message);
1222            }
1223
1224            function clearErrors() {
1225                $('.error-message').hide();
1226                $('.error-message').text("");
1227            }
1228
1229            function updateAddCommentBtnText(comment) {
1230                const truncatedComment = comment.length > MAX_COMMENT_DISPLAY_LENGTH
1231                    ? comment.slice(0, MAX_COMMENT_DISPLAY_LENGTH) + '...'
1232                    : comment;
1233                $addCommentBtn.text(`Votre message : ${truncatedComment}`);
1234            }
1235
1236            $addCommentBtn.on('click', function () {
1237                showCommentSection();
1238            });
1239
1240            $backCommentBtn.on('click', function () {
1241                hideCommentSection();
1242            });
1243
1244            $saveCommentBtn.on('click', function () {
1245                clearErrors();
1246                const commentText = $userComment.val().trim();
1247                const commentConsent = $sharingConsentInputs.filter(':checked').val();
1248                let hasError = false;
1249                if (commentText === '') {
1250                    showError($userComment, 'Entrez un message avant de sauvegarder.');
1251                    hasError = true;
1252                }
1253                if (!commentConsent) {
1254                    showError($('#consent-options-group'), 'Choisissez votre type de consentement.');
1255                    hasError = true;
1256                }
1257                if (hasError) {
1258                    return;
1259                }
1260                // $commentExist.val(true);
1261                updateAddCommentBtnText(commentText);
1262                $savedUserComment.val(commentText);
1263                $savedSharingConsent.val(commentConsent)
1264                hideCommentSection();
1265            });
1266
1267            function init() {
1268                initializeARIA();
1269            }
1270
1271            init();
1272        }
1273
1274        function handleEcardSection() {
1275            const $addEcardBtn = $('#addEcardBtn');
1276            const $stepsSection = $('#stepsSection');
1277            const $ecardSection = $('#ecardSection');
1278            const $backEcardBtn = $('#ecardSection-backBtn');
1279            const $saveEcardBtn = $('#ecardSection-saveEcardBtn');
1280            const $ecardFrom = $('#ecardSection-from');
1281            const $ecardUserComment = $('#ecardSection-userMessage');
1282            const $ecardHideAmount = $('#ecardSection-hide-amount');
1283            const $ecardRecipientFirstName = $('#ecardSection-recipient-first-name');
1284            const $ecardRecipientLastName = $('#ecardSection-recipient-last-name');
1285            const $ecardRecipientEmail = $('#ecardSection-recipient-email');
1286            const $ecardSavedUserMessage = $('#ecardSection-savedUserMessage');
1287            const MAX_COMMENT_DISPLAY_LENGTH = 20;
1288
1289            function initializeARIA() {
1290                $addEcardBtn.attr('aria-expanded', 'false');
1291                $backEcardBtn.attr('aria-label', 'Retour à la section précédente');
1292                $saveEcardBtn.attr('aria-label', 'Sauvegarder mon message');
1293            }
1294
1295            function showEcardSection() {
1296                $stepsSection.hide();
1297                $ecardSection.show();
1298                $addEcardBtn.attr('aria-expanded', 'true');
1299                $ecardFrom.focus();
1300            }
1301
1302            function hideEcardSection() {
1303                $ecardSection.hide();
1304                $stepsSection.show();
1305                $addEcardBtn.attr('aria-expanded', 'false');
1306                const ecardText = $ecardUserComment.val().trim();
1307                if (ecardText === '') {
1308                    $addEcardBtn.text('Je souhaite envoyer une carte éléctronique');
1309                    $ecardSavedUserMessage.val('');
1310                }
1311            }
1312
1313            function displayError($element, message) {
1314                $element.next('.error-message').remove();
1315                const $error = $('<div class="error-message"></div>').text(message);
1316                $element.after($error);
1317            }
1318
1319            function clearErrors() {
1320                $('.error-message').remove();
1321            }
1322
1323            function updateAddEcardBtnText(ecard) {
1324                const truncatedEcard = ecard.length > MAX_COMMENT_DISPLAY_LENGTH
1325                    ? ecard.slice(0, MAX_COMMENT_DISPLAY_LENGTH) + '...'
1326                    : ecard;
1327                $addEcardBtn.text(`Votre carte éléctronique : ${truncatedEcard}`);
1328            }
1329
1330            function displayConfirmation(message) {
1331                const $confirmation = $('#ecard-confirmation');
1332                if ($confirmation.length) {
1333                    $confirmation.text(message).fadeIn().delay(3000).fadeOut();
1334                } else {
1335                    console.warn('Conteneur de confirmation introuvable.');
1336                }
1337            }
1338
1339            $addEcardBtn.on('click', function () {
1340                showEcardSection();
1341            });
1342
1343            $backEcardBtn.on('click', function () {
1344                hideEcardSection();
1345            });
1346
1347            $saveEcardBtn.on('click', function () {
1348                clearErrors();
1349                const ecardFromVal = $ecardFrom.val().trim();
1350                const ecardUserCommentVal = $ecardUserComment.val().trim();
1351                const ecardRecipientFirstNameVal = $ecardRecipientFirstName.val().trim();
1352                const ecardRecipientLastNameVal = $ecardRecipientLastName.val().trim();
1353                const ecardRecipientEmailVal = $ecardRecipientEmail.val().trim();
1354                let hasError = false;
1355                if (ecardFromVal === '') {
1356                    displayError($ecardFrom, 'Entrez le nom de l\'expéditeur.');
1357                    hasError = true;
1358                }
1359                if (ecardUserCommentVal === '') {
1360                    displayError($ecardUserComment, 'Entrez un message pour votre carte éléctronique.');
1361                    hasError = true;
1362                }
1363                if (ecardRecipientFirstNameVal === '') {
1364                    displayError($ecardRecipientFirstName, 'Entrez le prénom du destinataire.');
1365                    hasError = true;
1366                }
1367                if (ecardRecipientLastNameVal === '') {
1368                    displayError($ecardRecipientLastName, 'Entrez le nom du destinataire.');
1369                    hasError = true;
1370                }
1371                if (ecardRecipientEmailVal === '') {
1372                    displayError($ecardRecipientEmail, 'Entrez le courriel du destinataire.');
1373                    hasError = true;
1374                } else if (!validateEmail(ecardRecipientEmailVal)) {
1375                    displayError($ecardRecipientEmail, 'Entrez un courriel valide.');
1376                    hasError = true;
1377                }
1378                if (hasError) {
1379                    return;
1380                }
1381                $ecardSavedUserMessage.val(ecardUserCommentVal);
1382                updateAddEcardBtnText(ecardUserCommentVal);
1383                hideEcardSection();
1384                displayConfirmation('Carte éléctronique sauvegardée avec succès!');
1385            });
1386
1387            function validateEmail(email) {
1388                const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1389                return emailRegex.test(email);
1390            }
1391
1392            function init() {
1393                initializeARIA();
1394            }
1395
1396            init();
1397        }
1398
1399        function handleEcardImageSelection() {
1400            // When a thumbnail is clicked...
1401            $('.ecard-thumbnail').on('click', function() {
1402                // Remove selected state from all thumbnails
1403                $('.ecard-thumbnail').removeClass('selected');
1404                // Add selected state to the clicked thumbnail
1405                $(this).addClass('selected');
1406                // Get the image ID from data attribute
1407                var selectedId = $(this).data('image-id');
1408                // Set the hidden input's value
1409                $('#selected-ecard-image').val(selectedId);
1410            });
1411        }
1412
1413        function handleProductSelections() {
1414            // Loop over each product group.
1415            $('.product-line--loto').each(function() {
1416                var $group = $(this);
1417                // Get the product's price from the data attribute.
1418                var itemPrice = $group.data('item-price');
1419                // Find the select element within this group.
1420                var $select = $group.find('select');
1421                // Find the target element to update the total.
1422                var $totalDisplay = $group.find('.sync-product-total');
1423                var $saveNumber = $group.find('.product-number--loto');
1424                var $saveTotal = $group.find('.product-total--loto');
1425
1426                var $lotoSummary = $('.loto-summary');
1427                var $lotoTotalSummary = $('.loto-summary .donation-summary');
1428
1429                // Attach a change event on the select element.
1430                $select.on('change', function() {
1431                    var ticketCount = parseInt($(this).val(), 10);
1432                    var total = calculateProductTotalPrice(itemPrice, ticketCount);
1433                    $totalDisplay.text(total);
1434                    $saveNumber.val(ticketCount);
1435                    $saveTotal.val(total);
1436
1437                    var pluralSuffix = (ticketCount > 1 ? "s" : "");
1438                    var newText = sprintf(DonationFormData.lotoTemplate, ticketCount, pluralSuffix, total);
1439                    $lotoTotalSummary.html(newText);
1440                    $lotoSummary.show();
1441                });
1442            });
1443        }
1444
1445        function handleLotoRecurrence() {
1446            var checkbox = $('#product-recurrence-checkbox');
1447            var saveNumber = $('#product-number--loto');
1448            checkbox.on('change', function() {
1449                if ($(this).is(':checked')) {
1450                    if (saveNumber.val() > 0) {
1451                        var recurrenceAmount = (25 * saveNumber.val());
1452                        var total = (175 * saveNumber.val());
1453                        var lotoSummary = $('.loto-summary');
1454                        var lotoTotalSummary = $('.loto-summary .donation-summary');
1455                        var lotoRecurrenceSummary = $('.loto-summary .recurrence-summary');
1456
1457                        lotoTotalSummary.hide();
1458
1459                        var pluralSuffix = (saveNumber.val() > 1 ? "s" : "");
1460                        var newText = sprintf(DonationFormData.lotoRecurrenceTemplate, saveNumber.val(), pluralSuffix, recurrenceAmount, total);
1461                        lotoRecurrenceSummary.html(newText);
1462
1463                    } else {
1464                        lotoTotalSummary.show();
1465                        lotoRecurrenceSummary.hide();
1466                        lotoRecurrenceSummary.html('');
1467                    }
1468                } else {
1469                    
1470                    
1471                }
1472            });
1473        }
1474
1475$(document).ready(function(){
1476    handleProductSelections(); // your existing function
1477    handleLotoRecurrence();    // our new function to handle the checkbox
1478});
1479
1480        // Call the function on document ready
1481        $(document).ready(function() {
1482            handleEcardImageSelection();
1483        });
1484
1485        return { init };
1486    })();
1487
1488    $(document).ready(function() {
1489        FormWizard.InteractionManager.init();
1490    });
1491})(jQuery, window.FormWizard = window.FormWizard || {});

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.