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.