1class RecaptchaForm { 2 /** 3 * @type {string} 4 */ 5 #fieldSelector = '[data-recaptcha-form-field]'; 6 7 #submitSelector = '[data-recaptcha-form-submit]'; 8 9 #invisibleSubmitSelector = '[data-invisible-recaptcha-form-submit]'; 10 11 /** 12 * @type {Recaptcha} 13 */ 14 #recaptcha = null; 15 16 /** 17 * @type {HTMLFormElement} 18 */ 19 #form = null; 20 21 /** 22 * @type {HTMLInputElement} 23 */ 24 #field = null; 25 26 /** 27 * @param {Recaptcha} recaptcha 28 * @param {HTMLFormElement} form 29 */ 30 constructor(recaptcha, form) { 31 this.#recaptcha = recaptcha; 32 this.#form = form; 33 34 this.initializeElements(form); 35 this.initializeEvents(); 36 } 37 38 /** 39 * @param {HTMLFormElement} form 40 */ 41 initializeElements(form) { 42 this.#field = form.querySelector(this.#fieldSelector); 43 } 44 45 initializeEvents() { 46 // used with visible recaptcha 47 [...this.#form.querySelectorAll(this.#submitSelector)] 48 .map(button => button.addEventListener('click', event => this.visibleRecaptchaButtonClicked(event))); 49 // used with invisible recaptcha 50 [...this.#form.querySelectorAll(this.#invisibleSubmitSelector)] 51 .map(button => button.addEventListener('click', () => this.#recaptcha.setLastForm(this))); 52 } 53 54 /** 55 * @param {PointerEvent} event 56 */ 57 visibleRecaptchaButtonClicked(event) { 58 if (!this.#form.reportValidity() || !this.fieldValid()) { 59 event.preventDefault(); 60 } 61 } 62 63 /** 64 * @return {boolean} 65 */ 66 fieldValid() { 67 return this.#field.value !== '' && this.#field.value === this.#recaptcha.getResponse(); 68 } 69 70 setFieldValue(value) { 71 this.#field.value = value; 72 } 73 74 /** 75 * @return {boolean} 76 */ 77 reportValidity() { 78 return this.#form.reportValidity(); 79 } 80 81 submit() { 82 this.#form.submit(); 83 } 84} 85 86class Recaptcha { 87 /** 88 * @type {RecaptchaForm[]} 89 */ 90 #forms = []; 91 92 /** 93 * @type {RecaptchaForm} 94 */ 95 #lastForm = null; 96 97 /** 98 * @type {string} 99 */ 100 #response = ''; 101 102 constructor() { 103 this.initializeForms(); 104 this.registerCallback(); 105 } 106 107 initializeForms() { 108 [...document.querySelectorAll('form .g-recaptcha')] 109 .map(element => { 110 const form = element.closest('form'); 111 if (form) { 112 this.#forms.push(new RecaptchaForm(this, form)); 113 } 114 }) 115 } 116 117 registerCallback() { 118 // for box recaptcha 119 window.onRecaptchaCallback = (response) => this.recaptchaCallback(response); 120 window.onRecaptchaExpired = () => this.recaptchaExpired(); 121 window.onRecaptchaError = () => this.recaptchaError(); 122 // for invisible recaptcha 123 window.onRecaptchaSubmit = (response) => this.recaptchaSubmit(response); 124 } 125 126 /** 127 * @param {string} response 128 */ 129 recaptchaCallback(response) { 130 this.#response = response; 131 this.#forms.forEach(form => form.setFieldValue(response)); 132 } 133 134 recaptchaExpired() { 135 this.#lastForm = null; 136 this.#response = ''; 137 this.#forms.forEach(form => form.setFieldValue('')); 138 } 139 140 recaptchaError() { 141 this.#lastForm = null; 142 this.#response = ''; 143 this.#forms.forEach(form => form.setFieldValue('')); 144 } 145 146 /** 147 * @param {string} response 148 */ 149 recaptchaSubmit(response) { 150 this.#response = response; 151 if (this.#lastForm && this.#lastForm.reportValidity()) { 152 this.#lastForm.setFieldValue(response); 153 this.#lastForm.submit(); 154 } 155 } 156 157 /** 158 * @returns {string} 159 */ 160 getResponse() { 161 return this.#response; 162 } 163 164 setLastForm(lastForm) { 165 this.#lastForm = lastForm; 166 } 167} 168new Recaptcha();
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.