1document.addEventListener('DOMContentLoaded', function() { 2 const formOffre = document.getElementById('offre_contact_form'); 3 const submitBtnOffre = document.getElementById('btn_submit_offre_contact'); 4 const messageDivOffre = document.getElementById('offre_form_message'); 5 const telInputOffre = document.getElementById('offre_contact_tel'); 6 7 // Validation du formulaire 8 function validateOffreForm() { 9 const requiredInputs = formOffre.querySelectorAll('[required]'); 10 const rgpdCheckbox = document.getElementById('offre_contact_rgpd'); 11 let isValid = true; 12 requiredInputs.forEach(input => { 13 if (!input.value.trim()) { 14 isValid = false; 15 } 16 }); 17 18 // Vérifie que la checkbox RGPD est cochée 19 if (!rgpdCheckbox.checked) { 20 isValid = false; 21 } 22 23 submitBtnOffre.disabled = !isValid; 24 } 25 26 // Restreindre le champ téléphone aux chiffres 27 if (telInputOffre) { 28 telInputOffre.addEventListener('input', function(e) { 29 this.value = this.value.replace(/[^0-9]/g, ''); 30 }); 31 } 32 33 // Validation en temps réel 34 formOffre.addEventListener('input', validateOffreForm); 35 36 // Soumission du formulaire 37 formOffre.addEventListener('submit', function(e) { 38 e.preventDefault(); 39 40 // reCAPTCHA : token frais genere au moment de la soumission, juste 41 // avant la construction du FormData, pour eviter les rejets 'expired' 42 // (token perime) et 'dupe' (token reutilise) lies au token global. 43 if( formOffre.dataset.recaptchaFresh === '1' ) { 44 formOffre.dataset.recaptchaFresh = ''; 45 } else if( typeof grecaptcha !== 'undefined' ) { 46 if( formOffre.dataset.recaptchaPending === '1' ) return; 47 formOffre.dataset.recaptchaPending = '1'; 48 submitBtnOffre.disabled = true; 49 var resoumettreOffre = function() { 50 formOffre.dataset.recaptchaFresh = '1'; 51 formOffre.dataset.recaptchaPending = ''; 52 if( formOffre.requestSubmit ) formOffre.requestSubmit(); 53 else formOffre.dispatchEvent(new Event('submit', { cancelable: true })); 54 }; 55 grecaptcha.ready(function() { 56 grecaptcha.execute('6LfreFQrAAAAABkcHTeLIj2kQD3YbQMkSTG7xrtP', { action: 'submit' }) 57 .then(function(token) { 58 var rc = formOffre.querySelector('.recaptcha-token, #recaptchaResponseOffre'); 59 if( rc ) rc.value = token; 60 resoumettreOffre(); 61 }) 62 .catch(resoumettreOffre); 63 }); 64 return; 65 } 66 67 const formData = new FormData(formOffre); 68 const originalText = submitBtnOffre.value; 69 submitBtnOffre.disabled = true; 70 submitBtnOffre.value = 'Envoi en cours...'; 71 72 fetch('/creer_lead.php', { 73 method: 'POST', 74 body: formData 75 }) 76 .then(response => response.json()) 77 .then(data => { 78 messageDivOffre.style.display = 'block'; 79 if (data.success) { 80 messageDivOffre.className = 'form_message success'; 81 messageDivOffre.textContent = data.message; 82 formOffre.reset(); 83 submitBtnOffre.disabled = true; 84 } else { 85 messageDivOffre.className = 'form_message error'; 86 messageDivOffre.textContent = data.message; 87 } 88 }) 89 .catch(error => { 90 messageDivOffre.style.display = 'block'; 91 messageDivOffre.className = 'form_message error'; 92 messageDivOffre.textContent = 'Une erreur est survenue. Veuillez réessayer.'; 93 }) 94 .finally(() => { 95 submitBtnOffre.disabled = false; 96 submitBtnOffre.value = originalText; 97 }); 98 }); 99 });
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.