@cap.js/widget 0.1.36 → 0.1.37
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.min.js +1 -1
- package/package.json +1 -1
- package/src/cap-floating.js +2 -2
- package/src/cap.css +13 -0
- package/src/cap.js +231 -18
- package/src/worker.js +21 -7
package/cap.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{const e="0.0.6";if("undefined"==typeof window)return;const t=(t,r={})=>{const i={...r,headers:{...r.headers||{},"Cap-Stamp":btoa(String.fromCharCode(...[[Date.now()]].map(e=>[e>>24,e>>16,e>>8,e[0]].map(e=>255&e))[0])).replace(/=/g,""),"Cap-Solver":`0,${e}`}};return window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(t,i):fetch(t,i)};function r(e,t){let r=function(e){let t=2166136261;for(let r=0;r<e.length;r++)t^=e.charCodeAt(r),t+=(t<<1)+(t<<4)+(t<<7)+(t<<8)+(t<<24);return t>>>0}(e),i="";function a(){return r^=r<<13,r^=r>>>17,r^=r<<5,r>>>0}for(;i.length<t;){i+=a().toString(16).padStart(8,"0")}return i.substring(0,t)}let i=null;const a=()=>{if(i)return i;const t=window.CAP_CUSTOM_WASM_URL||`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${e}/browser/cap_wasm_bg.wasm`;return i=fetch(t).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 i=null,e}),i};"object"==typeof WebAssembly&&"function"==typeof WebAssembly.compile&&a().catch(()=>{});class n extends HTMLElement{#e="";#t=null;#r=navigator.hardwareConcurrency||8;token=null;#i;#a;#n;#s=!1;#o;getI18nText(e,t){return this.getAttribute(`data-cap-i18n-${e}`)||t}static get observedAttributes(){return["onsolve","onprogress","onreset","onerror","data-cap-worker-count","data-cap-i18n-initial-state"]}constructor(){super(),this.#o&&this.#o.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#o=new Map,this.boundHandleProgress=this.handleProgress.bind(this),this.boundHandleSolve=this.handleSolve.bind(this),this.boundHandleError=this.handleError.bind(this),this.boundHandleReset=this.handleReset.bind(this)}initialize(){this.#e=URL.createObjectURL(new Blob(['(()=>{const e=async({salt:e,target:t})=>{let n=0;const r=new TextEncoder,o=new Uint8Array(t.length/2);for(let e=0;e<o.length;e++)o[e]=parseInt(t.substring(2*e,2*e+2),16);const s=o.length;for(;;)try{for(let t=0;t<5e4;t++){const t=e+n,l=r.encode(t),a=await crypto.subtle.digest("SHA-256",l),c=new Uint8Array(a,0,s);let i=!0;for(let e=0;e<s;e++)if(c[e]!==o[e]){i=!1;break}if(i)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"}))}attributeChangedCallback(e,t,r){if(e.startsWith("on")){const t=e.slice(2),i=this.#o.get(e);if(i&&this.removeEventListener(t,i),r){const r=t=>{const r=this.getAttribute(e);"function"==typeof window[r]&&window[r].call(this,t)};this.#o.set(e,r),this.addEventListener(t,r)}}"data-cap-worker-count"===e&&this.setWorkersCount(parseInt(r,10)),"data-cap-i18n-initial-state"===e&&this.#a&&this.#a?.querySelector(".label.active")&&this.animateLabel(this.getI18nText("initial-state","Verify you're human"))}async connectedCallback(){this.#n=this,this.#i=this.attachShadow({mode:"open"}),this.#a=document.createElement("div"),this.createUI(),this.addEventListeners(),this.initialize(),this.#a.removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count"),t=e?parseInt(e,10):null;this.setWorkersCount(t||navigator.hardwareConcurrency||8);const r=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.#n.innerHTML=`<input type="hidden" name="${r}">`}async solve(){if(!this.#s)try{this.#s=!0,this.updateUI("verifying",this.getI18nText("verifying-label","Verifying..."),!0),this.#a.setAttribute("aria-label",this.getI18nText("verifying-aria-label","Verifying you're a human, please wait")),this.dispatchEvent("progress",{progress:0});try{let e=this.getAttribute("data-cap-api-endpoint");if(!e&&window?.CAP_CUSTOM_FETCH)e="/";else if(!e)throw new Error("Missing API endpoint. Either custom fetch or an API endpoint must be provided.");e.endsWith("/")||(e+="/");const{challenge:i,token:a}=await(await t(`${e}challenge`,{method:"POST"})).json();let n=i;if(!Array.isArray(n)){let e=0;n=Array.from({length:i.c},()=>(e+=1,[r(`${a}${e}`,i.s),r(`${a}${e}d`,i.d)]))}const s=await this.solveChallenges(n),o=await(await t(`${e}redeem`,{method:"POST",body:JSON.stringify({token:a,solutions:s}),headers:{"Content-Type":"application/json"}})).json();if(this.dispatchEvent("progress",{progress:100}),!o.success)throw new Error("Invalid solution");const l=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${l}']`)&&(this.querySelector(`input[name='${l}']`).value=o.token),this.dispatchEvent("solve",{token:o.token}),this.token=o.token,this.#t&&clearTimeout(this.#t);const c=new Date(o.expires).getTime()-Date.now();return c>0&&c<864e5?this.#t=setTimeout(()=>this.reset(),c):this.error("Invalid expiration time"),this.#a.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),{success:!0,token:this.token}}catch(e){throw this.#a.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again")),this.error(e.message),e}}finally{this.#s=!1}}async solveChallenges(e){const t=e.length;let r=0,i=null;const n="object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate;if(n)try{i=await a()}catch(e){console.warn("[cap] wasm unavailable, falling back to JS solver:",e)}if(!n&&!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 s=Array(this.#r).fill(null).map(()=>{try{return new Worker(this.#e)}catch(e){throw console.error("[cap] Failed to create worker:",e),new Error("Worker creation failed")}}),o=([e,a],n)=>new Promise((o,l)=>{const c=s[n];c?(c.onmessage=({data:e})=>{e.found&&(r++,this.dispatchEvent("progress",{progress:Math.round(r/t*100)}),o(e.nonce))},c.onerror=e=>{this.error(`Error in worker: ${e.message||e}`),l(e)},i?c.postMessage({salt:e,target:a,wasmModule:i},[]):c.postMessage({salt:e,target:a})):l(new Error("Worker not available"))}),l=[];try{for(let t=0;t<e.length;t+=this.#r){const r=e.slice(t,Math.min(t+this.#r,e.length)),i=await Promise.all(r.map((e,t)=>o(e,t)));l.push(...i)}}finally{s.forEach(e=>{if(e)try{e.terminate()}catch(e){console.error("[cap] error terminating worker:",e)}})}return l}setWorkersCount(e){const t=parseInt(e,10),r=Math.min(navigator.hardwareConcurrency||8,16);this.#r=!Number.isNaN(t)&&t>0&&t<=r?t:navigator.hardwareConcurrency||8}createUI(){this.#a.classList.add("captcha"),this.#a.setAttribute("role","button"),this.#a.setAttribute("tabindex","0"),this.#a.setAttribute("aria-label",this.getI18nText("verify-aria-label","Click to verify you're a human")),this.#a.setAttribute("aria-live","polite"),this.#a.setAttribute("disabled","true"),this.#a.innerHTML=`<div class="checkbox" part="checkbox"><svg class="progress-ring" viewBox="0 0 32 32"><circle class="progress-ring-bg" cx="16" cy="16" r="14"></circle><circle class="progress-ring-circle" cx="16" cy="16" r="14"></circle></svg></div><p part="label" class="label-wrapper"><span class="label active">${this.getI18nText("initial-state","Verify you're human")}</span></p><a part="attribution" aria-label="Secured by Cap" href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener" title="Secured by Cap: Self-hosted CAPTCHA for the modern web.">Cap</a>`;this.#i.innerHTML=`<style${window.CAP_CSS_NONCE?` nonce=${window.CAP_CSS_NONCE}`:""}>@media (prefers-reduced-motion:reduce){.captcha,.captcha *{transition:none!important;animation:none!important}.label{filter:none!important;opacity:1!important;transition:none!important;transform:none!important}.label:not(.active),.label.exit{display:none!important}}.captcha,.captcha *{box-sizing:border-box}.captcha{background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);-webkit-user-select:none;user-select:none;height:var(--cap-widget-height,58px);width:var(--cap-widget-width,260px);padding:var(--cap-widget-padding,14px);align-items:center;gap:var(--cap-gap,15px);cursor:pointer;-webkit-tap-highlight-color:#fff0;color:var(--cap-color,#212121);transition:filter .2s,transform .2s;display:flex;position:relative;overflow:hidden}.captcha:hover{filter:brightness(98%)}.captcha *{font-family:var(--cap-font,system, -apple-system, "BlinkMacSystemFont", ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", "Ubuntu", "arial", sans-serif)}.checkbox{width:var(--cap-checkbox-size,25px);height:var(--cap-checkbox-size,25px);border:var(--cap-checkbox-border,1px solid #aaaaaad1);border-radius:var(--cap-checkbox-border-radius,6px);background-color:var(--cap-checkbox-background,#fafafa91);margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px);transition:opacity .2s}.captcha p{-webkit-user-select:none;user-select:none;margin:0;font-size:15px;font-weight:500}.label-wrapper{flex:1;align-items:center;min-width:0;height:2em;display:flex;position:relative;overflow:hidden}.label-wrapper:before,.label-wrapper:after{content:"";pointer-events:none;z-index:1;width:100%;height:.6em;position:absolute;left:0}.label-wrapper:before{background:linear-gradient(to bottom, var(--cap-background,#fdfdfd), transparent);top:0}.label-wrapper:after{background:linear-gradient(to top, var(--cap-background,#fdfdfd), transparent);bottom:0}.label{white-space:nowrap;opacity:0;filter:blur(2px);transition:transform .5s cubic-bezier(.25,1,.5,1),opacity .5s,filter .5s;position:absolute;transform:translateY(100%)}.label.active{opacity:1;filter:none;transform:translateY(0)}.label.exit{opacity:0;filter:blur(2px);transform:translateY(-100%)}.checkbox .progress-ring{width:100%;height:100%;display:none;transform:rotate(-90deg)}.checkbox .progress-ring-bg{fill:none;stroke:var(--cap-spinner-background-color,#eee);stroke-width:var(--cap-spinner-thickness,3)}.checkbox .progress-ring-circle{fill:none;stroke:var(--cap-spinner-color,#000);stroke-width:var(--cap-spinner-thickness,3);stroke-linecap:round;stroke-dasharray:87.96;stroke-dashoffset:87.96px;transition:stroke-dashoffset .3s}.captcha[data-state=verifying] .checkbox{background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;display:flex;transform:scale(1.1)}.captcha[data-state=verifying] .checkbox .progress-ring{display:block}.captcha[data-state=done] .checkbox{background-image:var(--cap-checkmark,url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cstyle%3E%40keyframes%20anim%7B0%25%7Bstroke-dashoffset%3A23.21320343017578px%7Dto%7Bstroke-dashoffset%3A0%7D%7D%3C%2Fstyle%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%2300a67d%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m5%2012%205%205L20%207%22%20style%3D%22stroke-dashoffset%3A0%3Bstroke-dasharray%3A23.21320343017578px%3Banimation%3Aanim%20.5s%20ease%22%2F%3E%3C%2Fsvg%3E));background-size:cover;border:1px solid #0000}.captcha[data-state=done] .checkbox .progress-ring{display:none}.captcha[data-state=error] .checkbox{background-image:var(--cap-error-cross,url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 24 24'%3E%3Cpath fill='%23f55b50' d='M11 15h2v2h-2zm0-8h2v6h-2zm1-5C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m0 18a8 8 0 0 1-8-8a8 8 0 0 1 8-8a8 8 0 0 1 8 8a8 8 0 0 1-8 8'/%3E%3C%2Fsvg%3E"));background-size:cover;border:1px solid #0000}.captcha[data-state=error] .checkbox .progress-ring{display:none}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{color:var(--cap-color,#212121);opacity:.8;text-underline-offset:.18em;z-index:6;font-size:12px;text-decoration-thickness:.08em;position:absolute;bottom:10px;right:10px}</style>`,this.#i.appendChild(this.#a)}addEventListeners(){this.#a&&(this.#a.querySelector("a").addEventListener("click",e=>{e.stopPropagation(),e.preventDefault(),window.open("https://capjs.js.org","_blank")}),this.#a.addEventListener("click",()=>{this.#a.hasAttribute("disabled")||this.solve()}),this.#a.addEventListener("keydown",e=>{"Enter"!==e.key&&" "!==e.key||this.#a.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.#a)return;const t=this.#a.querySelector(".label-wrapper");if(!t)return;if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches){const r=t.querySelector(".label.active");if(r)r.textContent=e;else{const r=document.createElement("span");r.className="label active",r.textContent=e,t.appendChild(r)}return}const r=t.querySelector(".label.active"),i=document.createElement("span");i.className="label",i.textContent=e,t.appendChild(i),i.offsetWidth,i.classList.add("active"),r&&(r.classList.remove("active"),r.classList.add("exit"),r.addEventListener("transitionend",()=>r.remove(),{once:!0}))}updateUI(e,t,r=!1){this.#a&&(this.#a.setAttribute("data-state",e),this.animateLabel(t),r?this.#a.setAttribute("disabled","true"):this.#a.removeAttribute("disabled"))}handleProgress(e){if(!this.#a)return;const t=this.#a.querySelector(".progress-ring-circle");if(t){const r=2*Math.PI*14,i=r-e.detail.progress/100*r;t.style.strokeDashoffset=i}const r=this.#a.querySelector(".label-wrapper");if(r){const t=r.querySelector(".label.active");t&&(t.textContent=`${this.getI18nText("verifying-label","Verifying...")} ${e.detail.progress}%`)}this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done",this.getI18nText("solved-label","You're a human"),!0),this.executeAttributeCode("onsolve",e)}handleError(e){this.updateUI("error",this.getI18nText("error-label","Error. Try again.")),this.executeAttributeCode("onerror",e)}handleReset(e){this.updateUI("",this.getI18nText("initial-state","I'm a human")),this.executeAttributeCode("onreset",e)}executeAttributeCode(e,t){const r=this.getAttribute(e);r&&new Function("event",r).call(this,t)}error(e="Unknown error"){console.error("[cap]",e),this.dispatchEvent("error",{isCap:!0,message:e})}dispatchEvent(e,t={}){const r=new CustomEvent(e,{bubbles:!0,composed:!0,detail:t});super.dispatchEvent(r)}reset(){this.#t&&(clearTimeout(this.#t),this.#t=null),this.dispatchEvent("reset"),this.token=null;const e=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${e}']`)&&(this.querySelector(`input[name='${e}']`).value="")}get tokenValue(){return this.token}disconnectedCallback(){this.removeEventListener("progress",this.boundHandleProgress),this.removeEventListener("solve",this.boundHandleSolve),this.removeEventListener("error",this.boundHandleError),this.removeEventListener("reset",this.boundHandleReset),this.#o.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#o.clear(),this.#i&&(this.#i.innerHTML=""),this.reset(),this.cleanup()}cleanup(){this.#t&&(clearTimeout(this.#t),this.#t=null),this.#e&&(URL.revokeObjectURL(this.#e),this.#e="")}}class s{constructor(e={},t){const r=t||document.createElement("cap-widget");if(Object.entries(e).forEach(([e,t])=>{r.setAttribute(e,t)}),!e.apiEndpoint&&!window?.CAP_CUSTOM_FETCH)throw r.remove(),new Error("Missing API endpoint. Either custom fetch or an API endpoint must be provided.");e.apiEndpoint&&r.setAttribute("data-cap-api-endpoint",e.apiEndpoint),this.widget=r,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:()=>r.token,configurable:!0,enumerable:!0}),t||(r.style.display="none",document.documentElement.appendChild(r))}}window.Cap=s,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",n),"object"==typeof exports&&"undefined"!=typeof module?module.exports=s:"function"==typeof define&&define.amd&&define([],()=>s),"undefined"!=typeof exports&&(exports.default=s)})();
|
|
1
|
+
(()=>{const e="0.0.6";if("undefined"==typeof window)return;const t=(t,r={})=>{const n={...r,headers:{...r.headers||{},"Cap-Stamp":btoa(String.fromCharCode(...[[Date.now()]].map(e=>[e>>24,e>>16,e>>8,e[0]].map(e=>255&e))[0])).replace(/=/g,""),"Cap-Solver":`0,${e}`}};return window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(t,n):fetch(t,n)};function r(e,t){let r=function(e){let t=2166136261;for(let r=0;r<e.length;r++)t^=e.charCodeAt(r),t+=(t<<1)+(t<<4)+(t<<7)+(t<<8)+(t<<24);return t>>>0}(e),n="";function i(){return r^=r<<13,r^=r>>>17,r^=r<<5,r>>>0}for(;n.length<t;){n+=i().toString(16).padStart(8,"0")}return n.substring(0,t)}let n=null;const i=()=>{if(n)return n;const t=window.CAP_CUSTOM_WASM_URL||`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${e}/browser/cap_wasm_bg.wasm`;return n=fetch(t).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 n=null,e}),n};"object"==typeof WebAssembly&&"function"==typeof WebAssembly.compile&&i().catch(()=>{});class a extends HTMLElement{#e="";#t=null;#r=navigator.hardwareConcurrency||8;token=null;#n;#i;#a;#s=!1;#o;getI18nText(e,t){return this.getAttribute(`data-cap-i18n-${e}`)||t}static get observedAttributes(){return["onsolve","onprogress","onreset","onerror","data-cap-worker-count","data-cap-i18n-initial-state"]}constructor(){super(),this.#o&&this.#o.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#o=new Map,this.boundHandleProgress=this.handleProgress.bind(this),this.boundHandleSolve=this.handleSolve.bind(this),this.boundHandleError=this.handleError.bind(this),this.boundHandleReset=this.handleReset.bind(this)}initialize(){this.#e=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"}))}attributeChangedCallback(e,t,r){if(e.startsWith("on")){const t=e.slice(2),n=this.#o.get(e);if(n&&this.removeEventListener(t,n),r){const r=t=>{const r=this.getAttribute(e);"function"==typeof window[r]&&window[r].call(this,t)};this.#o.set(e,r),this.addEventListener(t,r)}}"data-cap-worker-count"===e&&this.setWorkersCount(parseInt(r,10)),"data-cap-i18n-initial-state"===e&&this.#i&&this.#i?.querySelector(".label.active")&&this.animateLabel(this.getI18nText("initial-state","Verify you're human"))}async connectedCallback(){this.#a=this,this.#n=this.attachShadow({mode:"open"}),this.#i=document.createElement("div"),this.createUI(),this.addEventListeners(),this.initialize(),this.#i.removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count"),t=e?parseInt(e,10):null;this.setWorkersCount(t||navigator.hardwareConcurrency||8);const r=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.#a.innerHTML=`<input type="hidden" name="${r}">`}async solve(){if(!this.#s)try{this.#s=!0,this.updateUI("verifying",this.getI18nText("verifying-label","Verifying..."),!0),this.#i.setAttribute("aria-label",this.getI18nText("verifying-aria-label","Verifying you're a human, please wait")),this.dispatchEvent("progress",{progress:0});try{let e=this.getAttribute("data-cap-api-endpoint");if(!e&&window?.CAP_CUSTOM_FETCH)e="/";else if(!e)throw new Error("Missing API endpoint. Either custom fetch or an API endpoint must be provided.");e.endsWith("/")||(e+="/");const n=await t(`${e}challenge`,{method:"POST"});let i;try{i=await n.json()}catch(e){throw new Error("Failed to parse challenge response from server")}if(i.error)throw new Error(i.error);const{challenge:a,token:s}=i;let o=a;if(!Array.isArray(o)){let e=0;o=Array.from({length:a.c},()=>(e+=1,[r(`${s}${e}`,a.s),r(`${s}${e}d`,a.d)]))}const l=i.instrumentation?async function(e){var t=(e=>{const t=atob(e),r=new Uint8Array(t.length);for(let e=0;e<t.length;e++)r[e]=t.charCodeAt(e);return r})(e);const r=await new Promise(function(e,r){try{var n=new DecompressionStream("deflate-raw"),i=n.writable.getWriter(),a=n.readable.getReader(),s=[];a.read().then(function t(n){if(n.done){for(var i=0,o=0,l=0;l<s.length;l++)i+=s[l].length;var c=new Uint8Array(i);for(l=0;l<s.length;l++)c.set(s[l],o),o+=s[l].length;e((new TextDecoder).decode(c))}else s.push(n.value),a.read().then(t).catch(r)}).catch(r),i.write(t).then(function(){i.close()}).catch(r)}catch(e){r(e)}});return new Promise(function(e){var t=setTimeout(function(){a(),e({__timeout:!0})},2e4),n=document.createElement("iframe");n.setAttribute("sandbox","allow-scripts"),n.setAttribute("aria-hidden","true"),n.style.cssText="position:absolute;width:1px;height:1px;top:-9999px;left:-9999px;border:none;opacity:0;pointer-events:none;";var i=!1;function a(){i||(i=!0,clearTimeout(t),window.removeEventListener("message",s),n.parentNode&&n.parentNode.removeChild(n))}function s(t){var r=t.data;r&&"object"==typeof r&&("cap:instr"===r.type?(a(),r.blocked?e({__blocked:!0,blockReason:r.blockReason||"automated_browser"}):r.result?e(r.result):e({__timeout:!0})):"cap:error"===r.type&&(a(),e({__timeout:!0})))}window.addEventListener("message",s),n.srcdoc='<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><script>'+r+"\n<\/script></body></html>",document.body.appendChild(n)})}(i.instrumentation):Promise.resolve(null),c=this.solveChallenges(o),d=l.then(e=>e&&e.__timeout?e:null),h=await Promise.race([d,c.then(()=>null)]);if(h&&h.__timeout){const e="Instrumentation timeout — please try again later";this.updateUIBlocked(this.getI18nText("error-label","Error"),!0),this.#i.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again")),this.removeEventListener("error",this.boundHandleError);const t=new CustomEvent("error",{bubbles:!0,composed:!0,detail:{isCap:!0,message:e}});return super.dispatchEvent(t),this.addEventListener("error",this.boundHandleError),this.executeAttributeCode("onerror",t),console.error("[cap]",e),void(this.#s=!1)}const[p,u]=await Promise.all([c,l]);if(u?.__timeout||u?.__blocked){this.updateUIBlocked(this.getI18nText("error-label","Error"),u&&u.__blocked),this.#i.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.#s=!1)}const b=await t(`${e}redeem`,{method:"POST",body:JSON.stringify({token:s,solutions:p,...u&&{instr:u}}),headers:{"Content-Type":"application/json"}});let g;try{g=await b.json()}catch{throw new Error("Failed to parse server response")}if(this.dispatchEvent("progress",{progress:100}),!g.success)throw new Error(g.error||"Invalid solution");const m=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${m}']`)&&(this.querySelector(`input[name='${m}']`).value=g.token),this.dispatchEvent("solve",{token:g.token}),this.token=g.token,this.#t&&clearTimeout(this.#t);const v=new Date(g.expires).getTime()-Date.now();return v>0&&v<864e5?this.#t=setTimeout(()=>this.reset(),v):this.error("Invalid expiration time"),this.#i.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),{success:!0,token:this.token}}catch(e){throw this.#i.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again")),this.error(e.message),e}}finally{this.#s=!1}}async solveChallenges(e){const t=e.length;let r=0,n=null;const a="object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate;if(a)try{n=await i()}catch(e){console.warn("[cap] wasm unavailable, falling back to JS solver:",e)}if(!a&&!this.#n.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.#n.insertBefore(e,this.#n.firstChild),setTimeout(()=>{e.style.marginBottom="calc(-1 * var(--cap-border-radius, 14px))",e.style.opacity=1},10)}const s=Array(this.#r).fill(null).map(()=>{try{return new Worker(this.#e)}catch(e){throw console.error("[cap] Failed to create worker:",e),new Error("Worker creation failed")}}),o=([e,i],a)=>new Promise((o,l)=>{const c=s[a];c?(c.onmessage=({data:e})=>{e.found&&(r++,this.dispatchEvent("progress",{progress:Math.round(r/t*100)}),o(e.nonce))},c.onerror=e=>{this.error(`Error in worker: ${e.message||e}`),l(e)},n?c.postMessage({salt:e,target:i,wasmModule:n},[]):c.postMessage({salt:e,target:i})):l(new Error("Worker not available"))}),l=[];try{for(let t=0;t<e.length;t+=this.#r){const r=e.slice(t,Math.min(t+this.#r,e.length)),n=await Promise.all(r.map((e,t)=>o(e,t)));l.push(...n)}}finally{s.forEach(e=>{if(e)try{e.terminate()}catch(e){console.error("[cap] error terminating worker:",e)}})}return l}setWorkersCount(e){const t=parseInt(e,10),r=Math.min(navigator.hardwareConcurrency||8,16);this.#r=!Number.isNaN(t)&&t>0&&t<=r?t:navigator.hardwareConcurrency||8}createUI(){this.#i.classList.add("captcha"),this.#i.setAttribute("role","button"),this.#i.setAttribute("tabindex","0"),this.#i.setAttribute("aria-label",this.getI18nText("verify-aria-label","Click to verify you're a human")),this.#i.setAttribute("aria-live","polite"),this.#i.setAttribute("disabled","true"),this.#i.innerHTML=`<div class="checkbox" part="checkbox"><svg class="progress-ring" viewBox="0 0 32 32"><circle class="progress-ring-bg" cx="16" cy="16" r="14"></circle><circle class="progress-ring-circle" cx="16" cy="16" r="14"></circle></svg></div><p part="label" class="label-wrapper"><span class="label active">${this.getI18nText("initial-state","Verify you're human")}</span></p><a part="attribution" aria-label="Secured by Cap" href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener" title="Secured by Cap: Self-hosted CAPTCHA for the modern web.">Cap</a>`,this.#n.innerHTML=`<style${window.CAP_CSS_NONCE?` nonce=${window.CAP_CSS_NONCE}`:""}>@media (prefers-reduced-motion:reduce){.captcha,.captcha *{transition:none!important;animation:none!important}.label{filter:none!important;opacity:1!important;transition:none!important;transform:none!important}.label:not(.active),.label.exit{display:none!important}}.captcha,.captcha *{box-sizing:border-box}.captcha{background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);-webkit-user-select:none;user-select:none;height:var(--cap-widget-height,58px);width:var(--cap-widget-width,260px);padding:var(--cap-widget-padding,14px);align-items:center;gap:var(--cap-gap,15px);cursor:pointer;-webkit-tap-highlight-color:#fff0;color:var(--cap-color,#212121);transition:filter .2s,transform .2s;display:flex;position:relative;overflow:hidden}.captcha:hover{filter:brightness(98%)}.captcha *{font-family:var(--cap-font,system, -apple-system, "BlinkMacSystemFont", ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", "Ubuntu", "arial", sans-serif)}.checkbox{width:var(--cap-checkbox-size,25px);height:var(--cap-checkbox-size,25px);border:var(--cap-checkbox-border,1px solid #aaaaaad1);border-radius:var(--cap-checkbox-border-radius,6px);background-color:var(--cap-checkbox-background,#fafafa91);margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px);transition:opacity .2s}.captcha p{-webkit-user-select:none;user-select:none;margin:0;font-size:15px;font-weight:500}.label-wrapper{flex:1;align-items:center;min-width:0;height:2em;display:flex;position:relative;overflow:hidden}.label-wrapper:before,.label-wrapper:after{content:"";pointer-events:none;z-index:1;width:100%;height:.6em;position:absolute;left:0}.label-wrapper:before{background:linear-gradient(to bottom, var(--cap-background,#fdfdfd), transparent);top:0}.label-wrapper:after{background:linear-gradient(to top, var(--cap-background,#fdfdfd), transparent);bottom:0}.label{white-space:nowrap;opacity:0;filter:blur(2px);transition:transform .5s cubic-bezier(.25,1,.5,1),opacity .5s,filter .5s;position:absolute;transform:translateY(100%)}.label.active{opacity:1;filter:none;transform:translateY(0)}.label.exit{opacity:0;filter:blur(2px);transform:translateY(-100%)}.checkbox .progress-ring{width:100%;height:100%;display:none;transform:rotate(-90deg)}.checkbox .progress-ring-bg{fill:none;stroke:var(--cap-spinner-background-color,#eee);stroke-width:var(--cap-spinner-thickness,3)}.checkbox .progress-ring-circle{fill:none;stroke:var(--cap-spinner-color,#000);stroke-width:var(--cap-spinner-thickness,3);stroke-linecap:round;stroke-dasharray:87.96;stroke-dashoffset:87.96px;transition:stroke-dashoffset .3s}.captcha[data-state=verifying] .checkbox{background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;display:flex;transform:scale(1.1)}.captcha[data-state=verifying] .checkbox .progress-ring{display:block}.captcha[data-state=done] .checkbox{background-image:var(--cap-checkmark,url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cstyle%3E%40keyframes%20anim%7B0%25%7Bstroke-dashoffset%3A23.21320343017578px%7Dto%7Bstroke-dashoffset%3A0%7D%7D%3C%2Fstyle%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%2300a67d%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m5%2012%205%205L20%207%22%20style%3D%22stroke-dashoffset%3A0%3Bstroke-dasharray%3A23.21320343017578px%3Banimation%3Aanim%20.5s%20ease%22%2F%3E%3C%2Fsvg%3E));background-size:cover;border:1px solid #0000}.captcha[data-state=done] .checkbox .progress-ring{display:none}.captcha[data-state=error] .checkbox{background-image:var(--cap-error-cross,url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 24 24'%3E%3Cpath fill='%23f55b50' d='M11 15h2v2h-2zm0-8h2v6h-2zm1-5C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m0 18a8 8 0 0 1-8-8a8 8 0 0 1 8-8a8 8 0 0 1 8 8a8 8 0 0 1-8 8'/%3E%3C%2Fsvg%3E"));background-size:cover;border:1px solid #0000}.captcha[data-state=error] .checkbox .progress-ring{display:none}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{color:var(--cap-color,#212121);opacity:.8;text-underline-offset:.18em;z-index:6;font-size:12px;text-decoration-thickness:.08em;position:absolute;bottom:10px;right:10px}.cap-troubleshoot-link{color:#06c;text-underline-offset:.15em;cursor:pointer;font-weight:500;font-size:inherit;text-decoration:underline;text-decoration-thickness:.08em}.cap-troubleshoot-link:hover{opacity:.8}</style>`,this.#n.appendChild(this.#i)}addEventListeners(){this.#i&&(this.#i.querySelector("a").addEventListener("click",e=>{e.stopPropagation(),e.preventDefault(),window.open("https://capjs.js.org","_blank")}),this.#i.addEventListener("click",()=>{this.#i.hasAttribute("disabled")||this.solve()}),this.#i.addEventListener("keydown",e=>{"Enter"!==e.key&&" "!==e.key||this.#i.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.#i)return;const t=this.#i.querySelector(".label-wrapper");if(!t)return;if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches){const r=t.querySelector(".label.active");if(r)r.textContent=e;else{const r=document.createElement("span");r.className="label active",r.textContent=e,t.appendChild(r)}return}const r=t.querySelector(".label.active"),n=document.createElement("span");n.className="label",n.textContent=e,t.appendChild(n),n.offsetWidth,n.classList.add("active"),r&&(r.classList.remove("active"),r.classList.add("exit"),r.addEventListener("transitionend",()=>r.remove(),{once:!0}))}updateUI(e,t,r=!1){this.#i&&(this.#i.setAttribute("data-state",e),this.animateLabel(t),r?this.#i.setAttribute("disabled","true"):this.#i.removeAttribute("disabled"))}updateUIBlocked(e,t=!1){if(!this.#i)return;this.#i.setAttribute("data-state","error"),this.#i.removeAttribute("disabled");const r=this.#i.querySelector(".label-wrapper");if(!r)return;const n=this.getAttribute("data-cap-troubleshooting-url")||"https://capjs.js.org/guide/troubleshooting/instrumentation.html",i=r.querySelector(".label.active"),a=document.createElement("span");a.className="label",a.innerHTML=t?`${e} · <a class="cap-troubleshoot-link" href="${n}" target="_blank" rel="noopener">${this.getI18nText("troubleshooting-label","Troubleshoot")}</a>`:e,r.appendChild(a),a.offsetWidth,a.classList.add("active"),i&&(i.classList.remove("active"),i.classList.add("exit"),i.addEventListener("transitionend",()=>i.remove(),{once:!0}));const s=a.querySelector(".cap-troubleshoot-link");s&&s.addEventListener("click",e=>{e.stopPropagation()})}handleProgress(e){if(!this.#i)return;const t=this.#i.querySelector(".progress-ring-circle");if(t){const r=2*Math.PI*14,n=r-e.detail.progress/100*r;t.style.strokeDashoffset=n}const r=this.#i.querySelector(".label-wrapper");if(r){const t=r.querySelector(".label.active");t&&(t.textContent=`${this.getI18nText("verifying-label","Verifying...")} ${e.detail.progress}%`)}this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done",this.getI18nText("solved-label","You're a human"),!0),this.executeAttributeCode("onsolve",e)}handleError(e){this.updateUI("error",this.getI18nText("error-label","Error. Try again.")),this.executeAttributeCode("onerror",e)}handleReset(e){this.updateUI("",this.getI18nText("initial-state","I'm a human")),this.executeAttributeCode("onreset",e)}executeAttributeCode(e,t){const r=this.getAttribute(e);r&&(console.error("[cap] using `onxxx='…'` is strongly discouraged and will be deprecated soon. please use `addEventListener` callbacks instead."),new Function("event",r).call(this,t))}error(e="Unknown error"){console.error("[cap]",e),this.dispatchEvent("error",{isCap:!0,message:e})}dispatchEvent(e,t={}){const r=new CustomEvent(e,{bubbles:!0,composed:!0,detail:t});super.dispatchEvent(r)}reset(){this.#t&&(clearTimeout(this.#t),this.#t=null),this.dispatchEvent("reset"),this.token=null;const e=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${e}']`)&&(this.querySelector(`input[name='${e}']`).value="")}get tokenValue(){return this.token}disconnectedCallback(){this.removeEventListener("progress",this.boundHandleProgress),this.removeEventListener("solve",this.boundHandleSolve),this.removeEventListener("error",this.boundHandleError),this.removeEventListener("reset",this.boundHandleReset),this.#o.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#o.clear(),this.#n&&(this.#n.innerHTML=""),this.reset(),this.cleanup()}cleanup(){this.#t&&(clearTimeout(this.#t),this.#t=null),this.#e&&(URL.revokeObjectURL(this.#e),this.#e="")}}class s{constructor(e={},t){const r=t||document.createElement("cap-widget");if(Object.entries(e).forEach(([e,t])=>{r.setAttribute(e,t)}),!e.apiEndpoint&&!window?.CAP_CUSTOM_FETCH)throw r.remove(),new Error("Missing API endpoint. Either custom fetch or an API endpoint must be provided.");e.apiEndpoint&&r.setAttribute("data-cap-api-endpoint",e.apiEndpoint),this.widget=r,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:()=>r.token,configurable:!0,enumerable:!0}),t||(r.style.display="none",document.documentElement.appendChild(r))}}window.Cap=s,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",a),"object"==typeof exports&&"undefined"!=typeof module?module.exports=s:"function"==typeof define&&define.amd&&define([],()=>s),"undefined"!=typeof exports&&(exports.default=s)})();
|
package/package.json
CHANGED
package/src/cap-floating.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
setTimeout(() => {
|
|
10
10
|
element.onclick = null;
|
|
11
|
-
handlers.forEach((h) => element.removeEventListener("click", h));
|
|
11
|
+
handlers.forEach((h) => { return element.removeEventListener("click", h) });
|
|
12
12
|
element.onclick = (e) => handleClick(e, element, capWidget, handlers);
|
|
13
13
|
}, 50);
|
|
14
14
|
};
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
|
|
99
99
|
if (handlers.length) {
|
|
100
100
|
element.onclick = null;
|
|
101
|
-
handlers.forEach((h) => element.removeEventListener("click", h));
|
|
101
|
+
handlers.forEach((h) => { return element.removeEventListener("click", h) });
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
element.addEventListener("click", (e) => {
|
package/src/cap.css
CHANGED
|
@@ -229,3 +229,16 @@
|
|
|
229
229
|
text-decoration-thickness: 0.08em;
|
|
230
230
|
z-index: 6;
|
|
231
231
|
}
|
|
232
|
+
|
|
233
|
+
.cap-troubleshoot-link {
|
|
234
|
+
color: #0066cc;
|
|
235
|
+
text-decoration: underline;
|
|
236
|
+
text-underline-offset: 0.15em;
|
|
237
|
+
text-decoration-thickness: 0.08em;
|
|
238
|
+
cursor: pointer;
|
|
239
|
+
font-weight: 500;
|
|
240
|
+
font-size: inherit;
|
|
241
|
+
}
|
|
242
|
+
.cap-troubleshoot-link:hover {
|
|
243
|
+
opacity: 0.8;
|
|
244
|
+
}
|
package/src/cap.js
CHANGED
|
@@ -54,6 +54,101 @@
|
|
|
54
54
|
return result.substring(0, length);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
async function runInstrumentationChallenge(instrBytes) {
|
|
58
|
+
const b64ToUint8 = (b64) => {
|
|
59
|
+
const bin = atob(b64);
|
|
60
|
+
const arr = new Uint8Array(bin.length);
|
|
61
|
+
for (let i = 0; i < bin.length; i++) arr[i] = bin.charCodeAt(i);
|
|
62
|
+
return arr;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
var compressed = b64ToUint8(instrBytes);
|
|
66
|
+
|
|
67
|
+
const scriptText = await new Promise(function (resolve, reject) {
|
|
68
|
+
try {
|
|
69
|
+
var ds = new DecompressionStream("deflate-raw");
|
|
70
|
+
var writer = ds.writable.getWriter();
|
|
71
|
+
var reader = ds.readable.getReader();
|
|
72
|
+
var chunks = [];
|
|
73
|
+
function pump(res) {
|
|
74
|
+
if (res.done) {
|
|
75
|
+
var len = 0,
|
|
76
|
+
off = 0;
|
|
77
|
+
for (var i = 0; i < chunks.length; i++) len += chunks[i].length;
|
|
78
|
+
var out = new Uint8Array(len);
|
|
79
|
+
for (var i = 0; i < chunks.length; i++) {
|
|
80
|
+
out.set(chunks[i], off);
|
|
81
|
+
off += chunks[i].length;
|
|
82
|
+
}
|
|
83
|
+
resolve(new TextDecoder().decode(out));
|
|
84
|
+
} else {
|
|
85
|
+
chunks.push(res.value);
|
|
86
|
+
reader.read().then(pump).catch(reject);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
reader.read().then(pump).catch(reject);
|
|
90
|
+
writer
|
|
91
|
+
.write(compressed)
|
|
92
|
+
.then(function () {
|
|
93
|
+
writer.close();
|
|
94
|
+
})
|
|
95
|
+
.catch(reject);
|
|
96
|
+
} catch (e) {
|
|
97
|
+
reject(e);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return new Promise(function (resolve) {
|
|
102
|
+
var timeout = setTimeout(function () {
|
|
103
|
+
cleanup();
|
|
104
|
+
resolve({ __timeout: true });
|
|
105
|
+
}, 20000);
|
|
106
|
+
|
|
107
|
+
var iframe = document.createElement("iframe");
|
|
108
|
+
iframe.setAttribute("sandbox", "allow-scripts");
|
|
109
|
+
iframe.setAttribute("aria-hidden", "true");
|
|
110
|
+
iframe.style.cssText =
|
|
111
|
+
"position:absolute;width:1px;height:1px;top:-9999px;left:-9999px;border:none;opacity:0;pointer-events:none;";
|
|
112
|
+
|
|
113
|
+
var resolved = false;
|
|
114
|
+
function cleanup() {
|
|
115
|
+
if (resolved) return;
|
|
116
|
+
resolved = true;
|
|
117
|
+
clearTimeout(timeout);
|
|
118
|
+
window.removeEventListener("message", handler);
|
|
119
|
+
if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function handler(ev) {
|
|
123
|
+
var d = ev.data;
|
|
124
|
+
if (!d || typeof d !== "object") return;
|
|
125
|
+
if (d.type === "cap:instr") {
|
|
126
|
+
cleanup();
|
|
127
|
+
if (d.blocked) {
|
|
128
|
+
resolve({ __blocked: true, blockReason: d.blockReason || "automated_browser" });
|
|
129
|
+
} else if (d.result) {
|
|
130
|
+
resolve(d.result);
|
|
131
|
+
} else {
|
|
132
|
+
resolve({ __timeout: true });
|
|
133
|
+
}
|
|
134
|
+
} else if (d.type === "cap:error") {
|
|
135
|
+
cleanup();
|
|
136
|
+
resolve({ __timeout: true });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
window.addEventListener("message", handler);
|
|
141
|
+
|
|
142
|
+
iframe.srcdoc =
|
|
143
|
+
'<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><script>' +
|
|
144
|
+
scriptText +
|
|
145
|
+
"\n</scr" +
|
|
146
|
+
"ipt></body></html>";
|
|
147
|
+
|
|
148
|
+
document.body.appendChild(iframe);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
57
152
|
let wasmModulePromise = null;
|
|
58
153
|
|
|
59
154
|
const getWasmModule = () => {
|
|
@@ -214,11 +309,22 @@
|
|
|
214
309
|
apiEndpoint += "/";
|
|
215
310
|
}
|
|
216
311
|
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
312
|
+
const challengeRaw = await capFetch(`${apiEndpoint}challenge`, {
|
|
313
|
+
method: "POST",
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
let challengeResp;
|
|
317
|
+
try {
|
|
318
|
+
challengeResp = await challengeRaw.json();
|
|
319
|
+
} catch (parseErr) {
|
|
320
|
+
throw new Error("Failed to parse challenge response from server");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (challengeResp.error) {
|
|
324
|
+
throw new Error(challengeResp.error);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const { challenge, token } = challengeResp;
|
|
222
328
|
|
|
223
329
|
let challenges = challenge;
|
|
224
330
|
|
|
@@ -232,19 +338,89 @@
|
|
|
232
338
|
});
|
|
233
339
|
}
|
|
234
340
|
|
|
235
|
-
const
|
|
341
|
+
const instrPromise = challengeResp.instrumentation
|
|
342
|
+
? runInstrumentationChallenge(challengeResp.instrumentation)
|
|
343
|
+
: Promise.resolve(null);
|
|
236
344
|
|
|
237
|
-
const
|
|
238
|
-
await capFetch(`${apiEndpoint}redeem`, {
|
|
239
|
-
method: "POST",
|
|
240
|
-
body: JSON.stringify({ token, solutions }),
|
|
241
|
-
headers: { "Content-Type": "application/json" },
|
|
242
|
-
})
|
|
243
|
-
).json();
|
|
345
|
+
const powPromise = this.solveChallenges(challenges);
|
|
244
346
|
|
|
245
|
-
|
|
347
|
+
const instrErrorPromise = instrPromise.then((result) => {
|
|
348
|
+
if (result && result.__timeout) return result;
|
|
349
|
+
return null;
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const instrEarlyError = await Promise.race([
|
|
353
|
+
instrErrorPromise,
|
|
354
|
+
powPromise.then(() => null),
|
|
355
|
+
]);
|
|
356
|
+
|
|
357
|
+
if (instrEarlyError && instrEarlyError.__timeout) {
|
|
358
|
+
const errMsg = "Instrumentation timeout — please try again later";
|
|
359
|
+
this.updateUIBlocked(this.getI18nText("error-label", "Error"), true);
|
|
360
|
+
this.#div.setAttribute(
|
|
361
|
+
"aria-label",
|
|
362
|
+
this.getI18nText("error-aria-label", "An error occurred, please try again"),
|
|
363
|
+
);
|
|
364
|
+
this.removeEventListener("error", this.boundHandleError);
|
|
365
|
+
const errEvent = new CustomEvent("error", {
|
|
366
|
+
bubbles: true,
|
|
367
|
+
composed: true,
|
|
368
|
+
detail: { isCap: true, message: errMsg },
|
|
369
|
+
});
|
|
370
|
+
super.dispatchEvent(errEvent);
|
|
371
|
+
this.addEventListener("error", this.boundHandleError);
|
|
372
|
+
this.executeAttributeCode("onerror", errEvent);
|
|
373
|
+
console.error("[cap]", errMsg);
|
|
374
|
+
this.#solving = false;
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const [solutions, instrOut] = await Promise.all([powPromise, instrPromise]);
|
|
379
|
+
|
|
380
|
+
if (instrOut?.__timeout || instrOut?.__blocked) {
|
|
381
|
+
this.updateUIBlocked(
|
|
382
|
+
this.getI18nText("error-label", "Error"),
|
|
383
|
+
instrOut && instrOut.__blocked,
|
|
384
|
+
);
|
|
385
|
+
this.#div.setAttribute(
|
|
386
|
+
"aria-label",
|
|
387
|
+
this.getI18nText("error-aria-label", "An error occurred, please try again"),
|
|
388
|
+
);
|
|
246
389
|
|
|
247
|
-
|
|
390
|
+
this.removeEventListener("error", this.boundHandleError);
|
|
391
|
+
const errEvent = new CustomEvent("error", {
|
|
392
|
+
bubbles: true,
|
|
393
|
+
composed: true,
|
|
394
|
+
detail: { isCap: true, message: "Instrumentation failed" },
|
|
395
|
+
});
|
|
396
|
+
super.dispatchEvent(errEvent);
|
|
397
|
+
this.addEventListener("error", this.boundHandleError);
|
|
398
|
+
|
|
399
|
+
this.executeAttributeCode("onerror", errEvent);
|
|
400
|
+
console.error("[cap]", "Instrumentation failed");
|
|
401
|
+
this.#solving = false;
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const redeemResponse = await capFetch(`${apiEndpoint}redeem`, {
|
|
406
|
+
method: "POST",
|
|
407
|
+
body: JSON.stringify({
|
|
408
|
+
token,
|
|
409
|
+
solutions,
|
|
410
|
+
...(instrOut && { instr: instrOut }),
|
|
411
|
+
}),
|
|
412
|
+
headers: { "Content-Type": "application/json" },
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
let resp;
|
|
416
|
+
try {
|
|
417
|
+
resp = await redeemResponse.json();
|
|
418
|
+
} catch {
|
|
419
|
+
throw new Error("Failed to parse server response");
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
this.dispatchEvent("progress", { progress: 100 });
|
|
423
|
+
if (!resp.success) throw new Error(resp.error || "Invalid solution");
|
|
248
424
|
const fieldName = this.getAttribute("data-cap-hidden-field-name") || "cap-token";
|
|
249
425
|
if (this.querySelector(`input[name='${fieldName}']`)) {
|
|
250
426
|
this.querySelector(`input[name='${fieldName}']`).value = resp.token;
|
|
@@ -406,9 +582,7 @@
|
|
|
406
582
|
"Verify you're human",
|
|
407
583
|
)}</span></p><a part="attribution" aria-label="Secured by Cap" href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener" title="Secured by Cap: Self-hosted CAPTCHA for the modern web.">Cap</a>`;
|
|
408
584
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
this.#shadow.innerHTML = `<style${window.CAP_CSS_NONCE ? ` nonce=${window.CAP_CSS_NONCE}` : ""}>${css}</style>`;
|
|
585
|
+
this.#shadow.innerHTML = `<style${window.CAP_CSS_NONCE ? ` nonce=${window.CAP_CSS_NONCE}` : ""}>%%capCSS%%</style>`;
|
|
412
586
|
|
|
413
587
|
this.#shadow.appendChild(this.#div);
|
|
414
588
|
}
|
|
@@ -489,6 +663,43 @@
|
|
|
489
663
|
}
|
|
490
664
|
}
|
|
491
665
|
|
|
666
|
+
updateUIBlocked(label, showTroubleshooting = false) {
|
|
667
|
+
if (!this.#div) return;
|
|
668
|
+
|
|
669
|
+
this.#div.setAttribute("data-state", "error");
|
|
670
|
+
this.#div.removeAttribute("disabled");
|
|
671
|
+
|
|
672
|
+
const wrapper = this.#div.querySelector(".label-wrapper");
|
|
673
|
+
if (!wrapper) return;
|
|
674
|
+
|
|
675
|
+
const troubleshootingUrl =
|
|
676
|
+
this.getAttribute("data-cap-troubleshooting-url") ||
|
|
677
|
+
"https://capjs.js.org/guide/troubleshooting/instrumentation.html";
|
|
678
|
+
|
|
679
|
+
const current = wrapper.querySelector(".label.active");
|
|
680
|
+
const next = document.createElement("span");
|
|
681
|
+
next.className = "label";
|
|
682
|
+
next.innerHTML = showTroubleshooting
|
|
683
|
+
? `${label} · <a class="cap-troubleshoot-link" href="${troubleshootingUrl}" target="_blank" rel="noopener">${this.getI18nText("troubleshooting-label", "Troubleshoot")}</a>`
|
|
684
|
+
: label;
|
|
685
|
+
wrapper.appendChild(next);
|
|
686
|
+
|
|
687
|
+
void next.offsetWidth;
|
|
688
|
+
next.classList.add("active");
|
|
689
|
+
if (current) {
|
|
690
|
+
current.classList.remove("active");
|
|
691
|
+
current.classList.add("exit");
|
|
692
|
+
current.addEventListener("transitionend", () => current.remove(), { once: true });
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
const link = next.querySelector(".cap-troubleshoot-link");
|
|
696
|
+
if (link) {
|
|
697
|
+
link.addEventListener("click", (e) => {
|
|
698
|
+
e.stopPropagation();
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
492
703
|
handleProgress(event) {
|
|
493
704
|
if (!this.#div) return;
|
|
494
705
|
|
|
@@ -531,6 +742,8 @@
|
|
|
531
742
|
if (!code) {
|
|
532
743
|
return;
|
|
533
744
|
}
|
|
745
|
+
|
|
746
|
+
console.error("[cap] using `onxxx='…'` is strongly discouraged and will be deprecated soon. please use `addEventListener` callbacks instead.");
|
|
534
747
|
|
|
535
748
|
new Function("event", code).call(this, event);
|
|
536
749
|
}
|
package/src/worker.js
CHANGED
|
@@ -4,11 +4,18 @@
|
|
|
4
4
|
const batchSize = 50000;
|
|
5
5
|
const encoder = new TextEncoder();
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
const targetBits = target.length * 4;
|
|
8
|
+
const fullBytes = Math.floor(targetBits / 8);
|
|
9
|
+
const remainingBits = targetBits % 8;
|
|
10
|
+
|
|
11
|
+
const paddedTarget = target.length % 2 === 0 ? target : target + "0";
|
|
12
|
+
const targetBytesLength = paddedTarget.length / 2;
|
|
13
|
+
const targetBytes = new Uint8Array(targetBytesLength);
|
|
14
|
+
for (let k = 0; k < targetBytesLength; k++) {
|
|
15
|
+
targetBytes[k] = parseInt(paddedTarget.substring(k * 2, k * 2 + 2), 16);
|
|
10
16
|
}
|
|
11
|
-
|
|
17
|
+
|
|
18
|
+
const partialMask = remainingBits > 0 ? (0xff << (8 - remainingBits)) & 0xff : 0;
|
|
12
19
|
|
|
13
20
|
while (true) {
|
|
14
21
|
try {
|
|
@@ -18,16 +25,23 @@
|
|
|
18
25
|
|
|
19
26
|
const hashBuffer = await crypto.subtle.digest("SHA-256", inputBytes);
|
|
20
27
|
|
|
21
|
-
const hashBytes = new Uint8Array(hashBuffer
|
|
28
|
+
const hashBytes = new Uint8Array(hashBuffer);
|
|
22
29
|
|
|
23
30
|
let matches = true;
|
|
24
|
-
|
|
31
|
+
|
|
32
|
+
for (let k = 0; k < fullBytes; k++) {
|
|
25
33
|
if (hashBytes[k] !== targetBytes[k]) {
|
|
26
34
|
matches = false;
|
|
27
35
|
break;
|
|
28
36
|
}
|
|
29
37
|
}
|
|
30
38
|
|
|
39
|
+
if (matches && remainingBits > 0) {
|
|
40
|
+
if ((hashBytes[fullBytes] & partialMask) !== (targetBytes[fullBytes] & partialMask)) {
|
|
41
|
+
matches = false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
31
45
|
if (matches) {
|
|
32
46
|
self.postMessage({ nonce, found: true });
|
|
33
47
|
return;
|
|
@@ -123,7 +137,7 @@
|
|
|
123
137
|
|
|
124
138
|
const instance = new WebAssembly.Instance(wasmModule, imports);
|
|
125
139
|
wasm = instance.exports;
|
|
126
|
-
|
|
140
|
+
|
|
127
141
|
if (wasm.__wbindgen_start) wasm.__wbindgen_start();
|
|
128
142
|
|
|
129
143
|
solve_pow_function = (salt, target) => {
|