1(function () { 2 // If localization failed, let the browser submit normally (no-JS fallback). 3 if (typeof window.KLR_FORM === 'undefined' || !window.KLR_FORM.endpoint) { 4 console.warn('[KLR] KLR_FORM missing; using normal form POST.'); 5 return; 6 } 7 8 function qs(root, sel) { 9 return root ? root.querySelector(sel) : null; 10 } 11 12 function setStatus(form, msg, ok) { 13 var box = qs(form, '.klr-status'); 14 if (!box) return; 15 box.textContent = msg || ''; 16 box.setAttribute('data-ok', ok ? '1' : '0'); 17 } 18 19 function serialize(form) { 20 var fd = new FormData(form); 21 var out = {}; 22 fd.forEach(function (v, k) { 23 if (out[k] !== undefined) { 24 if (!Array.isArray(out[k])) out[k] = [out[k]]; 25 out[k].push(v); 26 } else { 27 out[k] = v; 28 } 29 }); 30 return out; 31 } 32 33 // --- Turnstile helpers ---------------------------------------------------- 34 function turnstileEnabled() { 35 return !!(window.KLR_FORM && 36 window.KLR_FORM.turnstile && 37 window.KLR_FORM.turnstile.enabled && 38 window.KLR_FORM.turnstile.siteKey); 39 } 40 41 // We store per-form widget state on the form element 42 function initTurnstileForForm(form) { 43 if (!turnstileEnabled()) return; 44 45 var holder = qs(form, '[data-klr-turnstile]'); 46 if (!holder) return; 47 48 // Prevent double-render 49 if (form.__klr_ts_inited) return; 50 51 // Turnstile script must be loaded (it defines window.turnstile) 52 if (typeof window.turnstile === 'undefined' || !window.turnstile.render) { 53 // Script may still be loading; we retry a few times. 54 var tries = 0; 55 var t = setInterval(function () { 56 tries++; 57 if (typeof window.turnstile !== 'undefined' && window.turnstile.render) { 58 clearInterval(t); 59 initTurnstileForForm(form); 60 } else if (tries > 40) { // ~4s 61 clearInterval(t); 62 console.warn('[KLR] Turnstile script did not load.'); 63 } 64 }, 100); 65 return; 66 } 67 68 // IMPORTANT: Invisible Turnstile requires execute() to get a token. 69 // If you want visible/managed instead, change size to 'normal'. 70 var size = (window.KLR_FORM.turnstile.size || 'invisible'); // default invisible 71 72 form.__klr_ts_inited = true; 73 form.__klr_ts_size = size; 74 form.__klr_ts_token = ''; 75 form.__klr_ts_pending = null; 76 77 // Ensure hidden field exists 78 var hidden = qs(form, 'input[name="turnstile_token"]'); 79 if (!hidden) { 80 hidden = document.createElement('input'); 81 hidden.type = 'hidden'; 82 hidden.name = 'turnstile_token'; 83 hidden.value = ''; 84 form.appendChild(hidden); 85 } 86 87 // Render widget 88 var widgetId = window.turnstile.render(holder, { 89 sitekey: window.KLR_FORM.turnstile.siteKey, 90 size: size, // 'invisible' or 'normal'/'compact' 91 callback: function (token) { 92 form.__klr_ts_token = token || ''; 93 hidden.value = token || ''; 94 95 // Resolve any pending execute() waiters 96 if (form.__klr_ts_pending && typeof form.__klr_ts_pending.resolve === 'function') { 97 form.__klr_ts_pending.resolve(token || ''); 98 form.__klr_ts_pending = null; 99 } 100 }, 101 'expired-callback': function () { 102 form.__klr_ts_token = ''; 103 hidden.value = ''; 104 }, 105 'error-callback': function () { 106 form.__klr_ts_token = ''; 107 hidden.value = ''; 108 if (form.__klr_ts_pending && typeof form.__klr_ts_pending.reject === 'function') { 109 form.__klr_ts_pending.reject(new Error('Anti-spam check failed. Please try again.')); 110 form.__klr_ts_pending = null; 111 } 112 } 113 }); 114 115 form.__klr_ts_widgetId = widgetId; 116 } 117 118 // Returns a Promise<string token> 119 function getTurnstileToken(form) { 120 if (!turnstileEnabled()) return Promise.resolve(''); 121 122 // If TS not rendered yet, initialize now 123 initTurnstileForForm(form); 124 125 var size = form.__klr_ts_size || (window.KLR_FORM.turnstile.size || 'invisible'); 126 var widgetId = form.__klr_ts_widgetId; 127 128 if (!widgetId && typeof window.turnstile !== 'undefined') { 129 // Not rendered (script still loading or missing holder) 130 return Promise.reject(new Error('Anti-spam is not ready yet. Please try again.')); 131 } 132 133 // If we already have a token, use it 134 if (form.__klr_ts_token) return Promise.resolve(form.__klr_ts_token); 135 136 // Invisible mode: execute on submit and wait for callback to provide token 137 if (size === 'invisible') { 138 return new Promise(function (resolve, reject) { 139 // Only one pending wait at a time 140 form.__klr_ts_pending = { resolve: resolve, reject: reject }; 141 142 try { 143 window.turnstile.execute(widgetId); 144 } catch (e) { 145 form.__klr_ts_pending = null; 146 reject(new Error('Please complete the anti-spam check.')); 147 return; 148 } 149 150 // Safety timeout (so we donât hang forever) 151 setTimeout(function () { 152 if (form.__klr_ts_pending) { 153 form.__klr_ts_pending = null; 154 reject(new Error('Please complete the anti-spam check.')); 155 } 156 }, 8000); 157 }); 158 } 159 160 // Visible/managed mode: user must interact (token arrives via callback) 161 return Promise.reject(new Error('Please complete the anti-spam check.')); 162 } 163 164 function resetTurnstile(form) { 165 if (!turnstileEnabled()) return; 166 167 try { 168 if (typeof window.turnstile !== 'undefined' && form.__klr_ts_widgetId !== undefined) { 169 window.turnstile.reset(form.__klr_ts_widgetId); 170 } 171 } catch (e) {} 172 173 form.__klr_ts_token = ''; 174 var hidden = qs(form, 'input[name="turnstile_token"]'); 175 if (hidden) hidden.value = ''; 176 } 177 178 async function submitViaAjax(form) { 179 setStatus(form, 'Submitting...', true); 180 181 var payload = serialize(form); 182 183 // Turnstile: ensure token exists when enabled 184 if (turnstileEnabled()) { 185 var token = await getTurnstileToken(form); 186 if (!token) throw new Error('Please complete the anti-spam check.'); 187 payload.turnstile_token = token; 188 } 189 190 var resp = await fetch(window.KLR_FORM.endpoint, { 191 method: 'POST', 192 credentials: 'same-origin', 193 headers: { 194 'Content-Type': 'application/json' 195 }, 196 body: JSON.stringify(payload) 197 }); 198 199 var data = null; 200 try { data = await resp.json(); } catch (e) {} 201 202 if (!resp.ok || !data || data.ok !== true) { 203 var msg = (data && data.message) ? data.message : ('Request failed (' + resp.status + ')'); 204 throw new Error(msg); 205 } 206 207 setStatus(form, window.KLR_FORM.successMessage || data.message || 'Thanks! You are signed up.', true); 208 form.reset(); 209 210 // reset Turnstile after success so the next submit works 211 resetTurnstile(form); 212 } 213 214 document.addEventListener('DOMContentLoaded', function () { 215 document.querySelectorAll('form[data-klr-form]').forEach(function (form) { 216 // Pre-init Turnstile (so invisible widgets are ready before submit) 217 initTurnstileForForm(form); 218 219 form.addEventListener('submit', function (e) { 220 e.preventDefault(); 221 submitViaAjax(form).catch(function (err) { 222 console.error('[KLR] Submit failed', err); 223 setStatus(form, err && err.message ? err.message : 'Something went wrong. Please try again.', false); 224 225 // If token expired/invalid, reset so user can retry 226 resetTurnstile(form); 227 }); 228 }); 229 }); 230 }); 231})();
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.