cap-widget 0.1.48 → 0.1.49
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.d.ts +1 -0
- package/cap.min.js +1 -1
- package/package.json +1 -1
- package/src/cap.css +115 -31
- package/src/cap.js +97 -64
package/cap.d.ts
CHANGED
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)}let s=null;async function r(e){const 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),i=(new TextDecoder).decode(await async function(e){if("undefined"!=typeof DecompressionStream)try{const t=new DecompressionStream("deflate-raw"),i=t.writable.getWriter(),s=t.readable.getReader();i.write(e).then(()=>i.close()).catch(()=>{});const r=[];for(;;){const{done:e,value:t}=await s.read();if(e)break;r.push(t)}let a=0;for(const e of r)a+=e.length;const n=new Uint8Array(a);let o=0;for(const e of r)n.set(e,o),o+=e.length;return n}catch{}return s||(s=new Promise((e,t)=>{const i=window.CAP_PAKO_URL||"https://cdn.jsdelivr.net/npm/pako@2.1.0/dist/pako_inflate.min.js",r=document.createElement("script");r.src=i,r.onload=()=>{window.pako?.inflateRaw?e(window.pako):t(new Error("[cap] pako loaded but inflateRaw is missing"))},r.onerror=()=>{s=null,t(new Error(`[cap] failed to load pako fallback from ${i}`))},document.head.appendChild(r)})),(await s).inflateRaw(e)}(t));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 a=null;const n=()=>{if(a)return a;const e=window.CAP_CUSTOM_WASM_URL||"https://cdn.jsdelivr.net/npm/@cap.js/wasm@0.0.6/browser/cap_wasm_bg.wasm";return a=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 a=null,e}),a};"object"==typeof WebAssembly&&"function"==typeof WebAssembly.compile&&n().catch(()=>{});let o=null;function l(){return o||(o=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"})),o)}class c{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=l(),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 d extends HTMLElement{static formAssociated=!0;#e=null;#t=navigator.hardwareConcurrency||8;token=null;#i;#s;#r;#a=!1;#n;#o;#l=null;#c=null;#d=null;#h=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.#h&&(window.removeEventListener("mousemove",this.#h),window.removeEventListener("touchstart",this.#h),window.removeEventListener("keydown",this.#h),this.#h=null)}#m(){this.#g();const e=()=>{this.#g(),this.#b()};this.#h=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){l();let t=null;try{t=await n()}catch{}this.#d||(this.#d=new c(1),this.#d._spawn()),this.#d.setWasm(t);const i=e.length,s=new Array(i);let r=1,a=!1;this.#l.promoteFn=e=>{a||(a=!0,r=e,this.#d._size=e,this.#d._ensureSize(e))},null!==this.#l.pendingPromotion&&(this.#l.promoteFn(this.#l.pendingPromotion),this.#l.pendingPromotion=null);let o=0;for(;o<i;){const t=r,n=[],l=[];for(let s=0;s<t&&o<i;s++)l.push(o),n.push(e[o]),o++;this.#d._ensureSize(Math.max(r,t));const c=await Promise.all(n.map(e=>this.#d.run(e[0],e[1]).then(e=>(this.#l.completedCount++,e))));for(let e=0;e<l.length;e++)s[l[e]]=c[e];!a&&o<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,s=i._apiEndpoint;if(!s)throw new Error("[cap] speculative redeem: missing apiEndpoint");let a=null;if(i.instrumentation&&(a=await r(i.instrumentation),a?.__timeout||a?.__blocked))return this.#l.state="done",void this.#l.notify();const n=await t(`${s}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(){l(),this.#l||(this.#l=this.#u()),this.#d||(this.#d=new c(1),this.#d._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.shadowRoot?this.#i=this.shadowRoot:this.#i=this.attachShadow({mode:"open"}),this.#s||(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,s,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,s=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,s=this.#l.challengeResp,this.dispatchEvent("progress",{progress:100})}else{const r=await t(`${a}challenge`,{method:"POST"});try{s=await r.json()}catch{throw new Error("Failed to parse challenge response from server")}if(s.error)throw new Error(s.error);const{challenge:n,token:o}=s;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=s.instrumentation?r(s.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}=s,c=await t(`${a}redeem`,{method:"POST",body:JSON.stringify({token:l,solutions:e,...o&&{instr:o}}),headers:{"Content-Type":"application/json"}});let d;try{d=await c.json()}catch{throw new Error("Failed to parse server response")}if(this.dispatchEvent("progress",{progress:100}),!d.success)throw new Error(d.error||"Invalid solution");const h=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${h}']`)&&(this.querySelector(`input[name='${h}']`).value=d.token),this.dispatchEvent("solve",{token:d.token}),this.token=d.token,this.#v(),this.#e&&clearTimeout(this.#e);const p=new Date(d.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 n()}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 a=new c(this.#t);a.setWasm(s),a._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)),n=await Promise.all(r.map(([e,s])=>a.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(...n)}}finally{a.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://trycap.dev" class="credits" target="_blank" tabindex="-1" title="Secured by Cap: The 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://trycap.dev/?${new URLSearchParams({utm_source:"cap_widget",utm_medium:"referral",utm_campaign:"widget",utm_content:window.CAP_DISABLE_WIDGET_REF?"":location.hostname,ref:window.CAP_DISABLE_WIDGET_REF?"":location.href||"",sub:window.CAP_DISABLE_WIDGET_REF?"":document.referrer||""}).toString()}`,"_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://trycap.dev/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.#d&&(this.#d.terminate(),this.#d=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",d),"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)}let s=null;async function r(e){const 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),i=(new TextDecoder).decode(await async function(e){if("undefined"!=typeof DecompressionStream)try{const t=new DecompressionStream("deflate-raw"),i=t.writable.getWriter(),s=t.readable.getReader();i.write(e).then(()=>i.close()).catch(()=>{});const r=[];for(;;){const{done:e,value:t}=await s.read();if(e)break;r.push(t)}let a=0;for(const e of r)a+=e.length;const n=new Uint8Array(a);let o=0;for(const e of r)n.set(e,o),o+=e.length;return n}catch{}return s||(s=new Promise((e,t)=>{const i=window.CAP_PAKO_URL||"https://cdn.jsdelivr.net/npm/pako@2.1.0/dist/pako_inflate.min.js",r=document.createElement("script");r.src=i;const a=window.CAP_SCRIPT_NONCE||window.CAP_CSS_NONCE;a&&r.setAttribute("nonce",a),r.onload=()=>{window.pako?.inflateRaw?e(window.pako):t(new Error("[cap] pako loaded but inflateRaw is missing"))},r.onerror=()=>{s=null,t(new Error(`[cap] failed to load pako fallback from ${i}`))},document.head.appendChild(r)})),(await s).inflateRaw(e)}(t));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);const o=window.CAP_SCRIPT_NONCE||window.CAP_CSS_NONCE,l=o?' nonce="'+String(o).replace(/"/g,""")+'"':"";s.srcdoc='<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><script'+l+">"+i+"\n<\/script></body></html>",document.body.appendChild(s)})}let a=null;const n=()=>{if(a)return a;const e=window.CAP_CUSTOM_WASM_URL||"https://cdn.jsdelivr.net/npm/@cap.js/wasm@0.0.6/browser/cap_wasm_bg.wasm";return a=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 a=null,e}),a};"object"==typeof WebAssembly&&"function"==typeof WebAssembly.compile&&n().catch(()=>{});let o=null;function l(){return o||(o=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"})),o)}class c{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=l(),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 h extends HTMLElement{static formAssociated=!0;#e=null;#t=navigator.hardwareConcurrency||8;token=null;#i;#s;#r;#a;#n;#o;#l=!1;#c;#h;#d=null;#p=null;#u=null;#g=null;get#v(){return e&&!window.CAP_DISABLE_HAPTICS&&!this.hasAttribute("data-cap-disable-haptics")}#b(){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)}}}#m(){this.#d=this.#b(),this.#f()}#w(){this.#g&&(window.removeEventListener("mousemove",this.#g),window.removeEventListener("touchstart",this.#g),window.removeEventListener("keydown",this.#g),this.#g=null)}#f(){this.#w();const e=()=>{this.#w(),this.#k()};this.#g=e,window.addEventListener("mousemove",e,{passive:!0}),window.addEventListener("touchstart",e,{passive:!0}),window.addEventListener("keydown",e,{passive:!0})}#y(){return"function"==typeof this.checkVisibility?this.checkVisibility({checkOpacity:!0,checkVisibilityCSS:!0}):!!(this.offsetParent||this.getClientRects().length>0)}#k(){"idle"===this.#d.state&&this.#y()&&(this.#d.state="waiting",this.#p=setTimeout(()=>{this.#x()},2500))}async#x(){if("waiting"!==this.#d.state)return;this.#d.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.#d.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.#d.challenges=o,this.#d.state="solving",this.#d.solvePromise=this.#E(o)}catch(e){console.warn("[cap] speculative challenge fetch failed:",e),this.#d.state="error",this.#d.notify()}}else this.#d.state="idle"}async#E(e){l();let t=null;try{t=await n()}catch{}this.#u||(this.#u=new c(1),this.#u._spawn()),this.#u.setWasm(t);const i=e.length,s=new Array(i);let r=1,a=!1;this.#d.promoteFn=e=>{a||(a=!0,r=e,this.#u._size=e,this.#u._ensureSize(e))},null!==this.#d.pendingPromotion&&(this.#d.promoteFn(this.#d.pendingPromotion),this.#d.pendingPromotion=null);let o=0;for(;o<i;){const t=r,n=[],l=[];for(let s=0;s<t&&o<i;s++)l.push(o),n.push(e[o]),o++;this.#u._ensureSize(Math.max(r,t));const c=await Promise.all(n.map(e=>this.#u.run(e[0],e[1]).then(e=>(this.#d.completedCount++,e))));for(let e=0;e<l.length;e++)s[l[e]]=c[e];!a&&o<i&&await new Promise(e=>setTimeout(e,120))}return this.#d.results=s,this.#d.state="redeeming",this.#_(s),s}async#_(e){try{const i=this.#d.challengeResp,s=i._apiEndpoint;if(!s)throw new Error("[cap] speculative redeem: missing apiEndpoint");let a=null;if(i.instrumentation&&(a=await r(i.instrumentation),a?.__timeout||a?.__blocked))return this.#d.state="done",void this.#d.notify();const n=await t(`${s}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.#d.token=o.token,this.#d.tokenExpires=new Date(o.expires).getTime(),this.#d.state="done",this.#d.notify()}catch(e){console.warn("[cap] speculative redeem failed (will redo on click):",e),this.#d.state="done",this.#d.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.#c&&this.#c.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#c=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.#h=this.attachInternals()}catch{}}#A(){this.#h?.setValidity&&(this.hasAttribute("required")&&!this.token?this.#h.setValidity({valueMissing:!0},this.getI18nText("required-label","Please verify you're human"),this.#s||this):this.#h.setValidity({}))}initialize(){l(),this.#d||(this.#d=this.#b()),this.#u||(this.#u=new c(1),this.#u._spawn())}attributeChangedCallback(e,t,i){if(e.startsWith("on")){const t=e.slice(2),s=this.#c.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.#c.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.#A()}async connectedCallback(){this.#o=this,this.shadowRoot?this.#i=this.shadowRoot:this.#i=this.attachShadow({mode:"open"}),this.#s||(this.#s=document.createElement("div")),this.createUI(),this.addEventListeners(),this.initialize(),this.#r.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.#o.innerHTML=`<input type="hidden" name="${i}">`,this.#f(),this.#A(),this.addEventListener("invalid",this.#C)}#C=()=>{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.#l)try{this.#l=!0,this.updateUI("verifying",this.getI18nText("verifying-label","Verifying..."),!0),this.#r.setAttribute("aria-label",this.getI18nText("verifying-aria-label","Verifying you're a human, please wait")),this.dispatchEvent("progress",{progress:0});try{let e,s,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.#d.state&&this.#d.token&&this.#d.tokenExpires&&Date.now()<this.#d.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.#d.token),this.dispatchEvent("solve",{token:this.#d.token}),this.token=this.#d.token;const t=this.#d.tokenExpires-Date.now();return this.#e&&clearTimeout(this.#e),this.#e=setTimeout(()=>this.reset(),t),this.#r.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#v&&navigator.vibrate([10,50,20,30,40]),this.#m(),this.#l=!1,{success:!0,token:this.token}}if("done"===this.#d.state)e=this.#d.results,s=this.#d.challengeResp,this.dispatchEvent("progress",{progress:100});else if("solving"===this.#d.state||"redeeming"===this.#d.state||"fetching"===this.#d.state||"waiting"===this.#d.state){"waiting"===this.#d.state&&(this.#p&&(clearTimeout(this.#p),this.#p=null),this.#d.state="waiting",this.#x()),this.#d.pendingPromotion=this.#t,this.#d.promoteFn&&this.#d.promoteFn(this.#t);const t=setInterval(()=>{const e=this.#d.state;if("done"===e||"error"===e)return void clearInterval(t);const i=this.#d.challenges?this.#d.challenges.length:1,s=this.#d.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.#d.onSettled(e)),clearInterval(t),"done"!==this.#d.state)throw new Error("Speculative solve failed – please try again");if(this.#d.token&&this.#d.tokenExpires&&Date.now()<this.#d.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.#d.token),this.dispatchEvent("solve",{token:this.#d.token}),this.token=this.#d.token;const t=this.#d.tokenExpires-Date.now();return this.#e&&clearTimeout(this.#e),this.#e=setTimeout(()=>this.reset(),t),this.#r.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#v&&navigator.vibrate([10,50,20,30,40]),this.#m(),this.#l=!1,{success:!0,token:this.token}}e=this.#d.results,s=this.#d.challengeResp,this.dispatchEvent("progress",{progress:100})}else{const r=await t(`${a}challenge`,{method:"POST"});try{s=await r.json()}catch{throw new Error("Failed to parse challenge response from server")}if(s.error)throw new Error(s.error);const{challenge:n,token:o}=s;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=s.instrumentation?r(s.instrumentation):Promise.resolve(null),o=await n;if(o?.__timeout||o?.__blocked){this.updateUIBlocked(this.getI18nText("error-label","Error"),o?.__blocked),this.#r.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.#l=!1)}const{token:l}=s,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.#m(),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.#r.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#v&&navigator.vibrate([10,50,20,30,40]),{success:!0,token:this.token}}catch(e){throw this.#r.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again")),this.error(e.message),e}}finally{this.#l=!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 n()}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 a=new c(this.#t);a.setWasm(s),a._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)),n=await Promise.all(r.map(([e,s])=>a.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(...n)}}finally{a.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","group"),this.#s.setAttribute("aria-label",this.getI18nText("group-aria-label","Cap verification")),this.#r=document.createElement("div"),this.#r.className="captcha-trigger",this.#r.setAttribute("part","trigger"),this.#r.setAttribute("role","button"),this.#r.setAttribute("tabindex","0"),this.#r.setAttribute("aria-label",this.getI18nText("verify-aria-label","Click to verify you're a human")),this.#r.setAttribute("aria-live","polite"),this.#r.setAttribute("disabled","true"),this.#r.innerHTML=`<div class="checkbox" part="checkbox" aria-hidden="true"><svg class="progress-ring" viewBox="0 0 32 32" aria-hidden="true"><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>`,this.#s.appendChild(this.#r),this.#n=document.createElement("a"),this.#n.className="cap-troubleshoot-link",this.#n.setAttribute("part","troubleshoot"),this.#n.setAttribute("target","_blank"),this.#n.setAttribute("rel","noopener"),this.#n.hidden=!0,this.#s.appendChild(this.#n),this.#a=document.createElement("a"),this.#a.className="credits",this.#a.setAttribute("part","attribution"),this.#a.setAttribute("aria-label","Secured by Cap"),this.#a.setAttribute("href","https://trycap.dev"),this.#a.setAttribute("target","_blank"),this.#a.setAttribute("title","Secured by Cap: The self-hosted CAPTCHA for the modern web."),this.#a.textContent="Cap",this.#s.appendChild(this.#a),this.#i.innerHTML=`<style${window.CAP_CSS_NONCE?` nonce=${window.CAP_CSS_NONCE}`:""}>:host{display:inline-block}@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{width:var(--cap-widget-width,260px);height:var(--cap-widget-height,58px);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;cursor:pointer;-webkit-tap-highlight-color:#fff0;color:var(--cap-color,#212121);font-family:var(--cap-font,system, -apple-system, "BlinkMacSystemFont", ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", "Ubuntu", "arial", sans-serif);transition:filter .2s,transform .2s,height .2s;display:inline-block;position:relative;overflow:hidden}.captcha.has-troubleshoot{height:calc(var(--cap-widget-height,58px) + 26px)}.captcha.has-troubleshoot .captcha-trigger{inset-block-end:26px}.captcha:not([data-state]):active{transform:scale(.97)}.captcha:hover{filter:brightness(98%)}.captcha-trigger{padding:var(--cap-widget-padding,14px);align-items:center;gap:var(--cap-gap,15px);border-radius:inherit;transition:inset .2s;display:flex;position:absolute;inset:0}.captcha-trigger:focus-visible{outline:2px solid var(--cap-focus-ring,#06c);outline-offset:-3px}.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);flex-shrink:0;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;inset-inline-start: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-trigger[disabled]{cursor:not-allowed}.captcha[data-state=verifying] .captcha-trigger{cursor:progress}.captcha[data-state=done] .captcha-trigger{cursor:default}.credits{color:var(--cap-color,#212121);text-underline-offset:.18em;z-index:2;border-radius:4px;justify-content:flex-end;align-items:center;min-width:24px;min-height:24px;padding:4px 6px;text-decoration:underline;position:absolute;inset-block-end:4px;inset-inline-end:4px;opacity:.8!important;font-size:12px!important;display:inline-flex!important}.credits:focus-visible{outline:2px solid var(--cap-focus-ring,#06c);outline-offset:2px;opacity:1!important}.cap-troubleshoot-link{min-width:24px;min-height:24px;color:var(--cap-troubleshoot-color,#0050a0);text-underline-offset:.15em;cursor:pointer;z-index:2;border-radius:4px;align-items:center;padding:4px 6px;font-size:13px;font-weight:500;text-decoration:underline;text-decoration-thickness:.08em;display:inline-flex;position:absolute;inset-block-end:4px;inset-inline-start:14px}.cap-troubleshoot-link[hidden]{display:none}.cap-troubleshoot-link:hover{opacity:.85}.cap-troubleshoot-link:focus-visible{outline:2px solid var(--cap-focus-ring,#06c);outline-offset:2px}@media (forced-colors:active){.captcha,.checkbox{border-color:canvastext}:is(.captcha-trigger:focus-visible,.credits:focus-visible,.cap-troubleshoot-link:focus-visible){outline-color:highlight}.progress-ring-bg{stroke:graytext}.progress-ring-circle{stroke:canvastext}.captcha.invalid{box-shadow:none;border-color:mark}.credits{opacity:1!important}}</style>`,this.#i.appendChild(this.#s)}addEventListeners(){this.#r&&(this.#a.addEventListener("click",e=>{e.preventDefault(),window.open(`https://trycap.dev/?${new URLSearchParams({utm_source:"cap_widget",utm_medium:"referral",utm_campaign:"widget",utm_content:window.CAP_DISABLE_WIDGET_REF?"":location.hostname,ref:window.CAP_DISABLE_WIDGET_REF?"":location.href||"",sub:window.CAP_DISABLE_WIDGET_REF?"":document.referrer||""}).toString()}`,"_blank")}),this.#r.addEventListener("click",()=>{this.#r.hasAttribute("disabled")||this.solve()}),this.#r.addEventListener("mousedown",()=>{!this.#r.hasAttribute("disabled")&&this.#v&&navigator.vibrate(5)}),this.#r.addEventListener("keydown",e=>{e.target===this.#r&&("Enter"!==e.key&&" "!==e.key||this.#r.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.#r)return;const t=this.#r.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.#r&&(this.#s.setAttribute("data-state",e),this.#s.classList.remove("has-troubleshoot"),this.animateLabel(t),this.#n&&(this.#n.hidden=!0),i?this.#r.setAttribute("disabled","true"):this.#r.removeAttribute("disabled"))}updateUIBlocked(e,t=!1){if(this.#s&&this.#r&&(this.#s.setAttribute("data-state","error"),this.#r.removeAttribute("disabled"),this.animateLabel(e),this.#n))if(t){const e=this.getAttribute("data-cap-troubleshooting-url")||"https://trycap.dev/guide/troubleshooting/instrumentation.html";this.#n.setAttribute("href",e),this.#n.textContent=this.getI18nText("troubleshooting-label","Troubleshoot"),this.#n.hidden=!1,this.#s.classList.add("has-troubleshoot")}else this.#n.hidden=!0,this.#s.classList.remove("has-troubleshoot")}handleProgress(e){if(!this.#r)return;const t=this.#r.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.#r.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.#h?.setValidity?.({}),this.#s?.classList.remove("invalid")}handleError(e){this.updateUI("error",this.getI18nText("error-label","Error. Try again.")),this.executeAttributeCode("onerror",e),this.#v&&navigator.vibrate([10,40,10])}handleReset(e){this.updateUI("",this.getI18nText("initial-state","I'm a human")),this.executeAttributeCode("onreset",e),this.#A()}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.#c.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#c.clear(),this.#i&&(this.#i.innerHTML=""),this.reset(),this.cleanup()}cleanup(){this.#e&&(clearTimeout(this.#e),this.#e=null),this.#w(),this.#p&&(clearTimeout(this.#p),this.#p=null),this.#u&&(this.#u.terminate(),this.#u=null),this.#d&&(this.#d.state="error",this.#d.notify(),this.#d=null)}}class d{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=d,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",h),"object"==typeof exports&&"undefined"!=typeof module?module.exports=d:"function"==typeof define&&define.amd&&define([],()=>d),"undefined"!=typeof exports&&(exports.default=d)})();
|
package/package.json
CHANGED
package/src/cap.css
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: inline-block;
|
|
3
|
+
}
|
|
4
|
+
|
|
1
5
|
@media (prefers-reduced-motion: reduce) {
|
|
2
6
|
.captcha,
|
|
3
7
|
.captcha * {
|
|
@@ -27,35 +31,22 @@
|
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
.captcha {
|
|
34
|
+
position: relative;
|
|
35
|
+
display: inline-block;
|
|
36
|
+
width: var(--cap-widget-width, 260px);
|
|
37
|
+
height: var(--cap-widget-height, 58px);
|
|
30
38
|
background-color: var(--cap-background, #fdfdfd);
|
|
31
39
|
border: 1px solid var(--cap-border-color, #dddddd8f);
|
|
32
40
|
border-radius: var(--cap-border-radius, 14px);
|
|
33
41
|
user-select: none;
|
|
34
|
-
height: var(--cap-widget-height, 58px);
|
|
35
|
-
width: var(--cap-widget-width, 260px);
|
|
36
|
-
display: flex;
|
|
37
|
-
align-items: center;
|
|
38
|
-
padding: var(--cap-widget-padding, 14px);
|
|
39
|
-
gap: var(--cap-gap, 15px);
|
|
40
42
|
cursor: pointer;
|
|
41
43
|
transition:
|
|
42
44
|
filter 0.2s,
|
|
43
|
-
transform 0.2s
|
|
44
|
-
|
|
45
|
+
transform 0.2s,
|
|
46
|
+
height 0.2s;
|
|
45
47
|
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
|
|
46
48
|
overflow: hidden;
|
|
47
49
|
color: var(--cap-color, #212121);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.captcha:not([data-state]):active {
|
|
51
|
-
transform: scale(0.97);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.captcha:hover {
|
|
55
|
-
filter: brightness(98%);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.captcha * {
|
|
59
50
|
font-family: var(
|
|
60
51
|
--cap-font,
|
|
61
52
|
system,
|
|
@@ -73,6 +64,38 @@
|
|
|
73
64
|
);
|
|
74
65
|
}
|
|
75
66
|
|
|
67
|
+
.captcha.has-troubleshoot {
|
|
68
|
+
height: calc(var(--cap-widget-height, 58px) + 26px);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.captcha.has-troubleshoot .captcha-trigger {
|
|
72
|
+
inset-block-end: 26px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.captcha:not([data-state]):active {
|
|
76
|
+
transform: scale(0.97);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.captcha:hover {
|
|
80
|
+
filter: brightness(98%);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.captcha-trigger {
|
|
84
|
+
position: absolute;
|
|
85
|
+
inset: 0;
|
|
86
|
+
display: flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
padding: var(--cap-widget-padding, 14px);
|
|
89
|
+
gap: var(--cap-gap, 15px);
|
|
90
|
+
border-radius: inherit;
|
|
91
|
+
transition: inset 0.2s;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.captcha-trigger:focus-visible {
|
|
95
|
+
outline: 2px solid var(--cap-focus-ring, #0066cc);
|
|
96
|
+
outline-offset: -3px;
|
|
97
|
+
}
|
|
98
|
+
|
|
76
99
|
.checkbox {
|
|
77
100
|
width: var(--cap-checkbox-size, 25px);
|
|
78
101
|
height: var(--cap-checkbox-size, 25px);
|
|
@@ -82,6 +105,7 @@
|
|
|
82
105
|
transition: opacity 0.2s;
|
|
83
106
|
margin-top: var(--cap-checkbox-margin, 2px);
|
|
84
107
|
margin-bottom: var(--cap-checkbox-margin, 2px);
|
|
108
|
+
flex-shrink: 0;
|
|
85
109
|
}
|
|
86
110
|
|
|
87
111
|
.captcha p {
|
|
@@ -106,7 +130,7 @@
|
|
|
106
130
|
content: "";
|
|
107
131
|
pointer-events: none;
|
|
108
132
|
position: absolute;
|
|
109
|
-
|
|
133
|
+
inset-inline-start: 0;
|
|
110
134
|
width: 100%;
|
|
111
135
|
height: 0.6em;
|
|
112
136
|
z-index: 1;
|
|
@@ -229,40 +253,100 @@
|
|
|
229
253
|
}
|
|
230
254
|
}
|
|
231
255
|
|
|
232
|
-
.captcha[disabled] {
|
|
256
|
+
.captcha-trigger[disabled] {
|
|
233
257
|
cursor: not-allowed;
|
|
234
258
|
}
|
|
235
259
|
|
|
236
|
-
.captcha[
|
|
260
|
+
.captcha[data-state="verifying"] .captcha-trigger {
|
|
237
261
|
cursor: progress;
|
|
238
262
|
}
|
|
239
263
|
|
|
240
|
-
.captcha[
|
|
264
|
+
.captcha[data-state="done"] .captcha-trigger {
|
|
241
265
|
cursor: default;
|
|
242
266
|
}
|
|
243
267
|
|
|
244
|
-
.
|
|
268
|
+
.credits {
|
|
245
269
|
position: absolute;
|
|
246
|
-
|
|
247
|
-
|
|
270
|
+
inset-block-end: 4px;
|
|
271
|
+
inset-inline-end: 4px;
|
|
248
272
|
font-size: 12px !important;
|
|
249
273
|
color: var(--cap-color, #212121);
|
|
250
274
|
opacity: 0.8 !important;
|
|
251
275
|
text-underline-offset: 0.18em;
|
|
252
276
|
text-decoration-thickness: 0.08em;
|
|
253
|
-
z-index:
|
|
254
|
-
|
|
277
|
+
z-index: 2;
|
|
278
|
+
min-width: 24px;
|
|
279
|
+
min-height: 24px;
|
|
280
|
+
padding: 4px 6px;
|
|
281
|
+
display: inline-flex !important;
|
|
282
|
+
align-items: center;
|
|
283
|
+
justify-content: flex-end;
|
|
284
|
+
border-radius: 4px;
|
|
285
|
+
text-decoration: underline;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.credits:focus-visible {
|
|
289
|
+
outline: 2px solid var(--cap-focus-ring, #0066cc);
|
|
290
|
+
outline-offset: 2px;
|
|
291
|
+
opacity: 1 !important;
|
|
255
292
|
}
|
|
256
293
|
|
|
257
294
|
.cap-troubleshoot-link {
|
|
258
|
-
|
|
295
|
+
position: absolute;
|
|
296
|
+
inset-block-end: 4px;
|
|
297
|
+
inset-inline-start: 14px;
|
|
298
|
+
display: inline-flex;
|
|
299
|
+
align-items: center;
|
|
300
|
+
padding: 4px 6px;
|
|
301
|
+
min-width: 24px;
|
|
302
|
+
min-height: 24px;
|
|
303
|
+
color: var(--cap-troubleshoot-color, #0050a0);
|
|
259
304
|
text-decoration: underline;
|
|
260
305
|
text-underline-offset: 0.15em;
|
|
261
306
|
text-decoration-thickness: 0.08em;
|
|
262
307
|
cursor: pointer;
|
|
263
308
|
font-weight: 500;
|
|
264
|
-
font-size:
|
|
309
|
+
font-size: 13px;
|
|
310
|
+
border-radius: 4px;
|
|
311
|
+
z-index: 2;
|
|
265
312
|
}
|
|
313
|
+
|
|
314
|
+
.cap-troubleshoot-link[hidden] {
|
|
315
|
+
display: none;
|
|
316
|
+
}
|
|
317
|
+
|
|
266
318
|
.cap-troubleshoot-link:hover {
|
|
267
|
-
opacity: 0.
|
|
319
|
+
opacity: 0.85;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.cap-troubleshoot-link:focus-visible {
|
|
323
|
+
outline: 2px solid var(--cap-focus-ring, #0066cc);
|
|
324
|
+
outline-offset: 2px;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@media (forced-colors: active) {
|
|
328
|
+
.captcha {
|
|
329
|
+
border-color: CanvasText;
|
|
330
|
+
}
|
|
331
|
+
.checkbox {
|
|
332
|
+
border-color: CanvasText;
|
|
333
|
+
}
|
|
334
|
+
.captcha-trigger:focus-visible,
|
|
335
|
+
.credits:focus-visible,
|
|
336
|
+
.cap-troubleshoot-link:focus-visible {
|
|
337
|
+
outline-color: Highlight;
|
|
338
|
+
}
|
|
339
|
+
.progress-ring-bg {
|
|
340
|
+
stroke: GrayText;
|
|
341
|
+
}
|
|
342
|
+
.progress-ring-circle {
|
|
343
|
+
stroke: CanvasText;
|
|
344
|
+
}
|
|
345
|
+
.captcha.invalid {
|
|
346
|
+
border-color: Mark;
|
|
347
|
+
box-shadow: none;
|
|
348
|
+
}
|
|
349
|
+
.credits {
|
|
350
|
+
opacity: 1 !important;
|
|
351
|
+
}
|
|
268
352
|
}
|
package/src/cap.js
CHANGED
|
@@ -78,6 +78,8 @@
|
|
|
78
78
|
"https://cdn.jsdelivr.net/npm/pako@2.1.0/dist/pako_inflate.min.js";
|
|
79
79
|
const script = document.createElement("script");
|
|
80
80
|
script.src = url;
|
|
81
|
+
const pakoNonce = window.CAP_SCRIPT_NONCE || window.CAP_CSS_NONCE;
|
|
82
|
+
if (pakoNonce) script.setAttribute("nonce", pakoNonce);
|
|
81
83
|
script.onload = () => {
|
|
82
84
|
if (window.pako?.inflateRaw) resolve(window.pako);
|
|
83
85
|
else reject(new Error("[cap] pako loaded but inflateRaw is missing"));
|
|
@@ -148,8 +150,14 @@
|
|
|
148
150
|
|
|
149
151
|
window.addEventListener("message", handler);
|
|
150
152
|
|
|
153
|
+
const scriptNonce = window.CAP_SCRIPT_NONCE || window.CAP_CSS_NONCE;
|
|
154
|
+
const nonceAttr = scriptNonce
|
|
155
|
+
? ' nonce="' + String(scriptNonce).replace(/"/g, """) + '"'
|
|
156
|
+
: "";
|
|
151
157
|
iframe.srcdoc =
|
|
152
|
-
'<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><script
|
|
158
|
+
'<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><script' +
|
|
159
|
+
nonceAttr +
|
|
160
|
+
">" +
|
|
153
161
|
scriptText +
|
|
154
162
|
"\n</scr" +
|
|
155
163
|
"ipt></body></html>";
|
|
@@ -320,6 +328,9 @@
|
|
|
320
328
|
token = null;
|
|
321
329
|
#shadow;
|
|
322
330
|
#div;
|
|
331
|
+
#trigger;
|
|
332
|
+
#credits;
|
|
333
|
+
#troubleshootLink;
|
|
323
334
|
#host;
|
|
324
335
|
#solving = false;
|
|
325
336
|
#eventHandlers;
|
|
@@ -684,7 +695,7 @@
|
|
|
684
695
|
this.createUI();
|
|
685
696
|
this.addEventListeners();
|
|
686
697
|
this.initialize();
|
|
687
|
-
this.#
|
|
698
|
+
this.#trigger.removeAttribute("disabled");
|
|
688
699
|
|
|
689
700
|
const workers = this.getAttribute("data-cap-worker-count");
|
|
690
701
|
const parsedWorkers = workers ? parseInt(workers, 10) : null;
|
|
@@ -719,7 +730,7 @@
|
|
|
719
730
|
try {
|
|
720
731
|
this.#solving = true;
|
|
721
732
|
this.updateUI("verifying", this.getI18nText("verifying-label", "Verifying..."), true);
|
|
722
|
-
this.#
|
|
733
|
+
this.#trigger.setAttribute(
|
|
723
734
|
"aria-label",
|
|
724
735
|
this.getI18nText("verifying-aria-label", "Verifying you're a human, please wait"),
|
|
725
736
|
);
|
|
@@ -758,7 +769,7 @@
|
|
|
758
769
|
if (this.#resetTimer) clearTimeout(this.#resetTimer);
|
|
759
770
|
this.#resetTimer = setTimeout(() => this.reset(), expiresIn);
|
|
760
771
|
|
|
761
|
-
this.#
|
|
772
|
+
this.#trigger.setAttribute(
|
|
762
773
|
"aria-label",
|
|
763
774
|
this.getI18nText(
|
|
764
775
|
"verified-aria-label",
|
|
@@ -838,7 +849,7 @@
|
|
|
838
849
|
if (this.#resetTimer) clearTimeout(this.#resetTimer);
|
|
839
850
|
this.#resetTimer = setTimeout(() => this.reset(), expiresIn);
|
|
840
851
|
|
|
841
|
-
this.#
|
|
852
|
+
this.#trigger.setAttribute(
|
|
842
853
|
"aria-label",
|
|
843
854
|
this.getI18nText(
|
|
844
855
|
"verified-aria-label",
|
|
@@ -887,7 +898,7 @@
|
|
|
887
898
|
|
|
888
899
|
if (instrOut?.__timeout || instrOut?.__blocked) {
|
|
889
900
|
this.updateUIBlocked(this.getI18nText("error-label", "Error"), instrOut?.__blocked);
|
|
890
|
-
this.#
|
|
901
|
+
this.#trigger.setAttribute(
|
|
891
902
|
"aria-label",
|
|
892
903
|
this.getI18nText("error-aria-label", "An error occurred, please try again"),
|
|
893
904
|
);
|
|
@@ -945,7 +956,7 @@
|
|
|
945
956
|
this.error("Invalid expiration time");
|
|
946
957
|
}
|
|
947
958
|
|
|
948
|
-
this.#
|
|
959
|
+
this.#trigger.setAttribute(
|
|
949
960
|
"aria-label",
|
|
950
961
|
this.getI18nText(
|
|
951
962
|
"verified-aria-label",
|
|
@@ -956,7 +967,7 @@
|
|
|
956
967
|
|
|
957
968
|
return { success: true, token: this.token };
|
|
958
969
|
} catch (err) {
|
|
959
|
-
this.#
|
|
970
|
+
this.#trigger.setAttribute(
|
|
960
971
|
"aria-label",
|
|
961
972
|
this.getI18nText("error-aria-label", "An error occurred, please try again"),
|
|
962
973
|
);
|
|
@@ -1044,18 +1055,49 @@
|
|
|
1044
1055
|
|
|
1045
1056
|
createUI() {
|
|
1046
1057
|
this.#div.classList.add("captcha");
|
|
1047
|
-
this.#div.setAttribute("role", "
|
|
1048
|
-
this.#div.setAttribute("tabindex", "0");
|
|
1058
|
+
this.#div.setAttribute("role", "group");
|
|
1049
1059
|
this.#div.setAttribute(
|
|
1060
|
+
"aria-label",
|
|
1061
|
+
this.getI18nText("group-aria-label", "Cap verification"),
|
|
1062
|
+
);
|
|
1063
|
+
|
|
1064
|
+
this.#trigger = document.createElement("div");
|
|
1065
|
+
this.#trigger.className = "captcha-trigger";
|
|
1066
|
+
this.#trigger.setAttribute("part", "trigger");
|
|
1067
|
+
this.#trigger.setAttribute("role", "button");
|
|
1068
|
+
this.#trigger.setAttribute("tabindex", "0");
|
|
1069
|
+
this.#trigger.setAttribute(
|
|
1050
1070
|
"aria-label",
|
|
1051
1071
|
this.getI18nText("verify-aria-label", "Click to verify you're a human"),
|
|
1052
1072
|
);
|
|
1053
|
-
this.#
|
|
1054
|
-
this.#
|
|
1055
|
-
this.#
|
|
1073
|
+
this.#trigger.setAttribute("aria-live", "polite");
|
|
1074
|
+
this.#trigger.setAttribute("disabled", "true");
|
|
1075
|
+
this.#trigger.innerHTML = `<div class="checkbox" part="checkbox" aria-hidden="true"><svg class="progress-ring" viewBox="0 0 32 32" aria-hidden="true"><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(
|
|
1056
1076
|
"initial-state",
|
|
1057
1077
|
"Verify you're human",
|
|
1058
|
-
)}</span></p
|
|
1078
|
+
)}</span></p>`;
|
|
1079
|
+
this.#div.appendChild(this.#trigger);
|
|
1080
|
+
|
|
1081
|
+
this.#troubleshootLink = document.createElement("a");
|
|
1082
|
+
this.#troubleshootLink.className = "cap-troubleshoot-link";
|
|
1083
|
+
this.#troubleshootLink.setAttribute("part", "troubleshoot");
|
|
1084
|
+
this.#troubleshootLink.setAttribute("target", "_blank");
|
|
1085
|
+
this.#troubleshootLink.setAttribute("rel", "noopener");
|
|
1086
|
+
this.#troubleshootLink.hidden = true;
|
|
1087
|
+
this.#div.appendChild(this.#troubleshootLink);
|
|
1088
|
+
|
|
1089
|
+
this.#credits = document.createElement("a");
|
|
1090
|
+
this.#credits.className = "credits";
|
|
1091
|
+
this.#credits.setAttribute("part", "attribution");
|
|
1092
|
+
this.#credits.setAttribute("aria-label", "Secured by Cap");
|
|
1093
|
+
this.#credits.setAttribute("href", "https://trycap.dev");
|
|
1094
|
+
this.#credits.setAttribute("target", "_blank");
|
|
1095
|
+
this.#credits.setAttribute(
|
|
1096
|
+
"title",
|
|
1097
|
+
"Secured by Cap: The self-hosted CAPTCHA for the modern web.",
|
|
1098
|
+
);
|
|
1099
|
+
this.#credits.textContent = "Cap";
|
|
1100
|
+
this.#div.appendChild(this.#credits);
|
|
1059
1101
|
|
|
1060
1102
|
this.#shadow.innerHTML = `<style${window.CAP_CSS_NONCE ? ` nonce=${window.CAP_CSS_NONCE}` : ""}>%%capCSS%%</style>`;
|
|
1061
1103
|
|
|
@@ -1063,10 +1105,9 @@
|
|
|
1063
1105
|
}
|
|
1064
1106
|
|
|
1065
1107
|
addEventListeners() {
|
|
1066
|
-
if (!this.#
|
|
1108
|
+
if (!this.#trigger) return;
|
|
1067
1109
|
|
|
1068
|
-
this.#
|
|
1069
|
-
e.stopPropagation();
|
|
1110
|
+
this.#credits.addEventListener("click", (e) => {
|
|
1070
1111
|
e.preventDefault();
|
|
1071
1112
|
window.open(
|
|
1072
1113
|
`https://trycap.dev/?${new URLSearchParams(
|
|
@@ -1085,17 +1126,18 @@
|
|
|
1085
1126
|
);
|
|
1086
1127
|
});
|
|
1087
1128
|
|
|
1088
|
-
this.#
|
|
1089
|
-
if (!this.#
|
|
1129
|
+
this.#trigger.addEventListener("click", () => {
|
|
1130
|
+
if (!this.#trigger.hasAttribute("disabled")) this.solve();
|
|
1090
1131
|
});
|
|
1091
|
-
this.#
|
|
1092
|
-
if (!this.#
|
|
1132
|
+
this.#trigger.addEventListener("mousedown", () => {
|
|
1133
|
+
if (!this.#trigger.hasAttribute("disabled") && this.#hasHaptics) {
|
|
1093
1134
|
navigator.vibrate(5);
|
|
1094
1135
|
}
|
|
1095
1136
|
});
|
|
1096
1137
|
|
|
1097
|
-
this.#
|
|
1098
|
-
if (
|
|
1138
|
+
this.#trigger.addEventListener("keydown", (e) => {
|
|
1139
|
+
if (e.target !== this.#trigger) return;
|
|
1140
|
+
if ((e.key === "Enter" || e.key === " ") && !this.#trigger.hasAttribute("disabled")) {
|
|
1099
1141
|
e.preventDefault();
|
|
1100
1142
|
e.stopPropagation();
|
|
1101
1143
|
this.solve();
|
|
@@ -1109,8 +1151,8 @@
|
|
|
1109
1151
|
}
|
|
1110
1152
|
|
|
1111
1153
|
animateLabel(text) {
|
|
1112
|
-
if (!this.#
|
|
1113
|
-
const wrapper = this.#
|
|
1154
|
+
if (!this.#trigger) return;
|
|
1155
|
+
const wrapper = this.#trigger.querySelector(".label-wrapper");
|
|
1114
1156
|
if (!wrapper) return;
|
|
1115
1157
|
|
|
1116
1158
|
if (prefersReducedMotion()) {
|
|
@@ -1146,62 +1188,53 @@
|
|
|
1146
1188
|
}
|
|
1147
1189
|
|
|
1148
1190
|
updateUI(state, text, disabled = false) {
|
|
1149
|
-
if (!this.#div) return;
|
|
1191
|
+
if (!this.#div || !this.#trigger) return;
|
|
1150
1192
|
|
|
1151
1193
|
this.#div.setAttribute("data-state", state);
|
|
1194
|
+
this.#div.classList.remove("has-troubleshoot");
|
|
1152
1195
|
|
|
1153
1196
|
this.animateLabel(text);
|
|
1154
1197
|
|
|
1198
|
+
if (this.#troubleshootLink) this.#troubleshootLink.hidden = true;
|
|
1199
|
+
|
|
1155
1200
|
if (disabled) {
|
|
1156
|
-
this.#
|
|
1201
|
+
this.#trigger.setAttribute("disabled", "true");
|
|
1157
1202
|
} else {
|
|
1158
|
-
this.#
|
|
1203
|
+
this.#trigger.removeAttribute("disabled");
|
|
1159
1204
|
}
|
|
1160
1205
|
}
|
|
1161
1206
|
|
|
1162
1207
|
updateUIBlocked(label, showTroubleshooting = false) {
|
|
1163
|
-
if (!this.#div) return;
|
|
1208
|
+
if (!this.#div || !this.#trigger) return;
|
|
1164
1209
|
|
|
1165
1210
|
this.#div.setAttribute("data-state", "error");
|
|
1166
|
-
this.#
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
current.classList.remove("active");
|
|
1187
|
-
current.classList.add("exit");
|
|
1188
|
-
current.addEventListener("transitionend", () => current.remove(), {
|
|
1189
|
-
once: true,
|
|
1190
|
-
});
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
const link = next.querySelector(".cap-troubleshoot-link");
|
|
1194
|
-
if (link) {
|
|
1195
|
-
link.addEventListener("click", (e) => {
|
|
1196
|
-
e.stopPropagation();
|
|
1197
|
-
});
|
|
1211
|
+
this.#trigger.removeAttribute("disabled");
|
|
1212
|
+
|
|
1213
|
+
this.animateLabel(label);
|
|
1214
|
+
|
|
1215
|
+
if (this.#troubleshootLink) {
|
|
1216
|
+
if (showTroubleshooting) {
|
|
1217
|
+
const troubleshootingUrl =
|
|
1218
|
+
this.getAttribute("data-cap-troubleshooting-url") ||
|
|
1219
|
+
"https://trycap.dev/guide/troubleshooting/instrumentation.html";
|
|
1220
|
+
this.#troubleshootLink.setAttribute("href", troubleshootingUrl);
|
|
1221
|
+
this.#troubleshootLink.textContent = this.getI18nText(
|
|
1222
|
+
"troubleshooting-label",
|
|
1223
|
+
"Troubleshoot",
|
|
1224
|
+
);
|
|
1225
|
+
this.#troubleshootLink.hidden = false;
|
|
1226
|
+
this.#div.classList.add("has-troubleshoot");
|
|
1227
|
+
} else {
|
|
1228
|
+
this.#troubleshootLink.hidden = true;
|
|
1229
|
+
this.#div.classList.remove("has-troubleshoot");
|
|
1230
|
+
}
|
|
1198
1231
|
}
|
|
1199
1232
|
}
|
|
1200
1233
|
|
|
1201
1234
|
handleProgress(event) {
|
|
1202
|
-
if (!this.#
|
|
1235
|
+
if (!this.#trigger) return;
|
|
1203
1236
|
|
|
1204
|
-
const progressCircle = this.#
|
|
1237
|
+
const progressCircle = this.#trigger.querySelector(".progress-ring-circle");
|
|
1205
1238
|
|
|
1206
1239
|
if (progressCircle) {
|
|
1207
1240
|
const circumference = 2 * Math.PI * 14;
|
|
@@ -1209,7 +1242,7 @@
|
|
|
1209
1242
|
progressCircle.style.strokeDashoffset = offset;
|
|
1210
1243
|
}
|
|
1211
1244
|
|
|
1212
|
-
const wrapper = this.#
|
|
1245
|
+
const wrapper = this.#trigger.querySelector(".label-wrapper");
|
|
1213
1246
|
if (wrapper) {
|
|
1214
1247
|
const activeLabel = wrapper.querySelector(".label.active");
|
|
1215
1248
|
if (activeLabel) {
|