PageSourceSearch

https://parlementdesexiles.eu/wp-content/plugins/parlement-voting-…/js/candidates-selection.js?ver=1752164950

js parlementdesexiles.eu collected 2026-10-08 23:52:18 UTC 34,424 bytes, 830 lines download raw bytes

1/**
2 * Parlement Voting Plugin - Frontend JavaScript
3 *
4 * This script manages candidate selection, modal interactions,
5 * and the AJAX-based voting submission process.
6 * It requires jQuery.
7 */
8jQuery(document).ready(function($) {
9
10    // --- Global State Variables ---
11    let selectedGenders = []; // Stores genders of selected candidates (max 2, one male, one female)
12    let selectedCount = 0;    // Counts currently selected candidates (max 2)
13    let selectedIds = [];     // Stores IDs of currently selected candidates
14
15    // DOM element references
16    const voteButton = $('#submit-vote');
17    const videoButtons = $('.candidate-video-btn, .candidate-modal-video-btn');
18    const videoModal = $('#candidateVideoModal');
19    const closeVideoModalBtn = $('.close-video-modal');
20    const youtubePlayer = $('#youtubeVideoPlayer');
21	const voterEmailInput = $('#voter-email');
22
23    const voteEmailModal = $('#voteEmailModal');
24    const closeVoteEmailModalBtn = $('.close-vote-email-modal');
25    const sendCodeButton = $('#send-code-button');
26    const verifyVoteButton = $('#verify-vote-button');
27    const verificationCodeGroup = $('.verification-code-group');
28    const electorEmailInput = $('#voter-email');
29    const verificationCodeInput = $('#verification-code');
30    const unregisteredFlowSection = $('#unregistered-flow-section');
31//    const registerAcceptanceCheckbox = $('#register-acceptance-checkbox');
32    const registerNowButton = $('#register-now-button');
33    const emailInputPrompt = $('.email-input-prompt');
34    const emailInputGroup = $('.email-input-group');
35
36    /**
37     * Handles candidate selection/deselection from the grid.
38     * Enforces rules: maximum 2 candidates, one male and one female.
39     */
40    $('.candidate-card').on('change', '.select-button .select-checkbox', function() {
41        const checkbox = $(this);
42        const card = checkbox.closest('.candidate-card');
43        const gender = card.hasClass('male') ? 'male' :
44                       card.hasClass('female') ? 'female' : 'other';
45        const candidateId = checkbox.val();
46
47        if (checkbox.is(':checked')) {
48            // Prevent selection if max candidates already selected
49            if (selectedCount >= 2) {
50                checkbox.prop('checked', false);
51                return;
52            }
53
54            // Prevent selection if same gender already selected
55            if (selectedGenders.includes(gender)) {
56                checkbox.prop('checked', false);
57                alert('Veuillez sélectionner des candidats de sexes différents.');
58                return;
59            }
60
61            // Add candidate to selected state
62            selectedCount++;
63            selectedGenders.push(gender);
64            selectedIds.push(candidateId);
65
66            card.addClass('selected');
67            card.find('.select-text').text('Désélectionner');
68
69            // Disable other candidates of the same gender if this is the first selection
70            if (selectedCount === 1) {
71                $(`.candidate-card.${gender}:not(.selected)`).each(function() {
72                    $(this).addClass('unavailable');
73                    $(this).find('.select-checkbox').prop('disabled', true);
74                });
75            }
76
77        } else { // Candidate deselected
78            selectedCount--;
79            selectedGenders = selectedGenders.filter(g => g !== gender);
80            selectedIds = selectedIds.filter(id => id !== candidateId);
81
82            card.removeClass('selected');
83            card.find('.select-text').text('Sélectionner');
84
85            // Re-evaluate availability for all cards
86            $('.candidate-card').each(function() {
87                const currentCard = $(this);
88                const currentCardCheckbox = currentCard.find('.select-checkbox');
89                const currentCardGender = currentCard.hasClass('male') ? 'male' :
90                                          currentCard.hasClass('female') ? 'female' : 'other';
91
92                if (!currentCard.hasClass('selected')) {
93                    if (selectedCount === 0 || !selectedGenders.includes(currentCardGender)) {
94                        currentCard.removeClass('unavailable');
95                        currentCardCheckbox.prop('disabled', false);
96                    }
97                }
98            });
99        }
100
101        // Update main "Vote" button and overall unavailable states
102        if (selectedCount === 2) {
103            voteButton.prop('disabled', false).addClass('enabled');
104            // Make all non-selected cards unavailable
105            $('.candidate-card:not(.selected)').each(function() {
106                $(this).addClass('unavailable');
107                $(this).find('.select-checkbox').prop('disabled', true);
108            });
109        } else {
110            voteButton.prop('disabled', true).removeClass('enabled');
111            // Re-evaluate unavailable state for ALL cards if less than 2 are selected
112            $('.candidate-card').each(function() {
113                const currentCard = $(this);
114                const currentCardCheckbox = currentCard.find('.select-checkbox');
115                const currentCardGender = currentCard.hasClass('male') ? 'male' :
116                                          currentCard.hasClass('female') ? 'female' : 'other';
117
118                if (currentCard.hasClass('selected')) {
119                    currentCard.prop('disabled', false);
120                    currentCard.removeClass('unavailable');
121                } else {
122                    if (selectedCount === 1 && selectedGenders.includes(currentCardGender)) {
123                        currentCard.addClass('unavailable');
124                        currentCardCheckbox.prop('disabled', true);
125                    } else {
126                        currentCard.removeClass('unavailable');
127                        currentCardCheckbox.prop('disabled', false);
128                    }
129                }
130            });
131        }
132    });
133
134    // --- Candidate Detail Modal Interactions ---
135
136    /**
137     * Opens the candidate detail modal when clicking on specific card elements.
138     */
139    $(document).on('click', '.candidate-short-text, .candidate-name, .candidate-photo', function(e) {
140        e.preventDefault();
141        const card = $(this).closest('.candidate-card');
142        const modal = $('#modal-' + card.data('candidate-id'));
143
144        // Sync modal checkbox state with card checkbox state
145        const cardCheckbox = card.find('.select-checkbox');
146        modal.find('.modal-select-button .select-checkbox').prop('checked', cardCheckbox.is(':checked'));
147
148        // Update modal button text based on sync
149        const modalSelectText = modal.find('.modal-select-button .select-text');
150        modalSelectText.text(cardCheckbox.is(':checked') ? 'Désélectionner' : 'Sélectionner');
151
152        modal.css('display', 'flex'); // Use flex for centering
153        $('body').addClass('modal-open'); // Prevent body scrolling
154    });
155
156    /**
157     * Closes candidate detail modals when clicking 'X' or outside the modal content.
158     */
159    $(document).on('click', function(e) {
160        if ($(e.target).hasClass('close-modal')) {
161            $(e.target).closest('.candidate-modal').css('display', 'none');
162            $('body').removeClass('modal-open');
163        } else if ($(e.target).hasClass('candidate-modal')) {
164            $(e.target).css('display', 'none');
165            $('body').removeClass('modal-open');
166        }
167    });
168
169    // --- Syncing Logic for Candidate Card and Modal Checkboxes ---
170
171    /**
172     * Syncs modal checkbox changes with the main candidate card checkbox.
173     */
174    $(document).on('change', '.modal-select-button .select-checkbox', function() {
175        const modalCheckbox = $(this);
176        const candidateId = modalCheckbox.val();
177        const cardCheckbox = $('.candidate-card[data-candidate-id="' + candidateId + '"]').find('.select-button .select-checkbox');
178
179        if (modalCheckbox.is(':checked') !== cardCheckbox.is(':checked')) {
180            cardCheckbox.prop('checked', modalCheckbox.is(':checked')).trigger('change');
181        }
182
183        modalCheckbox.closest('.candidate-modal').css('display', 'none');
184        $('body').removeClass('modal-open');
185    });
186
187    /**
188     * Syncs main card checkbox changes to modal checkboxes (important when modal is open).
189     */
190    $(document).on('change', '.candidate-card .select-checkbox', function() {
191        const cardCheckbox = $(this);
192        const candidateId = cardCheckbox.val();
193        const modalCheckbox = $('#modal-' + candidateId + ' .modal-select-button .select-checkbox');
194
195        if (modalCheckbox.length && cardCheckbox.is(':checked') !== modalCheckbox.is(':checked')) {
196            modalCheckbox.prop('checked', cardCheckbox.is(':checked'));
197            const modalSelectText = modalCheckbox.closest('.modal-select-button').find('.select-text');
198            modalSelectText.text(cardCheckbox.is(':checked') ? 'Désélectionner' : 'Sélectionner');
199        }
200    });
201
202// --- AJAX Function for Vote Submission ---
203	/**
204	 * Submits the selected candidates' IDs via AJAX after successful email verification.
205	 * @param {string} verifiedEmail The email address successfully verified.
206	 */
207	function submitSelectedVotes(verifiedEmail) {
208		let selectedCandidatesForSubmission = [];
209		$('input.select-checkbox:checked').each(function() {
210			selectedCandidatesForSubmission.push($(this).val());
211		});
212
213		// Ensure array contains unique IDs and exactly two candidates are selected
214		selectedCandidatesForSubmission = [...new Set(selectedCandidatesForSubmission)];
215		if (selectedCandidatesForSubmission.length !== 2) {
216			// This alert should ideally not be hit if previous validations are in place
217			alert('Veuillez sélectionner exactement deux candidats pour voter.');
218			voteButton.prop('disabled', false).text('Voter'); // Re-enable button
219			return;
220		}
221
222		voteButton.prop('disabled', true).text('Envoi en cours...'); // Disable button during submission
223
224		$.ajax({
225			url: candidate_voting_ajax.ajaxurl,
226			type: 'POST',
227			data: {
228				action: 'process_vote',
229				//security: candidate_voting_ajax.nonce,
230				candidate_ids: selectedCandidatesForSubmission,
231				voter_email: verifiedEmail // Pass verified email to backend
232			},
233			success: function(response) {
234				if (response.success) {
235					alert('Votes enregistrés avec succès !');
236
237					// Send voting confirmation email
238					sendUserVotingConfirmationEmail(verifiedEmail, selectedCandidatesForSubmission);
239
240					// Redirect user to the vote confirmation page
241					window.location.href = 'https://parlementdesexiles.eu/votes-enregistres/?e_email=' + encodeURIComponent(verifiedEmail);
242
243				} else {
244					alert('Erreur : ' + response.data.message);
245					voteButton.prop('disabled', false).text('Voter');
246				}
247			},
248			error: function(xhr, status, error) {
249				console.error('AJAX Error:', status, error, xhr);
250				alert('Une erreur est survenue lors du vote. Veuillez réessayer.');
251				voteButton.prop('disabled', false).text('Voter');
252			}
253		});
254	}
255
256    /**
257     * Sends an AJAX request to trigger a voting confirmation email.
258     * @param {string} userEmail The email address to send the confirmation to.
259     * @param {Array<string>} candidateIds The IDs of the candidates voted for.
260     */
261    function sendUserVotingConfirmationEmail(userEmail, candidateIds) {
262        console.log('Initiating voting confirmation email send for:', userEmail, 'Candidates:', candidateIds);
263
264        $.ajax({
265            url: candidate_voting_ajax.ajaxurl,
266            type: 'POST',
267            data: {
268                action: 'pvp_send_voting_confirmation_email',
269                //security: candidate_voting_ajax.email_nonce,
270                voter_email: userEmail,
271                candidate_ids: candidateIds
272            },
273            success: function(response) {
274                if (response.success) {
275                    console.log('Voting confirmation email request sent successfully to server.');
276                } else {
277                    console.error('Server reported failure to send voting confirmation email:', response.data.message);
278                }
279            },
280            error: function(xhr, status, error) {
281                console.error('AJAX Error sending voting confirmation email request:', status, error, xhr);
282            }
283        });
284    }
285
286	// --- Main "Vote" Button Click Handler ---
287	/**
288	 * Handles the click on the main "Vote" button, opening the email verification modal.
289	 */
290	$('#submit-vote').on('click', function() {
291		// Client-side validation before opening the email modal
292		if (selectedIds.length !== 2) {
293			alert('Veuillez sélectionner exactement deux candidats pour voter.');
294			return; // Stop execution if validation fails
295		}
296
297		// If validation passes, proceed to show the email modal
298		voteEmailModal.css('display', 'flex'); // Show the email modal
299		document.getElementById('submit-vote').parentNode.style.display = "none";
300		$('body').addClass('modal-open'); // Prevent body scrolling
301		resetEmailModalState(); // Ensure modal is reset to initial state each time it opens
302	});
303
304    // --- Email Verification Modal Logic ---
305
306    /**
307     * AJAX function to check if the provided email exists in the Flamingo table
308     * and sends an SMS verification code.
309     * @param {string} voter_email The email address to check.
310     */
311    function checkFlamengoTable(voter_email) {
312        $.ajax({
313            url: candidate_voting_ajax.ajaxurl,
314            type: 'POST',
315            data: {
316                action: 'pvp_check_elector_email_exists',
317                voter_email: voter_email,
318                //security: candidate_voting_ajax.flamingo_nonce
319            },
320            beforeSend: function() {
321                sendCodeButton.prop('disabled', true).text('Vérification...');
322                console.log('AJAX: Sending email check request for:', voter_email);
323            },
324            success: function(response) {
325                sendCodeButton.prop('disabled', false).text('Envoyer le code');
326                verifyVoteButton.prop('disabled', false).text('Vérifier et Voter');
327                registerNowButton.prop('disabled', false).text('Je comprends et accepte'); // Ensure button is enabled and text is updated
328				//registerNowButton.prop('disabled', !registerAcceptanceCheckbox.is(':checked')).text('S\'inscrire maintenant');
329
330                if (response.debug_info) {
331                    console.groupCollapsed('AJAX: Server Debug Info (Click to expand)');
332                    console.log(response.debug_info);
333                    console.groupEnd();
334                }
335
336                emailInputPrompt.removeClass('email-input-prompt-error'); // Clear previous error styling
337
338                if (response.success) {
339                    emailInputPrompt.show();
340                    emailInputGroup.show();
341                    verificationCodeGroup.show();
342                    sendCodeButton.hide();
343                    verifyVoteButton.show();
344                    unregisteredFlowSection.hide();
345
346                    const phoneNumber = response.data.phone_number;
347                    if (phoneNumber) {
348                        const lastFourDigits = phoneNumber.slice(-4);
349                        emailInputPrompt.html(`Un code de vérification vous sera envoyé sur le numéro de téléphone se terminant par **${lastFourDigits}**. Ce code est valable 5 min.`);
350                    } else {
351                        emailInputPrompt.text('Un code de vérification vous sera envoyé sur le numéro de téléphone associé.');
352                    }
353                } else {
354                    const errorMessage = response.data.message || 'Une erreur est survenue.';
355                    emailInputPrompt.addClass('email-input-prompt-error').html(errorMessage).show();
356
357                    if (errorMessage.startsWith('Votre adresse e-mail') && errorMessage.endsWith('n\'est pas enregistrée.')) {
358                        emailInputGroup.hide();
359                        sendCodeButton.hide();
360                        verificationCodeGroup.hide();
361                        verifyVoteButton.hide();
362                        unregisteredFlowSection.show();
363                    } else if (errorMessage === 'Vous avez déjà voté. Vous ne pouvez voter qu\'une seule fois.') {
364                        // Keep relevant elements shown if already voted
365                        console.log('https://parlementdesexiles.eu/votes-enregistres/?e_email=' + encodeURIComponent(voter_email));
366                        window.location.href = 'https://parlementdesexiles.eu/votes-enregistres/?e_email=' + encodeURIComponent(voter_email);
367                        emailInputGroup.show();
368                        sendCodeButton.show();
369                        verificationCodeGroup.hide();
370                        verifyVoteButton.hide();
371                        unregisteredFlowSection.hide();
372						
373                    } else {
374                        // General error, show initial state
375                        emailInputGroup.show();
376                        sendCodeButton.show();
377                        verificationCodeGroup.hide();
378                        verifyVoteButton.hide();
379                        unregisteredFlowSection.hide();
380                    }
381                }
382            },
383            error: function(xhr, status, error) {
384                sendCodeButton.prop('disabled', false).text('Envoyer le code');
385                console.error('AJAX Error:', status, error, xhr);
386                alert('Une erreur est survenue lors de la vérification de l\'e-mail. Veuillez réessayer.');
387                resetEmailModalState(); // Reset modal on critical error
388            }
389        });
390    }
391
392    /**
393     * Resets the state of the email verification modal to its initial appearance.
394     */
395    function resetEmailModalState() {
396		// show voting button
397		voteButton.show();
398		
399        electorEmailInput.val('');
400        verificationCodeInput.val('');
401        emailInputPrompt.removeClass('email-input-prompt-error'); // Remove error class
402		
403        // Hide all specific sections
404        emailInputPrompt.hide();
405        emailInputGroup.hide();
406        sendCodeButton.hide();
407        verificationCodeGroup.hide();
408        verifyVoteButton.hide();
409        unregisteredFlowSection.hide();
410
411        // Reset button texts and states
412        sendCodeButton.text('Envoyer le code').prop('disabled', false);
413        verifyVoteButton.text('Vérifier et Voter').prop('disabled', false);
414        registerNowButton.text('Je comprends et accepte').prop('disabled', false); // Changed text and ensured it's not disabled
415        // registerAcceptanceCheckbox.prop('checked', false); // Removed this line as checkbox is gone
416
417        // Show the initial email input elements
418        emailInputPrompt.html('Veuillez indiquer l\'adresse électronique correspondant à votre inscription. Un code de vérification vous sera transmis au numéro de téléphone qui y est associé.').show();
419        emailInputGroup.show();
420        sendCodeButton.show();
421    }
422
423    // Close email modal when clicking the 'x' button
424    closeVoteEmailModalBtn.on('click', function() {
425        voteEmailModal.css('display', 'none');
426		document.getElementById('submit-vote').parentNode.style.display = "block";
427        $('body').removeClass('modal-open');
428        resetEmailModalState();
429    });
430
431    // Close email modal when clicking outside of the content area
432    $(window).on('click', function(event) {
433        if ($(event.target).is(voteEmailModal)) {
434            voteEmailModal.css('display', 'none');
435			document.getElementById('submit-vote').parentNode.style.display = "block";
436            $('body').removeClass('modal-open');
437            resetEmailModalState();
438        }
439    });
440
441	/**
442	 * Handler for "Send Code" button in the email modal.
443	 * Initiates the email/SMS verification process.
444	 */
445	$('#send-code-button').on('click', function() {
446		const email = $('#voter-email').val().trim();
447		const phone = $('#voter-phone').val().trim();
448		const lname = $('#voter-lname').val().trim();
449		const fname = $('#voter-fname').val().trim();
450		const nation = $('#voter-nationality').val().trim();
451		const not_in_france = $('#voter-lives-in-france').is(':checked');
452		const not_european = $('#voter-not-european').is(':checked');
453		const accept_privacy_policy = $('#accept_privacy_policy').is(':checked');
454		
455		
456		
457		const $sendButton = $(this);
458
459		if (!email) {
460			alert('Veuillez saisir votre adresse e-mail');
461			return;
462		}
463		if (!phone) {
464			alert('Veuillez saisir votre numéro de téléphone');
465			return;
466		}
467
468		if (!lname) {
469			alert('Veuillez saisir votre nom');
470			return;
471		}
472		
473		if (!fname) {
474			alert('Veuillez saisir votre prenom');
475			return;
476		} 
477		
478		if (!nation) {
479			alert('Veuillez choisir votre nationalité');
480			return;
481		} 
482		
483		if(!not_in_france || !not_european || !accept_privacy_policy) {
484			alert('Veuillez cocher toutes les cases requises.');
485			return;
486		}
487		$sendButton.prop('disabled', true).text('Vérification...');
488		
489		$.ajax({
490			url: candidate_voting_ajax.ajaxurl,
491			type: 'POST',
492			data: {
493				action: 'pvp_check_flamingo_email',
494				email: email,
495				phone: phone,
496				lname: lname,
497				fname: fname,
498				nation: nation,
499				not_in_france: not_in_france,
500				not_european: not_european,
501				accept_privacy_policy: accept_privacy_policy,
502				//security: candidate_voting_ajax.flamingo_nonce
503			},
504			beforeSend: function() {
505				$('#send-code-button').prop('disabled', true).text('Vérification...');
506			},
507			success: function(response) {
508				console.log('Full response:', response);
509				// Handle both success and error cases from wp_send_json_success/error
510				const data = response.data || {};
511
512				if (response.success) {
513					if (response.data.exists) {
514						// Existing user flow
515						$('.verification-code-group').show();
516						$('#verify-vote-button').show();
517						$sendButton.hide();
518						alert('Code envoyé au: ' + (response.data.phone ? '••••' + response.data.phone.slice(-4) : 'numéro enregistré'));
519					} else {
520						// New user flow
521						$('.verification-code-group').show();
522						$('#verify-vote-button').show();
523						$sendButton.hide();
524						alert('Nouvel utilisateur enregistré! Code envoyé au: ' + (response.data.phone ? '••••' + response.data.phone.slice(-4) : 'numéro fourni'));
525					}
526
527				} else {
528					// Show specific error message
529					if (response.data && response.data.message) {
530						alert(response.data.message);
531
532						// Show registration section only for "email not found" errors
533						if (response.data.message.includes('non trouvé')) {
534							//$('#unregistered-flow-section').show();
535						}
536					} else {
537						alert('Erreur inconnue');
538					}
539				}
540			},
541			error: function(xhr) {
542				console.error('AJAX Error:', xhr.responseText);
543				alert('Erreur technique lors de la vérification');
544			},
545			complete: function() {
546				$('#send-code-button').prop('disabled', false).text('Envoyer le code');
547			}
548		});
549
550	});
551
552	/**
553	 * Handler for "Verify & Vote" button in the email modal.
554	 * Verifies the entered SMS code and, if successful, triggers vote submission.
555	 */
556	$('#verify-vote-button').on('click', function() {
557		const voter_email = $('#voter-email').val().trim(); // Trim whitespace
558		const verificationCode = verificationCodeInput.val().trim(); // Trim whitespace
559
560		console.log('Available nonces:', candidate_voting_ajax); // Debug all nonces
561
562		// Client-side validation for email and verification code
563		if (!voter_email) {
564			alert('Veuillez saisir votre adresse e-mail.');
565			return;
566		}
567		if (!isValidEmail(voter_email)) {
568			alert('Veuillez saisir une adresse e-mail valide.');
569			return;
570		}
571
572		if (!verificationCode) {
573			alert('Veuillez saisir un code de vérification.');
574			return;
575		}
576
577		// Also ensure candidates are still selected, though the main button click should handle this.
578		// This is a redundant but safe check.
579		if (selectedIds.length !== 2) {
580			alert('Erreur: Veuillez sélectionner exactement deux candidats avant de vérifier le vote.');
581			// Optionally close the modal and reset here if this state is unexpected
582			voteEmailModal.css('display', 'none');
583			$('body').removeClass('modal-open');
584			resetEmailModalState();
585			return;
586		}
587
588		verifyVoteButton.prop('disabled', true).text('Vérification...'); // Disable button during verification
589		console.log('Using nonce:', candidate_voting_ajax.verify_code_nonce);
590		console.log('Using action:', 'verify_code'); // or whatever your action name is
591		$.ajax({
592			url: candidate_voting_ajax.ajaxurl,
593			type: 'POST',
594			data: {
595				action: 'verify_code',
596				//security: '2e4eec5897', //candidate_voting_ajax.verify_code_nonce,
597				voter_email: voter_email,
598				verification_code: verificationCode
599			},
600			beforeSend: function() {
601				// Optional: Show a loading spinner or message here
602			},
603			success: function(response) {
604				console.log("ajax response: " + response);
605				console.log(response);
606				if (response.success) {
607					console.log("ajax success: " + response.success);
608					// Before submitting, a final check on selected candidates.
609					// The submitSelectedVotes function also has this, but it's safer here too.
610					if (selectedIds.length === 2) {
611						submitSelectedVotes(voter_email); // Trigger vote submission with the verified email
612						voteEmailModal.css('display', 'none'); // Close modal
613						$('body').removeClass('modal-open');
614						resetEmailModalState(); // Reset modal fields
615					} else {
616						alert('Erreur interne: Les candidats sélectionnés sont manquants. Veuillez réessayer.');
617						verifyVoteButton.prop('disabled', false).text('Vérifier et Voter');
618						voteEmailModal.css('display', 'none');
619						$('body').removeClass('modal-open');
620						resetEmailModalState();
621					}
622				} else {
623					alert('Code incorrect. ' + (response.data.message || 'Veuillez réessayer.'));
624					verifyVoteButton.prop('disabled', false).text('Vérifier et Voter'); // Re-enable
625					verificationCodeInput.val(''); // Clear code input for retry
626				}
627			},
628			error: function(xhr, status, error) {
629				console.error('AJAX Error:', status, error, xhr);
630				alert('Une erreur est survenue lors de la vérification du code. Veuillez réessayer.');
631				verifyVoteButton.prop('disabled', false).text('Vérifier et Voter'); // Re-enable
632			},
633			complete: function() {
634				console.log("ajax complete");
635				// Optional: Hide loading spinner or message here
636			}
637		});
638	});
639
640    // --- Registration Flow Logic ---
641
642
643/**
644     * Handles "Register Now" button click, redirecting to the registration page.
645     * The checkbox logic has been removed as per new requirements.
646     */
647    registerNowButton.on('click', function() {
648        // The condition to check the checkbox is removed as per the new requirement
649        window.location.href = 'https://parlementdesexiles.eu/je-mengage/formulaire-delecteur/';
650    });
651
652    // --- Video Modal Logic ---
653
654/**
655     * Extracts video ID and determines video type (YouTube or Google Drive) from a given URL.
656     * @param {string} url The video URL.
657     * @returns {object|null} An object { id: 'video_id', type: 'youtube' | 'drive' }, or null if not found.
658     */
659    function getVideoIdAndType(url) {
660        console.log('GET_VIDEO_INFO: Received URL:', url);
661
662        if (!url) {
663            console.log('GET_VIDEO_INFO: URL is null or empty.');
664            return null;
665        }
666
667        // --- YouTube Patterns ---
668        const youtubePatterns = [
669            // Standard YouTube URLs (watch?v=, embed/, v/, etc.)
670            /(?:https?:\/\/)?(?:www\.)?(?:m\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=|embed\/|v\/|)([\w-]{11})(?:\S+)?/,
671            // YouTube Shorts URLs (e.g., youtube.com/shorts/VIDEO_ID)
672            /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com)\/shorts\/([\w-]{11})(?:\S+)?/,
673            // Legacy/Direct googleusercontent.com YouTube URLs (if still in use)
674            /(?:http:\/\/)?(?:googleusercontent\.com\/youtube\.com\/)([\w-]{11})/,
675            // Fallback for just the 11-character YouTube ID
676            /^([\w-]{11})$/
677        ];
678
679        for (const pattern of youtubePatterns) {
680            const match = url.match(pattern);
681            if (match && match[1]) {
682                console.log('GET_VIDEO_INFO: YouTube Match found, ID:', match[1], 'using pattern:', pattern);
683                return { id: match[1], type: 'youtube' };
684            }
685        }
686
687        // --- Google Drive Patterns ---
688        const drivePatterns = [
689            // Standard Google Drive file view/preview links (e.g., drive.google.com/file/d/FILE_ID/view)
690            /(?:https?:\/\/)?(?:drive\.google\.com)\/file\/d\/([a-zA-Z0-9_-]{28,})\/(?:view|preview)(?:\?.*)?/,
691            // Google Drive embed links (e.g., drive.google.com/embed/preview?id=FILE_ID)
692            /(?:https?:\/\/)?(?:drive\.google\.com)\/embed\/preview\?id=([a-zA-Z0-9_-]{28,})/,
693            // More generic Google Drive file ID extraction (looks for the 28+ character ID)
694            // This is a fallback and might need to be more specific if it causes false positives
695            /([a-zA-Z0-9_-]{28,})/
696        ];
697
698        for (const pattern of drivePatterns) {
699            const match = url.match(pattern);
700            if (match && match[1]) {
701                console.log('GET_VIDEO_INFO: Google Drive Match found, ID:', match[1], 'using pattern:', pattern);
702                return { id: match[1], type: 'drive' };
703            }
704        }
705
706        console.log('GET_VIDEO_INFO: No video ID or type found for URL:', url);
707        return null;
708    }
709
710/**
711     * Opens the video modal and embeds the appropriate video (YouTube or Google Drive).
712     * @param {string} videoUrl The URL of the video.
713     */
714    function openVideoModal(videoUrl) {
715        console.log('OPEN_VIDEO_MODAL: Attempting to open video for URL:', videoUrl);
716        const videoInfo = getVideoIdAndType(videoUrl); // <--- Make sure this is getVideoIdAndType
717        console.log('OPEN_VIDEO_MODAL: Extracted video Info:', videoInfo);
718
719        if (videoInfo && videoInfo.id) {
720            let embedUrl = '';
721            if (videoInfo.type === 'youtube') {
722                embedUrl = `https://www.youtube.com/embed/${videoInfo.id}?autoplay=1&rel=0`; // Standard YouTube embed
723            } else if (videoInfo.type === 'drive') {
724                // Google Drive embed URL format. Ensure the file is "Anyone with the link can view".
725                embedUrl = `https://drive.google.com/file/d/${videoInfo.id}/preview`; // Google Drive embed
726            } else {
727                console.error('OPEN_VIDEO_MODAL: Unknown video type:', videoInfo.type);
728                alert('Impossible de charger la vidéo. Type de vidéo 
728non pris en charge.');
729                return;
730            }
731
732            youtubePlayer.attr('src', embedUrl);
733            videoModal.css('display', 'flex');
734            $('body').addClass('modal-open');
735            console.log('OPEN_VIDEO_MODAL: Video modal opened with src:', youtubePlayer.attr('src'));
736        } else {
737            console.error('Invalid video URL or ID:', videoUrl);
738            alert('Impossible de charger la vidéo. URL de vidéo invalide.');
739        }
740    }
741
742    /**
743     * Closes the video modal and stops the video playback.
744     */
745    function closeVideoModal() {
746        videoModal.css('display', 'none');
747        youtubePlayer.attr('src', ''); // Stop the video
748        $('body').removeClass('modal-open');
749    }
750
751    // Event listeners for opening the video modal
752    videoButtons.on('click', function(event) {
753        event.preventDefault();
754        const videoUrl = $(this).data('video-url');
755        if (videoUrl) {
756            openVideoModal(videoUrl);
757        }
758    });
759
760    // Event listener for closing the video modal via the 'X' button
761    closeVideoModalBtn.on('click', closeVideoModal);
762
763    // Event listener for closing the video modal by clicking outside
764    videoModal.on('click', function(event) {
765        if ($(event.target).is(videoModal)) {
766            closeVideoModal();
767        }
768    });
769
770    // Event listener for closing the video modal with the Escape key
771    $(document).on('keydown', function(event) {
772        if (event.key === 'Escape' && videoModal.css('display') === 'flex') {
773            closeVideoModal();
774        }
775    });
776
777	
778	// Add this helper function within the jQuery(document).ready(function($) { ... }); block
779	function isValidEmail(email) {
780		// A more robust regex for email validation
781		// This regex is from: https://emailregex.com/ (HTML5 pattern)
782		return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(email);
783	}
784	
785	function checkEmailFlamingoTable(voter_email) {
786		// Ensure the voter_email is not empty before sending AJAX
787		if (!voter_email) {
788			alert("Please enter an email address.");
789			return;
790		}
791
792		$.ajax({
793			url: candidate_voting_ajax.ajaxurl,
794			type: 'POST',
795			data: {
796				// !!! IMPORTANT: Use the new AJAX action name here !!!
797				action: 'pvp_check_email_existence',
798				voter_email: voter_email,
799				//security: candidate_voting_ajax.flamingo_nonce // Reusing the existing nonce
800			},
801			success: function(response) {
802				console.log('AJAX Success Response:', response); // For debugging
803
804				if (response.success) {
805					// Email found
806					alert(response.data.message); // Should alert 'E-mail trouvé.'
807					// You can add further logic here, e.g., show the next step of the modal
808				} else {
809					// Email not found OR security check failed
810					alert(response.data.message); // Should alert 'E-mail non trouvé.' or 'Échec du contrôle de sécurité.'
811					// You can add further logic here, e.g., show the "unregistered" section
812				}
813			},
814			error: function(jqXHR, textStatus, errorThrown) {
815				console.error('AJAX Error:', textStatus, errorThrown, jqXHR);
816				alert('Une erreur est survenue lors de la vérification de l\'e-mail.');
817			}
818		});
819	}
820
821// Example of how you might call this:
822// Assuming you have an input field with id="voter-email" and a button with id="send-code-button"
823// You would attach this to the click event of your "Send Code" button (or similar trigger).
824
825// For example, in your candidates-selection.js:
826// $('#send-code-button').on('click', function() {
827//     const email = $('#voter-email').val();
828//     checkFlamingoTable(email);
829// });
830});

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.