@capjs-persian/capjs-local 0.0.1
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/LICENSE +13 -0
- package/README.md +5 -0
- package/cap-floating.min.js +1 -0
- package/cap.compat.min.js +1 -0
- package/cap.d.ts +155 -0
- package/cap.min.js +1 -0
- package/capjs-persian-capjs-local-0.0.1.tgz +0 -0
- package/package.json +34 -0
- package/src/cap-floating.js +125 -0
- package/src/cap.js +613 -0
- package/src/worker.js +115 -0
- package/wasm/cap_wasm.min.js +7 -0
- package/wasm/cap_wasm_bg.wasm +0 -0
- package/wasm-hashes.min.js +395 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2025 Tiago
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
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",opacity:"0",transform:"scale(0.98)",marginTop:"-4px",boxShadow:"rgba(0, 0, 0, 0.05) 0px 6px 24px 0px",borderRadius:"14px",transition:"opacity 0.15s, margin-top 0.2s, transform 0.2s"}),setTimeout(()=>{n.style.transform="scale(1)",n.style.opacity="1",n.style.marginTop="0"},5);const r=l.left+(l.width-n.offsetWidth)/2,c=Math.min(r,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(c,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.transform="scale(0.98)",n.style.opacity="0",n.style.marginTop="-4px"},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})})();
|
|
@@ -0,0 +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)}();
|
package/cap.d.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
interface Window {
|
|
3
|
+
CAP_CUSTOM_FETCH?: typeof fetch;
|
|
4
|
+
CAP_CUSTOM_WASM_URL?: string;
|
|
5
|
+
CAP_CSS_NONCE?: string;
|
|
6
|
+
CAP_DONT_SKIP_REDEFINE?: boolean;
|
|
7
|
+
Cap: typeof Cap;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface CapProgressEventDetail {
|
|
12
|
+
progress: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface CapSolveEventDetail {
|
|
16
|
+
token: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface CapErrorEventDetail {
|
|
20
|
+
isCap: boolean;
|
|
21
|
+
message: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface CapProgressEvent extends CustomEvent {
|
|
25
|
+
detail: CapProgressEventDetail;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface CapSolveEvent extends CustomEvent {
|
|
29
|
+
detail: CapSolveEventDetail;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface CapErrorEvent extends CustomEvent {
|
|
33
|
+
detail: CapErrorEventDetail;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface CapResetEvent extends CustomEvent {
|
|
37
|
+
detail: Record<string, never>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface SolveResult {
|
|
41
|
+
success: boolean;
|
|
42
|
+
token: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface CapConfig {
|
|
46
|
+
apiEndpoint?: string;
|
|
47
|
+
"data-cap-api-endpoint"?: string;
|
|
48
|
+
"data-cap-worker-count"?: string;
|
|
49
|
+
"data-cap-hidden-field-name"?: string;
|
|
50
|
+
"data-cap-i18n-initial-state"?: string;
|
|
51
|
+
"data-cap-i18n-verifying-label"?: string;
|
|
52
|
+
"data-cap-i18n-solved-label"?: string;
|
|
53
|
+
"data-cap-i18n-error-label"?: string;
|
|
54
|
+
"data-cap-i18n-verify-aria-label"?: string;
|
|
55
|
+
"data-cap-i18n-verifying-aria-label"?: string;
|
|
56
|
+
"data-cap-i18n-verified-aria-label"?: string;
|
|
57
|
+
"data-cap-i18n-error-aria-label"?: string;
|
|
58
|
+
"data-cap-i18n-wasm-disabled"?: string;
|
|
59
|
+
onsolve?: string;
|
|
60
|
+
onprogress?: string;
|
|
61
|
+
onreset?: string;
|
|
62
|
+
onerror?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface CapWidget extends HTMLElement {
|
|
66
|
+
readonly token: string | null;
|
|
67
|
+
readonly tokenValue: string | null;
|
|
68
|
+
|
|
69
|
+
solve(): Promise<SolveResult>;
|
|
70
|
+
reset(): void;
|
|
71
|
+
setWorkersCount(workers: number): void;
|
|
72
|
+
|
|
73
|
+
addEventListener(
|
|
74
|
+
type: "progress",
|
|
75
|
+
listener: (event: CapProgressEvent) => void
|
|
76
|
+
): void;
|
|
77
|
+
addEventListener(
|
|
78
|
+
type: "solve",
|
|
79
|
+
listener: (event: CapSolveEvent) => void
|
|
80
|
+
): void;
|
|
81
|
+
addEventListener(
|
|
82
|
+
type: "error",
|
|
83
|
+
listener: (event: CapErrorEvent) => void
|
|
84
|
+
): void;
|
|
85
|
+
addEventListener(
|
|
86
|
+
type: "reset",
|
|
87
|
+
listener: (event: CapResetEvent) => void
|
|
88
|
+
): void;
|
|
89
|
+
addEventListener(type: string, listener: EventListener): void;
|
|
90
|
+
|
|
91
|
+
removeEventListener(
|
|
92
|
+
type: "progress",
|
|
93
|
+
listener: (event: CapProgressEvent) => void
|
|
94
|
+
): void;
|
|
95
|
+
removeEventListener(
|
|
96
|
+
type: "solve",
|
|
97
|
+
listener: (event: CapSolveEvent) => void
|
|
98
|
+
): void;
|
|
99
|
+
removeEventListener(
|
|
100
|
+
type: "error",
|
|
101
|
+
listener: (event: CapErrorEvent) => void
|
|
102
|
+
): void;
|
|
103
|
+
removeEventListener(
|
|
104
|
+
type: "reset",
|
|
105
|
+
listener: (event: CapResetEvent) => void
|
|
106
|
+
): void;
|
|
107
|
+
removeEventListener(type: string, listener: EventListener): void;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
declare class Cap {
|
|
111
|
+
readonly widget: CapWidget;
|
|
112
|
+
readonly token: string | null;
|
|
113
|
+
|
|
114
|
+
constructor(config?: CapConfig, el?: CapWidget);
|
|
115
|
+
|
|
116
|
+
solve(): Promise<SolveResult>;
|
|
117
|
+
reset(): void;
|
|
118
|
+
|
|
119
|
+
addEventListener(
|
|
120
|
+
type: "progress",
|
|
121
|
+
listener: (event: CapProgressEvent) => void
|
|
122
|
+
): void;
|
|
123
|
+
addEventListener(
|
|
124
|
+
type: "solve",
|
|
125
|
+
listener: (event: CapSolveEvent) => void
|
|
126
|
+
): void;
|
|
127
|
+
addEventListener(
|
|
128
|
+
type: "error",
|
|
129
|
+
listener: (event: CapErrorEvent) => void
|
|
130
|
+
): void;
|
|
131
|
+
addEventListener(
|
|
132
|
+
type: "reset",
|
|
133
|
+
listener: (event: CapResetEvent) => void
|
|
134
|
+
): void;
|
|
135
|
+
addEventListener(type: string, listener: EventListener): void;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
declare global {
|
|
139
|
+
interface HTMLElementTagNameMap {
|
|
140
|
+
"cap-widget": CapWidget;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export {
|
|
145
|
+
Cap,
|
|
146
|
+
type CapWidget,
|
|
147
|
+
type CapConfig,
|
|
148
|
+
type CapProgressEvent,
|
|
149
|
+
type CapSolveEvent,
|
|
150
|
+
type CapErrorEvent,
|
|
151
|
+
type CapResetEvent,
|
|
152
|
+
type SolveResult,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export default Cap;
|
package/cap.min.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(()=>{const e="0.0.6";if("undefined"==typeof window)return;const t=(...e)=>window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(...e):fetch(...e);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),s="";function i(){return r^=r<<13,r^=r>>>17,r^=r<<5,r>>>0}for(;s.length<t;){s+=i().toString(16).padStart(8,"0")}return s.substring(0,t)}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 s 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(['(()=>{const e=async({salt:e,target:t})=>{let r=0;const o=new TextEncoder,s=new Uint8Array(t.length/2);for(let e=0;e<s.length;e++)s[e]=parseInt(t.substring(2*e,2*e+2),16);const n=s.length;for(;;)try{for(let t=0;t<5e4;t++){const t=e+r,a=o.encode(t),l=await crypto.subtle.digest("SHA-256",a),c=new Uint8Array(l,0,n);let f=!0;for(let e=0;e<n;e++)if(c[e]!==s[e]){f=!1;break}if(f)return void self.postMessage({nonce:r,found:!0});r++}}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:r}})=>e({salt:t,target:r}));let t,r;self.onmessage=async({data:{salt:o,target:s,wasmUrl:n}})=>{let a;if(t===n||(t=n,await import(n).then(e=>e.default().then(t=>{r=(t?.exports?t.exports:e).solve_pow})).catch(t=>(console.error("[cap worker] using fallback solver due to error:",t),a=!0,e({salt:o,target:s}))),!a))try{const e=performance.now(),t=r(o,s),n=performance.now();self.postMessage({nonce:Number(t),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,10)),"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(),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",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:s,token:i}=await(await t(`${e}challenge`,{method:"POST"})).json();let a=s;if(!Array.isArray(a)){let e=0;a=Array.from({length:s.c},()=>(e+=1,[r(`${i}${e}`,s.s),r(`${i}${e}d`,s.d)]))}const n=await this.solveChallenges(a),o=await(await t(`${e}redeem`,{method:"POST",body:JSON.stringify({token:i,solutions:n}),headers:{"Content-Type":"application/json"}})).json();if(this.dispatchEvent("progress",{progress:100}),!o.success)throw new Error("Invalid solution");const d=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${d}']`)&&(this.querySelector(`input[name='${d}']`).value=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.#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.#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,d)=>{const c=i[n];if(c){if(c.onmessage=({data:e})=>{e.found&&(s++,this.dispatchEvent("progress",{progress:Math.round(s/r*100)}),o(e.nonce))},c.onerror=e=>{this.error(`Error in worker: ${e.message||e}`),d(e)},c.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`}),"object"!=typeof WebAssembly||"function"!=typeof WebAssembly?.instantiate){if(this.#s.querySelector(".warning"))return;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;padding-bottom:calc(var(--cap-border-radius,14px) + 5px);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.#s.insertBefore(e,this.#s.firstChild),setTimeout(()=>{e.style.marginBottom="calc(-1 * var(--cap-border-radius, 14px))",e.style.opacity=1},10)}}else d(new Error("Worker not available"))}),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=!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">${this.getI18nText("initial-state","I'm a human")}</p><a part="attribution" aria-label="Secured by Cap" href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener">Cap</a>`,this.#s.innerHTML=`<style${window.CAP_CSS_NONCE?` nonce=${window.CAP_CSS_NONCE}`:""}>.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);user-select:none;height:var(--cap-widget-height, 58px);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}.checkbox .progress-ring{display:none;width:100%;height:100%;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.96;transition:stroke-dashoffset 0.3s ease}.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-color:transparent}.captcha[data-state=verifying] .checkbox .progress-ring{display:block}.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=done] .checkbox .progress-ring{display:none}.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[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{position:absolute;bottom:10px;right:10px;font-size:12px;color:var(--cap-color,#212121);opacity:0.8;text-underline-offset: 1.5px;}</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(),e.stopPropagation(),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(".progress-ring-circle");if(t&&r){const s=2*Math.PI*14,i=s-e.detail.progress/100*s;r.style.strokeDashoffset=i,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 i{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=i,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",s),"object"==typeof exports&&"undefined"!=typeof module?module.exports=i:"function"==typeof define&&define.amd&&define([],()=>i),"undefined"!=typeof exports&&(exports.default=i)})();
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@capjs-persian/capjs-local",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Fork of cap.js widget with CDN removed and WASM assets bundled locally for restricted networks (e.g. Iran).",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"captcha",
|
|
7
|
+
"captcha-alternative",
|
|
8
|
+
"security",
|
|
9
|
+
"anti-bot",
|
|
10
|
+
"proof-of-work",
|
|
11
|
+
"pow",
|
|
12
|
+
"wasm",
|
|
13
|
+
"no-cdn",
|
|
14
|
+
"offline",
|
|
15
|
+
"iran",
|
|
16
|
+
"restricted-networks"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://github.com/USERNAME/capjs-local-persian#readme",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/USERNAME/capjs-local-persian/issues"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/USERNAME/capjs-local-persian.git"
|
|
25
|
+
},
|
|
26
|
+
"license": "Apache-2.0",
|
|
27
|
+
"author": "Fork maintained by USERNAME (original author: Tiago)",
|
|
28
|
+
"type": "commonjs",
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"main": "dist/cap.min.js",
|
|
33
|
+
"types": "dist/cap.d.ts"
|
|
34
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
const handleClick = (evt, element, capWidget, handlers) => {
|
|
3
|
+
const trigger = () => {
|
|
4
|
+
handlers.forEach((h) => {
|
|
5
|
+
element.addEventListener("click", h);
|
|
6
|
+
h.call(element, evt);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
setTimeout(() => {
|
|
10
|
+
element.onclick = null;
|
|
11
|
+
handlers.forEach((h) => element.removeEventListener("click", h));
|
|
12
|
+
element.onclick = (e) => handleClick(e, element, capWidget, handlers);
|
|
13
|
+
}, 50);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
element.onclick = null;
|
|
17
|
+
|
|
18
|
+
const offset =
|
|
19
|
+
parseInt(element.getAttribute("data-cap-floating-offset")) || 8;
|
|
20
|
+
const position =
|
|
21
|
+
element.getAttribute("data-cap-floating-position") || "top";
|
|
22
|
+
const rect = element.getBoundingClientRect();
|
|
23
|
+
|
|
24
|
+
Object.assign(capWidget.style, {
|
|
25
|
+
display: "block",
|
|
26
|
+
position: "absolute",
|
|
27
|
+
zIndex: "99999",
|
|
28
|
+
opacity: "0",
|
|
29
|
+
transform: "scale(0.98)",
|
|
30
|
+
marginTop: "-4px",
|
|
31
|
+
boxShadow: "rgba(0, 0, 0, 0.05) 0px 6px 24px 0px",
|
|
32
|
+
borderRadius: "14px",
|
|
33
|
+
transition: "opacity 0.15s, margin-top 0.2s, transform 0.2s",
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
setTimeout(() => {
|
|
37
|
+
capWidget.style.transform = "scale(1)";
|
|
38
|
+
capWidget.style.opacity = "1";
|
|
39
|
+
capWidget.style.marginTop = "0";
|
|
40
|
+
}, 5);
|
|
41
|
+
|
|
42
|
+
const centerX = rect.left + (rect.width - capWidget.offsetWidth) / 2;
|
|
43
|
+
const safeX = Math.min(centerX, window.innerWidth - capWidget.offsetWidth);
|
|
44
|
+
|
|
45
|
+
if (position === "top") {
|
|
46
|
+
capWidget.style.top = `${Math.max(
|
|
47
|
+
window.scrollY,
|
|
48
|
+
rect.top - capWidget.offsetHeight - offset + window.scrollY
|
|
49
|
+
)}px`;
|
|
50
|
+
} else {
|
|
51
|
+
capWidget.style.top = `${Math.min(
|
|
52
|
+
rect.bottom + offset + window.scrollY,
|
|
53
|
+
window.innerHeight - capWidget.offsetHeight + window.scrollY
|
|
54
|
+
)}px`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
capWidget.style.left = `${Math.max(safeX, 2)}px`;
|
|
58
|
+
capWidget.solve();
|
|
59
|
+
|
|
60
|
+
capWidget.addEventListener("solve", ({ detail }) => {
|
|
61
|
+
element.setAttribute("data-cap-token", detail.token);
|
|
62
|
+
element.setAttribute("data-cap-progress", "done");
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
trigger();
|
|
65
|
+
}, 500);
|
|
66
|
+
|
|
67
|
+
setTimeout(() => {
|
|
68
|
+
capWidget.style.transform = "scale(0.98)";
|
|
69
|
+
capWidget.style.opacity = "0";
|
|
70
|
+
capWidget.style.marginTop = "-4px";
|
|
71
|
+
}, 500);
|
|
72
|
+
|
|
73
|
+
setTimeout(() => {
|
|
74
|
+
capWidget.style.display = "none";
|
|
75
|
+
}, 700);
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const setupElement = (element) => {
|
|
80
|
+
const capWidgetSelector = element.getAttribute("data-cap-floating");
|
|
81
|
+
if (!capWidgetSelector) return;
|
|
82
|
+
|
|
83
|
+
const capWidget = document.querySelector(capWidgetSelector);
|
|
84
|
+
if (!document.contains(capWidget) && !capWidget.solve) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`[cap floating] "${capWidgetSelector}" doesn't exist or isn't a Cap widget`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
capWidget.style.display = "none";
|
|
91
|
+
const handlers = [element.onclick].filter(Boolean);
|
|
92
|
+
|
|
93
|
+
if (typeof getEventListeners === "function") {
|
|
94
|
+
handlers.push(
|
|
95
|
+
...(getEventListeners(element).click || []).map((l) => l.listener)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (handlers.length) {
|
|
100
|
+
element.onclick = null;
|
|
101
|
+
handlers.forEach((h) => element.removeEventListener("click", h));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
element.addEventListener("click", (e) => {
|
|
105
|
+
e.stopImmediatePropagation();
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
handleClick(e, element, capWidget, handlers);
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const init = (root) => {
|
|
112
|
+
setupElement(root);
|
|
113
|
+
root.querySelectorAll("[data-cap-floating]").forEach(setupElement);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
init(document.body);
|
|
117
|
+
|
|
118
|
+
new MutationObserver((mutations) =>
|
|
119
|
+
mutations.forEach((mutation) =>
|
|
120
|
+
mutation.addedNodes.forEach((node) => {
|
|
121
|
+
if (node.nodeType === Node.ELEMENT_NODE) init(node);
|
|
122
|
+
})
|
|
123
|
+
)
|
|
124
|
+
).observe(document.body, { childList: true, subtree: true });
|
|
125
|
+
})();
|