PageSourceSearch

https://knowmold.com/wp-content/plugins/simple-cloudflare-turnstile/js/integrations/elementor-forms.js?ver=3.1

js knowmold.com collected 2026-10-02 22:07:39 UTC 26,876 bytes, 623 lines download raw bytes

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        action: 'elementor-form',
151        callback: function(token) {
152          // Re-enable submit button when Turnstile is complete
153          if (disableSubmit && submitButton) {
154            cfturnstile_elementor_set_submit(submitButton, true);
155          }
156          if (typeof turnstileElementorCallback === 'function') {
157            turnstileElementorCallback(token);
158          }
159        },
160        'error-callback': function() {
161          if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); }
162        },
163        'expired-callback': function() {
164          if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); }
165        }
166      });
167
168      if (labelEl && labelEl.classList.contains('cfturnstile-widget-label-interaction') && typeof window.cfturnstileInitInteractionLabels === 'function') {
169        window.cfturnstileInitInteractionLabels();
170      }
171
172      form.classList.add('cft-processed');
173    }
174  });
175}
176
177// "Delay JavaScript" in performance plugins can run this file after DOMContentLoaded and
178// elementor/frontend/init have already fired (LiteSpeed Cache), or before jQuery has loaded
179// (Perfmatters, which this file is excluded from), so nothing here can rely on either.
180if (document.readyState === 'loading') {
181  document.addEventListener('DOMContentLoaded', function() {
182    cfturnstile_init_elementor_forms();
183  });
184} else {
185  cfturnstile_init_elementor_forms();
186}
187
188// Elementor frontend init, to handle cached elements
189function cfturnstile_elementor_frontend_init() {
190  cfturnstile_init_elementor_forms();
191
192  // Elementor runs on jQuery, so it has loaded by now
193  cfturnstile_elementor_jquery_events();
194
195  // Hook into Elementor's widget ready system for forms loaded dynamically (e.g. in popups)
196  if (window.elementorFrontend && elementorFrontend.hooks) {
197    elementorFrontend.hooks.addAction('frontend/element_ready/form.default', function($scope) {
198      cfturnstile_init_elementor_forms();
199    });
200    elementorFrontend.hooks.addAction('frontend/element_ready/login.default', function($scope) {
201      cfturnstile_init_elementor_forms();
202    });
203  }
204}
205
206// Elementor sets its hooks just before it fires elementor/frontend/init, so if they exist it has already run.
207// Current Elementor fires the event natively as well, for when jQuery has not loaded yet.
208cfturnstile_elementor_jquery_events();
209if (window.elementorFrontend && elementorFrontend.hooks) {
210  cfturnstile_elementor_frontend_init();
211} else if (window.jQuery) {
212  jQuery(window).on('elementor/frontend/init', cfturnstile_elementor_frontend_init);
213} else {
214  window.addEventListener('elementor/frontend/init', cfturnstile_elementor_frontend_init);
215}
216
217// Elementor Pro fires the form and popup events through jQuery
218function cfturnstile_elementor_jquery_events() {
219  if (window._cft_elementor_jquery_events || !window.jQuery) return;
220  window._cft_elementor_jquery_events = true;
221  jQuery(document).on('submit_error submit_success', '.elementor-form', cfturnstile_elementor_submit_result);
222  jQuery(document).on('elementor/popup/show', cfturnstile_elementor_popup_show);
223  jQuery(document).on('elementor/popup/hide', cfturnstile_elementor_popup_hide);
224}
225
226// Re-render Turnstile only after Elementor reports a submit error (e.g. failed validation)
227function cfturnstile_elementor_rerender(form) {
228  if (!form || !window.turnstile) return;
229  var settings = window.cfturnstileElementorSettings || {};
230  var widget = form.querySelector('.cf-turnstile');
231  if (!widget) return;
232  var submitButton = form.querySelector('button[type="submit"]');
233  var disableSubmit = settings.disableSubmit || false;
234  if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); }
235  try { turnstile.remove(widget); } catch (e) {}
236  turnstile.render(widget, {
237    sitekey: settings.sitekey,
238    theme: settings.theme || 'auto',
239    size: settings.size || 'normal',
240    appearance: settings.appearance || 'always',
241    action: 'elementor-form',
242    callback: function(token) {
243      if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, true); }
244      if (typeof turnstileElementorCallback === 'function') {
245        turnstileElementorCallback(token);
246      }
247    },
248    'error-callback': function() {
249      if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); }
250    },
251    'expired-callback': function() {
252      if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); }
253    }
254  });
255}
256
257function cfturnstile_elementor_submit_result() {
258  var settings = window.cfturnstileElementorSettings || {};
259  if ((settings.mode || 'turnstile') !== 'turnstile') return;
260  cfturnstile_elementor_rerender(this);
261}
262
263// Block submission until Turnstile is completed (client-side guard).
264// Server-side validation in cfturnstile_elementor_check is the source of truth;
265// this just avoids a wasted round-trip and gives immediate feedback.
266document.addEventListener('submit', function(event) {
267  var form = event.target;
268  if (!form.classList) return;
269  var isClassic = form.classList.contains('elementor-form');
270  var isAtomic = !isClassic && form.matches && form.matches(CFT_ATOMIC_FORM_SELECTOR);
271  if (!isClassic && !isAtomic) return;
272  var settings = window.cfturnstileElementorSettings || {};
273  if ((settings.mode || 'turnstile') !== 'turnstile' || !window.turnstile) return;
274
275  var widget = form.querySelector('.cf-turnstile');
276  if (!widget) return;
277  if (!cfturnstile_elementor_token(form)) {
278    event.preventDefault();
279    event.stopImmediatePropagation();
280    try { widget.scrollIntoView({ behavior: 'smooth', block: 'center' }); } catch (e) {}
281  }
282}, true);
283
284// Handle Elementor popup show events
285function cfturnstile_elementor_popup_show(event, id, instance) {
286  setTimeout(function() {
287    // First, inject Turnstile into any unprocessed forms inside the popup
288    cfturnstile_init_elementor_forms();
289
290    var settings = window.cfturnstileElementorSettings || {};
291    var mode = settings.mode || 'turnstile';
292    var disableSubmit = settings.disableSubmit || false;
293    if (mode !== 'turnstile' || !window.turnstile) {
294      return;
295    }
296
297    // Re-render all Turnstile widgets inside popup modals (they need explicit render after DOM insertion)
298    var popupTurnstiles = document.querySelectorAll('.elementor-popup-modal .cf-turnstile');
299    popupTurnstiles.forEach(function(widget) {
300      var failedText = widget.parentNode ? widget.parentNode.querySelector('.cf-turnstile-failed-text') : null;
301      if (failedText) {
302        failedText.style.display = 'none';
303      }
304
305      // Find the form and submit button for this widget
306      var form = widget.closest('.elementor-form');
307      var submitButton = form ? form.querySelector('button[type="submit"]') : null;
308
309      // A widget Turnstile still owns is already rendered, and may hold a solved token that
310      // re-rendering would throw away.
311      if (cfturnstile_elementor_widget_is_live(widget)) {
312        if (disableSubmit && submitButton) {
313          cfturnstile_elementor_set_submit(submitButton, !!cfturnstile_elementor_token(form));
314        }
315        return;
316      }
317
318      // Disable submit button if option is enabled
319      if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); }
320
321      // Orphan markup rebuilt from Elementor's popup cache: clear it, or render() appends a second
322      // widget and the form posts two cf-turnstile-response inputs - the first one already spent.
323      try { turnstile.remove(widget); } catch (e) {}
324      widget.innerHTML = '';
325
326      turnstile.render(widget, {
327        sitekey: cfturnstileElementorSettings.sitekey,
328        appearance: cfturnstileElementorSettings.appearance || 'always',
329        action: 'elementor-form',
330        callback: function(token) {
331          // Re-enable submit button when Turnstile is complete
332          if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, true); }
333          if (typeof turnstileElementorCallback === 'function') {
334            turnstileElementorCallback(token);
335          }
336        },
337        'error-callback': function() {
338          if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); }
339        },
340        'expired-callback': function() {
341          if (disableSubmit && submitButton) { cfturnstile_elementor_set_submit(submitButton, false); }
342        },
343        theme: cfturnstileElementorSettings.theme || 'auto',
344        size: cfturnstileElementorSettings.size || 'normal'
345      });
346    });
347  }, 500);
348}
349
350// Release the widgets of a popup that is closing. Elementor throws the popup DOM away and rebuilds
351// it from its cached string on the next open, so without this the widgets stay registered against
352// detached nodes for the rest of the page life. Deferred so the widget does not blink out of the
353// popup mid exit-animation - removing a detached container works just as well.
354function cfturnstile_elementor_popup_hide(event, id) {
355  var modal = document.getElementById('elementor-popup-modal-' + id);
356  if (!modal || !window.turnstile) return;
357  var widgets = Array.prototype.slice.call(modal.querySelectorAll('.cf-turnstile'));
358  if (!widgets.length) return;
359  setTimeout(function() {
360    widgets.forEach(function(widget) {
361      try { turnstile.remove(widget); } catch (e) {}
362    });
363  }, 1000);
364}
365
366/* ---------------------------------------------------------------------------
367 * Elementor Atomic Forms (Elementor 4 "e-form" elements)
368 *
369 * Atomic forms are submitted by an Alpine handler bound to the form, which posts
370 * a FormData payload built only from the field widgets Elementor itself rendered
371 * (input/textarea/select carrying data-interaction-id). A hidden token input
372 * sitting inside the form is therefore never sent, so the token is appended to
373 * the outgoing request instead - see cfturnstile_atomic_patch_fetch() below.
374 * ------------------------------------------------------------------------ */
375
376function cfturnstile_atomic_submit_button(form) {
377  return form ? form.querySelector('button[type="submit"], input[type="submit"]') : null;
378}
379
380/**
381 * Horizontal alignment of the widget inside its (column flex) wrapper.
382 */
383function cfturnstile_atomic_align(align) {
384  if (align === 'center') return 'center';
385  if (align === 'right') return 'flex-end';
386  return 'flex-start';
387}
388
389/**
390 * Place a node according to the configured widget position. Atomic forms are flex
391 * containers, so the node is inserted as a full width row of its own.
392 */
393function cfturnstile_atomic_insert(form, node, position, submitButton) {
394  if (position === 'afterform' || !submitButton || !submitButton.parentNode) {
395    form.appendChild(node);
396  } else if (position === 'after') {
397    submitButton.parentNode.insertBefore(node, submitButton.nextSibling);
398  } else {
399    submitButton.parentNode.insertBefore(node, submitButton);
400  }
401}
402
403function cfturnstile_atomic_render_options(form) {
404  var settings = window.cfturnstileElementorSettings || {};
405  var disableSubmit = settings.disableSubmit || false;
406  var submitButton = cfturnstile_atomic_submit_button(form);
407  return {
408    sitekey: settings.sitekey,
409    theme: settings.theme || 'auto',
410    size: settings.size || 'normal',
411    appearance: settings.appearance || 'always',
412    action: 'elementor-atomic-form',
413    callback: function(token) {
414      if (disableSubmit) { cfturnstile_elementor_set_submit(submitButton, true); }
415      if (typeof turnstileElementorCallback === 'function') {
416        turnstileElementorCallback(token);
417      }
418    },
419    'error-callback': function() {
420      if (disableSubmit) { cfturnstile_elementor_set_submit(submitButton, false); }
421    },
422    'expired-callback': function() {
423      if (disableSubmit) { cfturnstile_elementor_set_submit(submitButton, false); }
424    }
425  };
426}
427
428function cfturnstile_init_elementor_atomic_forms() {
429  var settings = window.cfturnstileElementorSettings || {};
430  var sitekey = settings.sitekey || '';
431  var position = settings.position || 'before';
432  var mode = settings.mode || 'turnstile';
433  var recaptchaSiteKey = settings.recaptchaSiteKey || '';
434  var disableSubmit = settings.disableSubmit || false;
435
436  if (!window._cft_atomic_idx) { window._cft_atomic_idx = 0; }
437
438  // Elementor re-renders form elements in place (editor edits, popups, Alpine refreshes),
439  // which drops the injected widget. Release those forms so they are processed again.
440  document.querySelectorAll(CFT_ATOMIC_FORM_SELECTOR + '.cft-processed').forEach(function(form) {
441    if (!form.querySelector('.cf-turnstile, .g-recaptcha, input[name="cfturnstile_failsafe"]')) {
442      form.classList.remove('cft-processed');
443    }
444  });
445
446  document.querySelectorAll(CFT_ATOMIC_FORM_SELECTOR + ':not(.cft-processed)').forEach(function(form) {
447    // Same popup caching trap as the classic forms above - wait until the popup is actually shown,
448    // at which point the observer below picks the form up with its atomic render options intact.
449    if (cfturnstile_elementor_is_unshown_popup(form)) { return; }
450
451    if (form.querySelector('.cf-turnstile, .g-recaptcha, input[name="cfturnstile_failsafe"]')) {
452      form.classList.add('cft-processed');
453      return;
454    }
455
456    var submitButton = cfturnstile_atomic_submit_button(form);
457
458    // Failsafe modes: post a marker, and render reCAPTCHA in its place if configured.
459    if (mode === 'allow' || mode === 'recaptcha') {
460      var marker = document.createElement('input');
461      marker.type = 'hidden';
462      marker.name = 'cfturnstile_failsafe';
463      marker.value = mode;
464      form.appendChild(marker);
465
466      if (mode === 'recaptcha' && recaptchaSiteKey) {
467        var recaptchaWrap = document.createElement('div');
468        recaptchaWrap.className = 'cfturnstile-atomic-field';
469        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;';
470        var recaptchaDiv = document.createElement('div');
471        recaptchaDiv.className = 'g-recaptcha';
472        recaptchaDiv.setAttribute('data-sitekey', recaptchaSiteKey);
473        recaptchaWrap.appendChild(recaptchaDiv);
474        cfturnstile_atomic_insert(form, recaptchaWrap, position, submitButton);
475      }
476
477      form.classList.add('cft-processed');
478      return;
479    }
480
481    // The submit button is rendered as a child widget, so it can still be missing on an
482    // early pass. Wait for the next pass rather than injecting into a half built form.
483    if (!submitButton || !window.turnstile || !sitekey) return;
484
485    if (disableSubmit) {
486      cfturnstile_elementor_set_submit(submitButton, false);
487    }
488
489    var index = window._cft_atomic_idx++;
490
491    var wrap = document.createElement('div');
492    wrap.className = 'cfturnstile-atomic-field';
493    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;';
494
495    if (settings.labelEnable && settings.labelText) {
496      var labelEl = document.createElement('p');
497      labelEl.className = 'cfturnstile-widget-label';
498      labelEl.style.cssText = 'font-size: 14px; margin: 0 0 6px 0;';
499      if ((settings.appearance || 'always') === 'interaction-only') {
500        labelEl.className += ' cfturnstile-widget-label-interaction';
501        labelEl.style.display = 'none';
502      }
503      var smallEl = document.createElement('small');
504      smallEl.textContent = settings.labelText;
505      labelEl.appendChild(smallEl);
506      wrap.appendChild(labelEl);
507    }
508
509    var turnstileDiv = document.createElement('div');
510    turnstileDiv.className = 'elementor-turnstile-field cf-turnstile';
511    turnstileDiv.id = 'cf-turnstile-elementor-atomic-' + index;
512    wrap.appendChild(turnstileDiv);
513
514    cfturnstile_atomic_insert(form, wrap, position, submitButton);
515
516    turnstile.render(turnstileDiv, cfturnstile_atomic_render_options(form));
517
518    if (settings.labelEnable && (settings.appearance || 'always') === 'interaction-only' && typeof window.cfturnstileInitInteractionLabels === 'function') {
519      window.cfturnstileInitInteractionLabels();
520    }
521
522    form.classList.add('cft-processed');
523  });
524}
525
526/**
527 * Reset a widget after its form has been submitted, so a retry gets a fresh token.
528 * Elementor calls form.reset() on success, which empties the token input while the
529 * widget still holds the spent token - the global refresh helper cannot detect that.
530 */
531function cfturnstile_atomic_reset(form) {
532  if (!form || !window.turnstile) return;
533  var widget = form.querySelector('.cf-turnstile');
534  if (!widget) return;
535  var settings = window.cfturnstileElementorSettings || {};
536  if (settings.disableSubmit) {
537    cfturnstile_elementor_set_submit(cfturnstile_atomic_submit_button(form), false);
538  }
539  try { turnstile.reset(widget); } catch (e) {}
540}
541
542/**
543 * Attach the token to the atomic form request.
544 *
545 * Elementor builds the FormData itself and only includes its own field widgets, so
546 * there is no markup based way to get the token to the server. The wrapper only
547 * touches requests carrying the atomic form action and leaves everything else alone.
548 */
549function cfturnstile_atomic_patch_fetch() {
550  if (window._cfturnstileAtomicFetchPatched) return;
551  var originalFetch = window.fetch;
552  if (typeof originalFetch !== 'function') return;
553  window._cfturnstileAtomicFetchPatched = true;
554
555  window.fetch = function(input, init) {
556    var form = null;
557    try {
558      var body = init && init.body;
559      if (body instanceof FormData && body.get('action') === CFT_ATOMIC_AJAX_ACTION) {
560        var formId = body.get('form_id');
561        if (formId && /^[A-Za-z0-9_-]+$/.test(formId)) {
562          form = document.querySelector(CFT_ATOMIC_FORM_SELECTOR + '[data-id="' + formId + '"]');
563        }
564        if (form) {
565          var token = cfturnstile_elementor_token(form);
566          if (token) {
567            body.set('cf-turnstile-response', token);
568          }
569          var failsafe = form.querySelector('input[name="cfturnstile_failsafe"]');
570          if (failsafe && failsafe.value) {
571            body.set('cfturnstile_failsafe', failsafe.value);
572          }
573          var recaptcha = form.querySelector('textarea[name="g-recaptcha-response"], input[name="g-recaptcha-response"]');
574          if (recaptcha && recaptcha.value) {
575            body.set('g-recaptcha-response', recaptcha.value);
576          }
577        }
578      }
579    } catch (e) {}
580
581    // Callers reaching fetch from strict mode code (bundles, ES modules) invoke it bare, so
582    // `this` arrives undefined. Current Chrome tolerates that, but engines that hold fetch to
583    // its WebIDL receiver throw "Illegal invocation" - fall back to the global rather than
584    // making that a property of whether this plugin happens to be active.
585    var request = originalFetch.apply(this || window, arguments);
586
587    if (form) {
588      var reset = function() {
589        // Let Elementor apply its own result handling (including form.reset()) first.
590        setTimeout(function() { cfturnstile_atomic_reset(form); }, 500);
591      };
592      request.then(reset, reset);
593    }
594
595    return request;
596  };
597}
598
599(function() {
600  cfturnstile_atomic_patch_fetch();
601
602  var scheduled = null;
603  function schedule() {
604    if (scheduled) return;
605    scheduled = setTimeout(function() {
606      scheduled = null;
607      cfturnstile_init_elementor_atomic_forms();
608    }, 100);
609  }
610
611  if (document.readyState === 'loading') {
612    document.addEventListener('DOMContentLoaded', schedule);
613  } else {
614    schedule();
615  }
616
617  // Atomic forms are mounted by Elementor's own handler system and can appear or be
618  // re-rendered at any point (popups, editor edits, lazy loaded content), so watch the
619  // document rather than hooking into a specific lifecycle event.
620  if (window.MutationObserver) {
621    new MutationObserver(schedule).observe(document.documentElement, { childList: true, subtree: true });
622  }
623})();

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.