1function cfturnstile_elementor_set_submit(btn, enabled) { 2 if (!btn) return; 3 btn.style.pointerEvents = enabled ? 'auto' : 'none'; 4 btn.style.opacity = enabled ? '1' : '0.5'; 5} 6 7function cfturnstile_init_elementor_forms() { 8 var settings = window.cfturnstileElementorSettings || {}; 9 var sitekey = settings.sitekey || ''; 10 var position = settings.position || 'before'; 11 var mode = settings.mode || 'turnstile'; 12 var recaptchaSiteKey = settings.recaptchaSiteKey || ''; 13 var disableSubmit = settings.disableSubmit || false; 14 var widgetSize = settings.size || 'normal'; 15 16 if (!window._cft_elementor_idx) { window._cft_elementor_idx = 0; } 17 var elementorForms = document.querySelectorAll('.elementor-form:not(.cft-processed)'); 18 elementorForms.forEach(function(form) { 19 var index = window._cft_elementor_idx++; 20 if (form.querySelector('.cf-turnstile') || form.querySelector('.g-recaptcha') || form.querySelector('input[name="cfturnstile_failsafe"]')) { 21 form.classList.add('cft-processed'); 22 return; 23 } 24 25 var submitButton = form.querySelector('button[type="submit"]'); 26 27 // Failsafe modes: inject marker and optionally reCAPTCHA widget. 28 if (submitButton && (mode === 'allow' || mode === 'recaptcha')) { 29 var marker = document.createElement('input'); 30 marker.type = 'hidden'; 31 marker.name = 'cfturnstile_failsafe'; 32 marker.value = mode; 33 form.appendChild(marker); 34 35 if (mode === 'recaptcha' && recaptchaSiteKey) { 36 var recaptchaDiv = document.createElement('div'); 37 recaptchaDiv.className = 'g-recaptcha'; 38 recaptchaDiv.setAttribute('data-sitekey', recaptchaSiteKey); 39 recaptchaDiv.style.cssText = 'display: block; margin: 10px 0 15px 0; width: 100%;'; 40 41 if (position === 'after') { 42 submitButton.parentNode.insertBefore(recaptchaDiv, submitButton.nextSibling); 43 } else if (position === 'afterform') { 44 form.appendChild(recaptchaDiv); 45 } else { 46 submitButton.parentNode.insertBefore(recaptchaDiv, submitButton); 47 } 48 } 49 50 form.classList.add('cft-processed'); 51 return; 52 } 53 54 if (submitButton && window.turnstile && sitekey) { 55 // Disable submit button if option is enabled 56 if (disableSubmit) { 57 cfturnstile_elementor_set_submit(submitButton, false); 58 } 59 60 var labelEl = null; 61 if (settings.labelEnable && settings.labelText) { 62 labelEl = document.createElement('p'); 63 labelEl.className = 'cfturnstile-widget-label'; 64 labelEl.style.cssText = 'font-size: 14px; margin: 0 0 6px 0; width: 100%;'; 65 if ((settings.appearance || 'always') === 'interaction-only') { 66 labelEl.className += ' cfturnstile-widget-label-interaction'; 67 labelEl.style.display = 'none'; 68 } 69 var smallEl = document.createElement('small'); 70 smallEl.textContent = settings.labelText; 71 labelEl.appendChild(smallEl); 72 } 73 74 var turnstileDiv = document.createElement('div'); 75 turnstileDiv.className = 'elementor-turnstile-field cf-turnstile'; 76 turnstileDiv.id = 'cf-turnstile-elementor-fallback-' + index; 77 turnstileDiv.style.cssText = 'display: block; margin: 10px 0 15px 0; width: 100%;'; 78 79 if (position === 'after') { 80 if (labelEl) submitButton.parentNode.insertBefore(labelEl, submitButton.nextSibling); 81 submitButton.parentNode.insertBefore(turnstileDiv, labelEl ? labelEl.nextSibling : submitButton.nextSibling); 82 } else if (position === 'afterform') { 83 if (labelEl) form.appendChild(labelEl); 84 form.appendChild(turnstileDiv); 85 } else { 86 if (labelEl) submitButton.parentNode.insertBefore(labelEl, submitButton); 87 submitButton.parentNode.insertBefore(turnstileDiv, submitButton); 88 } 89 90 turnstile.render('#cf-turnstile-elementor-fallback-' + index, { 91 sitekey: sitekey, 92 theme: settings.theme || 'auto', 93 size: widgetSize, 94 appearance: settings.appearance || 'always', 95 callback: function(token) { 96 // Re-enable submit button when Turnstile is complete 97 if (disableSubmit && submitButton) { 98 cfturnstile_elementor_set_submit(submitButton, true); 99 } 100 if (typeof turnstileElementorCallback === 'function') { 101 turnstileElementorCallback(token); 102 } 103 }, 104 'error-callback': function() { 105 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 106 }, 107 'expired-callback': function() { 108 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 109 } 110 }); 111 112 if (labelEl && labelEl.classList.contains('cfturnstile-widget-label-interaction') && typeof window.cfturnstileInitInteractionLabels === 'function') { 113 window.cfturnstileInitInteractionLabels(); 114 } 115 116 form.classList.add('cft-processed'); 117 } 118 }); 119} 120 121document.addEventListener('DOMContentLoaded', function() { 122 cfturnstile_init_elementor_forms(); 123}); 124 125// Listen to Elementor frontend init to handle cached elements 126jQuery(window).on('elementor/frontend/init', function() { 127 cfturnstile_init_elementor_forms(); 128 129 // Hook into Elementor's widget ready system for forms loaded dynamically (e.g. in popups) 130 if (window.elementorFrontend && elementorFrontend.hooks) { 131 elementorFrontend.hooks.addAction('frontend/element_ready/form.default', function($scope) { 132 cfturnstile_init_elementor_forms(); 133 });
134 elementorFrontend.hooks.addAction('frontend/element_ready/login.default', function($scope) { 135 cfturnstile_init_elementor_forms(); 136 }); 137 } 138}); 139 140// Re-render Turnstile only after Elementor reports a submit error (e.g. failed validation) 141function cfturnstile_elementor_rerender(form) { 142 if (!form || !window.turnstile) return; 143 var settings = window.cfturnstileElementorSettings || {}; 144 var widget = form.querySelector('.cf-turnstile'); 145 if (!widget) return; 146 var submitButton = form.querySelector('button[type="submit"]'); 147 var disableSubmit = settings.disableSubmit || false; 148 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 149 try { turnstile.remove(widget); } catch (e) {} 150 turnstile.render(widget, { 151 sitekey: settings.sitekey, 152 theme: settings.theme || 'auto', 153 size: settings.size || 'normal', 154 appearance: settings.appearance || 'always', 155 callback: function(token) { 156 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, true); } 157 if (typeof turnstileElementorCallback === 'function') { 158 turnstileElementorCallback(token); 159 } 160 }, 161 'error-callback': function() { 162 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 163 }, 164 'expired-callback': function() { 165 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 166 } 167 }); 168} 169 170jQuery(document).on('submit_error submit_success', '.elementor-form', function() { 171 var settings = window.cfturnstileElementorSettings || {}; 172 if ((settings.mode || 'turnstile') !== 'turnstile') return; 173 cfturnstile_elementor_rerender(this); 174}); 175 176// Block submission until Turnstile is completed (client-side guard). 177// Server-side validation in cfturnstile_elementor_check is the source of truth; 178// this just avoids a wasted round-trip and gives immediate feedback. 179document.addEventListener('submit', function(event) { 180 var form = event.target; 181 if (!form.classList || !form.classList.contains('elementor-form')) return; 182 var settings = window.cfturnstileElementorSettings || {}; 183 if ((settings.mode || 'turnstile') !== 'turnstile' || !window.turnstile) return; 184 185 var widget = form.querySelector('.cf-turnstile'); 186 if (!widget) return; 187 var token = ''; 188 try { token = turnstile.getResponse(widget) || ''; } catch (e) {} 189 if (!token) { 190 var responseInput = form.querySelector('[name="cf-turnstile-response"]'); 191 token = responseInput ? (responseInput.value || '') : ''; 192 } 193 if (!token) { 194 event.preventDefault(); 195 event.stopImmediatePropagation(); 196 try { widget.scrollIntoView({ behavior: 'smooth', block: 'center' }); } catch (e) {} 197 } 198}, true); 199 200// Handle Elementor popup show events (jQuery event - must use jQuery to listen) 201jQuery(document).on('elementor/popup/show', function(event, id, instance) { 202 setTimeout(function() { 203 // First, inject Turnstile into any unprocessed forms inside the popup 204 cfturnstile_init_elementor_forms(); 205 206 var settings = window.cfturnstileElementorSettings || {}; 207 var mode = settings.mode || 'turnstile'; 208 var disableSubmit = settings.disableSubmit || false; 209 if (mode !== 'turnstile' || !window.turnstile) { 210 return; 211 } 212 213 // Re-render all Turnstile widgets inside popup modals (they need explicit render after DOM insertion) 214 var popupTurnstiles = document.querySelectorAll('.elementor-popup-modal .cf-turnstile'); 215 popupTurnstiles.forEach(function(widget) { 216 var failedText = widget.parentNode ? widget.parentNode.querySelector('.cf-turnstile-failed-text') : null; 217 if (failedText) { 218 failedText.style.display = 'none'; 219 } 220 221 // Find the form and submit button for this widget 222 var form = widget.closest('.elementor-form'); 223 var submitButton = form ? form.querySelector('button[type="submit"]') : null; 224 225 // Disable submit button if option is enabled 226 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 227 228 turnstile.remove(widget); 229 turnstile.render(widget, { 230 sitekey: cfturnstileElementorSettings.sitekey, 231 appearance: cfturnstileElementorSettings.appearance || 'always', 232 callback: function(token) { 233 // Re-enable submit button when Turnstile is complete 234 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, true); } 235 if (typeof turnstileElementorCallback === 'function') { 236 turnstileElementorCallback(token); 237 } 238 }, 239 'error-callback': function() { 240 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 241 }, 242 'expired-callback': function() { 243 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 244 }, 245 theme: cfturnstileElementorSettings.theme || 'auto', 246 size: cfturnstileElementorSettings.size || 'normal' 247 }); 248 });
249 }, 500); 250});
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.