@cap.js/widget 0.1.50 → 0.1.52
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.min.js +1 -1
- package/package.json +1 -1
- package/src/cap-floating.js +27 -14
- package/src/cap.css +34 -7
- package/src/cap.js +304 -67
- package/src/worker.js +28 -5
package/cap-floating.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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(),
|
|
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"),10)||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(),i=(e.offsetParent??document.documentElement).getBoundingClientRect(),a=s.left+(s.width-e.offsetWidth)/2,r=Math.max(2,Math.min(a,window.innerWidth-e.offsetWidth));if(e.style.left=r-i.left+"px","top"===n){const t=s.top-e.offsetHeight-o,n=Math.max(0,t);e.style.top=n-i.top+"px"}else{const t=s.bottom+o,n=Math.min(t,window.innerHeight-e.offsetHeight);e.style.top=n-i.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=>{for(const o of t)for(const t of o.addedNodes)t.nodeType===Node.ELEMENT_NODE&&e(t)}).observe(document.body,{childList:!0,subtree:!0})})();
|
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;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.7/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)})();
|
|
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){if(s.contentWindow&&t.source===s.contentWindow){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.7/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 o=new TextEncoder,r=4*t.length,s=Math.floor(r/8),a=r%8,l=t.length%2==0?t:`${t}0`,i=l.length/2,c=new Uint8Array(i);for(let e=0;e<i;e++)c[e]=parseInt(l.substring(2*e,2*e+2),16);const f=a>0?255<<8-a&255:0;for(;;)try{for(let t=0;t<5e4;t++){const t=e+n,r=o.encode(t),l=await crypto.subtle.digest("SHA-256",r),i=new Uint8Array(l);let g=!0;for(let e=0;e<s;e++)if(i[e]!==c[e]){g=!1;break}if(g&&a>0&&(i[s]&f)!==(c[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})}},t=e=>{const t=BigInt("0x"+e.N);let n=BigInt("0x"+e.x);const o=0|e.t,r=Math.max(64,Math.floor(o/50)),s=performance.now();for(let e=0;e<o;e+=r){const s=Math.min(o,e+r);for(let o=e;o<s;o++)n=n*n%t;s<o&&self.postMessage({progress:s/o})}self.postMessage({found:!0,y:n.toString(16),durationMs:(performance.now()-s).toFixed(2)})};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:n})=>"rsw"===n?.kind?t(n):e({salt:n.salt,target:n.target}));let n=null;self.onmessage=async({data:o})=>{if("rsw"===o?.kind)return t(o);const{salt:r,target:s,wasmModule:a}=o;if(a instanceof WebAssembly.Module&&null===n){const t=(e=>{try{let t,o=0,r=null;const s=()=>(null!==r&&0!==r.byteLength||(r=new Uint8Array(t.memory.buffer)),r),a=new TextEncoder,l=(e,t,n)=>{if(void 0===n){const n=a.encode(e),r=t(n.length,1)>>>0;return s().subarray(r,r+n.length).set(n),o=n.length,r}let r=e.length,l=t(r,1)>>>0;const i=s();let c=0;for(;c<r;c++){const t=e.charCodeAt(c);if(t>127)break;i[l+c]=t}if(c!==r){0!==c&&(e=e.slice(c));const t=c+3*e.length;l=n(l,r,t,1)>>>0,r=t;const o=s().subarray(l+c,l+r),{written:i}=a.encodeInto(e,o);c+=i,l=n(l,r,c,1)>>>0}return o=c,l},i={wbg:{}};i.wbg.__wbindgen_init_externref_table=()=>{const e=t.__wbindgen_export_0,n=e.grow(4);e.set(0,void 0),e.set(n+0,void 0),e.set(n+1,null),e.set(n+2,!0),e.set(n+3,!1)};const c=new WebAssembly.Instance(e,i);return t=c.exports,t.__wbindgen_start&&t.__wbindgen_start(),n=(e,n)=>{const r=l(e,t.__wbindgen_malloc,t.__wbindgen_realloc),s=o,a=l(n,t.__wbindgen_malloc,t.__wbindgen_realloc),i=o;return BigInt.asUintN(64,t.solve_pow(r,s,a,i))},!0}catch(e){return console.error("[cap worker] failed to init wasm from module:",e),!1}})(a);if(!t)return console.warn("[cap worker] wasm init failed, falling back to JS solver."),e({salt:r,target:s})}if(null===n)return console.warn("[cap worker] no wasm module provided, falling back to JS solver."),e({salt:r,target:s});try{const e=performance.now(),t=n(r,s),o=performance.now();self.postMessage({nonce:Number(t),found:!0,durationMs:(o-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()})}runMsg(e,t){return new Promise((i,s)=>{this._queue.push({msg:e,onProgress:t,resolve:i,reject:s}),this._dispatch()})}_dispatch(){for(;this._idle.length>0&&this._queue.length>0;){const e=this._idle.shift(),t=this._queue.shift(),{resolve:i,reject:s}=t,r=void 0!==t.msg;let a=!1;const n=({data:l})=>{if(!a)if(!l||"number"!=typeof l.progress||l.found)a=!0,e.removeEventListener("message",n),e.removeEventListener("error",o),this._spawnFailures=0,this._idle.push(e),l.found?i(r?l:l.nonce):s(new Error(l.error||"worker failed")),this._dispatch();else if(t.onProgress)try{t.onProgress(l.progress)}catch{}},o=t=>{if(a)return;a=!0,e.removeEventListener("message",n),e.removeEventListener("error",o);const i=this._replaceWorker(e);s(t),i&&this._dispatch()};e.addEventListener("message",n),e.addEventListener("error",o),r?e.postMessage(t.msg):this._wasmModule?e.postMessage({salt:t.salt,target:t.target,wasmModule:this._wasmModule},[]):e.postMessage({salt:t.salt,target:t.target})}}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")}#m(){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)}}}#b(){this.#d=this.#m(),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.#y()};this.#g=e,window.addEventListener("mousemove",e,{passive:!0}),window.addEventListener("touchstart",e,{passive:!0}),window.addEventListener("keydown",e,{passive:!0})}#k(){return"function"==typeof this.checkVisibility?this.checkVisibility({checkOpacity:!0,checkVisibilityCSS:!0}):!!(this.offsetParent||this.getClientRects().length>0)}#y(){"idle"===this.#d.state&&this.#k()&&(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);if(r._apiEndpoint=e,this.#d.challengeResp=r,2===r.format&&Array.isArray(r.challenges))return this.#d.state="idle",void this.#d.notify();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.#_(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){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.#E(s),s}async#E(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.#m()),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.#b(),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),"idle"===this.#d.state&&2===this.#d.challengeResp?.format&&Array.isArray(this.#d.challengeResp.challenges))s=this.#d.challengeResp,this.#d.challengeResp=null,e=await this.solveChallengesV2(s.challenges);else{if("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.#b(),this.#l=!1,{success:!0,token:this.token}}e=this.#d.results,s=this.#d.challengeResp,this.dispatchEvent("progress",{progress:100})}}else{const r=this.#d.challengeResp;if(2===r?.format&&Array.isArray(r.challenges))s=r,this.#d.challengeResp=null;else{const e=await t(`${a}challenge`,{method:"POST"});try{s=await e.json()}catch{throw new Error("Failed to parse challenge response from server")}if(s.error)throw new Error(s.error)}if(2===s.format&&Array.isArray(s.challenges))e=await this.solveChallengesV2(s.challenges);else{const{challenge:t,token:r}=s;let a=t;if(!Array.isArray(a)){let e=0;a=Array.from({length:t.c},()=>(e++,[i(`${r}${e}`,t.s),i(`${r}${e}d`,t.d)]))}e=await this.solveChallenges(a)}}const n=s.instrumentation?r(s.instrumentation):Promise.resolve(null),o=await n;if(o?.__timeout||o?.__blocked){t(`${a}redeem`,{method:"POST",body:JSON.stringify({token:s.token,solutions:e,...o.__blocked&&{instr_blocked:!0},...o.__timeout&&{instr_timeout:!0}}),headers:{"Content-Type":"application/json"}}).catch(()=>{}),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 i=new CustomEvent("error",{bubbles:!0,composed:!0,detail:{isCap:!0,message:"Instrumentation failed"}});return super.dispatchEvent(i),this.addEventListener("error",this.boundHandleError),this.executeAttributeCode("onerror",i),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.#b(),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 solveChallengesV2(e){const t=e.length;let i=0;const s=new Array(e.length);for(let t=0;t<e.length;t++){const i=e[t];if(!i||"object"!=typeof i||"sha256-pow"!==i.protocol&&"rsw"!==i.protocol&&"instrumentation"!==i.protocol)throw new Error(`[cap] unsupported format-2 protocol '${i?.protocol}'`)}let a=null;if("object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate)try{a=await n()}catch(e){console.warn("[cap] wasm unavailable, falling back to JS solver:",e)}const o=Math.max(1,Math.min(this.#t,e.length)),l=new c(o);l.setWasm(a),l._ensureSize(o);const h=(e,t)=>Promise.race([e,new Promise((e,i)=>setTimeout(()=>i(new Error(`[cap] ${t} timed out after 60000ms`)),6e4))]),d=new Array(e.length).fill(0),p=()=>{const e=d.reduce((e,t)=>e+t,0)+i,s=Math.min(99,Math.round(e/t*100));this.dispatchEvent("progress",{progress:s})};try{await Promise.all(e.map((e,t)=>"sha256-pow"===e.protocol?h(l.run(e.payload.salt,e.payload.target),`sha256-pow worker #${t}`).then(e=>{s[t]={nonce:Number(e)},d[t]=0,i++,p()}):"rsw"===e.protocol?h(l.runMsg({kind:"rsw",N:e.payload.N,x:e.payload.x,t:0|e.payload.t},e=>{d[t]=e,p()}),`rsw worker #${t}`).then(e=>{s[t]={y:e.y},d[t]=0,i++,p()}):r(e.payload.blob).then(e=>{s[t]=e?.__timeout?{timeout:!0}:e?.__blocked?{blocked:!0}:{instr:e},d[t]=0,i++,p()})))}finally{l.terminate()}return s}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;text-decoration-thickness:.08em;position:absolute;inset-block-end:4px;inset-inline-end:4px;opacity:.8!important;font-size:12px!important;display:inline-flex!important}.captcha .credits[part=attribution]{visibility:visible!important;pointer-events:all!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-floating.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
const showWidget = (element, capWidget) => {
|
|
3
|
-
const offset =
|
|
4
|
-
|
|
3
|
+
const offset =
|
|
4
|
+
parseInt(element.getAttribute("data-cap-floating-offset"), 10) || 8;
|
|
5
|
+
const position =
|
|
6
|
+
element.getAttribute("data-cap-floating-position") || "top";
|
|
5
7
|
|
|
6
8
|
Object.assign(capWidget.style, {
|
|
7
9
|
display: "block",
|
|
@@ -24,10 +26,16 @@
|
|
|
24
26
|
// `position: absolute` is resolved against the nearest positioned ancestor (capWidget.offsetParent),
|
|
25
27
|
// but getBoundingClientRect() returns viewport-relative coords, convert to container-relative coords.
|
|
26
28
|
const triggerRect = element.getBoundingClientRect();
|
|
27
|
-
const containerRect = (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
const containerRect = (
|
|
30
|
+
capWidget.offsetParent ?? document.documentElement
|
|
31
|
+
).getBoundingClientRect();
|
|
32
|
+
|
|
33
|
+
const centeredLeft =
|
|
34
|
+
triggerRect.left + (triggerRect.width - capWidget.offsetWidth) / 2;
|
|
35
|
+
const clampedLeft = Math.max(
|
|
36
|
+
2,
|
|
37
|
+
Math.min(centeredLeft, window.innerWidth - capWidget.offsetWidth),
|
|
38
|
+
);
|
|
31
39
|
capWidget.style.left = `${clampedLeft - containerRect.left}px`;
|
|
32
40
|
|
|
33
41
|
if (position === "top") {
|
|
@@ -36,7 +44,10 @@
|
|
|
36
44
|
capWidget.style.top = `${clampedTop - containerRect.top}px`;
|
|
37
45
|
} else {
|
|
38
46
|
const idealTop = triggerRect.bottom + offset;
|
|
39
|
-
const clampedTop = Math.min(
|
|
47
|
+
const clampedTop = Math.min(
|
|
48
|
+
idealTop,
|
|
49
|
+
window.innerHeight - capWidget.offsetHeight,
|
|
50
|
+
);
|
|
40
51
|
capWidget.style.top = `${clampedTop - containerRect.top}px`;
|
|
41
52
|
}
|
|
42
53
|
|
|
@@ -61,7 +72,9 @@
|
|
|
61
72
|
|
|
62
73
|
const capWidget = document.querySelector(capWidgetSelector);
|
|
63
74
|
if (!document.contains(capWidget) && !capWidget.solve) {
|
|
64
|
-
throw new Error(
|
|
75
|
+
throw new Error(
|
|
76
|
+
`[cap floating] "${capWidgetSelector}" doesn't exist or isn't a Cap widget`,
|
|
77
|
+
);
|
|
65
78
|
}
|
|
66
79
|
|
|
67
80
|
capWidget.style.display = "none";
|
|
@@ -106,11 +119,11 @@
|
|
|
106
119
|
|
|
107
120
|
init(document.body);
|
|
108
121
|
|
|
109
|
-
new MutationObserver((mutations) =>
|
|
110
|
-
|
|
111
|
-
mutation.addedNodes
|
|
122
|
+
new MutationObserver((mutations) => {
|
|
123
|
+
for (const mutation of mutations) {
|
|
124
|
+
for (const node of mutation.addedNodes) {
|
|
112
125
|
if (node.nodeType === Node.ELEMENT_NODE) init(node);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
).observe(document.body, { childList: true, subtree: true });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}).observe(document.body, { childList: true, subtree: true });
|
|
116
129
|
})();
|
package/src/cap.css
CHANGED
|
@@ -138,12 +138,20 @@
|
|
|
138
138
|
|
|
139
139
|
.label-wrapper::before {
|
|
140
140
|
top: 0;
|
|
141
|
-
background: linear-gradient(
|
|
141
|
+
background: linear-gradient(
|
|
142
|
+
to bottom,
|
|
143
|
+
var(--cap-background, #fdfdfd),
|
|
144
|
+
transparent
|
|
145
|
+
);
|
|
142
146
|
}
|
|
143
147
|
|
|
144
148
|
.label-wrapper::after {
|
|
145
149
|
bottom: 0;
|
|
146
|
-
background: linear-gradient(
|
|
150
|
+
background: linear-gradient(
|
|
151
|
+
to top,
|
|
152
|
+
var(--cap-background, #fdfdfd),
|
|
153
|
+
transparent
|
|
154
|
+
);
|
|
147
155
|
}
|
|
148
156
|
|
|
149
157
|
.label {
|
|
@@ -241,10 +249,23 @@
|
|
|
241
249
|
}
|
|
242
250
|
|
|
243
251
|
@keyframes cap-shake {
|
|
244
|
-
10%,
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
252
|
+
10%,
|
|
253
|
+
90% {
|
|
254
|
+
transform: translateX(-1px);
|
|
255
|
+
}
|
|
256
|
+
20%,
|
|
257
|
+
80% {
|
|
258
|
+
transform: translateX(2px);
|
|
259
|
+
}
|
|
260
|
+
30%,
|
|
261
|
+
50%,
|
|
262
|
+
70% {
|
|
263
|
+
transform: translateX(-4px);
|
|
264
|
+
}
|
|
265
|
+
40%,
|
|
266
|
+
60% {
|
|
267
|
+
transform: translateX(4px);
|
|
268
|
+
}
|
|
248
269
|
}
|
|
249
270
|
|
|
250
271
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -272,6 +293,7 @@
|
|
|
272
293
|
font-size: 12px !important;
|
|
273
294
|
color: var(--cap-color, #212121);
|
|
274
295
|
opacity: 0.8 !important;
|
|
296
|
+
text-decoration: underline;
|
|
275
297
|
text-underline-offset: 0.18em;
|
|
276
298
|
text-decoration-thickness: 0.08em;
|
|
277
299
|
z-index: 2;
|
|
@@ -282,7 +304,12 @@
|
|
|
282
304
|
align-items: center;
|
|
283
305
|
justify-content: flex-end;
|
|
284
306
|
border-radius: 4px;
|
|
285
|
-
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.captcha .credits[part="attribution"] {
|
|
310
|
+
display: inline-flex !important;
|
|
311
|
+
visibility: visible !important;
|
|
312
|
+
pointer-events: all !important;
|
|
286
313
|
}
|
|
287
314
|
|
|
288
315
|
.credits:focus-visible {
|
package/src/cap.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
const WASM_VERSION = "0.0.7";
|
|
3
3
|
const _browserHasHaptics =
|
|
4
|
-
"vibrate" in navigator &&
|
|
4
|
+
"vibrate" in navigator &&
|
|
5
|
+
!window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
5
6
|
|
|
6
7
|
if (typeof window === "undefined") {
|
|
7
8
|
return;
|
|
@@ -20,7 +21,8 @@
|
|
|
20
21
|
let hash = 2166136261;
|
|
21
22
|
for (let i = 0; i < str.length; i++) {
|
|
22
23
|
hash ^= str.charCodeAt(i);
|
|
23
|
-
hash +=
|
|
24
|
+
hash +=
|
|
25
|
+
(hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
24
26
|
}
|
|
25
27
|
return hash >>> 0;
|
|
26
28
|
}
|
|
@@ -128,6 +130,7 @@
|
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
function handler(ev) {
|
|
133
|
+
if (!iframe.contentWindow || ev.source !== iframe.contentWindow) return;
|
|
131
134
|
var d = ev.data;
|
|
132
135
|
if (!d || typeof d !== "object") return;
|
|
133
136
|
if (d.type === "cap:instr") {
|
|
@@ -152,7 +155,7 @@
|
|
|
152
155
|
|
|
153
156
|
const scriptNonce = window.CAP_SCRIPT_NONCE || window.CAP_CSS_NONCE;
|
|
154
157
|
const nonceAttr = scriptNonce
|
|
155
|
-
?
|
|
158
|
+
? ` nonce="${String(scriptNonce).replace(/"/g, """)}"`
|
|
156
159
|
: "";
|
|
157
160
|
iframe.srcdoc =
|
|
158
161
|
'<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><script' +
|
|
@@ -189,7 +192,10 @@
|
|
|
189
192
|
return wasmModulePromise;
|
|
190
193
|
};
|
|
191
194
|
|
|
192
|
-
if (
|
|
195
|
+
if (
|
|
196
|
+
typeof WebAssembly === "object" &&
|
|
197
|
+
typeof WebAssembly.compile === "function"
|
|
198
|
+
) {
|
|
193
199
|
getWasmModule().catch(() => {});
|
|
194
200
|
}
|
|
195
201
|
|
|
@@ -206,7 +212,7 @@
|
|
|
206
212
|
if (_sharedWorkerUrl) return _sharedWorkerUrl;
|
|
207
213
|
|
|
208
214
|
_sharedWorkerUrl = URL.createObjectURL(
|
|
209
|
-
new Blob([
|
|
215
|
+
new Blob([%%workerScript%%], { type: "application/javascript" }),
|
|
210
216
|
);
|
|
211
217
|
return _sharedWorkerUrl;
|
|
212
218
|
}
|
|
@@ -264,15 +270,31 @@
|
|
|
264
270
|
});
|
|
265
271
|
}
|
|
266
272
|
|
|
273
|
+
runMsg(msg, onProgress) {
|
|
274
|
+
return new Promise((resolve, reject) => {
|
|
275
|
+
this._queue.push({ msg, onProgress, resolve, reject });
|
|
276
|
+
this._dispatch();
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
267
280
|
_dispatch() {
|
|
268
281
|
while (this._idle.length > 0 && this._queue.length > 0) {
|
|
269
282
|
const worker = this._idle.shift();
|
|
270
|
-
const
|
|
283
|
+
const task = this._queue.shift();
|
|
284
|
+
const { resolve, reject } = task;
|
|
285
|
+
const isMsg = task.msg !== undefined;
|
|
271
286
|
|
|
272
287
|
let settled = false;
|
|
273
288
|
|
|
274
289
|
const onMessage = ({ data }) => {
|
|
275
290
|
if (settled) return;
|
|
291
|
+
|
|
292
|
+
if (data && typeof data.progress === "number" && !data.found) {
|
|
293
|
+
if (task.onProgress) {
|
|
294
|
+
try { task.onProgress(data.progress); } catch {}
|
|
295
|
+
}
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
276
298
|
settled = true;
|
|
277
299
|
worker.removeEventListener("message", onMessage);
|
|
278
300
|
worker.removeEventListener("error", onError);
|
|
@@ -281,7 +303,7 @@
|
|
|
281
303
|
if (!data.found) {
|
|
282
304
|
reject(new Error(data.error || "worker failed"));
|
|
283
305
|
} else {
|
|
284
|
-
resolve(data.nonce);
|
|
306
|
+
resolve(isMsg ? data : data.nonce);
|
|
285
307
|
}
|
|
286
308
|
this._dispatch();
|
|
287
309
|
};
|
|
@@ -301,10 +323,15 @@
|
|
|
301
323
|
worker.addEventListener("message", onMessage);
|
|
302
324
|
worker.addEventListener("error", onError);
|
|
303
325
|
|
|
304
|
-
if (
|
|
305
|
-
worker.postMessage(
|
|
326
|
+
if (isMsg) {
|
|
327
|
+
worker.postMessage(task.msg);
|
|
328
|
+
} else if (this._wasmModule) {
|
|
329
|
+
worker.postMessage(
|
|
330
|
+
{ salt: task.salt, target: task.target, wasmModule: this._wasmModule },
|
|
331
|
+
[],
|
|
332
|
+
);
|
|
306
333
|
} else {
|
|
307
|
-
worker.postMessage({ salt, target });
|
|
334
|
+
worker.postMessage({ salt: task.salt, target: task.target });
|
|
308
335
|
}
|
|
309
336
|
}
|
|
310
337
|
}
|
|
@@ -406,7 +433,10 @@
|
|
|
406
433
|
|
|
407
434
|
#isVisible() {
|
|
408
435
|
if (typeof this.checkVisibility === "function") {
|
|
409
|
-
return this.checkVisibility({
|
|
436
|
+
return this.checkVisibility({
|
|
437
|
+
checkOpacity: true,
|
|
438
|
+
checkVisibilityCSS: true,
|
|
439
|
+
});
|
|
410
440
|
}
|
|
411
441
|
// Fallback: offsetParent is null for display:none; also check the style directly
|
|
412
442
|
return !!(this.offsetParent || this.getClientRects().length > 0);
|
|
@@ -436,7 +466,9 @@
|
|
|
436
466
|
if (!apiEndpoint.endsWith("/")) apiEndpoint += "/";
|
|
437
467
|
|
|
438
468
|
try {
|
|
439
|
-
const raw = await capFetch(`${apiEndpoint}challenge`, {
|
|
469
|
+
const raw = await capFetch(`${apiEndpoint}challenge`, {
|
|
470
|
+
method: "POST",
|
|
471
|
+
});
|
|
440
472
|
let resp;
|
|
441
473
|
try {
|
|
442
474
|
resp = await raw.json();
|
|
@@ -448,13 +480,22 @@
|
|
|
448
480
|
resp._apiEndpoint = apiEndpoint;
|
|
449
481
|
this.#speculative.challengeResp = resp;
|
|
450
482
|
|
|
483
|
+
if (resp.format === 2 && Array.isArray(resp.challenges)) {
|
|
484
|
+
this.#speculative.state = "idle";
|
|
485
|
+
this.#speculative.notify();
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
451
489
|
const { challenge, token } = resp;
|
|
452
490
|
let challenges = challenge;
|
|
453
491
|
if (!Array.isArray(challenges)) {
|
|
454
492
|
let i = 0;
|
|
455
493
|
challenges = Array.from({ length: challenge.c }, () => {
|
|
456
494
|
i++;
|
|
457
|
-
return [
|
|
495
|
+
return [
|
|
496
|
+
prng(`${token}${i}`, challenge.s),
|
|
497
|
+
prng(`${token}${i}d`, challenge.d),
|
|
498
|
+
];
|
|
458
499
|
});
|
|
459
500
|
}
|
|
460
501
|
this.#speculative.challenges = challenges;
|
|
@@ -518,10 +559,12 @@
|
|
|
518
559
|
|
|
519
560
|
const batchResults = await Promise.all(
|
|
520
561
|
batch.map((challenge) =>
|
|
521
|
-
this.#speculativePool
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
562
|
+
this.#speculativePool
|
|
563
|
+
.run(challenge[0], challenge[1])
|
|
564
|
+
.then((nonce) => {
|
|
565
|
+
this.#speculative.completedCount++;
|
|
566
|
+
return nonce;
|
|
567
|
+
}),
|
|
525
568
|
),
|
|
526
569
|
);
|
|
527
570
|
|
|
@@ -530,7 +573,9 @@
|
|
|
530
573
|
}
|
|
531
574
|
|
|
532
575
|
if (!promoted && nextIndex < total) {
|
|
533
|
-
await new Promise((resolve) =>
|
|
576
|
+
await new Promise((resolve) =>
|
|
577
|
+
setTimeout(resolve, SPECULATIVE_YIELD_MS),
|
|
578
|
+
);
|
|
534
579
|
}
|
|
535
580
|
}
|
|
536
581
|
|
|
@@ -544,11 +589,14 @@
|
|
|
544
589
|
try {
|
|
545
590
|
const challengeResp = this.#speculative.challengeResp;
|
|
546
591
|
const apiEndpoint = challengeResp._apiEndpoint;
|
|
547
|
-
if (!apiEndpoint)
|
|
592
|
+
if (!apiEndpoint)
|
|
593
|
+
throw new Error("[cap] speculative redeem: missing apiEndpoint");
|
|
548
594
|
|
|
549
595
|
let instrOut = null;
|
|
550
596
|
if (challengeResp.instrumentation) {
|
|
551
|
-
instrOut = await runInstrumentationChallenge(
|
|
597
|
+
instrOut = await runInstrumentationChallenge(
|
|
598
|
+
challengeResp.instrumentation,
|
|
599
|
+
);
|
|
552
600
|
if (instrOut?.__timeout || instrOut?.__blocked) {
|
|
553
601
|
this.#speculative.state = "done";
|
|
554
602
|
this.#speculative.notify();
|
|
@@ -573,14 +621,18 @@
|
|
|
573
621
|
throw new Error("Failed to parse speculative redeem response");
|
|
574
622
|
}
|
|
575
623
|
|
|
576
|
-
if (!resp.success)
|
|
624
|
+
if (!resp.success)
|
|
625
|
+
throw new Error(resp.error || "Speculative redeem failed");
|
|
577
626
|
|
|
578
627
|
this.#speculative.token = resp.token;
|
|
579
628
|
this.#speculative.tokenExpires = new Date(resp.expires).getTime();
|
|
580
629
|
this.#speculative.state = "done";
|
|
581
630
|
this.#speculative.notify();
|
|
582
631
|
} catch (e) {
|
|
583
|
-
console.warn(
|
|
632
|
+
console.warn(
|
|
633
|
+
"[cap] speculative redeem failed (will redo on click):",
|
|
634
|
+
e,
|
|
635
|
+
);
|
|
584
636
|
this.#speculative.state = "done";
|
|
585
637
|
this.#speculative.notify();
|
|
586
638
|
}
|
|
@@ -674,7 +726,9 @@
|
|
|
674
726
|
this.#div &&
|
|
675
727
|
this.#div?.querySelector(".label.active")
|
|
676
728
|
) {
|
|
677
|
-
this.animateLabel(
|
|
729
|
+
this.animateLabel(
|
|
730
|
+
this.getI18nText("initial-state", "Verify you're human"),
|
|
731
|
+
);
|
|
678
732
|
}
|
|
679
733
|
|
|
680
734
|
if (name === "required") {
|
|
@@ -700,7 +754,8 @@
|
|
|
700
754
|
const workers = this.getAttribute("data-cap-worker-count");
|
|
701
755
|
const parsedWorkers = workers ? parseInt(workers, 10) : null;
|
|
702
756
|
this.setWorkersCount(parsedWorkers || navigator.hardwareConcurrency || 8);
|
|
703
|
-
const fieldName =
|
|
757
|
+
const fieldName =
|
|
758
|
+
this.getAttribute("data-cap-hidden-field-name") || "cap-token";
|
|
704
759
|
this.#host.innerHTML = `<input type="hidden" name="${fieldName}">`;
|
|
705
760
|
|
|
706
761
|
this.#attachInteractionListeners();
|
|
@@ -729,10 +784,17 @@
|
|
|
729
784
|
|
|
730
785
|
try {
|
|
731
786
|
this.#solving = true;
|
|
732
|
-
this.updateUI(
|
|
787
|
+
this.updateUI(
|
|
788
|
+
"verifying",
|
|
789
|
+
this.getI18nText("verifying-label", "Verifying..."),
|
|
790
|
+
true,
|
|
791
|
+
);
|
|
733
792
|
this.#trigger.setAttribute(
|
|
734
793
|
"aria-label",
|
|
735
|
-
this.getI18nText(
|
|
794
|
+
this.getI18nText(
|
|
795
|
+
"verifying-aria-label",
|
|
796
|
+
"Verifying you're a human, please wait",
|
|
797
|
+
),
|
|
736
798
|
);
|
|
737
799
|
this.dispatchEvent("progress", { progress: 0 });
|
|
738
800
|
|
|
@@ -758,9 +820,11 @@
|
|
|
758
820
|
) {
|
|
759
821
|
this.dispatchEvent("progress", { progress: 100 });
|
|
760
822
|
|
|
761
|
-
const fieldName =
|
|
823
|
+
const fieldName =
|
|
824
|
+
this.getAttribute("data-cap-hidden-field-name") || "cap-token";
|
|
762
825
|
if (this.querySelector(`input[name='${fieldName}']`)) {
|
|
763
|
-
this.querySelector(`input[name='${fieldName}']`).value =
|
|
826
|
+
this.querySelector(`input[name='${fieldName}']`).value =
|
|
827
|
+
this.#speculative.token;
|
|
764
828
|
}
|
|
765
829
|
this.dispatchEvent("solve", { token: this.#speculative.token });
|
|
766
830
|
this.token = this.#speculative.token;
|
|
@@ -813,7 +877,9 @@
|
|
|
813
877
|
clearInterval(progressInterval);
|
|
814
878
|
return;
|
|
815
879
|
}
|
|
816
|
-
const total = this.#speculative.challenges
|
|
880
|
+
const total = this.#speculative.challenges
|
|
881
|
+
? this.#speculative.challenges.length
|
|
882
|
+
: 1;
|
|
817
883
|
const done = this.#speculative.completedCount;
|
|
818
884
|
const visual =
|
|
819
885
|
st === "redeeming"
|
|
@@ -824,12 +890,23 @@
|
|
|
824
890
|
this.dispatchEvent("progress", { progress: visual });
|
|
825
891
|
}, 150);
|
|
826
892
|
|
|
827
|
-
await new Promise((resolve) =>
|
|
893
|
+
await new Promise((resolve) =>
|
|
894
|
+
this.#speculative.onSettled(resolve),
|
|
895
|
+
);
|
|
828
896
|
clearInterval(progressInterval);
|
|
829
897
|
|
|
830
|
-
if (
|
|
831
|
-
|
|
832
|
-
|
|
898
|
+
if (
|
|
899
|
+
this.#speculative.state === "idle" &&
|
|
900
|
+
this.#speculative.challengeResp?.format === 2 &&
|
|
901
|
+
Array.isArray(this.#speculative.challengeResp.challenges)
|
|
902
|
+
) {
|
|
903
|
+
challengeResp = this.#speculative.challengeResp;
|
|
904
|
+
this.#speculative.challengeResp = null;
|
|
905
|
+
solutions = await this.solveChallengesV2(challengeResp.challenges);
|
|
906
|
+
} else {
|
|
907
|
+
if (this.#speculative.state !== "done") {
|
|
908
|
+
throw new Error("Speculative solve failed – please try again");
|
|
909
|
+
}
|
|
833
910
|
|
|
834
911
|
if (
|
|
835
912
|
this.#speculative.token &&
|
|
@@ -838,9 +915,11 @@
|
|
|
838
915
|
) {
|
|
839
916
|
this.dispatchEvent("progress", { progress: 100 });
|
|
840
917
|
|
|
841
|
-
const fieldName =
|
|
918
|
+
const fieldName =
|
|
919
|
+
this.getAttribute("data-cap-hidden-field-name") || "cap-token";
|
|
842
920
|
if (this.querySelector(`input[name='${fieldName}']`)) {
|
|
843
|
-
this.querySelector(`input[name='${fieldName}']`).value =
|
|
921
|
+
this.querySelector(`input[name='${fieldName}']`).value =
|
|
922
|
+
this.#speculative.token;
|
|
844
923
|
}
|
|
845
924
|
this.dispatchEvent("solve", { token: this.#speculative.token });
|
|
846
925
|
this.token = this.#speculative.token;
|
|
@@ -866,28 +945,44 @@
|
|
|
866
945
|
solutions = this.#speculative.results;
|
|
867
946
|
challengeResp = this.#speculative.challengeResp;
|
|
868
947
|
this.dispatchEvent("progress", { progress: 100 });
|
|
869
|
-
} else {
|
|
870
|
-
const challengeRaw = await capFetch(`${apiEndpoint}challenge`, {
|
|
871
|
-
method: "POST",
|
|
872
|
-
});
|
|
873
|
-
try {
|
|
874
|
-
challengeResp = await challengeRaw.json();
|
|
875
|
-
} catch {
|
|
876
|
-
throw new Error("Failed to parse challenge response from server");
|
|
877
948
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
return [prng(`${token}${i}`, challenge.s), prng(`${token}${i}d`, challenge.d)];
|
|
949
|
+
} else {
|
|
950
|
+
const cached = this.#speculative.challengeResp;
|
|
951
|
+
if (cached?.format === 2 && Array.isArray(cached.challenges)) {
|
|
952
|
+
challengeResp = cached;
|
|
953
|
+
this.#speculative.challengeResp = null;
|
|
954
|
+
} else {
|
|
955
|
+
const challengeRaw = await capFetch(`${apiEndpoint}challenge`, {
|
|
956
|
+
method: "POST",
|
|
887
957
|
});
|
|
958
|
+
try {
|
|
959
|
+
challengeResp = await challengeRaw.json();
|
|
960
|
+
} catch {
|
|
961
|
+
throw new Error("Failed to parse challenge response from server");
|
|
962
|
+
}
|
|
963
|
+
if (challengeResp.error) throw new Error(challengeResp.error);
|
|
888
964
|
}
|
|
889
965
|
|
|
890
|
-
|
|
966
|
+
if (
|
|
967
|
+
challengeResp.format === 2 &&
|
|
968
|
+
Array.isArray(challengeResp.challenges)
|
|
969
|
+
) {
|
|
970
|
+
solutions = await this.solveChallengesV2(challengeResp.challenges);
|
|
971
|
+
} else {
|
|
972
|
+
const { challenge, token } = challengeResp;
|
|
973
|
+
let challenges = challenge;
|
|
974
|
+
if (!Array.isArray(challenges)) {
|
|
975
|
+
let i = 0;
|
|
976
|
+
challenges = Array.from({ length: challenge.c }, () => {
|
|
977
|
+
i++;
|
|
978
|
+
return [
|
|
979
|
+
prng(`${token}${i}`, challenge.s),
|
|
980
|
+
prng(`${token}${i}d`, challenge.d),
|
|
981
|
+
];
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
solutions = await this.solveChallenges(challenges);
|
|
985
|
+
}
|
|
891
986
|
}
|
|
892
987
|
|
|
893
988
|
const instrPromise = challengeResp.instrumentation
|
|
@@ -897,10 +992,27 @@
|
|
|
897
992
|
const instrOut = await instrPromise;
|
|
898
993
|
|
|
899
994
|
if (instrOut?.__timeout || instrOut?.__blocked) {
|
|
900
|
-
|
|
995
|
+
capFetch(`${apiEndpoint}redeem`, {
|
|
996
|
+
method: "POST",
|
|
997
|
+
body: JSON.stringify({
|
|
998
|
+
token: challengeResp.token,
|
|
999
|
+
solutions,
|
|
1000
|
+
...(instrOut.__blocked && { instr_blocked: true }),
|
|
1001
|
+
...(instrOut.__timeout && { instr_timeout: true }),
|
|
1002
|
+
}),
|
|
1003
|
+
headers: { "Content-Type": "application/json" },
|
|
1004
|
+
}).catch(() => {});
|
|
1005
|
+
|
|
1006
|
+
this.updateUIBlocked(
|
|
1007
|
+
this.getI18nText("error-label", "Error"),
|
|
1008
|
+
instrOut?.__blocked,
|
|
1009
|
+
);
|
|
901
1010
|
this.#trigger.setAttribute(
|
|
902
1011
|
"aria-label",
|
|
903
|
-
this.getI18nText(
|
|
1012
|
+
this.getI18nText(
|
|
1013
|
+
"error-aria-label",
|
|
1014
|
+
"An error occurred, please try again",
|
|
1015
|
+
),
|
|
904
1016
|
);
|
|
905
1017
|
this.removeEventListener("error", this.boundHandleError);
|
|
906
1018
|
const errEvent = new CustomEvent("error", {
|
|
@@ -938,7 +1050,8 @@
|
|
|
938
1050
|
this.dispatchEvent("progress", { progress: 100 });
|
|
939
1051
|
if (!resp.success) throw new Error(resp.error || "Invalid solution");
|
|
940
1052
|
|
|
941
|
-
const fieldName =
|
|
1053
|
+
const fieldName =
|
|
1054
|
+
this.getAttribute("data-cap-hidden-field-name") || "cap-token";
|
|
942
1055
|
if (this.querySelector(`input[name='${fieldName}']`)) {
|
|
943
1056
|
this.querySelector(`input[name='${fieldName}']`).value = resp.token;
|
|
944
1057
|
}
|
|
@@ -969,7 +1082,10 @@
|
|
|
969
1082
|
} catch (err) {
|
|
970
1083
|
this.#trigger.setAttribute(
|
|
971
1084
|
"aria-label",
|
|
972
|
-
this.getI18nText(
|
|
1085
|
+
this.getI18nText(
|
|
1086
|
+
"error-aria-label",
|
|
1087
|
+
"An error occurred, please try again",
|
|
1088
|
+
),
|
|
973
1089
|
);
|
|
974
1090
|
this.error(err.message);
|
|
975
1091
|
throw err;
|
|
@@ -979,6 +1095,105 @@
|
|
|
979
1095
|
}
|
|
980
1096
|
}
|
|
981
1097
|
|
|
1098
|
+
async solveChallengesV2(challenges) {
|
|
1099
|
+
const total = challenges.length;
|
|
1100
|
+
let completed = 0;
|
|
1101
|
+
|
|
1102
|
+
const solutions = new Array(challenges.length);
|
|
1103
|
+
for (let i = 0; i < challenges.length; i++) {
|
|
1104
|
+
const ch = challenges[i];
|
|
1105
|
+
if (
|
|
1106
|
+
!ch ||
|
|
1107
|
+
typeof ch !== "object" ||
|
|
1108
|
+
!(
|
|
1109
|
+
ch.protocol === "sha256-pow" ||
|
|
1110
|
+
ch.protocol === "rsw" ||
|
|
1111
|
+
ch.protocol === "instrumentation"
|
|
1112
|
+
)
|
|
1113
|
+
) {
|
|
1114
|
+
// Unknown protocol = older widget on a newer server. Fall back to
|
|
1115
|
+
// erroring out -- the host can detect this and serve format-1.
|
|
1116
|
+
throw new Error(`[cap] unsupported format-2 protocol '${ch?.protocol}'`);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
let wasmModule = null;
|
|
1121
|
+
const wasmSupported =
|
|
1122
|
+
typeof WebAssembly === "object" &&
|
|
1123
|
+
typeof WebAssembly.instantiate === "function";
|
|
1124
|
+
if (wasmSupported) {
|
|
1125
|
+
try { wasmModule = await getWasmModule(); }
|
|
1126
|
+
catch (e) { console.warn("[cap] wasm unavailable, falling back to JS solver:", e); }
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
const poolSize = Math.max(1, Math.min(this.#workersCount, challenges.length));
|
|
1130
|
+
const pool = new WorkerPool(poolSize);
|
|
1131
|
+
pool.setWasm(wasmModule);
|
|
1132
|
+
pool._ensureSize(poolSize);
|
|
1133
|
+
|
|
1134
|
+
const TASK_TIMEOUT_MS = 60_000;
|
|
1135
|
+
const withTimeout = (promise, label) => Promise.race([
|
|
1136
|
+
promise,
|
|
1137
|
+
new Promise((_, rej) => setTimeout(
|
|
1138
|
+
() => rej(new Error(`[cap] ${label} timed out after ${TASK_TIMEOUT_MS}ms`)),
|
|
1139
|
+
TASK_TIMEOUT_MS,
|
|
1140
|
+
)),
|
|
1141
|
+
]);
|
|
1142
|
+
|
|
1143
|
+
const inFlight = new Array(challenges.length).fill(0);
|
|
1144
|
+
const emit = () => {
|
|
1145
|
+
const sum = inFlight.reduce((a, b) => a + b, 0) + completed;
|
|
1146
|
+
const visual = Math.min(99, Math.round((sum / total) * 100));
|
|
1147
|
+
this.dispatchEvent("progress", { progress: visual });
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1150
|
+
try {
|
|
1151
|
+
await Promise.all(
|
|
1152
|
+
challenges.map((ch, idx) => {
|
|
1153
|
+
if (ch.protocol === "sha256-pow") {
|
|
1154
|
+
return withTimeout(
|
|
1155
|
+
pool.run(ch.payload.salt, ch.payload.target),
|
|
1156
|
+
`sha256-pow worker #${idx}`,
|
|
1157
|
+
).then((nonce) => {
|
|
1158
|
+
solutions[idx] = { nonce: Number(nonce) };
|
|
1159
|
+
inFlight[idx] = 0;
|
|
1160
|
+
completed++;
|
|
1161
|
+
emit();
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
if (ch.protocol === "rsw") {
|
|
1166
|
+
return withTimeout(
|
|
1167
|
+
pool.runMsg(
|
|
1168
|
+
{ kind: "rsw", N: ch.payload.N, x: ch.payload.x, t: ch.payload.t | 0 },
|
|
1169
|
+
(p) => { inFlight[idx] = p; emit(); },
|
|
1170
|
+
),
|
|
1171
|
+
`rsw worker #${idx}`,
|
|
1172
|
+
).then((data) => {
|
|
1173
|
+
solutions[idx] = { y: data.y };
|
|
1174
|
+
inFlight[idx] = 0;
|
|
1175
|
+
completed++;
|
|
1176
|
+
emit();
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
return runInstrumentationChallenge(ch.payload.blob).then((out) => {
|
|
1181
|
+
if (out?.__timeout) solutions[idx] = { timeout: true };
|
|
1182
|
+
else if (out?.__blocked) solutions[idx] = { blocked: true };
|
|
1183
|
+
else solutions[idx] = { instr: out };
|
|
1184
|
+
inFlight[idx] = 0;
|
|
1185
|
+
completed++;
|
|
1186
|
+
emit();
|
|
1187
|
+
});
|
|
1188
|
+
}),
|
|
1189
|
+
);
|
|
1190
|
+
} finally {
|
|
1191
|
+
pool.terminate();
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
return solutions;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
982
1197
|
async solveChallenges(challenges) {
|
|
983
1198
|
const total = challenges.length;
|
|
984
1199
|
let completed = 0;
|
|
@@ -987,7 +1202,8 @@
|
|
|
987
1202
|
|
|
988
1203
|
let wasmModule = null;
|
|
989
1204
|
const wasmSupported =
|
|
990
|
-
typeof WebAssembly === "object" &&
|
|
1205
|
+
typeof WebAssembly === "object" &&
|
|
1206
|
+
typeof WebAssembly.instantiate === "function";
|
|
991
1207
|
|
|
992
1208
|
if (wasmSupported) {
|
|
993
1209
|
try {
|
|
@@ -1021,7 +1237,10 @@
|
|
|
1021
1237
|
const results = [];
|
|
1022
1238
|
try {
|
|
1023
1239
|
for (let i = 0; i < challenges.length; i += this.#workersCount) {
|
|
1024
|
-
const chunk = challenges.slice(
|
|
1240
|
+
const chunk = challenges.slice(
|
|
1241
|
+
i,
|
|
1242
|
+
Math.min(i + this.#workersCount, challenges.length),
|
|
1243
|
+
);
|
|
1025
1244
|
const chunkResults = await Promise.all(
|
|
1026
1245
|
chunk.map(([salt, target]) =>
|
|
1027
1246
|
pool.run(salt, target).then((nonce) => {
|
|
@@ -1048,7 +1267,9 @@
|
|
|
1048
1267
|
const parsedWorkers = parseInt(workers, 10);
|
|
1049
1268
|
const maxWorkers = Math.min(navigator.hardwareConcurrency || 8, 16);
|
|
1050
1269
|
this.#workersCount =
|
|
1051
|
-
!Number.isNaN(parsedWorkers) &&
|
|
1270
|
+
!Number.isNaN(parsedWorkers) &&
|
|
1271
|
+
parsedWorkers > 0 &&
|
|
1272
|
+
parsedWorkers <= maxWorkers
|
|
1052
1273
|
? parsedWorkers
|
|
1053
1274
|
: navigator.hardwareConcurrency || 8;
|
|
1054
1275
|
}
|
|
@@ -1117,7 +1338,9 @@
|
|
|
1117
1338
|
utm_source: "cap_widget",
|
|
1118
1339
|
utm_medium: "referral",
|
|
1119
1340
|
utm_campaign: "widget",
|
|
1120
|
-
utm_content: window.CAP_DISABLE_WIDGET_REF
|
|
1341
|
+
utm_content: window.CAP_DISABLE_WIDGET_REF
|
|
1342
|
+
? ""
|
|
1343
|
+
: location.hostname,
|
|
1121
1344
|
ref: window.CAP_DISABLE_WIDGET_REF ? "" : location.href || "",
|
|
1122
1345
|
sub: window.CAP_DISABLE_WIDGET_REF ? "" : document.referrer || "",
|
|
1123
1346
|
},
|
|
@@ -1137,7 +1360,10 @@
|
|
|
1137
1360
|
|
|
1138
1361
|
this.#trigger.addEventListener("keydown", (e) => {
|
|
1139
1362
|
if (e.target !== this.#trigger) return;
|
|
1140
|
-
if (
|
|
1363
|
+
if (
|
|
1364
|
+
(e.key === "Enter" || e.key === " ") &&
|
|
1365
|
+
!this.#trigger.hasAttribute("disabled")
|
|
1366
|
+
) {
|
|
1141
1367
|
e.preventDefault();
|
|
1142
1368
|
e.stopPropagation();
|
|
1143
1369
|
this.solve();
|
|
@@ -1234,11 +1460,14 @@
|
|
|
1234
1460
|
handleProgress(event) {
|
|
1235
1461
|
if (!this.#trigger) return;
|
|
1236
1462
|
|
|
1237
|
-
const progressCircle = this.#trigger.querySelector(
|
|
1463
|
+
const progressCircle = this.#trigger.querySelector(
|
|
1464
|
+
".progress-ring-circle",
|
|
1465
|
+
);
|
|
1238
1466
|
|
|
1239
1467
|
if (progressCircle) {
|
|
1240
1468
|
const circumference = 2 * Math.PI * 14;
|
|
1241
|
-
const offset =
|
|
1469
|
+
const offset =
|
|
1470
|
+
circumference - (event.detail.progress / 100) * circumference;
|
|
1242
1471
|
progressCircle.style.strokeDashoffset = offset;
|
|
1243
1472
|
}
|
|
1244
1473
|
|
|
@@ -1254,14 +1483,21 @@
|
|
|
1254
1483
|
}
|
|
1255
1484
|
|
|
1256
1485
|
handleSolve(event) {
|
|
1257
|
-
this.updateUI(
|
|
1486
|
+
this.updateUI(
|
|
1487
|
+
"done",
|
|
1488
|
+
this.getI18nText("solved-label", "You're a human"),
|
|
1489
|
+
true,
|
|
1490
|
+
);
|
|
1258
1491
|
this.executeAttributeCode("onsolve", event);
|
|
1259
1492
|
this.#internals?.setValidity?.({});
|
|
1260
1493
|
this.#div?.classList.remove("invalid");
|
|
1261
1494
|
}
|
|
1262
1495
|
|
|
1263
1496
|
handleError(event) {
|
|
1264
|
-
this.updateUI(
|
|
1497
|
+
this.updateUI(
|
|
1498
|
+
"error",
|
|
1499
|
+
this.getI18nText("error-label", "Error. Try again."),
|
|
1500
|
+
);
|
|
1265
1501
|
this.executeAttributeCode("onerror", event);
|
|
1266
1502
|
|
|
1267
1503
|
if (this.#hasHaptics) navigator.vibrate([10, 40, 10]);
|
|
@@ -1307,7 +1543,8 @@
|
|
|
1307
1543
|
}
|
|
1308
1544
|
this.token = null;
|
|
1309
1545
|
this.dispatchEvent("reset");
|
|
1310
|
-
const fieldName =
|
|
1546
|
+
const fieldName =
|
|
1547
|
+
this.getAttribute("data-cap-hidden-field-name") || "cap-token";
|
|
1311
1548
|
if (this.querySelector(`input[name='${fieldName}']`)) {
|
|
1312
1549
|
this.querySelector(`input[name='${fieldName}']`).value = "";
|
|
1313
1550
|
}
|
package/src/worker.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
const fullBytes = Math.floor(targetBits / 8);
|
|
9
9
|
const remainingBits = targetBits % 8;
|
|
10
10
|
|
|
11
|
-
const paddedTarget = target.length % 2 === 0 ? target : target
|
|
11
|
+
const paddedTarget = target.length % 2 === 0 ? target : `${target}0`;
|
|
12
12
|
const targetBytesLength = paddedTarget.length / 2;
|
|
13
13
|
const targetBytes = new Uint8Array(targetBytesLength);
|
|
14
14
|
for (let k = 0; k < targetBytesLength; k++) {
|
|
@@ -60,13 +60,32 @@
|
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
+
const solveRsw = (data) => {
|
|
64
|
+
const N = BigInt("0x" + data.N);
|
|
65
|
+
let y = BigInt("0x" + data.x);
|
|
66
|
+
const t = data.t | 0;
|
|
67
|
+
const chunk = Math.max(64, Math.floor(t / 50));
|
|
68
|
+
const t0 = performance.now();
|
|
69
|
+
for (let i = 0; i < t; i += chunk) {
|
|
70
|
+
const end = Math.min(t, i + chunk);
|
|
71
|
+
for (let j = i; j < end; j++) y = (y * y) % N;
|
|
72
|
+
if (end < t) self.postMessage({ progress: end / t });
|
|
73
|
+
}
|
|
74
|
+
self.postMessage({
|
|
75
|
+
found: true,
|
|
76
|
+
y: y.toString(16),
|
|
77
|
+
durationMs: (performance.now() - t0).toFixed(2),
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
63
81
|
if (typeof WebAssembly !== "object" || typeof WebAssembly?.instantiate !== "function") {
|
|
64
82
|
console.warn(
|
|
65
83
|
"[cap worker] wasm not supported, falling back to alternative solver. this will be significantly slower.",
|
|
66
84
|
);
|
|
67
85
|
|
|
68
|
-
self.onmessage = async ({ data
|
|
69
|
-
|
|
86
|
+
self.onmessage = async ({ data }) => {
|
|
87
|
+
if (data?.kind === "rsw") return solveRsw(data);
|
|
88
|
+
return solveFallback({ salt: data.salt, target: data.target });
|
|
70
89
|
};
|
|
71
90
|
|
|
72
91
|
return;
|
|
@@ -113,7 +132,9 @@
|
|
|
113
132
|
|
|
114
133
|
if (offset !== len) {
|
|
115
134
|
if (offset !== 0) str = str.slice(offset);
|
|
116
|
-
|
|
135
|
+
const newLen = offset + str.length * 3;
|
|
136
|
+
ptr = realloc(ptr, len, newLen, 1) >>> 0;
|
|
137
|
+
len = newLen;
|
|
117
138
|
const subarray = getMemory().subarray(ptr + offset, ptr + len);
|
|
118
139
|
const { written } = encoder.encodeInto(str, subarray);
|
|
119
140
|
offset += written;
|
|
@@ -155,7 +176,9 @@
|
|
|
155
176
|
}
|
|
156
177
|
};
|
|
157
178
|
|
|
158
|
-
self.onmessage = async ({ data
|
|
179
|
+
self.onmessage = async ({ data }) => {
|
|
180
|
+
if (data?.kind === "rsw") return solveRsw(data);
|
|
181
|
+
const { salt, target, wasmModule } = data;
|
|
159
182
|
if (wasmModule instanceof WebAssembly.Module && solve_pow_function === null) {
|
|
160
183
|
const ok = initFromModule(wasmModule);
|
|
161
184
|
if (!ok) {
|