(function(){
'use strict';
const CLOUDFLARE_CAPTCHA_ENABLED=true;
const CLOUDFLARE_DEBUG_ENABLED=true;
startScript();
function logCF(msg){
if(!CLOUDFLARE_DEBUG_ENABLED) return;
console.log('%c[Turnstile]', 'color:#ff9800;font-weight:600', msg);
}
function startScript(){
initTurnstileGF();
}
function initTurnstileGF(){
if(!CLOUDFLARE_CAPTCHA_ENABLED) return;
function setup(){
if(window.__diadaoTurnstileGfInitialized) return;
if(!window.gform||!window.gform.utils){
let attempts=0;
const timer=setInterval(function(){
attempts++;
if(window.gform&&window.gform.utils){
clearInterval(timer);
setup();
}else if(attempts >=100){
clearInterval(timer);
logCF('gform.utils non disponible après 10s - abandon');
}}, 100);
return;
}
window.__diadaoTurnstileGfInitialized=true;
document.addEventListener('click', function(e){
const button=e.target.closest('.diadao-sdk-button-gform');
if(!button) return;
if(button.hasAttribute('onclick')) return;
if(window.gform&&gform.submission&&typeof gform.submission.handleButtonClick==='function'){
e.preventDefault();
gform.submission.handleButtonClick(button);
}}, false);
const validatedForms=new Map();
const turnstileStates=new Map();
function getFormId(form, data){
return String(
data?.form?.dataset?.formid ||
form?.dataset?.formid ||
data?.formId ||
''
);
}
function getTurnstileState(formId){
const key=String(formId);
if(!turnstileStates.has(key)){
turnstileStates.set(key, {
widgetId: null,
pendingPromise: null,
pendingResolve: null,
pendingReject: null
});
}
return turnstileStates.get(key);
}
function clearPendingState(state){
state.pendingPromise=null;
state.pendingResolve=null;
state.pendingReject=null;
}
function resolvePendingState(state){
if(typeof state.pendingResolve==='function'){
state.pendingResolve();
}
clearPendingState(state);
}
function rejectPendingState(state, error){
if(typeof state.pendingReject==='function'){
state.pendingReject(error);
}
clearPendingState(state);
}
function getTurnstileElement(form){
return form.querySelector('.cf-turnstile');
}
function getTurnstileContainer(wrapper){
let container=wrapper.querySelector('[data-diadao-turnstile-widget="1"]');
if(!container){
container=document.createElement('div');
container.dataset.diadaoTurnstileWidget='1';
wrapper.appendChild(container);
}
return container;
}
function setTurnstileStatus(wrapper, status){
wrapper.classList.remove('diadao-turnstile-idle',
'diadao-turnstile-running',
'diadao-turnstile-success',
'diadao-turnstile-error',
'diadao-turnstile-interactive'
);
if(status){
wrapper.classList.add('diadao-turnstile-' + status);
}}
function isVisibleElement(element){
return !!(
element &&
!element.disabled &&
element.getClientRects().length &&
window.getComputedStyle(element).display!=='none' &&
window.getComputedStyle(element).visibility!=='hidden'
);
}
function getRequiredFieldInputs(field){
return Array.from(field.querySelectorAll('input[aria-required="true"], select[aria-required="true"], textarea[aria-required="true"]')
).filter((input)=> {
if(!isVisibleElement(input)){
return false;
}
return !['hidden', 'submit', 'button', 'image'].includes(input.type);
});
}
function isRequiredFieldFilled(field){
const inputs=getRequiredFieldInputs(field);
if(!inputs.length){
return true;
}
const firstInput=inputs[0];
const type=(firstInput.type||'').toLowerCase();
if(type==='radio'||type==='checkbox'){
return inputs.some((input)=> input.checked);
}
return inputs.every((input)=> String(input.value||'').trim()!=='');
}
function clearPreValidationSummary(form){
const container=form.querySelector('.gform_validation_errors.diadao-prevalidation-summary');
if(container){
container.remove();
}}
function clearPreValidationField(field){
field.classList.remove('gfield_error', 'diadao-prevalidation-error');
field
.querySelectorAll('.diadao-prevalidation-message')
.forEach((message)=> message.remove());
field
.querySelectorAll('[aria-invalid="true"]')
.forEach((input)=> input.setAttribute('aria-invalid', 'false'));
}
function clearPreValidation(form){
clearPreValidationSummary(form);
form
.querySelectorAll('.diadao-prevalidation-error')
.forEach((field)=> clearPreValidationField(field));
}
function markFieldInvalid(field){
field.classList.add('gfield_error', 'diadao-prevalidation-error');
getRequiredFieldInputs(field).forEach((input)=> {
input.setAttribute('aria-invalid', 'true');
});
if(!field.querySelector('.diadao-prevalidation-message')){
const message=document.createElement('div');
message.className='gfield_validation_message diadao-prevalidation-message';
message.textContent='Champ obligatoire.';
field.appendChild(message);
}}
function renderPreValidationSummary(form){
clearPreValidationSummary(form);
const heading=form.querySelector('.gform_body');
if(!heading){
return;
}
const container=document.createElement('div');
container.className='gform_validation_errors diadao-prevalidation-summary';
container.id=form.id + '_validation_container';
container.setAttribute('data-js', 'gform-focus-validation-error');
container.innerHTML='<h2 class="gform_submission_error hide_summary">Une erreur s\'est produite lors de votre envoi. Veuillez vérifier les champs ci-dessous.</h2>';
form.insertBefore(container, heading);
}
function findVisibleRequiredFieldErrors(form){
clearPreValidation(form);
const invalidFields=Array.from(form.querySelectorAll('.gfield.gfield_contains_required')
).filter((field)=> {
if(!isVisibleElement(field) ||
field.classList.contains('gfield--type-honeypot') ||
field.classList.contains('gfield--type-turnstile')
){
return false;
}
return !isRequiredFieldFilled(field);
});
if(!invalidFields.length){
return [];
}
invalidFields.forEach((field)=> markFieldInvalid(field));
renderPreValidationSummary(form);
const firstInvalidInput=getRequiredFieldInputs(invalidFields[0])[0];
if(firstInvalidInput){
firstInvalidInput.focus();
}
return invalidFields;
}
function isValid(formId){
const validatedAt=validatedForms.get(String(formId));
return typeof validatedAt==='number'&&(Date.now() - validatedAt < 300000);
}
function markValid(formId){
validatedForms.set(String(formId), Date.now());
}
function loadAPI(){
return new Promise((resolve, reject)=> {
if(window.turnstile) return resolve();
const existingScript=document.querySelector('script[data-diadao-turnstile-api="1"]');
if(existingScript){
existingScript.addEventListener('load', resolve, { once: true });
existingScript.addEventListener('error', ()=> reject(new Error('Turnstile API load failed')), { once: true });
return;
}
const s=document.createElement('script');
s.src='https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit';
s.defer=true;
s.dataset.diadaoTurnstileApi='1';
s.onload=resolve;
s.onerror=()=> reject(new Error('Turnstile API load failed'));
document.body.appendChild(s);
});
}
function ensureCaptchaWidget(form, formId){
const wrapper=getTurnstileElement(form);
if(!wrapper){
return null;
}
const state=getTurnstileState(formId);
const container=getTurnstileContainer(wrapper);
if(state.widgetId!==null){
return { wrapper, state };}
if(!wrapper.dataset.sitekey){
throw new Error('Turnstile sitekey missing');
}
setTurnstileStatus(wrapper, 'idle');
state.widgetId=window.turnstile.render(container, {
sitekey: wrapper.dataset.sitekey,
theme: wrapper.dataset.theme||'auto',
size: wrapper.dataset.size||'normal',
language: wrapper.dataset.language||'auto',
appearance: wrapper.dataset.appearance||'always',
execution: wrapper.dataset.execution||'execute',
callback: function(token){
wrapper.dataset.turnstileToken=token;
markValid(formId);
setTurnstileStatus(wrapper, 'success');
resolvePendingState(state);
},
'before-interactive-callback': function(){
setTurnstileStatus(wrapper, 'interactive');
},
'after-interactive-callback': function(){
if(!isValid(formId)){
setTurnstileStatus(wrapper, 'idle');
}},
'error-callback': function(errorCode){
validatedForms.delete(String(formId));
delete wrapper.dataset.turnstileToken;
setTurnstileStatus(wrapper, 'error');
rejectPendingState(state, new Error('Turnstile error: ' + (errorCode||'unknown')));
return true;
},
'expired-callback': function(){
validatedForms.delete(String(formId));
delete wrapper.dataset.turnstileToken;
setTurnstileStatus(wrapper, 'idle');
},
'timeout-callback': function(){
validatedForms.delete(String(formId));
delete wrapper.dataset.turnstileToken;
setTurnstileStatus(wrapper, 'error');
rejectPendingState(state, new Error('Turnstile timeout'));
}});
wrapper.dataset.turnstileWidgetId=String(state.widgetId);
return { wrapper, state };}
async function runCaptcha(form, formId){
const wrapper=getTurnstileElement(form);
if(!wrapper){
return;
}
await loadAPI();
const widget=ensureCaptchaWidget(form, formId);
if(!widget){
return;
}
const { state }=widget;
if(isValid(formId)){
return;
}
if(!state.pendingPromise){
state.pendingPromise=new Promise((resolve, reject)=> {
state.pendingResolve=resolve;
state.pendingReject=reject;
});
setTurnstileStatus(wrapper, 'running');
window.turnstile.execute(state.widgetId);
}
await state.pendingPromise;
}
gform.utils.addAsyncFilter('gform/submission/pre_submission', async (data)=> {
if(data.submissionType &&
data.submissionType!==gform.submission.SUBMISSION_TYPE_SUBMIT
){
return data;
}
const form=data.form||document.getElementById('gform_' + data.formId);
if(!form) return data;
const formId=getFormId(form, data);
const invalidFields=findVisibleRequiredFieldErrors(form);
logCF('GF submit #' + formId);
if(invalidFields.length){
data.abort=true;
logCF('Client precheck blocked captcha');
return data;
}
if(isValid(formId)){
logCF('Already validated');
return data;
}
try {
await runCaptcha(form, formId);
logCF('Captcha validated');
} catch (e){
data.abort=true;
logCF(e.message||'Captcha failed');
}
return data;
});
document.addEventListener('input', (event)=> {
const field=event.target.closest('.diadao-prevalidation-error');
if(!field){
return;
}
if(isRequiredFieldFilled(field)){
clearPreValidationField(field);
}});
document.addEventListener('change', (event)=> {
const field=event.target.closest('.diadao-prevalidation-error');
if(!field){
return;
}
if(isRequiredFieldFilled(field)){
clearPreValidationField(field);
}});
}
document.addEventListener('gform/theme/scripts_loaded', setup);
setup();
}})();