@cap.js/widget 0.1.44 → 0.1.45

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.
@@ -1 +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})})();
1
+ (()=>{const t=t=>{const e=t.getAttribute("data-cap-floating");if(!e)return;const o=document.querySelector(e);if(!document.contains(o)&&!o.solve)throw new Error(`[cap floating] "${e}" doesn't exist or isn't a Cap widget`);o.style.display="none";let n=!1;t.addEventListener("click",e=>{if(n)return void(n=!1);e.stopImmediatePropagation(),e.preventDefault(),((t,e)=>{const o=parseInt(t.getAttribute("data-cap-floating-offset"))||8,n=t.getAttribute("data-cap-floating-position")||"top";Object.assign(e.style,{display:"block",position:"absolute",zIndex:"99999",opacity:"0",transform:"scale(0.98)",marginTop:"-4px",boxShadow:"rgba(0, 0, 0, 0.05) 0px 6px 24px 0px",borderRadius:"14px",transition:"opacity 0.15s, margin-top 0.2s, transform 0.2s"}),setTimeout(()=>{e.style.transform="scale(1)",e.style.opacity="1",e.style.marginTop="0"},5);const s=t.getBoundingClientRect(),a=(e.offsetParent??document.documentElement).getBoundingClientRect(),i=s.left+(s.width-e.offsetWidth)/2,r=Math.max(2,Math.min(i,window.innerWidth-e.offsetWidth));if(e.style.left=r-a.left+"px","top"===n){const t=s.top-e.offsetHeight-o,n=Math.max(0,t);e.style.top=n-a.top+"px"}else{const t=s.bottom+o,n=Math.min(t,window.innerHeight-e.offsetHeight);e.style.top=n-a.top+"px"}e.solve()})(t,o);const s=({detail:e})=>{o.removeEventListener("solve",s),t.setAttribute("data-cap-token",e.token),t.setAttribute("data-cap-progress","done"),setTimeout(()=>{n=!0,t.click()},500),(t=>{setTimeout(()=>{t.style.transform="scale(0.98)",t.style.opacity="0",t.style.marginTop="-4px"},500),setTimeout(()=>{t.style.display="none"},700)})(o)};o.addEventListener("solve",s)},!0)},e=e=>{t(e),e.querySelectorAll("[data-cap-floating]").forEach(t)};e(document.body),new MutationObserver(t=>t.forEach(t=>t.addedNodes.forEach(t=>{t.nodeType===Node.ELEMENT_NODE&&e(t)}))).observe(document.body,{childList:!0,subtree:!0})})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap.js/widget",
3
- "version": "0.1.44",
3
+ "version": "0.1.45",
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
  "algorithm",
@@ -1,25 +1,7 @@
1
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) => {
12
- return element.removeEventListener("click", h);
13
- });
14
- element.onclick = (e) => handleClick(e, element, capWidget, handlers);
15
- }, 50);
16
- };
17
-
18
- element.onclick = null;
19
-
2
+ const showWidget = (element, capWidget) => {
20
3
  const offset = parseInt(element.getAttribute("data-cap-floating-offset")) || 8;
21
4
  const position = element.getAttribute("data-cap-floating-position") || "top";
22
- const rect = element.getBoundingClientRect();
23
5
 
24
6
  Object.assign(capWidget.style, {
25
7
  display: "block",
@@ -39,41 +21,38 @@
39
21
  capWidget.style.marginTop = "0";
40
22
  }, 5);
41
23
 
42
- const centerX = rect.left + (rect.width - capWidget.offsetWidth) / 2;
43
- const safeX = Math.min(centerX, window.innerWidth - capWidget.offsetWidth);
24
+ // `position: absolute` is resolved against the nearest positioned ancestor (capWidget.offsetParent),
25
+ // but getBoundingClientRect() returns viewport-relative coords, convert to container-relative coords.
26
+ const triggerRect = element.getBoundingClientRect();
27
+ const containerRect = (capWidget.offsetParent ?? document.documentElement).getBoundingClientRect();
28
+
29
+ const centeredLeft = triggerRect.left + (triggerRect.width - capWidget.offsetWidth) / 2;
30
+ const clampedLeft = Math.max(2, Math.min(centeredLeft, window.innerWidth - capWidget.offsetWidth));
31
+ capWidget.style.left = `${clampedLeft - containerRect.left}px`;
44
32
 
45
33
  if (position === "top") {
46
- capWidget.style.top = `${Math.max(
47
- window.scrollY,
48
- rect.top - capWidget.offsetHeight - offset + window.scrollY,
49
- )}px`;
34
+ const idealTop = triggerRect.top - capWidget.offsetHeight - offset;
35
+ const clampedTop = Math.max(0, idealTop);
36
+ capWidget.style.top = `${clampedTop - containerRect.top}px`;
50
37
  } else {
51
- capWidget.style.top = `${Math.min(
52
- rect.bottom + offset + window.scrollY,
53
- window.innerHeight - capWidget.offsetHeight + window.scrollY,
54
- )}px`;
38
+ const idealTop = triggerRect.bottom + offset;
39
+ const clampedTop = Math.min(idealTop, window.innerHeight - capWidget.offsetHeight);
40
+ capWidget.style.top = `${clampedTop - containerRect.top}px`;
55
41
  }
56
42
 
57
- capWidget.style.left = `${Math.max(safeX, 2)}px`;
58
43
  capWidget.solve();
44
+ };
59
45
 
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
- });
46
+ const hideWidget = (capWidget) => {
47
+ setTimeout(() => {
48
+ capWidget.style.transform = "scale(0.98)";
49
+ capWidget.style.opacity = "0";
50
+ capWidget.style.marginTop = "-4px";
51
+ }, 500);
52
+
53
+ setTimeout(() => {
54
+ capWidget.style.display = "none";
55
+ }, 700);
77
56
  };
78
57
 
79
58
  const setupElement = (element) => {
@@ -86,24 +65,38 @@
86
65
  }
87
66
 
88
67
  capWidget.style.display = "none";
89
- const handlers = [element.onclick].filter(Boolean);
90
-
91
- if (typeof getEventListeners === "function") {
92
- handlers.push(...(getEventListeners(element).click || []).map((l) => l.listener));
93
- }
94
-
95
- if (handlers.length) {
96
- element.onclick = null;
97
- handlers.forEach((h) => {
98
- return element.removeEventListener("click", h);
99
- });
100
- }
101
-
102
- element.addEventListener("click", (e) => {
103
- e.stopImmediatePropagation();
104
- e.preventDefault();
105
- handleClick(e, element, capWidget, handlers);
106
- });
68
+ let bypass = false;
69
+
70
+ element.addEventListener(
71
+ "click",
72
+ (e) => {
73
+ if (bypass) {
74
+ bypass = false;
75
+ return;
76
+ }
77
+
78
+ e.stopImmediatePropagation();
79
+ e.preventDefault();
80
+
81
+ showWidget(element, capWidget);
82
+
83
+ const onSolve = ({ detail }) => {
84
+ capWidget.removeEventListener("solve", onSolve);
85
+ element.setAttribute("data-cap-token", detail.token);
86
+ element.setAttribute("data-cap-progress", "done");
87
+
88
+ setTimeout(() => {
89
+ bypass = true;
90
+ element.click();
91
+ }, 500);
92
+
93
+ hideWidget(capWidget);
94
+ };
95
+
96
+ capWidget.addEventListener("solve", onSolve);
97
+ },
98
+ true,
99
+ );
107
100
  };
108
101
 
109
102
  const init = (root) => {