1// Elementor 4 "Atomic" forms are elements rather than widgets, so they carry no .elementor-form class. 2var CFT_ATOMIC_FORM_SELECTOR = 'form[data-element_type="e-form"]'; 3var CFT_ATOMIC_AJAX_ACTION = 'elementor_pro_atomic_forms_send_form'; 4 5function cfturnstile_elementor_set_submit(btn, enabled) { 6 if (!btn) return; 7 btn.style.pointerEvents = enabled ? 'auto' : 'none'; 8 btn.style.opacity = enabled ? '1' : '0.5'; 9} 10 11/** 12 * Current Turnstile token for a form, whichever way it is available. 13 */ 14function cfturnstile_elementor_token(form) { 15 var widget = form ? form.querySelector('.cf-turnstile') : null; 16 if (!widget) return ''; 17 var token = ''; 18 if (window.turnstile) { 19 try { token = turnstile.getResponse(widget) || ''; } catch (e) {} 20 } 21 if (!token) { 22 var responseInput = form.querySelector('[name="cf-turnstile-response"]'); 23 token = responseInput ? (responseInput.value || '') : ''; 24 } 25 return token; 26} 27 28/** 29 * A form belonging to a popup document that has not been moved into a modal yet. 30 * 31 * Elementor Pro caches a popup's markup as an HTML string in its handler's onInit(), then rebuilds 32 * the popup from that string on every open. Anything rendered into the form while it is still 33 * inline gets baked into the cache as dead markup, so these forms are left to the popup/show 34 * handler below. 35 */ 36function cfturnstile_elementor_is_unshown_popup(form) { 37 if (!form || !form.closest) return false; 38 return !!form.closest('[data-elementor-type="popup"]') && !form.closest('.elementor-popup-modal'); 39} 40 41/** 42 * True when this container is backed by a widget Turnstile still knows about. 43 * 44 * A container rebuilt from Elementor's popup cache carries markup copied from an earlier render 45 * that is no longer a live widget. turnstile.remove() only warns and returns on such a container 46 * (it does not throw, so try/catch does not detect it) while turnstile.render() appends alongside 47 * the copy - which is how a popup ends up with two widgets and two response inputs. getResponse() 48 * does throw for an unknown container, so it is the reliable probe. 49 */ 50function cfturnstile_elementor_widget_is_live(widget) { 51 if (!widget || !window.turnstile) return false; 52 try { 53 turnstile.getResponse(widget); 54 return true; 55 } catch (e) { 56 return false; 57 } 58} 59 60function cfturnstile_init_elementor_forms() { 61 var settings = window.cfturnstileElementorSettings || {}; 62 var sitekey = settings.sitekey || ''; 63 var position = settings.position || 'before'; 64 var mode = settings.mode || 'turnstile'; 65 var recaptchaSiteKey = settings.recaptchaSiteKey || ''; 66 var disableSubmit = settings.disableSubmit || false; 67 var widgetSize = settings.size || 'normal'; 68 69 if (!window._cft_elementor_idx) { window._cft_elementor_idx = 0; } 70 var elementorForms = document.querySelectorAll('.elementor-form:not(.cft-processed)'); 71 elementorForms.forEach(function(form) { 72 if (cfturnstile_elementor_is_unshown_popup(form)) { return; } 73 74 var index = window._cft_elementor_idx++; 75 if (form.querySelector('.cf-turnstile') || form.querySelector('.g-recaptcha') || form.querySelector('input[name="cfturnstile_failsafe"]')) { 76 form.classList.add('cft-processed'); 77 return; 78 } 79 80 var submitButton = form.querySelector('button[type="submit"]'); 81 82 // Failsafe modes: inject marker and optionally reCAPTCHA widget. 83 if (submitButton && (mode === 'allow' || mode === 'recaptcha')) { 84 var marker = document.createElement('input'); 85 marker.type = 'hidden'; 86 marker.name = 'cfturnstile_failsafe'; 87 marker.value = mode; 88 form.appendChild(marker); 89 90 if (mode === 'recaptcha' && recaptchaSiteKey) { 91 var recaptchaDiv = document.createElement('div'); 92 recaptchaDiv.className = 'g-recaptcha'; 93 recaptchaDiv.setAttribute('data-sitekey', recaptchaSiteKey); 94 recaptchaDiv.style.cssText = 'display: block; margin: 10px 0 15px 0; width: 100%;'; 95 96 if (position === 'after') { 97 submitButton.parentNode.insertBefore(recaptchaDiv, submitButton.nextSibling); 98 } else if (position === 'afterform') { 99 form.appendChild(recaptchaDiv); 100 } else { 101 submitButton.parentNode.insertBefore(recaptchaDiv, submitButton); 102 } 103 } 104 105 form.classList.add('cft-processed'); 106 return; 107 } 108 109 if (submitButton && window.turnstile && sitekey) { 110 // Disable submit button if option is enabled 111 if (disableSubmit) { 112 cfturnstile_elementor_set_submit(submitButton, false); 113 } 114 115 var labelEl = null; 116 if (settings.labelEnable && settings.labelText) { 117 labelEl = document.createElement('p'); 118 labelEl.className = 'cfturnstile-widget-label'; 119 labelEl.style.cssText = 'font-size: 14px; margin: 0 0 6px 0; width: 100%;';
120 if ((settings.appearance || 'always') === 'interaction-only') { 121 labelEl.className += ' cfturnstile-widget-label-interaction'; 122 labelEl.style.display = 'none'; 123 } 124 var smallEl = document.createElement('small'); 125 smallEl.textContent = settings.labelText; 126 labelEl.appendChild(smallEl); 127 } 128 129 var turnstileDiv = document.createElement('div'); 130 turnstileDiv.className = 'elementor-turnstile-field cf-turnstile'; 131 turnstileDiv.id = 'cf-turnstile-elementor-fallback-' + index; 132 turnstileDiv.style.cssText = 'display: block; margin: 10px 0 15px 0; width: 100%;'; 133 134 if (position === 'after') { 135 if (labelEl) submitButton.parentNode.insertBefore(labelEl, submitButton.nextSibling); 136 submitButton.parentNode.insertBefore(turnstileDiv, labelEl ? labelEl.nextSibling : submitButton.nextSibling); 137 } else if (position === 'afterform') { 138 if (labelEl) form.appendChild(labelEl); 139 form.appendChild(turnstileDiv); 140 } else { 141 if (labelEl) submitButton.parentNode.insertBefore(labelEl, submitButton); 142 submitButton.parentNode.insertBefore(turnstileDiv, submitButton); 143 } 144 145 turnstile.render('#cf-turnstile-elementor-fallback-' + index, { 146 sitekey: sitekey, 147 theme: settings.theme || 'auto', 148 size: widgetSize, 149 appearance: settings.appearance || 'always', 150 callback: function(token) { 151 // Re-enable submit button when Turnstile is complete 152 if (disableSubmit && submitButton) { 153 cfturnstile_elementor_set_submit(submitButton, true); 154 } 155 if (typeof turnstileElementorCallback === 'function') { 156 turnstileElementorCallback(token); 157 } 158 }, 159 'error-callback': function() { 160 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 161 }, 162 'expired-callback': function() { 163 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 164 } 165 }); 166 167 if (labelEl && labelEl.classList.contains('cfturnstile-widget-label-interaction') && typeof window.cfturnstileInitInteractionLabels === 'function') { 168 window.cfturnstileInitInteractionLabels(); 169 } 170 171 form.classList.add('cft-processed'); 172 } 173 }); 174} 175 176document.addEventListener('DOMContentLoaded', function() { 177 cfturnstile_init_elementor_forms(); 178}); 179 180// Listen to Elementor frontend init to handle cached elements 181jQuery(window).on('elementor/frontend/init', function() { 182 cfturnstile_init_elementor_forms(); 183 184 // Hook into Elementor's widget ready system for forms loaded dynamically (e.g. in popups) 185 if (window.elementorFrontend && elementorFrontend.hooks) { 186 elementorFrontend.hooks.addAction('frontend/element_ready/form.default', function($scope) { 187 cfturnstile_init_elementor_forms(); 188 }); 189 elementorFrontend.hooks.addAction('frontend/element_ready/login.default', function($scope) { 190 cfturnstile_init_elementor_forms(); 191 }); 192 } 193}); 194 195// Re-render Turnstile only after Elementor reports a submit error (e.g. failed validation) 196function cfturnstile_elementor_rerender(form) { 197 if (!form || !window.turnstile) return; 198 var settings = window.cfturnstileElementorSettings || {}; 199 var widget = form.querySelector('.cf-turnstile'); 200 if (!widget) return; 201 var submitButton = form.querySelector('button[type="submit"]'); 202 var disableSubmit = settings.disableSubmit || false; 203 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 204 try { turnstile.remove(widget); } catch (e) {} 205 turnstile.render(widget, { 206 sitekey: settings.sitekey, 207 theme: settings.theme || 'auto', 208 size: settings.size || 'normal', 209 appearance: settings.appearance || 'always', 210 callback: function(token) { 211 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, true); } 212 if (typeof turnstileElementorCallback === 'function') { 213 turnstileElementorCallback(token); 214 } 215 }, 216 'error-callback': function() { 217 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 218 }, 219 'expired-callback': function() { 220 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 221 } 222 }); 223} 224 225jQuery(document).on('submit_error submit_success', '.elementor-form', function() { 226 var settings = window.cfturnstileElementorSettings || {}; 227 if ((settings.mode || 'turnstile') !== 'turnstile') return; 228 cfturnstile_elementor_rerender(this); 229}); 230 231// Block submission until Turnstile is completed (client-side guard). 232// Server-side validation in cfturnstile_elementor_check is the source of truth; 233// this just avoids a wasted round-trip and gives immediate feedback. 234document.addEventListener('submit', function(event) { 235 var form = event.target; 236 if (!form.classList) return; 237 var isClassic = form.classList.contains('elementor-form');
238 var isAtomic = !isClassic && form.matches && form.matches(CFT_ATOMIC_FORM_SELECTOR); 239 if (!isClassic && !isAtomic) return; 240 var settings = window.cfturnstileElementorSettings || {}; 241 if ((settings.mode || 'turnstile') !== 'turnstile' || !window.turnstile) return; 242 243 var widget = form.querySelector('.cf-turnstile'); 244 if (!widget) return; 245 if (!cfturnstile_elementor_token(form)) { 246 event.preventDefault(); 247 event.stopImmediatePropagation(); 248 try { widget.scrollIntoView({ behavior: 'smooth', block: 'center' }); } catch (e) {} 249 } 250}, true); 251 252// Handle Elementor popup show events (jQuery event - must use jQuery to listen) 253jQuery(document).on('elementor/popup/show', function(event, id, instance) { 254 setTimeout(function() { 255 // First, inject Turnstile into any unprocessed forms inside the popup 256 cfturnstile_init_elementor_forms(); 257 258 var settings = window.cfturnstileElementorSettings || {}; 259 var mode = settings.mode || 'turnstile'; 260 var disableSubmit = settings.disableSubmit || false; 261 if (mode !== 'turnstile' || !window.turnstile) { 262 return; 263 } 264 265 // Re-render all Turnstile widgets inside popup modals (they need explicit render after DOM insertion) 266 var popupTurnstiles = document.querySelectorAll('.elementor-popup-modal .cf-turnstile'); 267 popupTurnstiles.forEach(function(widget) { 268 var failedText = widget.parentNode ? widget.parentNode.querySelector('.cf-turnstile-failed-text') : null; 269 if (failedText) { 270 failedText.style.display = 'none'; 271 } 272 273 // Find the form and submit button for this widget 274 var form = widget.closest('.elementor-form'); 275 var submitButton = form ? form.querySelector('button[type="submit"]') : null; 276 277 // A widget Turnstile still owns is already rendered, and may hold a solved token that 278 // re-rendering would throw away. 279 if (cfturnstile_elementor_widget_is_live(widget)) { 280 if (disableSubmit && submitButton) { 281 cfturnstile_elementor_set_submit(submitButton, !!cfturnstile_elementor_token(form)); 282 } 283 return; 284 } 285 286 // Disable submit button if option is enabled 287 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 288 289 // Orphan markup rebuilt from Elementor's popup cache: clear it, or render() appends a second 290 // widget and the form posts two cf-turnstile-response inputs - the first one already spent. 291 try { turnstile.remove(widget); } catch (e) {} 292 widget.innerHTML = ''; 293 294 turnstile.render(widget, { 295 sitekey: cfturnstileElementorSettings.sitekey, 296 appearance: cfturnstileElementorSettings.appearance || 'always', 297 callback: function(token) { 298 // Re-enable submit button when Turnstile is complete 299 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, true); } 300 if (typeof turnstileElementorCallback === 'function') { 301 turnstileElementorCallback(token); 302 } 303 }, 304 'error-callback': function() { 305 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 306 }, 307 'expired-callback': function() { 308 if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); } 309 }, 310 theme: cfturnstileElementorSettings.theme || 'auto', 311 size: cfturnstileElementorSettings.size || 'normal' 312 }); 313 }); 314 }, 500); 315}); 316 317// Release the widgets of a popup that is closing. Elementor throws the popup DOM away and rebuilds 318// it from its cached string on the next open, so without this the widgets stay registered against 319// detached nodes for the rest of the page life. Deferred so the widget does not blink out of the 320// popup mid exit-animation - removing a detached container works just as well. 321jQuery(document).on('elementor/popup/hide', function(event, id) { 322 var modal = document.getElementById('elementor-popup-modal-' + id); 323 if (!modal || !window.turnstile) return; 324 var widgets = Array.prototype.slice.call(modal.querySelectorAll('.cf-turnstile')); 325 if (!widgets.length) return; 326 setTimeout(function() {
327 widgets.forEach(function(widget) { 328 try { turnstile.remove(widget); } catch (e) {} 329 }); 330 }, 1000); 331}); 332 333/* --------------------------------------------------------------------------- 334 * Elementor Atomic Forms (Elementor 4 "e-form" elements) 335 * 336 * Atomic forms are submitted by an Alpine handler bound to the form, which posts 337 * a FormData payload built only from the field widgets Elementor itself rendered 338 * (input/textarea/select carrying data-interaction-id). A hidden token input 339 * sitting inside the form is therefore never sent, so the token is appended to 340 * the outgoing request instead - see cfturnstile_atomic_patch_fetch() below. 341 * ------------------------------------------------------------------------ */ 342 343function cfturnstile_atomic_submit_button(form) { 344 return form ? form.querySelector('button[type="submit"], input[type="submit"]') : null; 345} 346 347/** 348 * Horizontal alignment of the widget inside its (column flex) wrapper. 349 */ 350function cfturnstile_atomic_align(align) { 351 if (align === 'center') return 'center'; 352 if (align === 'right') return 'flex-end'; 353 return 'flex-start'; 354} 355 356/** 357 * Place a node according to the configured widget position. Atomic forms are flex 358 * containers, so the node is inserted as a full width row of its own. 359 */ 360function cfturnstile_atomic_insert(form, node, position, submitButton) { 361 if (position === 'afterform' || !submitButton || !submitButton.parentNode) { 362 form.appendChild(node); 363 } else if (position === 'after') { 364 submitButton.parentNode.insertBefore(node, submitButton.nextSibling); 365 } else { 366 submitButton.parentNode.insertBefore(node, submitButton); 367 } 368} 369 370function cfturnstile_atomic_render_options(form) { 371 var settings = window.cfturnstileElementorSettings || {}; 372 var disableSubmit = settings.disableSubmit || false; 373 var submitButton = cfturnstile_atomic_submit_button(form); 374 return { 375 sitekey: settings.sitekey, 376 theme: settings.theme || 'auto', 377 size: settings.size || 'normal', 378 appearance: settings.appearance || 'always', 379 action: 'elementor-atomic-form', 380 callback: function(token) { 381 if (disableSubmit) { cfturnstile_elementor_set_submit(submitButton, true); } 382 if (typeof turnstileElementorCallback === 'function') { 383 turnstileElementorCallback(token); 384 } 385 }, 386 'error-callback': function() { 387 if (disableSubmit) { cfturnstile_elementor_set_submit(submitButton, false); } 388 }, 389 'expired-callback': function() { 390 if (disableSubmit) { cfturnstile_elementor_set_submit(submitButton, false); } 391 } 392 }; 393} 394 395function cfturnstile_init_elementor_atomic_forms() { 396 var settings = window.cfturnstileElementorSettings || {}; 397 var sitekey = settings.sitekey || ''; 398 var position = settings.position || 'before'; 399 var mode = settings.mode || 'turnstile'; 400 var recaptchaSiteKey = settings.recaptchaSiteKey || ''; 401 var disableSubmit = settings.disableSubmit || false; 402 403 if (!window._cft_atomic_idx) { window._cft_atomic_idx = 0; } 404 405 // Elementor re-renders form elements in place (editor edits, popups, Alpine refreshes), 406 // which drops the injected widget. Release those forms so they are processed again. 407 document.querySelectorAll(CFT_ATOMIC_FORM_SELECTOR + '.cft-processed').forEach(function(form) { 408 if (!form.querySelector('.cf-turnstile, .g-recaptcha, input[name="cfturnstile_failsafe"]')) { 409 form.classList.remove('cft-processed'); 410 } 411 }); 412 413 document.querySelectorAll(CFT_ATOMIC_FORM_SELECTOR + ':not(.cft-processed)').forEach(function(form) { 414 // Same popup caching trap as the classic forms above - wait until the popup is actually shown, 415 // at which point the observer below picks the form up with its atomic render options intact. 416 if (cfturnstile_elementor_is_unshown_popup(form)) { return; } 417 418 if (form.querySelector('.cf-turnstile, .g-recaptcha, input[name="cfturnstile_failsafe"]')) { 419 form.classList.add('cft-processed'); 420 return; 421 } 422 423 var submitButton = cfturnstile_atomic_submit_button(form); 424
425 // Failsafe modes: post a marker, and render reCAPTCHA in its place if configured. 426 if (mode === 'allow' || mode === 'recaptcha') { 427 var marker = document.createElement('input'); 428 marker.type = 'hidden'; 429 marker.name = 'cfturnstile_failsafe'; 430 marker.value = mode; 431 form.appendChild(marker); 432 433 if (mode === 'recaptcha' && recaptchaSiteKey) { 434 var recaptchaWrap = document.createElement('div'); 435 recaptchaWrap.className = 'cfturnstile-atomic-field'; 436 recaptchaWrap.style.cssText = 'display: flex; flex-direction: column; align-items: ' + cfturnstile_atomic_align(settings.align) + '; flex: 0 0 100%; width: 100%; margin: 10px 0 15px 0;'; 437 var recaptchaDiv = document.createElement('div'); 438 recaptchaDiv.className = 'g-recaptcha'; 439 recaptchaDiv.setAttribute('data-sitekey', recaptchaSiteKey); 440 recaptchaWrap.appendChild(recaptchaDiv); 441 cfturnstile_atomic_insert(form, recaptchaWrap, position, submitButton); 442 } 443 444 form.classList.add('cft-processed'); 445 return; 446 } 447 448 // The submit button is rendered as a child widget, so it can still be missing on an 449 // early pass. Wait for the next pass rather than injecting into a half built form. 450 if (!submitButton || !window.turnstile || !sitekey) return; 451 452 if (disableSubmit) { 453 cfturnstile_elementor_set_submit(submitButton, false); 454 } 455 456 var index = window._cft_atomic_idx++; 457 458 var wrap = document.createElement('div'); 459 wrap.className = 'cfturnstile-atomic-field'; 460 wrap.style.cssText = 'display: flex; flex-direction: column; align-items: ' + cfturnstile_atomic_align(settings.align) + '; flex: 0 0 100%; width: 100%; margin: 10px 0 15px 0;'; 461 462 if (settings.labelEnable && settings.labelText) { 463 var labelEl = document.createElement('p'); 464 labelEl.className = 'cfturnstile-widget-label'; 465 labelEl.style.cssText = 'font-size: 14px; margin: 0 0 6px 0;'; 466 if ((settings.appearance || 'always') === 'interaction-only') { 467 labelEl.className += ' cfturnstile-widget-label-interaction'; 468 labelEl.style.display = 'none'; 469 } 470 var smallEl = document.createElement('small'); 471 smallEl.textContent = settings.labelText; 472 labelEl.appendChild(smallEl); 473 wrap.appendChild(labelEl); 474 } 475 476 var turnstileDiv = document.createElement('div'); 477 turnstileDiv.className = 'elementor-turnstile-field cf-turnstile'; 478 turnstileDiv.id = 'cf-turnstile-elementor-atomic-' + index; 479 wrap.appendChild(turnstileDiv); 480 481 cfturnstile_atomic_insert(form, wrap, position, submitButton); 482 483 turnstile.render(turnstileDiv, cfturnstile_atomic_render_options(form)); 484 485 if (settings.labelEnable && (settings.appearance || 'always') === 'interaction-only' && typeof window.cfturnstileInitInteractionLabels === 'function') { 486 window.cfturnstileInitInteractionLabels(); 487 } 488 489 form.classList.add('cft-processed'); 490 }); 491} 492 493/** 494 * Reset a widget after its form has been submitted, so a retry gets a fresh token. 495 * Elementor calls form.reset() on success, which empties the token input while the 496 * widget still holds the spent token - the global refresh helper cannot detect that. 497 */ 498function cfturnstile_atomic_reset(form) { 499 if (!form || !window.turnstile) return; 500 var widget = form.querySelector('.cf-turnstile'); 501 if (!widget) return; 502 var settings = window.cfturnstileElementorSettings || {}; 503 if (settings.disableSubmit) { 504 cfturnstile_elementor_set_submit(cfturnstile_atomic_submit_button(form), false); 505 } 506 try { turnstile.reset(widget); } catch (e) {} 507} 508 509/** 510 * Attach the token to the atomic form request. 511 * 512 * Elementor builds the FormData itself and only includes its own field widgets, so 513 * there is no markup based way to get the token to the server. The wrapper only 514 * touches requests carrying the atomic form action and leaves everything else alone. 515 */ 516function cfturnstile_atomic_patch_fetch() { 517 if (window._cfturnstileAtomicFetchPatched) return; 518 var originalFetch = window.fetch; 519 if (typeof originalFetch !== 'function') return; 520 window._cfturnstileAtomicFetchPatched = true; 521 522 window.fetch = function(input, init) { 523 var form = null; 524 try { 525 var body = init && init.body; 526 if (body instanceof FormData && body.get('action') === CFT_ATOMIC_AJAX_ACTION) { 527 var formId = body.get('form_id'); 528 if (formId && /^[A-Za-z0-9_-]+$/.test(formId)) {
529 form = document.querySelector(CFT_ATOMIC_FORM_SELECTOR + '[data-id="' + formId + '"]'); 530 } 531 if (form) { 532 var token = cfturnstile_elementor_token(form); 533 if (token) { 534 body.set('cf-turnstile-response', token); 535 } 536 var failsafe = form.querySelector('input[name="cfturnstile_failsafe"]'); 537 if (failsafe && failsafe.value) { 538 body.set('cfturnstile_failsafe', failsafe.value); 539 } 540 var recaptcha = form.querySelector('textarea[name="g-recaptcha-response"], input[name="g-recaptcha-response"]'); 541 if (recaptcha && recaptcha.value) { 542 body.set('g-recaptcha-response', recaptcha.value); 543 } 544 } 545 } 546 } catch (e) {} 547 548 // Callers reaching fetch from strict mode code (bundles, ES modules) invoke it bare, so 549 // `this` arrives undefined. Current Chrome tolerates that, but engines that hold fetch to 550 // its WebIDL receiver throw "Illegal invocation" - fall back to the global rather than 551 // making that a property of whether this plugin happens to be active. 552 var request = originalFetch.apply(this || window, arguments); 553 554 if (form) { 555 var reset = function() { 556 // Let Elementor apply its own result handling (including form.reset()) first. 557 setTimeout(function() { cfturnstile_atomic_reset(form); }, 500); 558 }; 559 request.then(reset, reset); 560 } 561 562 return request; 563 }; 564} 565 566(function() { 567 cfturnstile_atomic_patch_fetch(); 568 569 var scheduled = null; 570 function schedule() { 571 if (scheduled) return; 572 scheduled = setTimeout(function() { 573 scheduled = null; 574 cfturnstile_init_elementor_atomic_forms(); 575 }, 100); 576 } 577 578 if (document.readyState === 'loading') { 579 document.addEventListener('DOMContentLoaded', schedule); 580 } else { 581 schedule(); 582 } 583 584 // Atomic forms are mounted by Elementor's own handler system and can appear or be 585 // re-rendered at any point (popups, editor edits, lazy loaded content), so watch the 586 // document rather than hooking into a specific lifecycle event. 587 if (window.MutationObserver) { 588 new MutationObserver(schedule).observe(document.documentElement, { childList: true, subtree: true }); 589 } 590})();
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.