1(function(){ 2 const settings = window.cbtRecaptchaSettings || {}; 3 if (!settings.siteKey || !settings.version || !settings.apiUrl) { 4 return; 5 } 6 7 const state = { 8 ready: false, 9 queue: [], 10 seen: new WeakSet(), 11 observer: null 12 }; 13 14 const MAX_ATTEMPTS = 20; 15 const RETRY_DELAY = 600; 16 17 function ready(fn){ 18 if (document.readyState === 'loading') { 19 document.addEventListener('DOMContentLoaded', fn); 20 } else { 21 fn(); 22 } 23 } 24 25 function loadApi(callback) { 26 if (window.grecaptcha && (window.grecaptcha.render || window.grecaptcha.execute)) { 27 callback(); 28 return; 29 } 30 31 const existing = document.querySelector('script[data-cbt-recaptcha="1"]'); 32 if (existing) { 33 const handleLoad = function(){ 34 existing.dataset.cbtRecaptchaLoaded = '1'; 35 callback(); 36 }; 37 if (existing.dataset.cbtRecaptchaLoaded === '1' || existing.readyState === 'complete') { 38 callback(); 39 } else { 40 existing.addEventListener('load', handleLoad, { once: true }); 41 } 42 return; 43 } 44 45 const script = document.createElement('script'); 46 script.src = settings.apiUrl; 47 script.async = true; 48 script.defer = true; 49 script.dataset.cbtRecaptcha = '1'; 50 script.addEventListener('load', function(){ 51 script.dataset.cbtRecaptchaLoaded = '1'; 52 callback(); 53 }); 54 document.head.appendChild(script); 55 } 56 57 function waitForApi(callback, attempt){ 58 const tries = typeof attempt === 'number' ? attempt : 0; 59 const isV3 = settings.version === 'v3'; 60 const hasApi = !!(window.grecaptcha && ( 61 (isV3 && typeof window.grecaptcha.execute === 'function') || 62 (!isV3 && typeof window.grecaptcha.render === 'function') 63 )); 64 65 if (hasApi) { 66 callback(); 67 return; 68 } 69 70 if (tries >= MAX_ATTEMPTS) { 71 return; 72 } 73 74 setTimeout(function(){ 75 waitForApi(callback, tries + 1); 76 }, 200); 77 } 78 79 function ensureActionField(form, action){ 80 if (!form) return; 81 let field = form.querySelector('input[name="cbt_recaptcha_action"]'); 82 if (!field) { 83 field = document.createElement('input'); 84 field.type = 'hidden'; 85 field.name = 'cbt_recaptcha_action'; 86 form.appendChild(field); 87 } 88 field.value = action || 'form'; 89 } 90 91 function ensureResponseField(form){ 92 if (!form) return null; 93 let field = form.querySelector('input[name="g-recaptcha-response"]'); 94 if (!field) { 95 field = document.createElement('input'); 96 field.type = 'hidden'; 97 field.name = 'g-recaptcha-response'; 98 form.appendChild(field); 99 } 100 return field; 101 } 102 103 function findForm(placeholder){ 104 if (!placeholder) { 105 return null; 106 } 107 let form = placeholder.closest ? placeholder.closest('form') : null; 108 if (form) { 109 return form; 110 } 111 const selector = placeholder.getAttribute('data-form-selector'); 112 if (!selector) { 113 return null; 114 } 115 try { 116 form = document.querySelector(selector); 117 } catch (error) { 118 form = null; 119 } 120 return form; 121 } 122 123 function renderV3(placeholder){ 124 if (!window.grecaptcha || typeof window.grecaptcha.execute !== 'function') { 125 return false; 126 } 127 const form = findForm(placeholder); 128 if (!form) { 129 return false; 130 } 131 const action = placeholder.getAttribute('data-action') || 'form'; 132 ensureActionField(form, action); 133 const responseField = ensureResponseField(form); 134 if (!responseField) { 135 return false; 136 } 137 138 function execute(){ 139 try { 140 window.grecaptcha.execute(settings.siteKey, { action: action }).then(function(token){ 141 responseField.value = token; 142 }).catch(function(){ 143 responseField.value = ''; 144 }); 145 } catch (error) { 146 responseField.value = ''; 147 } 148 } 149 150 execute(); 151 152 if (!placeholder.dataset.cbtRecaptchaSubmitBound) { 153 form.addEventListener('submit', function(){ 154 if (!responseField.value) { 155 execute(); 156 } 157 }); 158 placeholder.dataset.cbtRecaptchaSubmitBound = '1'; 159 } 160 161 placeholder.dataset.cbtRecaptchaRendered = '1'; 162 return true; 163 } 164 165 function renderV2(placeholder){ 166 if (!window.grecaptcha || typeof window.grecaptcha.render !== 'function') { 167 return false;
168 } 169 const form = findForm(placeholder); 170 if (!form) { 171 return false; 172 } 173 const action = placeholder.getAttribute('data-action') || 'form'; 174 ensureActionField(form, action); 175 176 let container = placeholder.querySelector('.g-recaptcha'); 177 if (!container) { 178 container = document.createElement('div'); 179 container.className = 'g-recaptcha'; 180 placeholder.appendChild(container); 181 } 182 container.setAttribute('data-sitekey', settings.siteKey); 183 184 const invisible = settings.version === 'v2_invisible'; 185 let widgetId; 186 try { 187 widgetId = window.grecaptcha.render(container, { 188 sitekey: settings.siteKey, 189 size: invisible ? 'invisible' : 'normal', 190 callback: function(){ 191 if (invisible) { 192 placeholder.dataset.cbtRecaptchaSubmitting = '1'; 193 form.submit(); 194 } 195 }, 196 'error-callback': function(){ 197 delete placeholder.dataset.cbtRecaptchaSubmitting; 198 } 199 }); 200 } catch (error) { 201 container.innerHTML = ''; 202 return false; 203 } 204 205 if (invisible && !placeholder.dataset.cbtRecaptchaSubmitBound) { 206 form.addEventListener('submit', function(event){ 207 if (placeholder.dataset.cbtRecaptchaSubmitting === '1') { 208 delete placeholder.dataset.cbtRecaptchaSubmitting; 209 return; 210 } 211 event.preventDefault(); 212 window.grecaptcha.reset(widgetId); 213 window.grecaptcha.execute(widgetId); 214 }); 215 placeholder.dataset.cbtRecaptchaSubmitBound = '1'; 216 } 217 218 placeholder.dataset.cbtRecaptchaWidgetId = String(widgetId); 219 placeholder.dataset.cbtRecaptchaRendered = '1'; 220 return true; 221 } 222 223 function renderPlaceholder(placeholder){ 224 if (!placeholder || placeholder.dataset.cbtRecaptchaRendered === '1') { 225 return true; 226 } 227 if (!window.grecaptcha) { 228 return false; 229 } 230 if (settings.version === 'v3') { 231 return renderV3(placeholder); 232 } 233 return renderV2(placeholder); 234 } 235 236 function processQueue(){ 237 if (!state.ready || !state.queue.length) { 238 return; 239 } 240 241 const pending = state.queue.slice(); 242 state.queue.length = 0; 243 const retry = []; 244 245 pending.forEach(function(entry){ 246 if (!entry || !entry.placeholder) { 247 return; 248 } 249 const placeholder = entry.placeholder; 250 if (placeholder.dataset.cbtRecaptchaRendered === '1') { 251 return; 252 } 253 if (!document.documentElement.contains(placeholder)) { 254 return; 255 } 256 const success = renderPlaceholder(placeholder); 257 if (!success) { 258 if (entry.attempts >= MAX_ATTEMPTS) { 259 return; 260 } 261 retry.push({ placeholder: placeholder, attempts: entry.attempts + 1 }); 262 } 263 }); 264 265 if (retry.length) { 266 Array.prototype.push.apply(state.queue, retry); 267 setTimeout(processQueue, RETRY_DELAY); 268 } 269 } 270 271 function enqueuePlaceholder(placeholder){ 272 if (!placeholder || state.seen.has(placeholder)) { 273 return; 274 } 275 state.seen.add(placeholder); 276 state.queue.push({ placeholder: placeholder, attempts: 0 }); 277 if (state.ready) { 278 processQueue(); 279 } 280 } 281 282 function discoverPlaceholders(root){ 283 if (!root || !root.querySelectorAll) { 284 return; 285 } 286 const items = root.querySelectorAll('.cbt-recaptcha-placeholder'); 287 Array.prototype.forEach.call(items, enqueuePlaceholder); 288 } 289 290 function observeMutations(){ 291 if (!window.MutationObserver || !document.body) { 292 return; 293 } 294 state.observer = new MutationObserver(function(mutations){ 295 mutations.forEach(function(mutation){ 296 Array.prototype.forEach.call(mutation.addedNodes || [], function(node){ 297 if (!node || node.nodeType !== 1) { 298 return; 299 } 300 if (node.classList && node.classList.contains('cbt-recaptcha-placeholder')) { 301 enqueuePlaceholder(node); 302 } 303 if (node.querySelectorAll) { 304 discoverPlaceholders(node); 305 } 306 }); 307 }); 308 }); 309 state.observer.observe(document.body, { childList: true, subtree: true }); 310 } 311 312 ready(function(){ 313 discoverPlaceholders(document); 314 observeMutations(); 315 316 loadApi(function(){ 317 waitForApi(function(){ 318 const activate = function(){ 319 if (state.ready) { 320 return; 321 } 322 state.ready = true; 323 processQueue(); 324 }; 325 if (window.grecaptcha && typeof window.grecaptcha.ready === 'function') { 326 window.grecaptcha.ready(activate); 327 } else { 328 activate(); 329 } 330 }); 331 }); 332 333 window.addEventListener('load', processQueue); 334 }); 335})();
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.