@cap.js/widget 0.1.43 → 0.1.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cap-floating.min.js +1 -1
- package/cap.d.ts +2 -0
- package/cap.min.js +1 -1
- package/package.json +1 -1
- package/src/cap-floating.js +58 -65
- package/src/cap.css +22 -2
- package/src/cap.js +44 -1
package/cap-floating.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{const t=(e
|
|
1
|
+
(()=>{const t=t=>{const e=t.getAttribute("data-cap-floating");if(!e)return;const o=document.querySelector(e);if(!document.contains(o)&&!o.solve)throw new Error(`[cap floating] "${e}" doesn't exist or isn't a Cap widget`);o.style.display="none";let n=!1;t.addEventListener("click",e=>{if(n)return void(n=!1);e.stopImmediatePropagation(),e.preventDefault(),((t,e)=>{const o=parseInt(t.getAttribute("data-cap-floating-offset"))||8,n=t.getAttribute("data-cap-floating-position")||"top";Object.assign(e.style,{display:"block",position:"absolute",zIndex:"99999",opacity:"0",transform:"scale(0.98)",marginTop:"-4px",boxShadow:"rgba(0, 0, 0, 0.05) 0px 6px 24px 0px",borderRadius:"14px",transition:"opacity 0.15s, margin-top 0.2s, transform 0.2s"}),setTimeout(()=>{e.style.transform="scale(1)",e.style.opacity="1",e.style.marginTop="0"},5);const s=t.getBoundingClientRect(),a=(e.offsetParent??document.documentElement).getBoundingClientRect(),i=s.left+(s.width-e.offsetWidth)/2,r=Math.max(2,Math.min(i,window.innerWidth-e.offsetWidth));if(e.style.left=r-a.left+"px","top"===n){const t=s.top-e.offsetHeight-o,n=Math.max(0,t);e.style.top=n-a.top+"px"}else{const t=s.bottom+o,n=Math.min(t,window.innerHeight-e.offsetHeight);e.style.top=n-a.top+"px"}e.solve()})(t,o);const s=({detail:e})=>{o.removeEventListener("solve",s),t.setAttribute("data-cap-token",e.token),t.setAttribute("data-cap-progress","done"),setTimeout(()=>{n=!0,t.click()},500),(t=>{setTimeout(()=>{t.style.transform="scale(0.98)",t.style.opacity="0",t.style.marginTop="-4px"},500),setTimeout(()=>{t.style.display="none"},700)})(o)};o.addEventListener("solve",s)},!0)},e=e=>{t(e),e.querySelectorAll("[data-cap-floating]").forEach(t)};e(document.body),new MutationObserver(t=>t.forEach(t=>t.addedNodes.forEach(t=>{t.nodeType===Node.ELEMENT_NODE&&e(t)}))).observe(document.body,{childList:!0,subtree:!0})})();
|
package/cap.d.ts
CHANGED
|
@@ -56,7 +56,9 @@ interface CapConfig {
|
|
|
56
56
|
"data-cap-i18n-verified-aria-label"?: string;
|
|
57
57
|
"data-cap-i18n-error-aria-label"?: string;
|
|
58
58
|
"data-cap-i18n-wasm-disabled"?: string;
|
|
59
|
+
"data-cap-i18n-required-label"?: string;
|
|
59
60
|
"data-cap-troubleshooting-url"?: string;
|
|
61
|
+
required?: boolean | "";
|
|
60
62
|
onsolve?: string;
|
|
61
63
|
onprogress?: string;
|
|
62
64
|
onreset?: string;
|
package/cap.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{const e="vibrate"in navigator&&!window.matchMedia("(prefers-reduced-motion: reduce)").matches;if("undefined"==typeof window)return;const t=(e,t={})=>window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(e,t):fetch(e,t);function i(e,t){let i=function(e){let t=2166136261;for(let i=0;i<e.length;i++)t^=e.charCodeAt(i),t+=(t<<1)+(t<<4)+(t<<7)+(t<<8)+(t<<24);return t>>>0}(e),s="";function r(){return i^=i<<13,i^=i>>>17,i^=i<<5,i>>>0}for(;s.length<t;){s+=r().toString(16).padStart(8,"0")}return s.substring(0,t)}async function s(e){var t=(e=>{const t=atob(e),i=new Uint8Array(t.length);for(let e=0;e<t.length;e++)i[e]=t.charCodeAt(e);return i})(e);const i=await new Promise((e,i)=>{try{var s=new DecompressionStream("deflate-raw"),r=s.writable.getWriter(),a=s.readable.getReader(),n=[];a.read().then(function t(s){if(s.done){for(var r=0,o=0,l=0;l<n.length;l++)r+=n[l].length;var c=new Uint8Array(r);for(l=0;l<n.length;l++)c.set(n[l],o),o+=n[l].length;e((new TextDecoder).decode(c))}else n.push(s.value),a.read().then(t).catch(i)}).catch(i),r.write(t).then(()=>{r.close()}).catch(i)}catch(e){i(e)}});return new Promise(e=>{var t=setTimeout(()=>{a(),e({__timeout:!0})},2e4),s=document.createElement("iframe");s.setAttribute("sandbox","allow-scripts"),s.setAttribute("aria-hidden","true"),s.style.cssText="position:absolute;width:1px;height:1px;top:-9999px;left:-9999px;border:none;opacity:0;pointer-events:none;";var r=!1;function a(){r||(r=!0,clearTimeout(t),window.removeEventListener("message",n),s.parentNode&&s.parentNode.removeChild(s))}function n(t){var i=t.data;i&&"object"==typeof i&&("cap:instr"===i.type?(a(),i.blocked?e({__blocked:!0,blockReason:i.blockReason||"automated_browser"}):i.result?e(i.result):e({__timeout:!0})):"cap:error"===i.type&&(a(),e({__timeout:!0})))}window.addEventListener("message",n),s.srcdoc='<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><script>'+i+"\n<\/script></body></html>",document.body.appendChild(s)})}let r=null;const a=()=>{if(r)return r;const e=window.CAP_CUSTOM_WASM_URL||"https://cdn.jsdelivr.net/npm/@cap.js/wasm@0.0.6/browser/cap_wasm_bg.wasm";return r=fetch(e).then(e=>{if(!e.ok)throw new Error(`Failed to fetch wasm: ${e.status}`);return e.arrayBuffer()}).then(e=>WebAssembly.compile(e)).catch(e=>{throw r=null,e}),r};"object"==typeof WebAssembly&&"function"==typeof WebAssembly.compile&&a().catch(()=>{});let n=null;function o(){return n||(n=URL.createObjectURL(new Blob(['(()=>{const e=async({salt:e,target:t})=>{let n=0;const r=new TextEncoder,o=4*t.length,s=Math.floor(o/8),l=o%8,a=t.length%2==0?t:t+"0",c=a.length/2,i=new Uint8Array(c);for(let e=0;e<c;e++)i[e]=parseInt(a.substring(2*e,2*e+2),16);const f=l>0?255<<8-l&255:0;for(;;)try{for(let t=0;t<5e4;t++){const t=e+n,o=r.encode(t),a=await crypto.subtle.digest("SHA-256",o),c=new Uint8Array(a);let g=!0;for(let e=0;e<s;e++)if(c[e]!==i[e]){g=!1;break}if(g&&l>0&&(c[s]&f)!==(i[s]&f)&&(g=!1),g)return void self.postMessage({nonce:n,found:!0});n++}}catch(e){return console.error("[cap worker]",e),void self.postMessage({found:!1,error:e.message})}};if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly?.instantiate)return console.warn("[cap worker] wasm not supported, falling back to alternative solver. this will be significantly slower."),void(self.onmessage=async({data:{salt:t,target:n}})=>e({salt:t,target:n}));let t=null;self.onmessage=async({data:{salt:n,target:r,wasmModule:o}})=>{if(o instanceof WebAssembly.Module&&null===t){const s=(e=>{try{let n,r=0,o=null;const s=()=>(null!==o&&0!==o.byteLength||(o=new Uint8Array(n.memory.buffer)),o),l=new TextEncoder,a=(e,t,n)=>{if(void 0===n){const n=l.encode(e),o=t(n.length,1)>>>0;return s().subarray(o,o+n.length).set(n),r=n.length,o}let o=e.length,a=t(o,1)>>>0;const c=s();let i=0;for(;i<o;i++){const t=e.charCodeAt(i);if(t>127)break;c[a+i]=t}if(i!==o){0!==i&&(e=e.slice(i)),a=n(a,o,o=i+3*e.length,1)>>>0;const t=s().subarray(a+i,a+o),{written:r}=l.encodeInto(e,t);i+=r,a=n(a,o,i,1)>>>0}return r=i,a},c={wbg:{}};c.wbg.__wbindgen_init_externref_table=()=>{const e=n.__wbindgen_export_0,t=e.grow(4);e.set(0,void 0),e.set(t+0,void 0),e.set(t+1,null),e.set(t+2,!0),e.set(t+3,!1)};const i=new WebAssembly.Instance(e,c);return n=i.exports,n.__wbindgen_start&&n.__wbindgen_start(),t=(e,t)=>{const o=a(e,n.__wbindgen_malloc,n.__wbindgen_realloc),s=r,l=a(t,n.__wbindgen_malloc,n.__wbindgen_realloc),c=r;return BigInt.asUintN(64,n.solve_pow(o,s,l,c))},!0}catch(e){return console.error("[cap worker] failed to init wasm from module:",e),!1}})(o);if(!s)return console.warn("[cap worker] wasm init failed, falling back to JS solver."),e({salt:n,target:r})}if(null===t)return console.warn("[cap worker] no wasm module provided, falling back to JS solver."),e({salt:n,target:r});try{const e=performance.now(),o=t(n,r),s=performance.now();self.postMessage({nonce:Number(o),found:!0,durationMs:(s-e).toFixed(2)})}catch(e){console.error("[cap worker]",e),self.postMessage({found:!1,error:e.message||String(e)})}},self.onerror=e=>{self.postMessage({found:!1,error:e})}})();'],{type:"application/javascript"})),n)}class l{constructor(e){this._size=e,this._workers=[],this._idle=[],this._queue=[],this._wasmModule=null,this._spawnFailures=0}setWasm(e){this._wasmModule=e}_spawn(){const e=o(),t=new Worker(e);return t._busy=!1,this._workers.push(t),this._idle.push(t),t}_replaceWorker(e){const t=this._workers.indexOf(e);-1!==t&&this._workers.splice(t,1);const i=this._idle.indexOf(e);-1!==i&&this._idle.splice(i,1);try{e.terminate()}catch{}return this._spawnFailures++,this._spawnFailures>3?(console.error("[cap] worker spawn failed repeatedly, not retrying"),null):this._spawn()}_ensureSize(e){for(;this._workers.length<e;)this._spawn()}run(e,t){return new Promise((i,s)=>{this._queue.push({salt:e,target:t,resolve:i,reject:s}),this._dispatch()})}_dispatch(){for(;this._idle.length>0&&this._queue.length>0;){const e=this._idle.shift(),{salt:t,target:i,resolve:s,reject:r}=this._queue.shift();let a=!1;const n=({data:t})=>{a||(a=!0,e.removeEventListener("message",n),e.removeEventListener("error",o),this._spawnFailures=0,this._idle.push(e),t.found?s(t.nonce):r(new Error(t.error||"worker failed")),this._dispatch())},o=t=>{if(a)return;a=!0,e.removeEventListener("message",n),e.removeEventListener("error",o);const i=this._replaceWorker(e);r(t),i&&this._dispatch()};e.addEventListener("message",n),e.addEventListener("error",o),this._wasmModule?e.postMessage({salt:t,target:i,wasmModule:this._wasmModule},[]):e.postMessage({salt:t,target:i})}}terminate(){for(const e of this._workers)try{e.terminate()}catch{}this._workers=[],this._idle=[],this._queue=[]}}class c extends HTMLElement{#e=null;#t=navigator.hardwareConcurrency||8;token=null;#i;#s;#r;#a=!1;#n;#o=null;#l=null;#c=null;#h=null;get#d(){return e&&!window.CAP_DISABLE_HAPTICS&&!this.hasAttribute("data-cap-disable-haptics")}#p(){return{state:"idle",challengeResp:null,challenges:null,results:[],completedCount:0,solvePromise:null,promoteFn:null,_listeners:[],pendingPromotion:null,token:null,tokenExpires:null,notify(){for(const e of this._listeners)e();this._listeners=[]},onSettled(e){"done"===this.state||"error"===this.state?e():this._listeners.push(e)}}}#u(){this.#o=this.#p(),this.#v()}#g(){this.#h&&(window.removeEventListener("mousemove",this.#h),window.removeEventListener("touchstart",this.#h),window.removeEventListener("keydown",this.#h),this.#h=null)}#v(){this.#g();const e=()=>{this.#g(),this.#m()};this.#h=e,window.addEventListener("mousemove",e,{passive:!0}),window.addEventListener("touchstart",e,{passive:!0}),window.addEventListener("keydown",e,{passive:!0})}#b(){return"function"==typeof this.checkVisibility?this.checkVisibility({checkOpacity:!0,checkVisibilityCSS:!0}):!!(this.offsetParent||this.getClientRects().length>0)}#m(){"idle"===this.#o.state&&this.#b()&&(this.#o.state="waiting",this.#l=setTimeout(()=>{this.#w()},2500))}async#w(){if("waiting"!==this.#o.state)return;this.#o.state="fetching";let e=this.getAttribute("data-cap-api-endpoint");if(!e&&window?.CAP_CUSTOM_FETCH&&(e="/"),e){e.endsWith("/")||(e+="/");try{const s=await t(`${e}challenge`,{method:"POST"});let r;try{r=await s.json()}catch{throw new Error("Failed to parse speculative challenge response")}if(r.error)throw new Error(r.error);r._apiEndpoint=e,this.#o.challengeResp=r;const{challenge:a,token:n}=r;let o=a;if(!Array.isArray(o)){let e=0;o=Array.from({length:a.c},()=>(e++,[i(`${n}${e}`,a.s),i(`${n}${e}d`,a.d)]))}this.#o.challenges=o,this.#o.state="solving",this.#o.solvePromise=this.#f(o)}catch(e){console.warn("[cap] speculative challenge fetch failed:",e),this.#o.state="error",this.#o.notify()}}else this.#o.state="idle"}async#f(e){o();let t=null;try{t=await a()}catch{}this.#c||(this.#c=new l(1),this.#c._spawn()),this.#c.setWasm(t);const i=e.length,s=new Array(i);let r=1,n=!1;this.#o.promoteFn=e=>{n||(n=!0,r=e,this.#c._size=e,this.#c._ensureSize(e))},null!==this.#o.pendingPromotion&&(this.#o.promoteFn(this.#o.pendingPromotion),this.#o.pendingPromotion=null);let c=0;for(;c<i;){const t=r,a=[],o=[];for(let s=0;s<t&&c<i;s++)o.push(c),a.push(e[c]),c++;this.#c._ensureSize(Math.max(r,t));const l=await Promise.all(a.map(e=>this.#c.run(e[0],e[1]).then(e=>(this.#o.completedCount++,e))));for(let e=0;e<o.length;e++)s[o[e]]=l[e];!n&&c<i&&await new Promise(e=>setTimeout(e,120))}return this.#o.results=s,this.#o.state="redeeming",this.#y(s),s}async#y(e){try{const i=this.#o.challengeResp,r=i._apiEndpoint;if(!r)throw new Error("[cap] speculative redeem: missing apiEndpoint");let a=null;if(i.instrumentation&&(a=await s(i.instrumentation),a?.__timeout||a?.__blocked))return this.#o.state="done",void this.#o.notify();const n=await t(`${r}redeem`,{method:"POST",body:JSON.stringify({token:i.token,solutions:e,...a&&{instr:a}}),headers:{"Content-Type":"application/json"}});let o;try{o=await n.json()}catch{throw new Error("Failed to parse speculative redeem response")}if(!o.success)throw new Error(o.error||"Speculative redeem failed");this.#o.token=o.token,this.#o.tokenExpires=new Date(o.expires).getTime(),this.#o.state="done",this.#o.notify()}catch(e){console.warn("[cap] speculative redeem failed (will redo on click):",e),this.#o.state="done",this.#o.notify()}}getI18nText(e,t){return this.getAttribute(`data-cap-i18n-${e}`)||t}static get observedAttributes(){return["onsolve","onprogress","onreset","onerror","data-cap-worker-count","data-cap-i18n-initial-state"]}constructor(){super(),this.#n&&this.#n.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#n=new Map,this.boundHandleProgress=this.handleProgress.bind(this),this.boundHandleSolve=this.handleSolve.bind(this),this.boundHandleError=this.handleError.bind(this),this.boundHandleReset=this.handleReset.bind(this)}initialize(){o(),this.#o||(this.#o=this.#p()),this.#c||(this.#c=new l(1),this.#c._spawn())}attributeChangedCallback(e,t,i){if(e.startsWith("on")){const t=e.slice(2),s=this.#n.get(e);if(s&&this.removeEventListener(t,s),i){const i=t=>{const i=this.getAttribute(e);"function"==typeof window[i]&&window[i].call(this,t)};this.#n.set(e,i),this.addEventListener(t,i)}}"data-cap-worker-count"===e&&this.setWorkersCount(parseInt(i,10)),"data-cap-i18n-initial-state"===e&&this.#s&&this.#s?.querySelector(".label.active")&&this.animateLabel(this.getI18nText("initial-state","Verify you're human"))}async connectedCallback(){this.#r=this,this.#i=this.attachShadow({mode:"open"}),this.#s=document.createElement("div"),this.createUI(),this.addEventListeners(),this.initialize(),this.#s.removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count"),t=e?parseInt(e,10):null;this.setWorkersCount(t||navigator.hardwareConcurrency||8);const i=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.#r.innerHTML=`<input type="hidden" name="${i}">`,this.#v()}async solve(){if(!this.#a)try{this.#a=!0,this.updateUI("verifying",this.getI18nText("verifying-label","Verifying..."),!0),this.#s.setAttribute("aria-label",this.getI18nText("verifying-aria-label","Verifying you're a human, please wait")),this.dispatchEvent("progress",{progress:0});try{let e,r,a=this.getAttribute("data-cap-api-endpoint");if(!a&&window?.CAP_CUSTOM_FETCH)a="/";else if(!a)throw new Error("Missing API endpoint. Either custom fetch or an API endpoint must be provided.");if(a.endsWith("/")||(a+="/"),"done"===this.#o.state&&this.#o.token&&this.#o.tokenExpires&&Date.now()<this.#o.tokenExpires){this.dispatchEvent("progress",{progress:100});const e=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${e}']`)&&(this.querySelector(`input[name='${e}']`).value=this.#o.token),this.dispatchEvent("solve",{token:this.#o.token}),this.token=this.#o.token;const t=this.#o.tokenExpires-Date.now();return this.#e&&clearTimeout(this.#e),this.#e=setTimeout(()=>this.reset(),t),this.#s.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#d&&navigator.vibrate([10,50,20,30,40]),this.#u(),this.#a=!1,{success:!0,token:this.token}}if("done"===this.#o.state)e=this.#o.results,r=this.#o.challengeResp,this.dispatchEvent("progress",{progress:100});else if("solving"===this.#o.state||"redeeming"===this.#o.state||"fetching"===this.#o.state||"waiting"===this.#o.state){"waiting"===this.#o.state&&(this.#l&&(clearTimeout(this.#l),this.#l=null),this.#o.state="waiting",this.#w()),this.#o.pendingPromotion=this.#t,this.#o.promoteFn&&this.#o.promoteFn(this.#t);const t=setInterval(()=>{const e=this.#o.state;if("done"===e||"error"===e)return void clearInterval(t);const i=this.#o.challenges?this.#o.challenges.length:1,s=this.#o.completedCount,r="redeeming"===e?99:"fetching"===e||"waiting"===e?0:Math.min(98,Math.round(s/i*100));this.dispatchEvent("progress",{progress:r})},150);if(await new Promise(e=>this.#o.onSettled(e)),clearInterval(t),"done"!==this.#o.state)throw new Error("Speculative solve failed – please try again");if(this.#o.token&&this.#o.tokenExpires&&Date.now()<this.#o.tokenExpires){this.dispatchEvent("progress",{progress:100});const e=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${e}']`)&&(this.querySelector(`input[name='${e}']`).value=this.#o.token),this.dispatchEvent("solve",{token:this.#o.token}),this.token=this.#o.token;const t=this.#o.tokenExpires-Date.now();return this.#e&&clearTimeout(this.#e),this.#e=setTimeout(()=>this.reset(),t),this.#s.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#d&&navigator.vibrate([10,50,20,30,40]),this.#u(),this.#a=!1,{success:!0,token:this.token}}e=this.#o.results,r=this.#o.challengeResp,this.dispatchEvent("progress",{progress:100})}else{const s=await t(`${a}challenge`,{method:"POST"});try{r=await s.json()}catch{throw new Error("Failed to parse challenge response from server")}if(r.error)throw new Error(r.error);const{challenge:n,token:o}=r;let l=n;if(!Array.isArray(l)){let e=0;l=Array.from({length:n.c},()=>(e++,[i(`${o}${e}`,n.s),i(`${o}${e}d`,n.d)]))}e=await this.solveChallenges(l)}const n=r.instrumentation?s(r.instrumentation):Promise.resolve(null),o=await n;if(o?.__timeout||o?.__blocked){this.updateUIBlocked(this.getI18nText("error-label","Error"),o?.__blocked),this.#s.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again")),this.removeEventListener("error",this.boundHandleError);const e=new CustomEvent("error",{bubbles:!0,composed:!0,detail:{isCap:!0,message:"Instrumentation failed"}});return super.dispatchEvent(e),this.addEventListener("error",this.boundHandleError),this.executeAttributeCode("onerror",e),console.error("[cap]","Instrumentation failed"),void(this.#a=!1)}const{token:l}=r,c=await t(`${a}redeem`,{method:"POST",body:JSON.stringify({token:l,solutions:e,...o&&{instr:o}}),headers:{"Content-Type":"application/json"}});let h;try{h=await c.json()}catch{throw new Error("Failed to parse server response")}if(this.dispatchEvent("progress",{progress:100}),!h.success)throw new Error(h.error||"Invalid solution");const d=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${d}']`)&&(this.querySelector(`input[name='${d}']`).value=h.token),this.dispatchEvent("solve",{token:h.token}),this.token=h.token,this.#u(),this.#e&&clearTimeout(this.#e);const p=new Date(h.expires).getTime()-Date.now();return p>0&&p<864e5?this.#e=setTimeout(()=>this.reset(),p):this.error("Invalid expiration time"),this.#s.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#d&&navigator.vibrate([10,50,20,30,40]),{success:!0,token:this.token}}catch(e){throw this.#s.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again")),this.error(e.message),e}}finally{this.#a=!1}}async solveChallenges(e){const t=e.length;let i=0;let s=null;const r="object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate;if(r)try{s=await a()}catch(e){console.warn("[cap] wasm unavailable, falling back to JS solver:",e)}if(!r&&!this.#i.querySelector(".warning")){const e=document.createElement("div");e.className="warning",e.style.cssText="width:var(--cap-widget-width,230px);background:rgb(237,56,46);color:white;padding:4px 6px;padding-bottom:calc(var(--cap-border-radius,14px) + 5px);font-size:10px;box-sizing:border-box;font-family:system-ui;border-top-left-radius:8px;border-top-right-radius:8px;text-align:center;user-select:none;margin-bottom:-35.5px;opacity:0;transition:margin-bottom .3s,opacity .3s;",e.innerText=this.getI18nText("wasm-disabled","Enable WASM for significantly faster solving"),this.#i.insertBefore(e,this.#i.firstChild),setTimeout(()=>{e.style.marginBottom="calc(-1 * var(--cap-border-radius, 14px))",e.style.opacity=1},10)}const n=new l(this.#t);n.setWasm(s),n._ensureSize(this.#t);const o=[];try{for(let s=0;s<e.length;s+=this.#t){const r=e.slice(s,Math.min(s+this.#t,e.length)),a=await Promise.all(r.map(([e,s])=>n.run(e,s).then(e=>{i++;const s=Math.min(99,Math.round((0+i)/t*100));return this.dispatchEvent("progress",{progress:s}),e})));o.push(...a)}}finally{n.terminate()}return o}setWorkersCount(e){const t=parseInt(e,10),i=Math.min(navigator.hardwareConcurrency||8,16);this.#t=!Number.isNaN(t)&&t>0&&t<=i?t:navigator.hardwareConcurrency||8}createUI(){this.#s.classList.add("captcha"),this.#s.setAttribute("role","button"),this.#s.setAttribute("tabindex","0"),this.#s.setAttribute("aria-label",this.getI18nText("verify-aria-label","Click to verify you're a human")),this.#s.setAttribute("aria-live","polite"),this.#s.setAttribute("disabled","true"),this.#s.innerHTML=`<div class="checkbox" part="checkbox"><svg class="progress-ring" viewBox="0 0 32 32"><circle class="progress-ring-bg" cx="16" cy="16" r="14"></circle><circle class="progress-ring-circle" cx="16" cy="16" r="14"></circle></svg></div><p part="label" class="label-wrapper"><span class="label active">${this.getI18nText("initial-state","Verify you're human")}</span></p><a part="attribution" aria-label="Secured by Cap" href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener" title="Secured by Cap: Self-hosted CAPTCHA for the modern web.">Cap</a>`,this.#i.innerHTML=`<style${window.CAP_CSS_NONCE?` nonce=${window.CAP_CSS_NONCE}`:""}>@media (prefers-reduced-motion:reduce){.captcha,.captcha *{transition:none!important;animation:none!important}.label{filter:none!important;opacity:1!important;transition:none!important;transform:none!important}.label:not(.active),.label.exit{display:none!important}}.captcha,.captcha *{box-sizing:border-box}.captcha{background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);-webkit-user-select:none;user-select:none;height:var(--cap-widget-height,58px);width:var(--cap-widget-width,260px);padding:var(--cap-widget-padding,14px);align-items:center;gap:var(--cap-gap,15px);cursor:pointer;-webkit-tap-highlight-color:#fff0;color:var(--cap-color,#212121);transition:filter .2s,transform .2s;display:flex;position:relative;overflow:hidden}.captcha:not([data-state]):active{transform:scale(.97)}.captcha:hover{filter:brightness(98%)}.captcha *{font-family:var(--cap-font,system, -apple-system, "BlinkMacSystemFont", ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", "Ubuntu", "arial", sans-serif)}.checkbox{width:var(--cap-checkbox-size,25px);height:var(--cap-checkbox-size,25px);border:var(--cap-checkbox-border,1px solid #aaaaaad1);border-radius:var(--cap-checkbox-border-radius,6px);background-color:var(--cap-checkbox-background,#fafafa91);margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px);transition:opacity .2s}.captcha p{-webkit-user-select:none;user-select:none;margin:0;font-size:15px;font-weight:500}.label-wrapper{flex:1;align-items:center;min-width:0;height:2em;display:flex;position:relative;overflow:hidden}.label-wrapper:before,.label-wrapper:after{content:"";pointer-events:none;z-index:1;width:100%;height:.6em;position:absolute;left:0}.label-wrapper:before{background:linear-gradient(to bottom, var(--cap-background,#fdfdfd), transparent);top:0}.label-wrapper:after{background:linear-gradient(to top, var(--cap-background,#fdfdfd), transparent);bottom:0}.label{white-space:nowrap;opacity:0;filter:blur(2px);transition:transform .5s cubic-bezier(.25,1,.5,1),opacity .5s,filter .5s;position:absolute;transform:translateY(100%)}.label.active{opacity:1;filter:none;transform:translateY(0)}.label.exit{opacity:0;filter:blur(2px);transform:translateY(-100%)}.checkbox .progress-ring{width:100%;height:100%;display:none;transform:rotate(-90deg)}.checkbox .progress-ring-bg{fill:none;stroke:var(--cap-spinner-background-color,#eee);stroke-width:var(--cap-spinner-thickness,3)}.checkbox .progress-ring-circle{fill:none;stroke:var(--cap-spinner-color,#000);stroke-width:var(--cap-spinner-thickness,3);stroke-linecap:round;stroke-dasharray:87.96;stroke-dashoffset:87.96px;transition:stroke-dashoffset .3s}.captcha[data-state=verifying] .checkbox{background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;display:flex;transform:scale(1.1)}.captcha[data-state=verifying] .checkbox .progress-ring{display:block}.captcha[data-state=done] .checkbox{background-image:var(--cap-checkmark,url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cstyle%3E%40keyframes%20anim%7B0%25%7Bstroke-dashoffset%3A23.21320343017578px%7Dto%7Bstroke-dashoffset%3A0%7D%7D%3C%2Fstyle%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%2300a67d%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m5%2012%205%205L20%207%22%20style%3D%22stroke-dashoffset%3A0%3Bstroke-dasharray%3A23.21320343017578px%3Banimation%3Aanim%20.5s%20ease%22%2F%3E%3C%2Fsvg%3E));background-size:cover;border:1px solid #0000}.captcha[data-state=done] .checkbox .progress-ring{display:none}.captcha[data-state=error] .checkbox{background-image:var(--cap-error-cross,url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 24 24'%3E%3Cpath fill='%23f55b50' d='M11 15h2v2h-2zm0-8h2v6h-2zm1-5C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m0 18a8 8 0 0 1-8-8a8 8 0 0 1 8-8a8 8 0 0 1 8 8a8 8 0 0 1-8 8'/%3E%3C%2Fsvg%3E"));background-size:cover;border:1px solid #0000}.captcha[data-state=error] .checkbox .progress-ring{display:none}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{color:var(--cap-color,#212121);opacity:.8;text-underline-offset:.18em;z-index:6;font-size:12px;text-decoration-thickness:.08em;position:absolute;bottom:10px;right:10px}.cap-troubleshoot-link{color:#06c;text-underline-offset:.15em;cursor:pointer;font-weight:500;font-size:inherit;text-decoration:underline;text-decoration-thickness:.08em}.cap-troubleshoot-link:hover{opacity:.8}</style>`,this.#i.appendChild(this.#s)}addEventListeners(){this.#s&&(this.#s.querySelector("a").addEventListener("click",e=>{e.stopPropagation(),e.preventDefault(),window.open("https://capjs.js.org","_blank")}),this.#s.addEventListener("click",()=>{this.#s.hasAttribute("disabled")||this.solve()}),this.#s.addEventListener("mousedown",()=>{!this.#s.hasAttribute("disabled")&&this.#d&&navigator.vibrate(5)}),this.#s.addEventListener("keydown",e=>{"Enter"!==e.key&&" "!==e.key||this.#s.hasAttribute("disabled")||(e.preventDefault(),e.stopPropagation(),this.solve())}),this.addEventListener("progress",this.boundHandleProgress),this.addEventListener("solve",this.boundHandleSolve),this.addEventListener("error",this.boundHandleError),this.addEventListener("reset",this.boundHandleReset))}animateLabel(e){if(!this.#s)return;const t=this.#s.querySelector(".label-wrapper");if(!t)return;if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches){const i=t.querySelector(".label.active");if(i)i.textContent=e;else{const i=document.createElement("span");i.className="label active",i.textContent=e,t.appendChild(i)}return}const i=t.querySelector(".label.active"),s=document.createElement("span");s.className="label",s.textContent=e,t.appendChild(s),s.offsetWidth,s.classList.add("active"),i&&(i.classList.remove("active"),i.classList.add("exit"),i.addEventListener("transitionend",()=>i.remove(),{once:!0}))}updateUI(e,t,i=!1){this.#s&&(this.#s.setAttribute("data-state",e),this.animateLabel(t),i?this.#s.setAttribute("disabled","true"):this.#s.removeAttribute("disabled"))}updateUIBlocked(e,t=!1){if(!this.#s)return;this.#s.setAttribute("data-state","error"),this.#s.removeAttribute("disabled");const i=this.#s.querySelector(".label-wrapper");if(!i)return;const s=this.getAttribute("data-cap-troubleshooting-url")||"https://capjs.js.org/guide/troubleshooting/instrumentation.html",r=i.querySelector(".label.active"),a=document.createElement("span");a.className="label",a.innerHTML=t?`${e} · <a class="cap-troubleshoot-link" href="${s}" target="_blank" rel="noopener">${this.getI18nText("troubleshooting-label","Troubleshoot")}</a>`:e,i.appendChild(a),a.offsetWidth,a.classList.add("active"),r&&(r.classList.remove("active"),r.classList.add("exit"),r.addEventListener("transitionend",()=>r.remove(),{once:!0}));const n=a.querySelector(".cap-troubleshoot-link");n&&n.addEventListener("click",e=>{e.stopPropagation()})}handleProgress(e){if(!this.#s)return;const t=this.#s.querySelector(".progress-ring-circle");if(t){const i=2*Math.PI*14,s=i-e.detail.progress/100*i;t.style.strokeDashoffset=s}const i=this.#s.querySelector(".label-wrapper");if(i){const e=i.querySelector(".label.active");e&&(e.textContent=`${this.getI18nText("verifying-label","Verifying...")}`)}this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done",this.getI18nText("solved-label","You're a human"),!0),this.executeAttributeCode("onsolve",e)}handleError(e){this.updateUI("error",this.getI18nText("error-label","Error. Try again.")),this.executeAttributeCode("onerror",e),this.#d&&navigator.vibrate([10,40,10])}handleReset(e){this.updateUI("",this.getI18nText("initial-state","I'm a human")),this.executeAttributeCode("onreset",e)}executeAttributeCode(e,t){const i=this.getAttribute(e);i&&(console.error("[cap] using `onxxx='…'` is strongly discouraged and will be deprecated soon. please use `addEventListener` callbacks instead."),new Function("event",i).call(this,t))}error(e="Unknown error"){console.error("[cap]",e),this.dispatchEvent("error",{isCap:!0,message:e})}dispatchEvent(e,t={}){const i=new CustomEvent(e,{bubbles:!0,composed:!0,detail:t});super.dispatchEvent(i)}reset(){this.#e&&(clearTimeout(this.#e),this.#e=null),this.dispatchEvent("reset"),this.token=null;const e=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${e}']`)&&(this.querySelector(`input[name='${e}']`).value="")}get tokenValue(){return this.token}disconnectedCallback(){this.removeEventListener("progress",this.boundHandleProgress),this.removeEventListener("solve",this.boundHandleSolve),this.removeEventListener("error",this.boundHandleError),this.removeEventListener("reset",this.boundHandleReset),this.#n.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#n.clear(),this.#i&&(this.#i.innerHTML=""),this.reset(),this.cleanup()}cleanup(){this.#e&&(clearTimeout(this.#e),this.#e=null),this.#g(),this.#l&&(clearTimeout(this.#l),this.#l=null),this.#c&&(this.#c.terminate(),this.#c=null),this.#o&&(this.#o.state="error",this.#o.notify(),this.#o=null)}}class h{constructor(e={},t){const i=t||document.createElement("cap-widget");if(Object.entries(e).forEach(([e,t])=>{i.setAttribute(e,t)}),!e.apiEndpoint&&!window?.CAP_CUSTOM_FETCH)throw i.remove(),new Error("Missing API endpoint. Either custom fetch or an API endpoint must be provided.");e.apiEndpoint&&i.setAttribute("data-cap-api-endpoint",e.apiEndpoint),t||i.hasAttribute("data-cap-disable-haptics")||i.setAttribute("data-cap-disable-haptics",""),this.widget=i,this.solve=this.widget.solve.bind(this.widget),this.reset=this.widget.reset.bind(this.widget),this.addEventListener=this.widget.addEventListener.bind(this.widget),Object.defineProperty(this,"token",{get:()=>i.token,configurable:!0,enumerable:!0}),t||(i.style.display="none",document.documentElement.appendChild(i))}}window.Cap=h,customElements.get("cap-widget")||window?.CAP_DONT_SKIP_REDEFINE?customElements.get("cap-widget")&&console.warn("[cap] the cap-widget element has already been defined, skipping re-defining it.\nto prevent this, set window.CAP_DONT_SKIP_REDEFINE to true"):customElements.define("cap-widget",c),"object"==typeof exports&&"undefined"!=typeof module?module.exports=h:"function"==typeof define&&define.amd&&define([],()=>h),"undefined"!=typeof exports&&(exports.default=h)})();
|
|
1
|
+
(()=>{const e="vibrate"in navigator&&!window.matchMedia("(prefers-reduced-motion: reduce)").matches;if("undefined"==typeof window)return;const t=(e,t={})=>window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(e,t):fetch(e,t);function i(e,t){let i=function(e){let t=2166136261;for(let i=0;i<e.length;i++)t^=e.charCodeAt(i),t+=(t<<1)+(t<<4)+(t<<7)+(t<<8)+(t<<24);return t>>>0}(e),s="";function r(){return i^=i<<13,i^=i>>>17,i^=i<<5,i>>>0}for(;s.length<t;){s+=r().toString(16).padStart(8,"0")}return s.substring(0,t)}async function s(e){var t=(e=>{const t=atob(e),i=new Uint8Array(t.length);for(let e=0;e<t.length;e++)i[e]=t.charCodeAt(e);return i})(e);const i=await new Promise((e,i)=>{try{var s=new DecompressionStream("deflate-raw"),r=s.writable.getWriter(),a=s.readable.getReader(),n=[];a.read().then(function t(s){if(s.done){for(var r=0,o=0,l=0;l<n.length;l++)r+=n[l].length;var c=new Uint8Array(r);for(l=0;l<n.length;l++)c.set(n[l],o),o+=n[l].length;e((new TextDecoder).decode(c))}else n.push(s.value),a.read().then(t).catch(i)}).catch(i),r.write(t).then(()=>{r.close()}).catch(i)}catch(e){i(e)}});return new Promise(e=>{var t=setTimeout(()=>{a(),e({__timeout:!0})},2e4),s=document.createElement("iframe");s.setAttribute("sandbox","allow-scripts"),s.setAttribute("aria-hidden","true"),s.style.cssText="position:absolute;width:1px;height:1px;top:-9999px;left:-9999px;border:none;opacity:0;pointer-events:none;";var r=!1;function a(){r||(r=!0,clearTimeout(t),window.removeEventListener("message",n),s.parentNode&&s.parentNode.removeChild(s))}function n(t){var i=t.data;i&&"object"==typeof i&&("cap:instr"===i.type?(a(),i.blocked?e({__blocked:!0,blockReason:i.blockReason||"automated_browser"}):i.result?e(i.result):e({__timeout:!0})):"cap:error"===i.type&&(a(),e({__timeout:!0})))}window.addEventListener("message",n),s.srcdoc='<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><script>'+i+"\n<\/script></body></html>",document.body.appendChild(s)})}let r=null;const a=()=>{if(r)return r;const e=window.CAP_CUSTOM_WASM_URL||"https://cdn.jsdelivr.net/npm/@cap.js/wasm@0.0.6/browser/cap_wasm_bg.wasm";return r=fetch(e).then(e=>{if(!e.ok)throw new Error(`Failed to fetch wasm: ${e.status}`);return e.arrayBuffer()}).then(e=>WebAssembly.compile(e)).catch(e=>{throw r=null,e}),r};"object"==typeof WebAssembly&&"function"==typeof WebAssembly.compile&&a().catch(()=>{});let n=null;function o(){return n||(n=URL.createObjectURL(new Blob(['(()=>{const e=async({salt:e,target:t})=>{let n=0;const r=new TextEncoder,o=4*t.length,s=Math.floor(o/8),l=o%8,a=t.length%2==0?t:t+"0",c=a.length/2,i=new Uint8Array(c);for(let e=0;e<c;e++)i[e]=parseInt(a.substring(2*e,2*e+2),16);const f=l>0?255<<8-l&255:0;for(;;)try{for(let t=0;t<5e4;t++){const t=e+n,o=r.encode(t),a=await crypto.subtle.digest("SHA-256",o),c=new Uint8Array(a);let g=!0;for(let e=0;e<s;e++)if(c[e]!==i[e]){g=!1;break}if(g&&l>0&&(c[s]&f)!==(i[s]&f)&&(g=!1),g)return void self.postMessage({nonce:n,found:!0});n++}}catch(e){return console.error("[cap worker]",e),void self.postMessage({found:!1,error:e.message})}};if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly?.instantiate)return console.warn("[cap worker] wasm not supported, falling back to alternative solver. this will be significantly slower."),void(self.onmessage=async({data:{salt:t,target:n}})=>e({salt:t,target:n}));let t=null;self.onmessage=async({data:{salt:n,target:r,wasmModule:o}})=>{if(o instanceof WebAssembly.Module&&null===t){const s=(e=>{try{let n,r=0,o=null;const s=()=>(null!==o&&0!==o.byteLength||(o=new Uint8Array(n.memory.buffer)),o),l=new TextEncoder,a=(e,t,n)=>{if(void 0===n){const n=l.encode(e),o=t(n.length,1)>>>0;return s().subarray(o,o+n.length).set(n),r=n.length,o}let o=e.length,a=t(o,1)>>>0;const c=s();let i=0;for(;i<o;i++){const t=e.charCodeAt(i);if(t>127)break;c[a+i]=t}if(i!==o){0!==i&&(e=e.slice(i)),a=n(a,o,o=i+3*e.length,1)>>>0;const t=s().subarray(a+i,a+o),{written:r}=l.encodeInto(e,t);i+=r,a=n(a,o,i,1)>>>0}return r=i,a},c={wbg:{}};c.wbg.__wbindgen_init_externref_table=()=>{const e=n.__wbindgen_export_0,t=e.grow(4);e.set(0,void 0),e.set(t+0,void 0),e.set(t+1,null),e.set(t+2,!0),e.set(t+3,!1)};const i=new WebAssembly.Instance(e,c);return n=i.exports,n.__wbindgen_start&&n.__wbindgen_start(),t=(e,t)=>{const o=a(e,n.__wbindgen_malloc,n.__wbindgen_realloc),s=r,l=a(t,n.__wbindgen_malloc,n.__wbindgen_realloc),c=r;return BigInt.asUintN(64,n.solve_pow(o,s,l,c))},!0}catch(e){return console.error("[cap worker] failed to init wasm from module:",e),!1}})(o);if(!s)return console.warn("[cap worker] wasm init failed, falling back to JS solver."),e({salt:n,target:r})}if(null===t)return console.warn("[cap worker] no wasm module provided, falling back to JS solver."),e({salt:n,target:r});try{const e=performance.now(),o=t(n,r),s=performance.now();self.postMessage({nonce:Number(o),found:!0,durationMs:(s-e).toFixed(2)})}catch(e){console.error("[cap worker]",e),self.postMessage({found:!1,error:e.message||String(e)})}},self.onerror=e=>{self.postMessage({found:!1,error:e})}})();'],{type:"application/javascript"})),n)}class l{constructor(e){this._size=e,this._workers=[],this._idle=[],this._queue=[],this._wasmModule=null,this._spawnFailures=0}setWasm(e){this._wasmModule=e}_spawn(){const e=o(),t=new Worker(e);return t._busy=!1,this._workers.push(t),this._idle.push(t),t}_replaceWorker(e){const t=this._workers.indexOf(e);-1!==t&&this._workers.splice(t,1);const i=this._idle.indexOf(e);-1!==i&&this._idle.splice(i,1);try{e.terminate()}catch{}return this._spawnFailures++,this._spawnFailures>3?(console.error("[cap] worker spawn failed repeatedly, not retrying"),null):this._spawn()}_ensureSize(e){for(;this._workers.length<e;)this._spawn()}run(e,t){return new Promise((i,s)=>{this._queue.push({salt:e,target:t,resolve:i,reject:s}),this._dispatch()})}_dispatch(){for(;this._idle.length>0&&this._queue.length>0;){const e=this._idle.shift(),{salt:t,target:i,resolve:s,reject:r}=this._queue.shift();let a=!1;const n=({data:t})=>{a||(a=!0,e.removeEventListener("message",n),e.removeEventListener("error",o),this._spawnFailures=0,this._idle.push(e),t.found?s(t.nonce):r(new Error(t.error||"worker failed")),this._dispatch())},o=t=>{if(a)return;a=!0,e.removeEventListener("message",n),e.removeEventListener("error",o);const i=this._replaceWorker(e);r(t),i&&this._dispatch()};e.addEventListener("message",n),e.addEventListener("error",o),this._wasmModule?e.postMessage({salt:t,target:i,wasmModule:this._wasmModule},[]):e.postMessage({salt:t,target:i})}}terminate(){for(const e of this._workers)try{e.terminate()}catch{}this._workers=[],this._idle=[],this._queue=[]}}class c extends HTMLElement{static formAssociated=!0;#e=null;#t=navigator.hardwareConcurrency||8;token=null;#i;#s;#r;#a=!1;#n;#o;#l=null;#c=null;#h=null;#d=null;get#p(){return e&&!window.CAP_DISABLE_HAPTICS&&!this.hasAttribute("data-cap-disable-haptics")}#u(){return{state:"idle",challengeResp:null,challenges:null,results:[],completedCount:0,solvePromise:null,promoteFn:null,_listeners:[],pendingPromotion:null,token:null,tokenExpires:null,notify(){for(const e of this._listeners)e();this._listeners=[]},onSettled(e){"done"===this.state||"error"===this.state?e():this._listeners.push(e)}}}#v(){this.#l=this.#u(),this.#m()}#g(){this.#d&&(window.removeEventListener("mousemove",this.#d),window.removeEventListener("touchstart",this.#d),window.removeEventListener("keydown",this.#d),this.#d=null)}#m(){this.#g();const e=()=>{this.#g(),this.#b()};this.#d=e,window.addEventListener("mousemove",e,{passive:!0}),window.addEventListener("touchstart",e,{passive:!0}),window.addEventListener("keydown",e,{passive:!0})}#f(){return"function"==typeof this.checkVisibility?this.checkVisibility({checkOpacity:!0,checkVisibilityCSS:!0}):!!(this.offsetParent||this.getClientRects().length>0)}#b(){"idle"===this.#l.state&&this.#f()&&(this.#l.state="waiting",this.#c=setTimeout(()=>{this.#w()},2500))}async#w(){if("waiting"!==this.#l.state)return;this.#l.state="fetching";let e=this.getAttribute("data-cap-api-endpoint");if(!e&&window?.CAP_CUSTOM_FETCH&&(e="/"),e){e.endsWith("/")||(e+="/");try{const s=await t(`${e}challenge`,{method:"POST"});let r;try{r=await s.json()}catch{throw new Error("Failed to parse speculative challenge response")}if(r.error)throw new Error(r.error);r._apiEndpoint=e,this.#l.challengeResp=r;const{challenge:a,token:n}=r;let o=a;if(!Array.isArray(o)){let e=0;o=Array.from({length:a.c},()=>(e++,[i(`${n}${e}`,a.s),i(`${n}${e}d`,a.d)]))}this.#l.challenges=o,this.#l.state="solving",this.#l.solvePromise=this.#y(o)}catch(e){console.warn("[cap] speculative challenge fetch failed:",e),this.#l.state="error",this.#l.notify()}}else this.#l.state="idle"}async#y(e){o();let t=null;try{t=await a()}catch{}this.#h||(this.#h=new l(1),this.#h._spawn()),this.#h.setWasm(t);const i=e.length,s=new Array(i);let r=1,n=!1;this.#l.promoteFn=e=>{n||(n=!0,r=e,this.#h._size=e,this.#h._ensureSize(e))},null!==this.#l.pendingPromotion&&(this.#l.promoteFn(this.#l.pendingPromotion),this.#l.pendingPromotion=null);let c=0;for(;c<i;){const t=r,a=[],o=[];for(let s=0;s<t&&c<i;s++)o.push(c),a.push(e[c]),c++;this.#h._ensureSize(Math.max(r,t));const l=await Promise.all(a.map(e=>this.#h.run(e[0],e[1]).then(e=>(this.#l.completedCount++,e))));for(let e=0;e<o.length;e++)s[o[e]]=l[e];!n&&c<i&&await new Promise(e=>setTimeout(e,120))}return this.#l.results=s,this.#l.state="redeeming",this.#k(s),s}async#k(e){try{const i=this.#l.challengeResp,r=i._apiEndpoint;if(!r)throw new Error("[cap] speculative redeem: missing apiEndpoint");let a=null;if(i.instrumentation&&(a=await s(i.instrumentation),a?.__timeout||a?.__blocked))return this.#l.state="done",void this.#l.notify();const n=await t(`${r}redeem`,{method:"POST",body:JSON.stringify({token:i.token,solutions:e,...a&&{instr:a}}),headers:{"Content-Type":"application/json"}});let o;try{o=await n.json()}catch{throw new Error("Failed to parse speculative redeem response")}if(!o.success)throw new Error(o.error||"Speculative redeem failed");this.#l.token=o.token,this.#l.tokenExpires=new Date(o.expires).getTime(),this.#l.state="done",this.#l.notify()}catch(e){console.warn("[cap] speculative redeem failed (will redo on click):",e),this.#l.state="done",this.#l.notify()}}getI18nText(e,t){return this.getAttribute(`data-cap-i18n-${e}`)||t}static get observedAttributes(){return["onsolve","onprogress","onreset","onerror","data-cap-worker-count","data-cap-i18n-initial-state","required"]}constructor(){super(),this.#n&&this.#n.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#n=new Map,this.boundHandleProgress=this.handleProgress.bind(this),this.boundHandleSolve=this.handleSolve.bind(this),this.boundHandleError=this.handleError.bind(this),this.boundHandleReset=this.handleReset.bind(this);try{this.#o=this.attachInternals()}catch{}}#x(){this.#o?.setValidity&&(this.hasAttribute("required")&&!this.token?this.#o.setValidity({valueMissing:!0},this.getI18nText("required-label","Please verify you're human"),this.#s||this):this.#o.setValidity({}))}initialize(){o(),this.#l||(this.#l=this.#u()),this.#h||(this.#h=new l(1),this.#h._spawn())}attributeChangedCallback(e,t,i){if(e.startsWith("on")){const t=e.slice(2),s=this.#n.get(e);if(s&&this.removeEventListener(t,s),i){const i=t=>{const i=this.getAttribute(e);"function"==typeof window[i]&&window[i].call(this,t)};this.#n.set(e,i),this.addEventListener(t,i)}}"data-cap-worker-count"===e&&this.setWorkersCount(parseInt(i,10)),"data-cap-i18n-initial-state"===e&&this.#s&&this.#s?.querySelector(".label.active")&&this.animateLabel(this.getI18nText("initial-state","Verify you're human")),"required"===e&&this.#x()}async connectedCallback(){this.#r=this,this.#i=this.attachShadow({mode:"open"}),this.#s=document.createElement("div"),this.createUI(),this.addEventListeners(),this.initialize(),this.#s.removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count"),t=e?parseInt(e,10):null;this.setWorkersCount(t||navigator.hardwareConcurrency||8);const i=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.#r.innerHTML=`<input type="hidden" name="${i}">`,this.#m(),this.#x(),this.addEventListener("invalid",this.#E)}#E=()=>{if(this.#s){try{this.scrollIntoView({behavior:"smooth",block:"center"})}catch{this.scrollIntoView()}this.#s.classList.remove("invalid"),this.#s.offsetWidth,this.#s.classList.add("invalid"),setTimeout(()=>this.#s?.classList.remove("invalid"),1500)}};async solve(){if(!this.#a)try{this.#a=!0,this.updateUI("verifying",this.getI18nText("verifying-label","Verifying..."),!0),this.#s.setAttribute("aria-label",this.getI18nText("verifying-aria-label","Verifying you're a human, please wait")),this.dispatchEvent("progress",{progress:0});try{let e,r,a=this.getAttribute("data-cap-api-endpoint");if(!a&&window?.CAP_CUSTOM_FETCH)a="/";else if(!a)throw new Error("Missing API endpoint. Either custom fetch or an API endpoint must be provided.");if(a.endsWith("/")||(a+="/"),"done"===this.#l.state&&this.#l.token&&this.#l.tokenExpires&&Date.now()<this.#l.tokenExpires){this.dispatchEvent("progress",{progress:100});const e=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${e}']`)&&(this.querySelector(`input[name='${e}']`).value=this.#l.token),this.dispatchEvent("solve",{token:this.#l.token}),this.token=this.#l.token;const t=this.#l.tokenExpires-Date.now();return this.#e&&clearTimeout(this.#e),this.#e=setTimeout(()=>this.reset(),t),this.#s.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#p&&navigator.vibrate([10,50,20,30,40]),this.#v(),this.#a=!1,{success:!0,token:this.token}}if("done"===this.#l.state)e=this.#l.results,r=this.#l.challengeResp,this.dispatchEvent("progress",{progress:100});else if("solving"===this.#l.state||"redeeming"===this.#l.state||"fetching"===this.#l.state||"waiting"===this.#l.state){"waiting"===this.#l.state&&(this.#c&&(clearTimeout(this.#c),this.#c=null),this.#l.state="waiting",this.#w()),this.#l.pendingPromotion=this.#t,this.#l.promoteFn&&this.#l.promoteFn(this.#t);const t=setInterval(()=>{const e=this.#l.state;if("done"===e||"error"===e)return void clearInterval(t);const i=this.#l.challenges?this.#l.challenges.length:1,s=this.#l.completedCount,r="redeeming"===e?99:"fetching"===e||"waiting"===e?0:Math.min(98,Math.round(s/i*100));this.dispatchEvent("progress",{progress:r})},150);if(await new Promise(e=>this.#l.onSettled(e)),clearInterval(t),"done"!==this.#l.state)throw new Error("Speculative solve failed – please try again");if(this.#l.token&&this.#l.tokenExpires&&Date.now()<this.#l.tokenExpires){this.dispatchEvent("progress",{progress:100});const e=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${e}']`)&&(this.querySelector(`input[name='${e}']`).value=this.#l.token),this.dispatchEvent("solve",{token:this.#l.token}),this.token=this.#l.token;const t=this.#l.tokenExpires-Date.now();return this.#e&&clearTimeout(this.#e),this.#e=setTimeout(()=>this.reset(),t),this.#s.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#p&&navigator.vibrate([10,50,20,30,40]),this.#v(),this.#a=!1,{success:!0,token:this.token}}e=this.#l.results,r=this.#l.challengeResp,this.dispatchEvent("progress",{progress:100})}else{const s=await t(`${a}challenge`,{method:"POST"});try{r=await s.json()}catch{throw new Error("Failed to parse challenge response from server")}if(r.error)throw new Error(r.error);const{challenge:n,token:o}=r;let l=n;if(!Array.isArray(l)){let e=0;l=Array.from({length:n.c},()=>(e++,[i(`${o}${e}`,n.s),i(`${o}${e}d`,n.d)]))}e=await this.solveChallenges(l)}const n=r.instrumentation?s(r.instrumentation):Promise.resolve(null),o=await n;if(o?.__timeout||o?.__blocked){this.updateUIBlocked(this.getI18nText("error-label","Error"),o?.__blocked),this.#s.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again")),this.removeEventListener("error",this.boundHandleError);const e=new CustomEvent("error",{bubbles:!0,composed:!0,detail:{isCap:!0,message:"Instrumentation failed"}});return super.dispatchEvent(e),this.addEventListener("error",this.boundHandleError),this.executeAttributeCode("onerror",e),console.error("[cap]","Instrumentation failed"),void(this.#a=!1)}const{token:l}=r,c=await t(`${a}redeem`,{method:"POST",body:JSON.stringify({token:l,solutions:e,...o&&{instr:o}}),headers:{"Content-Type":"application/json"}});let h;try{h=await c.json()}catch{throw new Error("Failed to parse server response")}if(this.dispatchEvent("progress",{progress:100}),!h.success)throw new Error(h.error||"Invalid solution");const d=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${d}']`)&&(this.querySelector(`input[name='${d}']`).value=h.token),this.dispatchEvent("solve",{token:h.token}),this.token=h.token,this.#v(),this.#e&&clearTimeout(this.#e);const p=new Date(h.expires).getTime()-Date.now();return p>0&&p<864e5?this.#e=setTimeout(()=>this.reset(),p):this.error("Invalid expiration time"),this.#s.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#p&&navigator.vibrate([10,50,20,30,40]),{success:!0,token:this.token}}catch(e){throw this.#s.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again")),this.error(e.message),e}}finally{this.#a=!1}}async solveChallenges(e){const t=e.length;let i=0;let s=null;const r="object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate;if(r)try{s=await a()}catch(e){console.warn("[cap] wasm unavailable, falling back to JS solver:",e)}if(!r&&!this.#i.querySelector(".warning")){const e=document.createElement("div");e.className="warning",e.style.cssText="width:var(--cap-widget-width,230px);background:rgb(237,56,46);color:white;padding:4px 6px;padding-bottom:calc(var(--cap-border-radius,14px) + 5px);font-size:10px;box-sizing:border-box;font-family:system-ui;border-top-left-radius:8px;border-top-right-radius:8px;text-align:center;user-select:none;margin-bottom:-35.5px;opacity:0;transition:margin-bottom .3s,opacity .3s;",e.innerText=this.getI18nText("wasm-disabled","Enable WASM for significantly faster solving"),this.#i.insertBefore(e,this.#i.firstChild),setTimeout(()=>{e.style.marginBottom="calc(-1 * var(--cap-border-radius, 14px))",e.style.opacity=1},10)}const n=new l(this.#t);n.setWasm(s),n._ensureSize(this.#t);const o=[];try{for(let s=0;s<e.length;s+=this.#t){const r=e.slice(s,Math.min(s+this.#t,e.length)),a=await Promise.all(r.map(([e,s])=>n.run(e,s).then(e=>{i++;const s=Math.min(99,Math.round((0+i)/t*100));return this.dispatchEvent("progress",{progress:s}),e})));o.push(...a)}}finally{n.terminate()}return o}setWorkersCount(e){const t=parseInt(e,10),i=Math.min(navigator.hardwareConcurrency||8,16);this.#t=!Number.isNaN(t)&&t>0&&t<=i?t:navigator.hardwareConcurrency||8}createUI(){this.#s.classList.add("captcha"),this.#s.setAttribute("role","button"),this.#s.setAttribute("tabindex","0"),this.#s.setAttribute("aria-label",this.getI18nText("verify-aria-label","Click to verify you're a human")),this.#s.setAttribute("aria-live","polite"),this.#s.setAttribute("disabled","true"),this.#s.innerHTML=`<div class="checkbox" part="checkbox"><svg class="progress-ring" viewBox="0 0 32 32"><circle class="progress-ring-bg" cx="16" cy="16" r="14"></circle><circle class="progress-ring-circle" cx="16" cy="16" r="14"></circle></svg></div><p part="label" class="label-wrapper"><span class="label active">${this.getI18nText("initial-state","Verify you're human")}</span></p><a part="attribution" aria-label="Secured by Cap" href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener" title="Secured by Cap: Self-hosted CAPTCHA for the modern web.">Cap</a>`,this.#i.innerHTML=`<style${window.CAP_CSS_NONCE?` nonce=${window.CAP_CSS_NONCE}`:""}>@media (prefers-reduced-motion:reduce){.captcha,.captcha *{transition:none!important;animation:none!important}.label{filter:none!important;opacity:1!important;transition:none!important;transform:none!important}.label:not(.active),.label.exit{display:none!important}}.captcha,.captcha *{box-sizing:border-box}.captcha{background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);-webkit-user-select:none;user-select:none;height:var(--cap-widget-height,58px);width:var(--cap-widget-width,260px);padding:var(--cap-widget-padding,14px);align-items:center;gap:var(--cap-gap,15px);cursor:pointer;-webkit-tap-highlight-color:#fff0;color:var(--cap-color,#212121);transition:filter .2s,transform .2s;display:flex;position:relative;overflow:hidden}.captcha:not([data-state]):active{transform:scale(.97)}.captcha:hover{filter:brightness(98%)}.captcha *{font-family:var(--cap-font,system, -apple-system, "BlinkMacSystemFont", ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", "Ubuntu", "arial", sans-serif)}.checkbox{width:var(--cap-checkbox-size,25px);height:var(--cap-checkbox-size,25px);border:var(--cap-checkbox-border,1px solid #aaaaaad1);border-radius:var(--cap-checkbox-border-radius,6px);background-color:var(--cap-checkbox-background,#fafafa91);margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px);transition:opacity .2s}.captcha p{-webkit-user-select:none;user-select:none;margin:0;font-size:15px;font-weight:500}.label-wrapper{flex:1;align-items:center;min-width:0;height:2em;display:flex;position:relative;overflow:hidden}.label-wrapper:before,.label-wrapper:after{content:"";pointer-events:none;z-index:1;width:100%;height:.6em;position:absolute;left:0}.label-wrapper:before{background:linear-gradient(to bottom, var(--cap-background,#fdfdfd), transparent);top:0}.label-wrapper:after{background:linear-gradient(to top, var(--cap-background,#fdfdfd), transparent);bottom:0}.label{white-space:nowrap;opacity:0;filter:blur(2px);transition:transform .5s cubic-bezier(.25,1,.5,1),opacity .5s,filter .5s;position:absolute;transform:translateY(100%)}.label.active{opacity:1;filter:none;transform:translateY(0)}.label.exit{opacity:0;filter:blur(2px);transform:translateY(-100%)}.checkbox .progress-ring{width:100%;height:100%;display:none;transform:rotate(-90deg)}.checkbox .progress-ring-bg{fill:none;stroke:var(--cap-spinner-background-color,#eee);stroke-width:var(--cap-spinner-thickness,3)}.checkbox .progress-ring-circle{fill:none;stroke:var(--cap-spinner-color,#000);stroke-width:var(--cap-spinner-thickness,3);stroke-linecap:round;stroke-dasharray:87.96;stroke-dashoffset:87.96px;transition:stroke-dashoffset .3s}.captcha[data-state=verifying] .checkbox{background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;display:flex;transform:scale(1.1)}.captcha[data-state=verifying] .checkbox .progress-ring{display:block}.captcha[data-state=done] .checkbox{background-image:var(--cap-checkmark,url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cstyle%3E%40keyframes%20anim%7B0%25%7Bstroke-dashoffset%3A23.21320343017578px%7Dto%7Bstroke-dashoffset%3A0%7D%7D%3C%2Fstyle%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%2300a67d%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m5%2012%205%205L20%207%22%20style%3D%22stroke-dashoffset%3A0%3Bstroke-dasharray%3A23.21320343017578px%3Banimation%3Aanim%20.5s%20ease%22%2F%3E%3C%2Fsvg%3E));background-size:cover;border:1px solid #0000}.captcha[data-state=done] .checkbox .progress-ring{display:none}.captcha[data-state=error] .checkbox{background-image:var(--cap-error-cross,url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 24 24'%3E%3Cpath fill='%23f55b50' d='M11 15h2v2h-2zm0-8h2v6h-2zm1-5C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m0 18a8 8 0 0 1-8-8a8 8 0 0 1 8-8a8 8 0 0 1 8 8a8 8 0 0 1-8 8'/%3E%3C%2Fsvg%3E"));background-size:cover;border:1px solid #0000}.captcha[data-state=error] .checkbox .progress-ring{display:none}.captcha.invalid{border-color:var(--cap-invalid-border-color,#f55b50);box-shadow:0 0 0 3px var(--cap-invalid-ring-color,#f55b5033);animation:.5s cubic-bezier(.36,.07,.19,.97) cap-shake}@keyframes cap-shake{10%,90%{transform:translate(-1px)}20%,80%{transform:translate(2px)}30%,50%,70%{transform:translate(-4px)}40%,60%{transform:translate(4px)}}@media (prefers-reduced-motion:reduce){.captcha.invalid{animation:none!important}}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{color:var(--cap-color,#212121);text-underline-offset:.18em;z-index:6;text-decoration-thickness:.08em;position:absolute;bottom:10px;right:10px;opacity:.8!important;font-size:12px!important;display:block!important}.cap-troubleshoot-link{color:#06c;text-underline-offset:.15em;cursor:pointer;font-weight:500;font-size:inherit;text-decoration:underline;text-decoration-thickness:.08em}.cap-troubleshoot-link:hover{opacity:.8}</style>`,this.#i.appendChild(this.#s)}addEventListeners(){this.#s&&(this.#s.querySelector("a").addEventListener("click",e=>{e.stopPropagation(),e.preventDefault(),window.open("https://capjs.js.org","_blank")}),this.#s.addEventListener("click",()=>{this.#s.hasAttribute("disabled")||this.solve()}),this.#s.addEventListener("mousedown",()=>{!this.#s.hasAttribute("disabled")&&this.#p&&navigator.vibrate(5)}),this.#s.addEventListener("keydown",e=>{"Enter"!==e.key&&" "!==e.key||this.#s.hasAttribute("disabled")||(e.preventDefault(),e.stopPropagation(),this.solve())}),this.addEventListener("progress",this.boundHandleProgress),this.addEventListener("solve",this.boundHandleSolve),this.addEventListener("error",this.boundHandleError),this.addEventListener("reset",this.boundHandleReset))}animateLabel(e){if(!this.#s)return;const t=this.#s.querySelector(".label-wrapper");if(!t)return;if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches){const i=t.querySelector(".label.active");if(i)i.textContent=e;else{const i=document.createElement("span");i.className="label active",i.textContent=e,t.appendChild(i)}return}const i=t.querySelector(".label.active"),s=document.createElement("span");s.className="label",s.textContent=e,t.appendChild(s),s.offsetWidth,s.classList.add("active"),i&&(i.classList.remove("active"),i.classList.add("exit"),i.addEventListener("transitionend",()=>i.remove(),{once:!0}))}updateUI(e,t,i=!1){this.#s&&(this.#s.setAttribute("data-state",e),this.animateLabel(t),i?this.#s.setAttribute("disabled","true"):this.#s.removeAttribute("disabled"))}updateUIBlocked(e,t=!1){if(!this.#s)return;this.#s.setAttribute("data-state","error"),this.#s.removeAttribute("disabled");const i=this.#s.querySelector(".label-wrapper");if(!i)return;const s=this.getAttribute("data-cap-troubleshooting-url")||"https://capjs.js.org/guide/troubleshooting/instrumentation.html",r=i.querySelector(".label.active"),a=document.createElement("span");a.className="label",a.innerHTML=t?`${e} · <a class="cap-troubleshoot-link" href="${s}" target="_blank" rel="noopener">${this.getI18nText("troubleshooting-label","Troubleshoot")}</a>`:e,i.appendChild(a),a.offsetWidth,a.classList.add("active"),r&&(r.classList.remove("active"),r.classList.add("exit"),r.addEventListener("transitionend",()=>r.remove(),{once:!0}));const n=a.querySelector(".cap-troubleshoot-link");n&&n.addEventListener("click",e=>{e.stopPropagation()})}handleProgress(e){if(!this.#s)return;const t=this.#s.querySelector(".progress-ring-circle");if(t){const i=2*Math.PI*14,s=i-e.detail.progress/100*i;t.style.strokeDashoffset=s}const i=this.#s.querySelector(".label-wrapper");if(i){const e=i.querySelector(".label.active");e&&(e.textContent=`${this.getI18nText("verifying-label","Verifying...")}`)}this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done",this.getI18nText("solved-label","You're a human"),!0),this.executeAttributeCode("onsolve",e),this.#o?.setValidity?.({}),this.#s?.classList.remove("invalid")}handleError(e){this.updateUI("error",this.getI18nText("error-label","Error. Try again.")),this.executeAttributeCode("onerror",e),this.#p&&navigator.vibrate([10,40,10])}handleReset(e){this.updateUI("",this.getI18nText("initial-state","I'm a human")),this.executeAttributeCode("onreset",e),this.#x()}executeAttributeCode(e,t){const i=this.getAttribute(e);i&&(console.error("[cap] using `onxxx='…'` is strongly discouraged and will be deprecated soon. please use `addEventListener` callbacks instead."),new Function("event",i).call(this,t))}error(e="Unknown error"){console.error("[cap]",e),this.dispatchEvent("error",{isCap:!0,message:e})}dispatchEvent(e,t={}){const i=new CustomEvent(e,{bubbles:!0,composed:!0,detail:t});super.dispatchEvent(i)}reset(){this.#e&&(clearTimeout(this.#e),this.#e=null),this.token=null,this.dispatchEvent("reset");const e=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${e}']`)&&(this.querySelector(`input[name='${e}']`).value="")}get tokenValue(){return this.token}disconnectedCallback(){this.removeEventListener("progress",this.boundHandleProgress),this.removeEventListener("solve",this.boundHandleSolve),this.removeEventListener("error",this.boundHandleError),this.removeEventListener("reset",this.boundHandleReset),this.#n.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#n.clear(),this.#i&&(this.#i.innerHTML=""),this.reset(),this.cleanup()}cleanup(){this.#e&&(clearTimeout(this.#e),this.#e=null),this.#g(),this.#c&&(clearTimeout(this.#c),this.#c=null),this.#h&&(this.#h.terminate(),this.#h=null),this.#l&&(this.#l.state="error",this.#l.notify(),this.#l=null)}}class h{constructor(e={},t){const i=t||document.createElement("cap-widget");if(Object.entries(e).forEach(([e,t])=>{i.setAttribute(e,t)}),!e.apiEndpoint&&!window?.CAP_CUSTOM_FETCH)throw i.remove(),new Error("Missing API endpoint. Either custom fetch or an API endpoint must be provided.");e.apiEndpoint&&i.setAttribute("data-cap-api-endpoint",e.apiEndpoint),t||i.hasAttribute("data-cap-disable-haptics")||i.setAttribute("data-cap-disable-haptics",""),this.widget=i,this.solve=this.widget.solve.bind(this.widget),this.reset=this.widget.reset.bind(this.widget),this.addEventListener=this.widget.addEventListener.bind(this.widget),Object.defineProperty(this,"token",{get:()=>i.token,configurable:!0,enumerable:!0}),t||(i.style.display="none",document.documentElement.appendChild(i))}}window.Cap=h,customElements.get("cap-widget")||window?.CAP_DONT_SKIP_REDEFINE?customElements.get("cap-widget")&&console.warn("[cap] the cap-widget element has already been defined, skipping re-defining it.\nto prevent this, set window.CAP_DONT_SKIP_REDEFINE to true"):customElements.define("cap-widget",c),"object"==typeof exports&&"undefined"!=typeof module?module.exports=h:"function"==typeof define&&define.amd&&define([],()=>h),"undefined"!=typeof exports&&(exports.default=h)})();
|
package/package.json
CHANGED
package/src/cap-floating.js
CHANGED
|
@@ -1,25 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
|
-
const
|
|
3
|
-
const trigger = () => {
|
|
4
|
-
handlers.forEach((h) => {
|
|
5
|
-
element.addEventListener("click", h);
|
|
6
|
-
h.call(element, evt);
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
setTimeout(() => {
|
|
10
|
-
element.onclick = null;
|
|
11
|
-
handlers.forEach((h) => {
|
|
12
|
-
return element.removeEventListener("click", h);
|
|
13
|
-
});
|
|
14
|
-
element.onclick = (e) => handleClick(e, element, capWidget, handlers);
|
|
15
|
-
}, 50);
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
element.onclick = null;
|
|
19
|
-
|
|
2
|
+
const showWidget = (element, capWidget) => {
|
|
20
3
|
const offset = parseInt(element.getAttribute("data-cap-floating-offset")) || 8;
|
|
21
4
|
const position = element.getAttribute("data-cap-floating-position") || "top";
|
|
22
|
-
const rect = element.getBoundingClientRect();
|
|
23
5
|
|
|
24
6
|
Object.assign(capWidget.style, {
|
|
25
7
|
display: "block",
|
|
@@ -39,41 +21,38 @@
|
|
|
39
21
|
capWidget.style.marginTop = "0";
|
|
40
22
|
}, 5);
|
|
41
23
|
|
|
42
|
-
|
|
43
|
-
|
|
24
|
+
// `position: absolute` is resolved against the nearest positioned ancestor (capWidget.offsetParent),
|
|
25
|
+
// but getBoundingClientRect() returns viewport-relative coords, convert to container-relative coords.
|
|
26
|
+
const triggerRect = element.getBoundingClientRect();
|
|
27
|
+
const containerRect = (capWidget.offsetParent ?? document.documentElement).getBoundingClientRect();
|
|
28
|
+
|
|
29
|
+
const centeredLeft = triggerRect.left + (triggerRect.width - capWidget.offsetWidth) / 2;
|
|
30
|
+
const clampedLeft = Math.max(2, Math.min(centeredLeft, window.innerWidth - capWidget.offsetWidth));
|
|
31
|
+
capWidget.style.left = `${clampedLeft - containerRect.left}px`;
|
|
44
32
|
|
|
45
33
|
if (position === "top") {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
)}px`;
|
|
34
|
+
const idealTop = triggerRect.top - capWidget.offsetHeight - offset;
|
|
35
|
+
const clampedTop = Math.max(0, idealTop);
|
|
36
|
+
capWidget.style.top = `${clampedTop - containerRect.top}px`;
|
|
50
37
|
} else {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
)}px`;
|
|
38
|
+
const idealTop = triggerRect.bottom + offset;
|
|
39
|
+
const clampedTop = Math.min(idealTop, window.innerHeight - capWidget.offsetHeight);
|
|
40
|
+
capWidget.style.top = `${clampedTop - containerRect.top}px`;
|
|
55
41
|
}
|
|
56
42
|
|
|
57
|
-
capWidget.style.left = `${Math.max(safeX, 2)}px`;
|
|
58
43
|
capWidget.solve();
|
|
44
|
+
};
|
|
59
45
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
capWidget.style.marginTop = "-4px";
|
|
71
|
-
}, 500);
|
|
72
|
-
|
|
73
|
-
setTimeout(() => {
|
|
74
|
-
capWidget.style.display = "none";
|
|
75
|
-
}, 700);
|
|
76
|
-
});
|
|
46
|
+
const hideWidget = (capWidget) => {
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
capWidget.style.transform = "scale(0.98)";
|
|
49
|
+
capWidget.style.opacity = "0";
|
|
50
|
+
capWidget.style.marginTop = "-4px";
|
|
51
|
+
}, 500);
|
|
52
|
+
|
|
53
|
+
setTimeout(() => {
|
|
54
|
+
capWidget.style.display = "none";
|
|
55
|
+
}, 700);
|
|
77
56
|
};
|
|
78
57
|
|
|
79
58
|
const setupElement = (element) => {
|
|
@@ -86,24 +65,38 @@
|
|
|
86
65
|
}
|
|
87
66
|
|
|
88
67
|
capWidget.style.display = "none";
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
68
|
+
let bypass = false;
|
|
69
|
+
|
|
70
|
+
element.addEventListener(
|
|
71
|
+
"click",
|
|
72
|
+
(e) => {
|
|
73
|
+
if (bypass) {
|
|
74
|
+
bypass = false;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
e.stopImmediatePropagation();
|
|
79
|
+
e.preventDefault();
|
|
80
|
+
|
|
81
|
+
showWidget(element, capWidget);
|
|
82
|
+
|
|
83
|
+
const onSolve = ({ detail }) => {
|
|
84
|
+
capWidget.removeEventListener("solve", onSolve);
|
|
85
|
+
element.setAttribute("data-cap-token", detail.token);
|
|
86
|
+
element.setAttribute("data-cap-progress", "done");
|
|
87
|
+
|
|
88
|
+
setTimeout(() => {
|
|
89
|
+
bypass = true;
|
|
90
|
+
element.click();
|
|
91
|
+
}, 500);
|
|
92
|
+
|
|
93
|
+
hideWidget(capWidget);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
capWidget.addEventListener("solve", onSolve);
|
|
97
|
+
},
|
|
98
|
+
true,
|
|
99
|
+
);
|
|
107
100
|
};
|
|
108
101
|
|
|
109
102
|
const init = (root) => {
|
package/src/cap.css
CHANGED
|
@@ -210,6 +210,25 @@
|
|
|
210
210
|
display: none;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
.captcha.invalid {
|
|
214
|
+
animation: cap-shake 0.5s cubic-bezier(0.36, 0.07, 0.19, 0.97);
|
|
215
|
+
border-color: var(--cap-invalid-border-color, #f55b50);
|
|
216
|
+
box-shadow: 0 0 0 3px var(--cap-invalid-ring-color, rgba(245, 91, 80, 0.2));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
@keyframes cap-shake {
|
|
220
|
+
10%, 90% { transform: translateX(-1px); }
|
|
221
|
+
20%, 80% { transform: translateX(2px); }
|
|
222
|
+
30%, 50%, 70% { transform: translateX(-4px); }
|
|
223
|
+
40%, 60% { transform: translateX(4px); }
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
@media (prefers-reduced-motion: reduce) {
|
|
227
|
+
.captcha.invalid {
|
|
228
|
+
animation: none !important;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
213
232
|
.captcha[disabled] {
|
|
214
233
|
cursor: not-allowed;
|
|
215
234
|
}
|
|
@@ -226,12 +245,13 @@
|
|
|
226
245
|
position: absolute;
|
|
227
246
|
bottom: 10px;
|
|
228
247
|
right: 10px;
|
|
229
|
-
font-size: 12px;
|
|
248
|
+
font-size: 12px !important;
|
|
230
249
|
color: var(--cap-color, #212121);
|
|
231
|
-
opacity: 0.8;
|
|
250
|
+
opacity: 0.8 !important;
|
|
232
251
|
text-underline-offset: 0.18em;
|
|
233
252
|
text-decoration-thickness: 0.08em;
|
|
234
253
|
z-index: 6;
|
|
254
|
+
display: block !important;
|
|
235
255
|
}
|
|
236
256
|
|
|
237
257
|
.cap-troubleshoot-link {
|
package/src/cap.js
CHANGED
|
@@ -297,6 +297,7 @@
|
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
class CapWidget extends HTMLElement {
|
|
300
|
+
static formAssociated = true;
|
|
300
301
|
#resetTimer = null;
|
|
301
302
|
#workersCount = navigator.hardwareConcurrency || 8;
|
|
302
303
|
token = null;
|
|
@@ -305,6 +306,7 @@
|
|
|
305
306
|
#host;
|
|
306
307
|
#solving = false;
|
|
307
308
|
#eventHandlers;
|
|
309
|
+
#internals;
|
|
308
310
|
|
|
309
311
|
#speculative = null;
|
|
310
312
|
#speculativeTimer = null;
|
|
@@ -564,6 +566,7 @@
|
|
|
564
566
|
"onerror",
|
|
565
567
|
"data-cap-worker-count",
|
|
566
568
|
"data-cap-i18n-initial-state",
|
|
569
|
+
"required",
|
|
567
570
|
];
|
|
568
571
|
}
|
|
569
572
|
|
|
@@ -580,6 +583,23 @@
|
|
|
580
583
|
this.boundHandleSolve = this.handleSolve.bind(this);
|
|
581
584
|
this.boundHandleError = this.handleError.bind(this);
|
|
582
585
|
this.boundHandleReset = this.handleReset.bind(this);
|
|
586
|
+
|
|
587
|
+
try {
|
|
588
|
+
this.#internals = this.attachInternals();
|
|
589
|
+
} catch {}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
#updateValidity() {
|
|
593
|
+
if (!this.#internals?.setValidity) return;
|
|
594
|
+
if (this.hasAttribute("required") && !this.token) {
|
|
595
|
+
this.#internals.setValidity(
|
|
596
|
+
{ valueMissing: true },
|
|
597
|
+
this.getI18nText("required-label", "Please verify you're human"),
|
|
598
|
+
this.#div || this,
|
|
599
|
+
);
|
|
600
|
+
} else {
|
|
601
|
+
this.#internals.setValidity({});
|
|
602
|
+
}
|
|
583
603
|
}
|
|
584
604
|
|
|
585
605
|
initialize() {
|
|
@@ -624,6 +644,10 @@
|
|
|
624
644
|
) {
|
|
625
645
|
this.animateLabel(this.getI18nText("initial-state", "Verify you're human"));
|
|
626
646
|
}
|
|
647
|
+
|
|
648
|
+
if (name === "required") {
|
|
649
|
+
this.#updateValidity();
|
|
650
|
+
}
|
|
627
651
|
}
|
|
628
652
|
|
|
629
653
|
async connectedCallback() {
|
|
@@ -642,8 +666,24 @@
|
|
|
642
666
|
this.#host.innerHTML = `<input type="hidden" name="${fieldName}">`;
|
|
643
667
|
|
|
644
668
|
this.#attachInteractionListeners();
|
|
669
|
+
this.#updateValidity();
|
|
670
|
+
|
|
671
|
+
this.addEventListener("invalid", this.#handleInvalid);
|
|
645
672
|
}
|
|
646
673
|
|
|
674
|
+
#handleInvalid = () => {
|
|
675
|
+
if (!this.#div) return;
|
|
676
|
+
try {
|
|
677
|
+
this.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
678
|
+
} catch {
|
|
679
|
+
this.scrollIntoView();
|
|
680
|
+
}
|
|
681
|
+
this.#div.classList.remove("invalid");
|
|
682
|
+
void this.#div.offsetWidth;
|
|
683
|
+
this.#div.classList.add("invalid");
|
|
684
|
+
setTimeout(() => this.#div?.classList.remove("invalid"), 1500);
|
|
685
|
+
};
|
|
686
|
+
|
|
647
687
|
async solve() {
|
|
648
688
|
if (this.#solving) {
|
|
649
689
|
return;
|
|
@@ -1142,6 +1182,8 @@
|
|
|
1142
1182
|
handleSolve(event) {
|
|
1143
1183
|
this.updateUI("done", this.getI18nText("solved-label", "You're a human"), true);
|
|
1144
1184
|
this.executeAttributeCode("onsolve", event);
|
|
1185
|
+
this.#internals?.setValidity?.({});
|
|
1186
|
+
this.#div?.classList.remove("invalid");
|
|
1145
1187
|
}
|
|
1146
1188
|
|
|
1147
1189
|
handleError(event) {
|
|
@@ -1154,6 +1196,7 @@
|
|
|
1154
1196
|
handleReset(event) {
|
|
1155
1197
|
this.updateUI("", this.getI18nText("initial-state", "I'm a human"));
|
|
1156
1198
|
this.executeAttributeCode("onreset", event);
|
|
1199
|
+
this.#updateValidity();
|
|
1157
1200
|
}
|
|
1158
1201
|
|
|
1159
1202
|
executeAttributeCode(attributeName, event) {
|
|
@@ -1188,8 +1231,8 @@
|
|
|
1188
1231
|
clearTimeout(this.#resetTimer);
|
|
1189
1232
|
this.#resetTimer = null;
|
|
1190
1233
|
}
|
|
1191
|
-
this.dispatchEvent("reset");
|
|
1192
1234
|
this.token = null;
|
|
1235
|
+
this.dispatchEvent("reset");
|
|
1193
1236
|
const fieldName = this.getAttribute("data-cap-hidden-field-name") || "cap-token";
|
|
1194
1237
|
if (this.querySelector(`input[name='${fieldName}']`)) {
|
|
1195
1238
|
this.querySelector(`input[name='${fieldName}']`).value = "";
|