1/** 2 * reCAPTCHA v3 Integration 3 * Invisible reCAPTCHA v3 implementation for forms 4 * Version: 1.2.0 5 * Last Updated: 2025-09-29 6 */ 7 8if (typeof window.RecaptchaV3 !== 'undefined') { 9} else { 10 11function RecaptchaV3(siteKey) { 12 this.siteKey = siteKey; 13 this.isLoaded = false; 14 this.init(); 15} 16 17RecaptchaV3.prototype.init = function() { 18 19 if (document.querySelector('script[src*="recaptcha/api.js"]')) { 20 this.isLoaded = true; 21 } else { 22 var self = this; 23 var checkLoaded = function() { 24 if (typeof grecaptcha !== 'undefined') { 25 self.isLoaded = true; 26 } else { 27 setTimeout(checkLoaded, 100); 28 } 29 }; 30 checkLoaded(); 31 } 32}; 33 34RecaptchaV3.prototype.execute = function(action) { 35 var self = this; 36 return new Promise(function(resolve, reject) { 37 if (!self.isLoaded) { 38 reject(new Error('reCAPTCHA v3 not loaded yet')); 39 return; 40 } 41 42 if (typeof grecaptcha === 'undefined') { 43 reject(new Error('grecaptcha is not available')); 44 return; 45 } 46 47 try { 48 grecaptcha.execute(self.siteKey, { action: action }).then(function(token) { 49 resolve(token); 50 }).catch(function(error) { 51 console.error('reCAPTCHA v3 execution failed:', error); 52 reject(error); 53 }); 54 } catch (error) { 55 console.error('reCAPTCHA v3 execution failed:', error); 56 reject(error); 57 } 58 }); 59}; 60 61RecaptchaV3.prototype.addToForm = function(formSelector, action, onSuccess, onError) { 62 var self = this; 63 var form = document.querySelector(formSelector); 64 65 if (!form || !(form instanceof HTMLFormElement)) { 66 console.error('Form not found or invalid:', formSelector, form); 67 return; 68 } 69 70 var recaptchaInput = form.querySelector('input[name="g-recaptcha-response"]'); 71 if (!recaptchaInput) { 72 recaptchaInput = document.createElement('input'); 73 recaptchaInput.type = 'hidden'; 74 recaptchaInput.name = 'g-recaptcha-response'; 75 form.appendChild(recaptchaInput); 76 } 77 78 var isSubmitting = false; 79 form.addEventListener('submit', function(event) { 80 if (isSubmitting) return; 81 isSubmitting = true; 82 83 event.preventDefault(); // pour éviter double submit 84 85 self.execute(action).then(function(token) { 86 recaptchaInput.value = token; 87 88 if (onSuccess) onSuccess(token); 89 90 setTimeout(function() { 91 try { 92 HTMLFormElement.prototype.submit.call(form); 93 } catch (e) { 94 console.warn('Native submit failed, fallback to click:', e); 95 var submitButton = form.querySelector('input[type="submit"], button[type="submit"]'); 96 if (submitButton) { 97 submitButton.click(); 98 } else { 99 var tempSubmit = document.createElement('input'); 100 tempSubmit.type = 'submit'; 101 tempSubmit.style.display = 'none'; 102 form.appendChild(tempSubmit); 103 tempSubmit.click(); 104 form.removeChild(tempSubmit); 105 } 106 } 107 }, 50); 108 109 }).catch(function(error) { 110 console.error('reCAPTCHA v3 error:', error); 111 isSubmitting = false; 112 113 if (onError) { 114 onError(error); 115 } else { 116 if (typeof swal !== 'undefined') { 117 swal({ 118 title: 'Erreur', 119 text: 'Vérification de sécurité échouée. Veuillez réessayer.', 120 icon: 'error' 121 }); 122 } 123 } 124 }); 125 }); 126}; 127 128 129RecaptchaV3.prototype.addToForms = function(forms) { 130 var self = this;
131 forms.forEach(function(config) { 132 self.addToForm( 133 config.selector, 134 config.action || 'submit', 135 config.onSuccess, 136 config.onError 137 ); 138 }); 139}; 140 141window.RecaptchaV3 = RecaptchaV3; 142 143document.addEventListener('DOMContentLoaded', function() { 144 var currentVersion = '1.2.0'; 145 var loadedVersion = document.querySelector('meta[name="recaptcha-v3-version"]'); 146 147 if (loadedVersion && loadedVersion.content === currentVersion) { 148 console.warn('reCAPTCHA v3 version ' + currentVersion + ' already loaded'); 149 return; 150 } 151 152 var meta = document.createElement('meta'); 153 meta.name = 'recaptcha-v3-version'; 154 meta.content = currentVersion; 155 document.head.appendChild(meta); 156 157 var getSiteKeyFromScript = function() { 158 var script = document.querySelector('script[src*="recaptcha/api.js"]'); 159 if (script) { 160 var match = script.src.match(/render=([^&]+)/); 161 return match ? match[1] : null; 162 } 163 return null; 164 }; 165 166 var siteKey = getSiteKeyFromScript(); 167 168 var checkGrecaptcha = function() { 169 return typeof grecaptcha !== 'undefined'; 170 }; 171 172 if (siteKey && checkGrecaptcha()) { 173 window.recaptchaV3 = new RecaptchaV3(siteKey); 174 175 var forms = document.querySelectorAll('form[data-recaptcha]'); 176 forms.forEach(function(form) { 177 var action = form.dataset.recaptchaAction || 'submit'; 178 window.recaptchaV3.addToForm('#' + form.id, action); 179 }); 180 } else { 181 var forms = document.querySelectorAll('form[data-recaptcha]'); 182 forms.forEach(function(form) { 183 form.addEventListener('submit', function(event) { 184 event.preventDefault(); 185 return false; 186 }); 187 }); 188 } 189}); 190 191} // End of double loading protection
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.