UNPKG

@cap.js/widget

Version:

Client-side widget for Cap, a lightweight, modern open-source CAPTCHA alternative designed using SHA-256 PoW.

126 lines (105 loc) 3.71 kB
(() => { const handleClick = (evt, element, capWidget, handlers) => { const trigger = () => { handlers.forEach((h) => { element.addEventListener("click", h); h.call(element, evt); }); setTimeout(() => { element.onclick = null; handlers.forEach((h) => element.removeEventListener("click", h)); element.onclick = (e) => handleClick(e, element, capWidget, handlers); }, 50); }; element.onclick = null; const offset = parseInt(element.getAttribute("data-cap-floating-offset")) || 8; const position = element.getAttribute("data-cap-floating-position") || "top"; const rect = element.getBoundingClientRect(); Object.assign(capWidget.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(() => { capWidget.style.transform = "scale(1)"; capWidget.style.opacity = "1"; capWidget.style.marginTop = "0"; }, 5); const centerX = rect.left + (rect.width - capWidget.offsetWidth) / 2; const safeX = Math.min(centerX, window.innerWidth - capWidget.offsetWidth); if (position === "top") { capWidget.style.top = `${Math.max( window.scrollY, rect.top - capWidget.offsetHeight - offset + window.scrollY )}px`; } else { capWidget.style.top = `${Math.min( rect.bottom + offset + window.scrollY, window.innerHeight - capWidget.offsetHeight + window.scrollY )}px`; } capWidget.style.left = `${Math.max(safeX, 2)}px`; capWidget.solve(); capWidget.addEventListener("solve", ({ detail }) => { element.setAttribute("data-cap-token", detail.token); element.setAttribute("data-cap-progress", "done"); setTimeout(() => { trigger(); }, 500); setTimeout(() => { capWidget.style.transform = "scale(0.98)"; capWidget.style.opacity = "0"; capWidget.style.marginTop = "-4px"; }, 500); setTimeout(() => { capWidget.style.display = "none"; }, 700); }); }; const setupElement = (element) => { const capWidgetSelector = element.getAttribute("data-cap-floating"); if (!capWidgetSelector) return; const capWidget = document.querySelector(capWidgetSelector); if (!document.contains(capWidget) && !capWidget.solve) { throw new Error( `[cap floating] "${capWidgetSelector}" doesn't exist or isn't a Cap widget` ); } capWidget.style.display = "none"; const handlers = [element.onclick].filter(Boolean); if (typeof getEventListeners === "function") { handlers.push( ...(getEventListeners(element).click || []).map((l) => l.listener) ); } if (handlers.length) { element.onclick = null; handlers.forEach((h) => element.removeEventListener("click", h)); } element.addEventListener("click", (e) => { e.stopImmediatePropagation(); e.preventDefault(); handleClick(e, element, capWidget, handlers); }); }; const init = (root) => { setupElement(root); root.querySelectorAll("[data-cap-floating]").forEach(setupElement); }; init(document.body); new MutationObserver((mutations) => mutations.forEach((mutation) => mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) init(node); }) ) ).observe(document.body, { childList: true, subtree: true }); })();