PageSourceSearch

https://www.foreignaffairs.com/modules/contrib/hcaptcha/js/hcaptcha-invisible.js?tm6fyk

js foreignaffairs.com collected 2026-10-01 06:15:49 UTC 4,725 bytes, 122 lines download raw bytes

1(function ($, Drupal, drupalSettings, once) {
2  'use strict';
3
4  function fetchHcaptchaToken(form, triggeringElemName) {
5    var promise = $.Deferred();
6
7    const invisibleWidget = document.querySelector(`#${form.id} .h-captcha[data-size="invisible"]`);
8    if (!invisibleWidget) {
9      return promise.resolve(true);
10    }
11
12    const triggerElem = form.querySelector(`[name="${triggeringElemName}"]`);
13    if (triggerElem) {
14      // Disable the triggering element before
15      // Drupal.Ajax.prototype.beforeSend() does, since the hCaptcha token
16      // round-trip is potentially slow. Logic below copies some ideas from
17      // ...beforeSend().
18      const triggerElemValue = ($.fieldValue && $.fieldValue(triggerElem)) || null;
19      triggerElem.setAttribute('disabled', true);
20      if (triggerElem.type !== 'submit' && triggerElemValue !== null) {
21        // This should only run for AJAX-submitted forms, triggering with
22        // non-submit elements.
23        const ajaxOpPreserve = form.querySelector('.hcaptcha-ajax-op-preserve') || document.createElement('input');
24        ajaxOpPreserve.setAttribute("type", "hidden");
25        ajaxOpPreserve.className = 'hcaptcha-ajax-op-preserve';
26        ajaxOpPreserve.name = triggerElem.name;
27        ajaxOpPreserve.value = triggerElemValue;
28        triggerElem.before(ajaxOpPreserve);
29      }
30    }
31
32    hcaptcha.execute(invisibleWidget.dataset.hcaptchaWidgetId, { async: true })
33      .then(function ({ response }) {
34        ["h-captcha-response", 'g-recaptcha-response']
35          .map(elemName => form.querySelector(`[name="${elemName}"]`))
36          .filter(v => v) // keep only actual elements.
37          .forEach(elem => elem.value = response);
38        promise.resolve(true);
39      })
40      .catch(err => {
41        console.error(err);
42        // Simplistic handling of close/expired challenge; resolving the
43        // promise lets the caller continue the submission flow, which must
44        // then fail due to the missing hcaptcha response.
45        promise.resolve(false);
46      });
47
48    return promise;
49  }
50
51  // Handle ajax forms.
52  var originalAjaxSubmit;
53  Drupal.behaviors.hcaptchaAjaxSubmitOverride = {
54    attach: function (context, settings) {
55      if (originalAjaxSubmit || !$.fn.ajaxSubmit) return;
56
57      originalAjaxSubmit = $.fn.ajaxSubmit;
58      $.fn.ajaxSubmit = function (options, data, dataType, onSuccess) {
59        var self = this;
60        fetchHcaptchaToken(self[0], options.data._triggering_element_name)
61          .then(() => {
62            const origComplete = options.complete;
63            options.complete = function(xmlhttprequest, status) {
64              // Clean-up: remove hidden element used to mirror the disabled
65              // element.
66              self[0].querySelector('.hcaptcha-ajax-op-preserve')?.remove();
67              origComplete(xmlhttprequest, status);
68            };
69            originalAjaxSubmit.call(self, options, data, dataType, onSuccess);
70          });
71      };
72    }
73  };
74
75  Drupal.behaviors.hcaptchaInvisible = {
76    attach: function (context, settings) {
77      // Handle non-ajax forms.
78      let isFetchingToken = false;
79
80      const formsWithInvisible = Array.from(document.querySelectorAll('.h-captcha[data-size="invisible"]'))
81        .map((elem) => elem.closest('form'));
82
83      $(formsWithInvisible).one('submit', function (e) {
84        const self = this;
85        if (e.originalEvent.submitter.getAttribute('formnovalidate')) {
86          return;
87        }
88
89        if (isFetchingToken) return false;
90        isFetchingToken = true;
91
92        fetchHcaptchaToken(self, e.originalEvent.submitter && e.originalEvent.submitter.getAttribute('name'))
93          .then(() => {
94            isFetchingToken = false;
95            self.submit();
96          });
97
98        return false;
99      });
100
101      // Calling formElement.submit() (see above) causes the POST data to not
102      // include [type=submit] elements. Let's inject a hidden element to mimic
103      // name + value for the type=submit element that was clicked to trigger
104      // the form submission.
105      once('hcaptcha-op-preserve', formsWithInvisible).forEach((theForm) => {
106        const opPreserve = document.createElement("input");
107        opPreserve.setAttribute("type", "hidden");
108
109        $(theForm).on('click', '[type=submit]', function(e) {
110          const submitId = e.currentTarget.id;
111          // Do not do this on AJAX-y submissions.
112          if (!drupalSettings.ajax || !drupalSettings.ajax.hasOwnProperty(submitId)) {
113            opPreserve.name = this.name;
114            opPreserve.value = this.value;
115            theForm.appendChild(opPreserve);
116          }
117        });
118      });
119    }
120  };
121
122})(jQuery, Drupal, drupalSettings, once);

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.