@cap.js/widget 0.1.17 → 0.1.19
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 +2 -6
- package/src/cap-floating.js +1 -1
- package/src/cap.js +31 -149
- package/src/worker.js +109 -0
package/cap-floating.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{const t=(e,o,n,i)=>{o.onclick=null;const s=parseInt(o.getAttribute("data-cap-floating-offset"))||8,
|
|
1
|
+
(()=>{const t=(e,o,n,i)=>{o.onclick=null;const s=parseInt(o.getAttribute("data-cap-floating-offset"))||8,a=o.getAttribute("data-cap-floating-position")||"top",l=o.getBoundingClientRect();Object.assign(n.style,{display:"block",position:"absolute",zIndex:"99999"});const c=l.left+(l.width-n.offsetWidth)/2,r=Math.min(c,window.innerWidth-n.offsetWidth);n.style.top="top"===a?`${Math.max(window.scrollY,l.top-n.offsetHeight-s+window.scrollY)}px`:`${Math.min(l.bottom+s+window.scrollY,window.innerHeight-n.offsetHeight+window.scrollY)}px`,n.style.left=`${Math.max(r,2)}px`,n.solve(),n.addEventListener("solve",(({detail:s})=>{o.setAttribute("data-cap-token",s.token),o.setAttribute("data-cap-progress","done"),setTimeout((()=>{i.forEach((t=>{o.addEventListener("click",t),t.call(o,e)})),setTimeout((()=>{o.onclick=null,i.forEach((t=>o.removeEventListener("click",t))),o.onclick=e=>t(e,o,n,i)}),50)}),500),setTimeout((()=>{n.style.display="none"}),700)}))},e=e=>{const o=e.getAttribute("data-cap-floating");if(!o)return;const n=document.querySelector(o);if(!document.contains(n)&&!n.solve)throw new Error(`[cap floating] "${o}" doesn't exist or isn't a Cap widget`);n.style.display="none";const i=[e.onclick].filter(Boolean);"function"==typeof getEventListeners&&i.push(...(getEventListeners(e).click||[]).map((t=>t.listener))),i.length&&(e.onclick=null,i.forEach((t=>e.removeEventListener("click",t)))),e.addEventListener("click",(o=>{o.stopImmediatePropagation(),o.preventDefault(),t(o,e,n,i)}))},o=t=>{e(t),t.querySelectorAll("[data-cap-floating]").forEach(e)};o(document.body),new MutationObserver((t=>t.forEach((t=>t.addedNodes.forEach((t=>{t.nodeType===Node.ELEMENT_NODE&&o(t)})))))).observe(document.body,{childList:!0,subtree:!0})})();
|
package/cap.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(){let e;const t="0.0.5",r=function(){return window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(...arguments):fetch(...arguments)};window.CAP_CUSTOM_WASM_URL||[`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${t}/browser/cap_wasm.min.js`,`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${t}/browser/cap_wasm_bg.wasm`].forEach((e=>{const t=document.createElement("link");t.rel="prefetch",t.href=e,t.as=e.endsWith(".wasm")?"fetch":"script",document.head.appendChild(t)}));class s extends HTMLElement{#e="";#t=null;#r=navigator.hardwareConcurrency||8;token=null;#s;#i;#n;#a=!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","[cap]"]}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([e],{type:"application/javascript"}))}attributeChangedCallback(e,t,r){if(e.startsWith("on")){const t=e.slice(2),s=this.#o.get(e);if(s&&this.removeEventListener(t,s),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)),"data-cap-i18n-initial-state"===e&&this.#i&&this.#i?.querySelector("p")?.innerText&&(this.#i.querySelector("p").innerText=this.getI18nText("initial-state","I'm a human"))}async connectedCallback(){this.#n=this,this.#s=this.attachShadow({mode:"open"}),this.#i=document.createElement("div"),this.createUI(),this.addEventListeners(),await 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.#n.innerHTML=`<input type="hidden" name="${r}">`}async solve(){if(!this.#a)try{this.#a=!0,this.updateUI("verifying",this.getI18nText("verifying-label","Verifying..."),!0),this.dispatchEvent("progress",{progress:0});try{const e=this.getAttribute("data-cap-api-endpoint");if(!e)throw new Error("Missing API endpoint");const{challenge:t,token:s}=await(await r(`${e}challenge`,{method:"POST"})).json(),i=await this.solveChallenges(t),n=await(await r(`${e}redeem`,{method:"POST",body:JSON.stringify({token:s,solutions:i}),headers:{"Content-Type":"application/json"}})).json();if(this.dispatchEvent("progress",{progress:100}),!n.success)throw new Error("Invalid solution");const a=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${a}']`)&&(this.querySelector(`input[name='${a}']`).value=n.token),this.dispatchEvent("solve",{token:n.token}),this.token=n.token,this.#t&&clearTimeout(this.#t);const o=new Date(n.expires).getTime()-Date.now();return o>0&&o<864e5?this.#t=setTimeout((()=>this.reset()),o):this.error("Invalid expiration time"),{success:!0,token:this.token}}catch(e){throw this.error(e.message),e}}finally{this.#a=!1}}async solveChallenges(e){const r=e.length;let s=0;const i=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")}})),n=([e,n],a)=>new Promise(((o,c)=>{const d=i[a];if(!d)return void c(new Error("Worker not available"));const h=setTimeout((()=>{try{d.terminate(),i[a]=new Worker(this.#e)}catch(e){console.error("[cap] Error terminating/recreating worker:",e)}c(new Error("Worker timeout"))}),3e4);d.onmessage=({data:t})=>{t.found&&(clearTimeout(h),s++,this.dispatchEvent("progress",{progress:Math.round(s/r*100)}),o([e,n,t.nonce]))},d.onerror=e=>{clearTimeout(h),this.error(`Error in worker: ${e.message||e}`),c(e)},d.postMessage({salt:e,target:n,wasmUrl:window.CAP_CUSTOM_WASM_URL||`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${t}/browser/cap_wasm.min.js`})})),a=[];try{for(let t=0;t<e.length;t+=this.#r){const r=e.slice(t,Math.min(t+this.#r,e.length)),s=await Promise.all(r.map(((e,t)=>n(e,t))));a.push(...s)}}finally{i.forEach((e=>{if(e)try{e.terminate()}catch(e){console.error("[cap] Error terminating worker:",e)}}))}return a}setWorkersCount(e){const t=parseInt(e,10),r=Math.min(navigator.hardwareConcurrency||8,16);this.#r=!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("disabled","true"),this.#i.innerHTML=`<div class="checkbox"></div><p>${this.getI18nText("initial-state","I'm a human")}</p><a href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener"><span>Secured by </span>Cap</a>`,this.#s.innerHTML='<style>\n\n.captcha * {box-sizing:border-box;}\n\n.captcha{background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);\nuser-select:none;\n\nheight:var(--cap-widget-height, 30px);\n\nwidth:var(--cap-widget-width, 230px);display:flex;align-items:center;padding:var(--cap-widget-padding,14px);gap:var(--cap-gap,15px);cursor:pointer;transition:filter .2s,transform .2s;position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color,#212121)}.captcha:hover{filter:brightness(98%)}\n\n.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);transition:opacity .2s;margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px)}.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)}\n\n.captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity .2s}.captcha[data-state=verifying] .checkbox{background: none;display:flex;align-items:center;justify-content:center;transform: scale(1.1);border: none;border-radius: 50%;background: conic-gradient(var(--cap-spinner-color,#000) 0%, var(--cap-spinner-color,#000) var(--progress, 0%), var(--cap-spinner-background-color,#eee) var(--progress, 0%), var(--cap-spinner-background-color,#eee) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background,#fdfdfd);width: calc(100% - var(--cap-spinner-thickness,5px));height: calc(100% - var(--cap-spinner-thickness,5px));border-radius: 50%;margin:calc(var(--cap-spinner-thickness,5px) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;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}.captcha[data-state=error] .checkbox{border:1px solid transparent;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/svg%3E"));background-size:cover}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{position:absolute;bottom:10px;right:10px;font-size:var(--cap-credits-font-size,12px);color:var(--cap-color,#212121);opacity:var(--cap-opacity-hover,0.8)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>',this.#s.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(),this.solve())})),this.addEventListener("progress",this.boundHandleProgress),this.addEventListener("solve",this.boundHandleSolve),this.addEventListener("error",this.boundHandleError),this.addEventListener("reset",this.boundHandleReset))}updateUI(e,t,r=!1){this.#i&&(this.#i.setAttribute("data-state",e),this.#i.querySelector("p").innerText=t,r?this.#i.setAttribute("disabled","true"):this.#i.removeAttribute("disabled"))}handleProgress(e){if(!this.#i)return;const t=this.#i.querySelector("p"),r=this.#i.querySelector(".checkbox");t&&r&&(r.style.setProperty("--progress",`${e.detail.progress}%`),t.innerText=`${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);if(r)try{new Function("event",r).call(this,t)}catch(t){console.error(`[cap] Error executing ${e}:`,t)}}error(e="Unknown error"){console.error("[cap] Error:",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.#s&&(this.#s.innerHTML=""),this.reset(),this.cleanup()}cleanup(){this.#t&&(clearTimeout(this.#t),this.#t=null),this.#e&&(URL.revokeObjectURL(this.#e),this.#e="")}}class i{constructor(e={},t){let r=t||document.createElement("cap-widget");if(Object.entries(e).forEach((([e,t])=>{r.setAttribute(e,t)})),!e.apiEndpoint)throw r.remove(),new Error("Missing API endpoint");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))}}e=`(() => {${function(){if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly?.instantiate)return self.onmessage=async({data:{salt:e,target:t}})=>{let r=0;let s=0;const i=new TextEncoder,n=new Uint8Array(t.length/2);for(let e=0;e<n.length;e++)n[e]=parseInt(t.substring(2*e,2*e+2),16);const a=n.length;for(;;)try{for(let t=0;t<5e4;t++){const t=e+r,s=i.encode(t),o=await crypto.subtle.digest("SHA-256",s),c=new Uint8Array(o,0,a);let d=!0;for(let e=0;e<a;e++)if(c[e]!==n[e]){d=!1;break}if(d)return void self.postMessage({nonce:r,found:!0});r++}s+=5e4}catch(e){return console.error("[cap] fallback worker error",e),void self.postMessage({found:!1,error:e.message})}},console.warn("[cap] WebAssembly is not supported, falling back to alternative solver.");let e,t;self.onmessage=async({data:{salt:r,target:s,wasmUrl:i}})=>{e!==i&&(e=i,await import(i).then((e=>e.default().then((r=>{t=(r&&r.exports?r.exports:e).solve_pow})))).catch((e=>{console.error("[cap] using fallback solver due to error:",e)})));try{const e=performance.now(),i=t(r,s),n=performance.now();self.postMessage({nonce:Number(i),found:!0,durationMs:(n-e).toFixed(2)})}catch(e){console.error("[cap] solver error",e),self.postMessage({found:!1,error:e.message||String(e)})}},self.onerror=e=>{self.postMessage({found:!1,error:`Worker error: ${e.message||e}`})}}.toString().replace(/^function\s*\([^\)]*\)\s*{|\}$/g,"").trim()}})()`,window.Cap=i,customElements.get("cap-widget")?console.warn("The cap-widget element has already been defined. Skipping re-defining it."):customElements.define("cap-widget",s),"object"==typeof exports&&"undefined"!=typeof module?module.exports=i:"function"==typeof define&&define.amd&&define([],(function(){return i})),"undefined"!=typeof exports&&(exports.default=i)}();
|
|
1
|
+
!function(){const e="0.0.5",t=function(){return window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(...arguments):fetch(...arguments)};window.CAP_CUSTOM_WASM_URL||[`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${e}/browser/cap_wasm.min.js`,`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${e}/browser/cap_wasm_bg.wasm`].forEach((e=>{const t=document.createElement("link");t.rel="prefetch",t.href=e,t.as=e.endsWith(".wasm")?"fetch":"script",document.head.appendChild(t)}));class r extends HTMLElement{#e="";#t=null;#r=navigator.hardwareConcurrency||8;token=null;#s;#i;#a;#n=!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","[cap]"]}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(['(()=>{if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly?.instantiate)return self.onmessage=async({data:{salt:e,target:r}})=>{let o=0;let t=0;const s=new TextEncoder,n=new Uint8Array(r.length/2);for(let e=0;e<n.length;e++)n[e]=parseInt(r.substring(2*e,2*e+2),16);const a=n.length;for(;;)try{for(let r=0;r<5e4;r++){const r=e+o,t=s.encode(r),l=await crypto.subtle.digest("SHA-256",t),c=new Uint8Array(l,0,a);let f=!0;for(let e=0;e<a;e++)if(c[e]!==n[e]){f=!1;break}if(f)return void self.postMessage({nonce:o,found:!0});o++}t+=5e4}catch(e){return console.error("[cap worker] fallback worker error",e),void self.postMessage({found:!1,error:e.message})}},console.warn("[cap worker] wasm not supported, falling back to alternative solver. this will be significantly slower.");let e,r;self.onmessage=async({data:{salt:o,target:t,wasmUrl:s}})=>{e!==s&&(e=s,await import(s).then((e=>e.default().then((o=>{r=(o&&o.exports?o.exports:e).solve_pow})))).catch((e=>{console.error("[cap worker] using fallback solver due to error:",e)})));try{const e=performance.now(),s=r(o,t),n=performance.now();self.postMessage({nonce:Number(s),found:!0,durationMs:(n-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),s=this.#o.get(e);if(s&&this.removeEventListener(t,s),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)),"data-cap-i18n-initial-state"===e&&this.#i&&this.#i?.querySelector("p")?.innerText&&(this.#i.querySelector("p").innerText=this.getI18nText("initial-state","I'm a human"))}async connectedCallback(){this.#a=this,this.#s=this.attachShadow({mode:"open"}),this.#i=document.createElement("div"),this.createUI(),this.addEventListeners(),await 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.#n)try{this.#n=!0,this.updateUI("verifying",this.getI18nText("verifying-label","Verifying..."),!0),this.#i.setAttribute("aria-label","Verifying you're a human, please wait"),this.dispatchEvent("progress",{progress:0});try{const e=this.getAttribute("data-cap-api-endpoint");if(!e)throw new Error("Missing API endpoint");const{challenge:r,token:s}=await(await t(`${e}challenge`,{method:"POST"})).json(),i=await this.solveChallenges(r),a=await(await t(`${e}redeem`,{method:"POST",body:JSON.stringify({token:s,solutions:i}),headers:{"Content-Type":"application/json"}})).json();if(this.dispatchEvent("progress",{progress:100}),!a.success)throw new Error("Invalid solution");const n=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${n}']`)&&(this.querySelector(`input[name='${n}']`).value=a.token),this.dispatchEvent("solve",{token:a.token}),this.token=a.token,this.#t&&clearTimeout(this.#t);const o=new Date(a.expires).getTime()-Date.now();return o>0&&o<864e5?this.#t=setTimeout((()=>this.reset()),o):this.error("Invalid expiration time"),this.#i.setAttribute("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","An error occurred, please try again"),this.error(e.message),e}}finally{this.#n=!1}}async solveChallenges(t){const r=t.length;let s=0;const i=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")}})),a=([t,a],n)=>new Promise(((o,c)=>{const d=i[n];if(!d)return void c(new Error("Worker not available"));const l=setTimeout((()=>{try{d.terminate(),i[n]=new Worker(this.#e)}catch(e){console.error("[cap] error terminating/recreating worker:",e)}c(new Error("Worker timeout"))}),3e4);d.onmessage=({data:e})=>{e.found&&(clearTimeout(l),s++,this.dispatchEvent("progress",{progress:Math.round(s/r*100)}),o([t,a,e.nonce]))},d.onerror=e=>{clearTimeout(l),this.error(`Error in worker: ${e.message||e}`),c(e)},d.postMessage({salt:t,target:a,wasmUrl:window.CAP_CUSTOM_WASM_URL||`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${e}/browser/cap_wasm.min.js`})})),n=[];try{for(let e=0;e<t.length;e+=this.#r){const r=t.slice(e,Math.min(e+this.#r,t.length)),s=await Promise.all(r.map(((e,t)=>a(e,t))));n.push(...s)}}finally{i.forEach((e=>{if(e)try{e.terminate()}catch(e){console.error("[cap] error terminating worker:",e)}}))}return n}setWorkersCount(e){const t=parseInt(e,10),r=Math.min(navigator.hardwareConcurrency||8,16);this.#r=!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","Click to verify you're a human"),this.#i.setAttribute("aria-live","polite"),this.#i.setAttribute("disabled","true"),this.#i.innerHTML=`<div class="checkbox"></div><p>${this.getI18nText("initial-state","I'm a human")}</p><a aria-label="Secured by Cap" href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener"><span>Secured by </span>Cap</a>`,this.#s.innerHTML='<style>.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);\nuser-select:none;height:var(--cap-widget-height, 30px);width:var(--cap-widget-width, 230px);display:flex;align-items:center;padding:var(--cap-widget-padding,14px);gap:var(--cap-gap,15px);cursor:pointer;transition:filter .2s,transform .2s;position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color,#212121)}.captcha:hover{filter:brightness(98%)}.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);transition:opacity .2s;margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px)}.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)}.captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity .2s}.captcha[data-state=verifying]\n.checkbox{background: none;display:flex;align-items:center;justify-content:center;transform: scale(1.1);border: none;border-radius: 50%;background: conic-gradient(var(--cap-spinner-color,#000) 0%, var(--cap-spinner-color,#000) var(--progress, 0%), var(--cap-spinner-background-color,#eee) var(--progress, 0%), var(--cap-spinner-background-color,#eee) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background,#fdfdfd);width: calc(100% - var(--cap-spinner-thickness,5px));height: calc(100% - var(--cap-spinner-thickness,5px));border-radius: 50%;margin:calc(var(--cap-spinner-thickness,5px) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;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}.captcha[data-state=error] .checkbox{border:1px solid transparent;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/svg%3E"));background-size:cover}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{position:absolute;bottom:10px;right:10px;font-size:var(--cap-credits-font-size,12px);color:var(--cap-color,#212121);opacity:var(--cap-opacity-hover,0.8)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>',this.#s.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(),this.solve())})),this.addEventListener("progress",this.boundHandleProgress),this.addEventListener("solve",this.boundHandleSolve),this.addEventListener("error",this.boundHandleError),this.addEventListener("reset",this.boundHandleReset))}updateUI(e,t,r=!1){this.#i&&(this.#i.setAttribute("data-state",e),this.#i.querySelector("p").innerText=t,r?this.#i.setAttribute("disabled","true"):this.#i.removeAttribute("disabled"))}handleProgress(e){if(!this.#i)return;const t=this.#i.querySelector("p"),r=this.#i.querySelector(".checkbox");t&&r&&(r.style.setProperty("--progress",`${e.detail.progress}%`),t.innerText=`${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.#s&&(this.#s.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){let r=t||document.createElement("cap-widget");if(Object.entries(e).forEach((([e,t])=>{r.setAttribute(e,t)})),!e.apiEndpoint)throw r.remove(),new Error("Missing API endpoint");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?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",r),"object"==typeof exports&&"undefined"!=typeof module?module.exports=s:"function"==typeof define&&define.amd&&define([],(function(){return s})),"undefined"!=typeof exports&&(exports.default=s)}();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap.js/widget",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
4
4
|
"description": "Client-side widget for Cap, a lightweight, modern open-source CAPTCHA alternative designed using SHA-256 PoW.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"security",
|
|
@@ -51,9 +51,5 @@
|
|
|
51
51
|
"license": "Apache-2.0",
|
|
52
52
|
"author": "Tiago Rangel",
|
|
53
53
|
"type": "commonjs",
|
|
54
|
-
"main": "cap.min.js"
|
|
55
|
-
"scripts": {
|
|
56
|
-
"test": "echo \"Error: no test specified\" && exit 1",
|
|
57
|
-
"npm:publish": "sudo npm publish --access public"
|
|
58
|
-
}
|
|
54
|
+
"main": "cap.min.js"
|
|
59
55
|
}
|
package/src/cap-floating.js
CHANGED
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
|
|
64
64
|
const capWidget = document.querySelector(capWidgetSelector);
|
|
65
65
|
if (!document.contains(capWidget) && !capWidget.solve) {
|
|
66
|
-
throw new Error(`[
|
|
66
|
+
throw new Error(`[cap floating] "${capWidgetSelector}" doesn't exist or isn't a Cap widget`);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
capWidget.style.display = "none";
|
package/src/cap.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
(function () {
|
|
2
|
-
let workerScript;
|
|
3
|
-
|
|
4
2
|
const WASM_VERSION = "0.0.5";
|
|
5
3
|
|
|
6
4
|
const capFetch = function () {
|
|
@@ -68,7 +66,10 @@
|
|
|
68
66
|
|
|
69
67
|
initialize() {
|
|
70
68
|
this.#workerUrl = URL.createObjectURL(
|
|
71
|
-
|
|
69
|
+
// MARK: worker injection
|
|
70
|
+
// this placeholder will be replaced with the actual worker by the build script
|
|
71
|
+
|
|
72
|
+
new Blob([`%%workerScript%%`], {
|
|
72
73
|
type: "application/javascript",
|
|
73
74
|
})
|
|
74
75
|
);
|
|
@@ -140,6 +141,11 @@
|
|
|
140
141
|
true
|
|
141
142
|
);
|
|
142
143
|
|
|
144
|
+
this.#div.setAttribute(
|
|
145
|
+
"aria-label",
|
|
146
|
+
"Verifying you're a human, please wait"
|
|
147
|
+
);
|
|
148
|
+
|
|
143
149
|
this.dispatchEvent("progress", { progress: 0 });
|
|
144
150
|
|
|
145
151
|
try {
|
|
@@ -181,8 +187,17 @@
|
|
|
181
187
|
this.error("Invalid expiration time");
|
|
182
188
|
}
|
|
183
189
|
|
|
190
|
+
this.#div.setAttribute(
|
|
191
|
+
"aria-label",
|
|
192
|
+
"We have verified you're a human, you may now continue"
|
|
193
|
+
);
|
|
194
|
+
|
|
184
195
|
return { success: true, token: this.token };
|
|
185
196
|
} catch (err) {
|
|
197
|
+
this.#div.setAttribute(
|
|
198
|
+
"aria-label",
|
|
199
|
+
"An error occurred, please try again"
|
|
200
|
+
);
|
|
186
201
|
this.error(err.message);
|
|
187
202
|
throw err;
|
|
188
203
|
}
|
|
@@ -220,7 +235,7 @@
|
|
|
220
235
|
workers[workerId] = new Worker(this.#workerUrl);
|
|
221
236
|
} catch (error) {
|
|
222
237
|
console.error(
|
|
223
|
-
"[cap]
|
|
238
|
+
"[cap] error terminating/recreating worker:",
|
|
224
239
|
error
|
|
225
240
|
);
|
|
226
241
|
}
|
|
@@ -270,7 +285,7 @@
|
|
|
270
285
|
try {
|
|
271
286
|
w.terminate();
|
|
272
287
|
} catch (error) {
|
|
273
|
-
console.error("[cap]
|
|
288
|
+
console.error("[cap] error terminating worker:", error);
|
|
274
289
|
}
|
|
275
290
|
}
|
|
276
291
|
});
|
|
@@ -294,25 +309,17 @@
|
|
|
294
309
|
this.#div.classList.add("captcha");
|
|
295
310
|
this.#div.setAttribute("role", "button");
|
|
296
311
|
this.#div.setAttribute("tabindex", "0");
|
|
312
|
+
this.#div.setAttribute("aria-label", "Click to verify you're a human");
|
|
313
|
+
this.#div.setAttribute("aria-live", "polite");
|
|
297
314
|
this.#div.setAttribute("disabled", "true");
|
|
298
315
|
this.#div.innerHTML = `<div class="checkbox"></div><p>${this.getI18nText(
|
|
299
316
|
"initial-state",
|
|
300
317
|
"I'm a human"
|
|
301
|
-
)}</p><a href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener"><span>Secured by </span>Cap</a>`;
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
.captcha{background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);
|
|
307
|
-
user-select:none;
|
|
308
|
-
|
|
309
|
-
height:var(--cap-widget-height, 30px);
|
|
310
|
-
|
|
311
|
-
width:var(--cap-widget-width, 230px);display:flex;align-items:center;padding:var(--cap-widget-padding,14px);gap:var(--cap-gap,15px);cursor:pointer;transition:filter .2s,transform .2s;position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color,#212121)}.captcha:hover{filter:brightness(98%)}
|
|
312
|
-
|
|
313
|
-
.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);transition:opacity .2s;margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px)}.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)}
|
|
314
|
-
|
|
315
|
-
.captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity .2s}.captcha[data-state=verifying] .checkbox{background: none;display:flex;align-items:center;justify-content:center;transform: scale(1.1);border: none;border-radius: 50%;background: conic-gradient(var(--cap-spinner-color,#000) 0%, var(--cap-spinner-color,#000) var(--progress, 0%), var(--cap-spinner-background-color,#eee) var(--progress, 0%), var(--cap-spinner-background-color,#eee) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background,#fdfdfd);width: calc(100% - var(--cap-spinner-thickness,5px));height: calc(100% - var(--cap-spinner-thickness,5px));border-radius: 50%;margin:calc(var(--cap-spinner-thickness,5px) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;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}.captcha[data-state=error] .checkbox{border:1px solid transparent;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/svg%3E"));background-size:cover}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{position:absolute;bottom:10px;right:10px;font-size:var(--cap-credits-font-size,12px);color:var(--cap-color,#212121);opacity:var(--cap-opacity-hover,0.8)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>`;
|
|
318
|
+
)}</p><a aria-label="Secured by Cap" href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener"><span>Secured by </span>Cap</a>`;
|
|
319
|
+
|
|
320
|
+
this.#shadow.innerHTML = `<style>.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);
|
|
321
|
+
user-select:none;height:var(--cap-widget-height, 30px);width:var(--cap-widget-width, 230px);display:flex;align-items:center;padding:var(--cap-widget-padding,14px);gap:var(--cap-gap,15px);cursor:pointer;transition:filter .2s,transform .2s;position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color,#212121)}.captcha:hover{filter:brightness(98%)}.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);transition:opacity .2s;margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px)}.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)}.captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity .2s}.captcha[data-state=verifying]
|
|
322
|
+
.checkbox{background: none;display:flex;align-items:center;justify-content:center;transform: scale(1.1);border: none;border-radius: 50%;background: conic-gradient(var(--cap-spinner-color,#000) 0%, var(--cap-spinner-color,#000) var(--progress, 0%), var(--cap-spinner-background-color,#eee) var(--progress, 0%), var(--cap-spinner-background-color,#eee) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background,#fdfdfd);width: calc(100% - var(--cap-spinner-thickness,5px));height: calc(100% - var(--cap-spinner-thickness,5px));border-radius: 50%;margin:calc(var(--cap-spinner-thickness,5px) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;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}.captcha[data-state=error] .checkbox{border:1px solid transparent;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/svg%3E"));background-size:cover}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{position:absolute;bottom:10px;right:10px;font-size:var(--cap-credits-font-size,12px);color:var(--cap-color,#212121);opacity:var(--cap-opacity-hover,0.8)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>`;
|
|
316
323
|
|
|
317
324
|
this.#shadow.appendChild(this.#div);
|
|
318
325
|
}
|
|
@@ -407,16 +414,11 @@
|
|
|
407
414
|
return;
|
|
408
415
|
}
|
|
409
416
|
|
|
410
|
-
|
|
411
|
-
const func = new Function("event", code);
|
|
412
|
-
func.call(this, event);
|
|
413
|
-
} catch (error) {
|
|
414
|
-
console.error(`[cap] Error executing ${attributeName}:`, error);
|
|
415
|
-
}
|
|
417
|
+
new Function("event", code).call(this, event);
|
|
416
418
|
}
|
|
417
419
|
|
|
418
420
|
error(message = "Unknown error") {
|
|
419
|
-
console.error("[cap]
|
|
421
|
+
console.error("[cap]", message);
|
|
420
422
|
this.dispatchEvent("error", { isCap: true, message });
|
|
421
423
|
}
|
|
422
424
|
|
|
@@ -512,133 +514,13 @@
|
|
|
512
514
|
}
|
|
513
515
|
}
|
|
514
516
|
}
|
|
515
|
-
|
|
516
|
-
const workerFunct = function () {
|
|
517
|
-
if (
|
|
518
|
-
typeof WebAssembly !== "object" ||
|
|
519
|
-
typeof WebAssembly?.instantiate !== "function"
|
|
520
|
-
) {
|
|
521
|
-
self.onmessage = async ({ data: { salt, target } }) => {
|
|
522
|
-
// Fallback solver in case WASM is not available
|
|
523
|
-
|
|
524
|
-
let nonce = 0;
|
|
525
|
-
const batchSize = 50000;
|
|
526
|
-
let processed = 0;
|
|
527
|
-
const encoder = new TextEncoder();
|
|
528
|
-
|
|
529
|
-
const targetBytes = new Uint8Array(target.length / 2);
|
|
530
|
-
for (let k = 0; k < targetBytes.length; k++) {
|
|
531
|
-
targetBytes[k] = parseInt(target.substring(k * 2, k * 2 + 2), 16);
|
|
532
|
-
}
|
|
533
|
-
const targetBytesLength = targetBytes.length;
|
|
534
|
-
|
|
535
|
-
while (true) {
|
|
536
|
-
try {
|
|
537
|
-
for (let i = 0; i < batchSize; i++) {
|
|
538
|
-
const inputString = salt + nonce;
|
|
539
|
-
const inputBytes = encoder.encode(inputString);
|
|
540
|
-
|
|
541
|
-
const hashBuffer = await crypto.subtle.digest(
|
|
542
|
-
"SHA-256",
|
|
543
|
-
inputBytes
|
|
544
|
-
);
|
|
545
|
-
|
|
546
|
-
const hashBytes = new Uint8Array(
|
|
547
|
-
hashBuffer,
|
|
548
|
-
0,
|
|
549
|
-
targetBytesLength
|
|
550
|
-
);
|
|
551
|
-
|
|
552
|
-
let matches = true;
|
|
553
|
-
for (let k = 0; k < targetBytesLength; k++) {
|
|
554
|
-
if (hashBytes[k] !== targetBytes[k]) {
|
|
555
|
-
matches = false;
|
|
556
|
-
break;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
if (matches) {
|
|
561
|
-
self.postMessage({ nonce, found: true });
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
nonce++;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
processed += batchSize;
|
|
569
|
-
} catch (error) {
|
|
570
|
-
console.error("[cap] fallback worker error", error);
|
|
571
|
-
self.postMessage({
|
|
572
|
-
found: false,
|
|
573
|
-
error: error.message,
|
|
574
|
-
});
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
};
|
|
579
|
-
|
|
580
|
-
return console.warn(
|
|
581
|
-
"[cap] WebAssembly is not supported, falling back to alternative solver."
|
|
582
|
-
);
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
let wasmCacheUrl, solve_pow_function;
|
|
586
|
-
|
|
587
|
-
self.onmessage = async ({ data: { salt, target, wasmUrl } }) => {
|
|
588
|
-
if (wasmCacheUrl !== wasmUrl) {
|
|
589
|
-
wasmCacheUrl = wasmUrl;
|
|
590
|
-
await import(wasmUrl)
|
|
591
|
-
.then((wasmModule) => {
|
|
592
|
-
return wasmModule.default().then((instance) => {
|
|
593
|
-
solve_pow_function = (
|
|
594
|
-
instance && instance.exports ? instance.exports : wasmModule
|
|
595
|
-
).solve_pow;
|
|
596
|
-
});
|
|
597
|
-
})
|
|
598
|
-
.catch((e) => {
|
|
599
|
-
console.error("[cap] using fallback solver due to error:", e);
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
try {
|
|
604
|
-
const startTime = performance.now();
|
|
605
|
-
const nonce = solve_pow_function(salt, target);
|
|
606
|
-
const endTime = performance.now();
|
|
607
|
-
|
|
608
|
-
self.postMessage({
|
|
609
|
-
nonce: Number(nonce),
|
|
610
|
-
found: true,
|
|
611
|
-
durationMs: (endTime - startTime).toFixed(2),
|
|
612
|
-
});
|
|
613
|
-
} catch (error) {
|
|
614
|
-
console.error("[cap] solver error", error);
|
|
615
|
-
self.postMessage({
|
|
616
|
-
found: false,
|
|
617
|
-
error: error.message || String(error),
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
};
|
|
621
|
-
|
|
622
|
-
self.onerror = (error) => {
|
|
623
|
-
self.postMessage({
|
|
624
|
-
found: false,
|
|
625
|
-
error: `Worker error: ${error.message || error}`,
|
|
626
|
-
});
|
|
627
|
-
};
|
|
628
|
-
};
|
|
629
|
-
|
|
630
|
-
workerScript = `(() => {${workerFunct
|
|
631
|
-
.toString()
|
|
632
|
-
.replace(/^function\s*\([^\)]*\)\s*{|\}$/g, "")
|
|
633
|
-
.trim()}})()`;
|
|
634
|
-
|
|
635
517
|
window.Cap = Cap;
|
|
636
518
|
|
|
637
|
-
if (!customElements.get("cap-widget")) {
|
|
519
|
+
if (!customElements.get("cap-widget") && !window?.CAP_DONT_SKIP_REDEFINE) {
|
|
638
520
|
customElements.define("cap-widget", CapWidget);
|
|
639
521
|
} else {
|
|
640
522
|
console.warn(
|
|
641
|
-
"
|
|
523
|
+
"[cap] the cap-widget element has already been defined, skipping re-defining it.\nto prevent this, set window.CAP_DONT_SKIP_REDEFINE to true"
|
|
642
524
|
);
|
|
643
525
|
}
|
|
644
526
|
|
package/src/worker.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
if (
|
|
3
|
+
typeof WebAssembly !== "object" ||
|
|
4
|
+
typeof WebAssembly?.instantiate !== "function"
|
|
5
|
+
) {
|
|
6
|
+
self.onmessage = async ({ data: { salt, target } }) => {
|
|
7
|
+
// Fallback solver in case WASM is not available
|
|
8
|
+
|
|
9
|
+
let nonce = 0;
|
|
10
|
+
const batchSize = 50000;
|
|
11
|
+
let processed = 0;
|
|
12
|
+
const encoder = new TextEncoder();
|
|
13
|
+
|
|
14
|
+
const targetBytes = new Uint8Array(target.length / 2);
|
|
15
|
+
for (let k = 0; k < targetBytes.length; k++) {
|
|
16
|
+
targetBytes[k] = parseInt(target.substring(k * 2, k * 2 + 2), 16);
|
|
17
|
+
}
|
|
18
|
+
const targetBytesLength = targetBytes.length;
|
|
19
|
+
|
|
20
|
+
while (true) {
|
|
21
|
+
try {
|
|
22
|
+
for (let i = 0; i < batchSize; i++) {
|
|
23
|
+
const inputString = salt + nonce;
|
|
24
|
+
const inputBytes = encoder.encode(inputString);
|
|
25
|
+
|
|
26
|
+
const hashBuffer = await crypto.subtle.digest(
|
|
27
|
+
"SHA-256",
|
|
28
|
+
inputBytes
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const hashBytes = new Uint8Array(hashBuffer, 0, targetBytesLength);
|
|
32
|
+
|
|
33
|
+
let matches = true;
|
|
34
|
+
for (let k = 0; k < targetBytesLength; k++) {
|
|
35
|
+
if (hashBytes[k] !== targetBytes[k]) {
|
|
36
|
+
matches = false;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (matches) {
|
|
42
|
+
self.postMessage({ nonce, found: true });
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
nonce++;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
processed += batchSize;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error("[cap worker] fallback worker error", error);
|
|
52
|
+
self.postMessage({
|
|
53
|
+
found: false,
|
|
54
|
+
error: error.message,
|
|
55
|
+
});
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return console.warn(
|
|
62
|
+
"[cap worker] wasm not supported, falling back to alternative solver. this will be significantly slower."
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let wasmCacheUrl, solve_pow_function;
|
|
67
|
+
|
|
68
|
+
self.onmessage = async ({ data: { salt, target, wasmUrl } }) => {
|
|
69
|
+
if (wasmCacheUrl !== wasmUrl) {
|
|
70
|
+
wasmCacheUrl = wasmUrl;
|
|
71
|
+
await import(wasmUrl)
|
|
72
|
+
.then((wasmModule) => {
|
|
73
|
+
return wasmModule.default().then((instance) => {
|
|
74
|
+
solve_pow_function = (
|
|
75
|
+
instance && instance.exports ? instance.exports : wasmModule
|
|
76
|
+
).solve_pow;
|
|
77
|
+
});
|
|
78
|
+
})
|
|
79
|
+
.catch((e) => {
|
|
80
|
+
console.error("[cap worker] using fallback solver due to error:", e);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const startTime = performance.now();
|
|
86
|
+
const nonce = solve_pow_function(salt, target);
|
|
87
|
+
const endTime = performance.now();
|
|
88
|
+
|
|
89
|
+
self.postMessage({
|
|
90
|
+
nonce: Number(nonce),
|
|
91
|
+
found: true,
|
|
92
|
+
durationMs: (endTime - startTime).toFixed(2),
|
|
93
|
+
});
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error("[cap worker]", error);
|
|
96
|
+
self.postMessage({
|
|
97
|
+
found: false,
|
|
98
|
+
error: error.message || String(error),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
self.onerror = (error) => {
|
|
104
|
+
self.postMessage({
|
|
105
|
+
found: false,
|
|
106
|
+
error,
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
})();
|