@tn3w/openage 1.0.4 → 1.0.6
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/README.md +26 -6
- package/dist/openage.esm.js +276 -28
- package/dist/openage.esm.js.map +1 -1
- package/dist/openage.min.js +1 -1
- package/dist/openage.min.js.map +1 -1
- package/dist/openage.umd.js +276 -28
- package/dist/openage.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/challenge.js +86 -14
- package/src/constants.d.ts +1 -0
- package/src/constants.js +4 -4
- package/src/index.d.ts +1 -0
- package/src/index.js +17 -0
- package/src/ui.d.ts +4 -0
- package/src/ui.js +71 -0
- package/src/widget.d.ts +2 -0
- package/src/widget.js +100 -10
package/dist/openage.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).OpenAge={})}(this,function(e){"use strict";const n='<svg viewBox="0 0 100 120"\n fill="none" class="oa-face-svg">\n <ellipse cx="50" cy="55" rx="35" ry="45"\n stroke="currentColor" stroke-width="1.8"/>\n <ellipse cx="36" cy="45" rx="5" ry="3.5"\n fill="currentColor" class="oa-eye"/>\n <ellipse cx="64" cy="45" rx="5" ry="3.5"\n fill="currentColor" class="oa-eye"/>\n <line x1="50" y1="52" x2="48" y2="62"\n stroke="currentColor" stroke-width="1.5"\n stroke-linecap="round"/>\n <path d="M40 72 Q50 79 60 72"\n stroke="currentColor" stroke-width="1.5"\n stroke-linecap="round" fill="none"/>\n</svg>';function t(e){return"light"===e||"dark"===e?e:a()}function o(e,n){if("light"===n||"dark"===n)return;if("undefined"==typeof window)return;const t=()=>{e.setAttribute("data-theme",a())},o=[];for(const e of["(prefers-color-scheme: dark)","(prefers-color-scheme: light)"]){const n=i(e,t);n&&o.push(n)}if("function"==typeof MutationObserver){const e=new MutationObserver(t),n={attributes:!0,attributeFilter:["class","data-theme","style"]};document.documentElement&&e.observe(document.documentElement,n),document.body&&e.observe(document.body,n),o.push(()=>e.disconnect())}return()=>{for(const e of o)e()}}function a(){if("undefined"==typeof window)return"dark";const e=function(){if("undefined"==typeof document)return null;for(const e of[document.documentElement,document.body]){const n=r(e);if(n)return n}return null}();if(e)return e;const n=function(){if("function"!=typeof window.matchMedia)return null;if(window.matchMedia("(prefers-color-scheme: dark)").matches)return"dark";if(window.matchMedia("(prefers-color-scheme: light)").matches)return"light";return null}();return n||"dark"}function r(e){if(!e||"function"!=typeof window.getComputedStyle)return null;const n=function(e){const n=e.getAttribute("data-theme");if("light"===n||"dark"===n)return n;const t=e.className;if("string"!=typeof t)return null;if(/\bdark\b|theme-dark|dark-theme/i.test(t))return"dark";if(/\blight\b|theme-light|light-theme/i.test(t))return"light";return null}(e);if(n)return n;const t=window.getComputedStyle(e),o=function(e){if(!e||"normal"===e)return null;const n=e.toLowerCase(),t=n.includes("dark"),o=n.includes("light");return t&&!o?"dark":o&&!t?"light":null}(t.colorScheme);return o||function(e){if(!e||"transparent"===e)return null;const n=e.match(/rgba?\(([^)]+)\)/i);if(!n)return null;const t=n[1].split(",").slice(0,3).map(e=>Number.parseFloat(e.trim()));if(3!==t.length||t.some(Number.isNaN))return null;const[o,a,r]=t,i=(299*o+587*a+114*r)/1e3;return i<=140?"dark":i>=180?"light":null}(t.backgroundColor)}function i(e,n){if("function"!=typeof window.matchMedia)return null;const t=window.matchMedia(e);return"function"==typeof t.addEventListener?(t.addEventListener("change",n),()=>{t.removeEventListener("change",n)}):"function"==typeof t.addListener?(t.addListener(n),()=>{t.removeListener(n)}):null}const s='\n:host {\n --oa-bg: #0b0d11;\n --oa-surface: #14161d;\n --oa-border: #1f2230;\n --oa-text: #e8e8ed;\n --oa-text-muted: #7a7d8c;\n --oa-accent: #4ae68a;\n --oa-accent-dim: rgba(74, 230, 138, 0.12);\n --oa-danger: #ef4444;\n --oa-warn: #f59e0b;\n --oa-radius: 16px;\n --oa-font: \'DM Sans\', system-ui, -apple-system,\n sans-serif;\n --oa-mono: \'Space Mono\', monospace;\n\n display: block;\n font-family: var(--oa-font);\n color: var(--oa-text);\n line-height: 1.4;\n}\n\n:host([data-theme="light"]) {\n --oa-bg: #f8f9fb;\n --oa-surface: #ffffff;\n --oa-border: #e2e4ea;\n --oa-text: #1a1c24;\n --oa-text-muted: #6b6e7b;\n --oa-accent: #22c55e;\n --oa-accent-dim: rgba(34, 197, 94, 0.1);\n}\n\n* { box-sizing: border-box; margin: 0; padding: 0; }\n\n/* ── Checkbox ────────────────────────────────── */\n\n.oa-checkbox {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n background: var(--oa-surface);\n border: 2px solid var(--oa-border);\n border-radius: var(--oa-radius);\n cursor: pointer;\n user-select: none;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.oa-checkbox:hover {\n border-color: var(--oa-text-muted);\n}\n\n.oa-checkbox.oa-verified {\n border-color: var(--oa-accent);\n cursor: default;\n}\n\n.oa-checkbox.oa-failed {\n border-color: var(--oa-danger);\n}\n\n.oa-checkbox.oa-retry {\n border-color: var(--oa-danger);\n}\n\n.oa-checkbox.oa-expired {\n border-color: var(--oa-warn);\n}\n\n.oa-checkbox.oa-loading {\n align-items: center;\n}\n\n.oa-check-box {\n width: 24px;\n height: 24px;\n border: 2px solid var(--oa-border);\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n transition: all 0.2s;\n}\n\n.oa-loading .oa-check-box {\n border-color: transparent;\n background: transparent;\n}\n\n.oa-loading .oa-check-box .oa-spinner {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n transform: translateY(2px);\n}\n\n.oa-verified .oa-check-box {\n background: var(--oa-accent);\n border-color: var(--oa-accent);\n color: var(--oa-bg);\n}\n\n.oa-failed .oa-check-box {\n background: var(--oa-danger);\n border-color: var(--oa-danger);\n color: white;\n}\n\n.oa-retry .oa-check-box {\n background: var(--oa-danger);\n border-color: var(--oa-danger);\n color: white;\n}\n\n.oa-check-box svg {\n width: 14px;\n height: 14px;\n}\n\n.oa-check-box .oa-spinner svg {\n width: 18px;\n height: 18px;\n}\n\n.oa-label {\n display: flex;\n flex-direction: column;\n gap: 1px;\n flex: 1;\n min-width: 0;\n}\n\n.oa-label-text {\n font-size: 13px;\n font-weight: 600;\n}\n\n.oa-right-section {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n margin-left: auto;\n}\n\n.oa-face-icon-wrap {\n width: 18px;\n height: 22px;\n color: var(--oa-text-muted);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.oa-face-icon-wrap .oa-face-icon-svg {\n width: 100%;\n height: 100%;\n}\n\n.oa-face-icon-wrap .oa-eye {\n transform-box: fill-box;\n transform-origin: center;\n animation: oa-idle-blink 5s ease-in-out infinite;\n}\n\n.oa-face-icon-wrap .oa-face-icon-svg {\n animation: oa-breathe 4s ease-in-out infinite;\n}\n\n.oa-branding-row {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n}\n\n.oa-branding-link {\n font-size: 10px;\n font-weight: 700;\n font-family: var(--oa-mono);\n color: var(--oa-text-muted);\n text-decoration: none;\n letter-spacing: -0.02em;\n cursor: pointer;\n}\n\n.oa-branding-link:hover {\n color: var(--oa-text);\n}\n\n.oa-links-row {\n display: flex;\n gap: 6px;\n justify-content: center;\n}\n\n.oa-links-row a {\n font-size: 9px;\n color: var(--oa-text-muted);\n text-decoration: none;\n opacity: 0.7;\n cursor: pointer;\n}\n\n.oa-links-row a:hover {\n opacity: 1;\n text-decoration: underline;\n}\n\n.oa-compact .oa-label-text {\n font-size: 11px;\n}\n\n.oa-compact .oa-face-icon-wrap {\n width: 16px;\n height: 20px;\n}\n\n/* ── Error banner (inline in checkbox) ───────── */\n\n.oa-error-banner {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n margin-top: 6px;\n background: rgba(239, 68, 68, 0.08);\n border: 1px solid rgba(239, 68, 68, 0.2);\n border-radius: 8px;\n font-size: 12px;\n color: var(--oa-danger);\n animation: oa-fade-in 0.3s ease;\n}\n\n.oa-error-banner button {\n margin-left: auto;\n background: none;\n border: 1px solid var(--oa-danger);\n border-radius: 6px;\n color: var(--oa-danger);\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n cursor: pointer;\n font-family: var(--oa-font);\n flex-shrink: 0;\n}\n\n.oa-error-banner button:hover {\n background: rgba(239, 68, 68, 0.1);\n}\n\n/* ── Popup / Modal shell ─────────────────────── */\n\n.oa-popup {\n position: fixed;\n z-index: 100000;\n background: var(--oa-bg);\n border: 1px solid var(--oa-border);\n border-radius: var(--oa-radius);\n box-shadow: 0 20px 60px rgba(0,0,0,0.4),\n 0 0 0 1px rgba(0,0,0,0.08);\n width: 340px;\n max-height: 90vh;\n overflow: hidden;\n animation: oa-popup-in 0.25s ease;\n display: flex;\n flex-direction: column;\n}\n\n.oa-modal-overlay {\n position: fixed;\n inset: 0;\n z-index: 99999;\n background: rgba(0,0,0,0.65);\n display: flex;\n align-items: center;\n justify-content: center;\n animation: oa-fade-in 0.2s ease;\n backdrop-filter: blur(6px);\n}\n\n.oa-modal {\n background: var(--oa-bg);\n border: 1px solid var(--oa-border);\n border-radius: var(--oa-radius);\n width: 360px;\n max-width: 95vw;\n max-height: 95vh;\n overflow: hidden;\n animation: oa-slide-in 0.3s ease;\n display: flex;\n flex-direction: column;\n}\n\n/* ── Header ──────────────────────────────────── */\n\n.oa-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 14px;\n border-bottom: 1px solid var(--oa-border);\n flex-shrink: 0;\n}\n\n.oa-logo {\n font-family: var(--oa-mono);\n font-size: 0.85rem;\n letter-spacing: -0.03em;\n color: var(--oa-text-muted);\n text-decoration: none;\n cursor: pointer;\n}\n\n.oa-logo:hover {\n color: var(--oa-text);\n}\n\n.oa-logo strong {\n color: var(--oa-text);\n font-weight: 700;\n}\n\n.oa-badge {\n font-size: 0.55rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--oa-accent);\n background: var(--oa-accent-dim);\n padding: 2px 6px;\n border-radius: 100px;\n margin-left: 6px;\n}\n\n.oa-title {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.oa-close-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n color: var(--oa-text-muted);\n transition: background 0.15s, color 0.15s;\n}\n\n.oa-close-btn:hover {\n background: var(--oa-border);\n color: var(--oa-text);\n}\n\n.oa-close-btn svg {\n width: 16px;\n height: 16px;\n}\n\n/* ── Body (viewport) ─────────────────────────── */\n\n.oa-body {\n flex: 1;\n position: relative;\n overflow: hidden;\n}\n\n/* ── Hero / start screen ─────────────────────── */\n\n.oa-hero {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n padding: 2rem 1.2rem 1.2rem;\n background: var(--oa-surface);\n border: 1px solid var(--oa-border);\n border-radius: var(--oa-radius);\n margin: 10px;\n animation: oa-fade-in 0.4s ease;\n}\n\n.oa-hero-icon {\n width: 80px;\n height: 96px;\n color: var(--oa-text-muted);\n}\n\n.oa-hero-icon .oa-face-svg.oa-idle {\n animation: oa-breathe 4s ease-in-out infinite;\n}\n\n.oa-hero-icon .oa-eye {\n transform-box: fill-box;\n transform-origin: center;\n animation: oa-idle-blink 5s ease-in-out infinite;\n}\n\n.oa-hero-status {\n color: var(--oa-text-muted);\n font-size: 0.8rem;\n font-weight: 500;\n text-align: center;\n min-height: 1.4em;\n}\n\n.oa-hero-privacy {\n font-size: 0.68rem;\n color: var(--oa-text-muted);\n text-align: center;\n line-height: 1.5;\n max-width: 260px;\n opacity: 0.7;\n}\n\n.oa-hero-privacy svg {\n width: 10px;\n height: 10px;\n vertical-align: -2px;\n margin-right: 2px;\n}\n\n/* ── Start / Retry button ────────────────────── */\n\n.oa-actions {\n display: flex;\n justify-content: center;\n padding: 0 14px 14px;\n flex-shrink: 0;\n}\n\n.oa-btn {\n font-family: var(--oa-font);\n padding: 0.55rem 1.5rem;\n border: none;\n border-radius: 10px;\n font-size: 0.8rem;\n font-weight: 600;\n cursor: pointer;\n background: var(--oa-accent);\n color: var(--oa-bg);\n transition: transform 0.12s, opacity 0.15s;\n width: 100%;\n max-width: 240px;\n}\n\n.oa-btn:hover { opacity: 0.88; }\n.oa-btn:active { transform: scale(0.97); }\n\n/* ── Video area ──────────────────────────────── */\n\n.oa-video-area {\n position: relative;\n width: 100%;\n aspect-ratio: 3/4;\n max-height: 55vh;\n background: var(--oa-surface);\n overflow: hidden;\n border-radius: var(--oa-radius);\n margin: 10px;\n width: calc(100% - 20px);\n border: 1px solid var(--oa-border);\n animation: oa-fade-in 0.3s ease;\n}\n\n.oa-video-area video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n transform: scaleX(-1);\n}\n\n/* ── Face guide overlay ──────────────────────── */\n\n.oa-face-guide {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 80%;\n max-width: 360px;\n aspect-ratio: 5/6;\n perspective: 400px;\n color: rgba(255, 255, 255, 0.45);\n pointer-events: none;\n z-index: 2;\n transition: opacity 0.4s ease;\n}\n\n.oa-face-guide .oa-face-svg {\n width: 100%;\n height: 100%;\n transform-origin: center center;\n filter: drop-shadow(\n 0 0 16px rgba(15, 23, 42, 0.16)\n );\n}\n\n.oa-face-guide .oa-eye {\n transform-box: fill-box;\n transform-origin: center;\n}\n\n.oa-face-guide[data-task="turn-left"] .oa-face-svg {\n animation: oa-turn-left 2s ease-in-out infinite;\n}\n.oa-face-guide[data-task="turn-right"] .oa-face-svg {\n animation: oa-turn-right 2s ease-in-out infinite;\n}\n.oa-face-guide[data-task="nod"] .oa-face-svg {\n animation: oa-nod 2s ease-in-out infinite;\n}\n.oa-face-guide[data-task="blink-twice"] .oa-eye {\n animation: oa-blink 2.4s ease-in-out infinite;\n}\n.oa-face-guide[data-task="move-closer"] .oa-face-svg {\n animation: oa-closer 2.5s ease-in-out infinite;\n}\n\n/* ── Challenge HUD (bottom gradient overlay) ── */\n\n.oa-challenge-hud {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 1rem 0.8rem 0.6rem;\n background: linear-gradient(\n to top,\n rgba(11, 13, 17, 0.92) 0%,\n rgba(11, 13, 17, 0) 100%\n );\n z-index: 3;\n pointer-events: none;\n}\n\n.oa-challenge-text {\n font-size: 0.85rem;\n font-weight: 600;\n text-align: center;\n color: var(--oa-text);\n text-shadow: 0 1px 6px rgba(0, 0, 0, 0.7);\n margin-bottom: 0.4rem;\n}\n\n.oa-challenge-bar {\n height: 3px;\n background: rgba(255, 255, 255, 0.1);\n border-radius: 2px;\n overflow: hidden;\n}\n\n.oa-challenge-fill {\n height: 100%;\n background: var(--oa-accent);\n border-radius: 2px;\n transition: width 0.25s ease;\n}\n\n/* ── Video status (top gradient overlay) ─────── */\n\n.oa-video-status {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n padding: 0.8rem 0.8rem 1.2rem;\n background: linear-gradient(\n to bottom,\n rgba(11, 13, 17, 0.85) 0%,\n rgba(11, 13, 17, 0) 100%\n );\n z-index: 3;\n pointer-events: none;\n}\n\n.oa-video-status p {\n font-size: 0.75rem;\n font-weight: 500;\n text-align: center;\n color: var(--oa-text-muted);\n text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);\n}\n\n/* ── Result area ─────────────────────────────── */\n\n.oa-result {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 0.5rem;\n padding: 2rem 1.2rem;\n background: var(--oa-surface);\n border: 1px solid var(--oa-border);\n border-radius: var(--oa-radius);\n margin: 10px;\n animation: oa-fade-in 0.4s ease;\n}\n\n.oa-result-icon {\n font-size: 2.4rem;\n line-height: 1;\n}\n\n.oa-result-text {\n font-size: 0.85rem;\n font-weight: 500;\n text-align: center;\n}\n\n.oa-result-pass { color: var(--oa-accent); }\n.oa-result-fail { color: var(--oa-danger); }\n.oa-result-retry { color: var(--oa-warn); }\n\n.oa-hidden { display: none !important; }\n\n/* ── Animations ──────────────────────────────── */\n\n@keyframes oa-popup-in {\n from { opacity: 0; transform: scale(0.95); }\n to { opacity: 1; transform: scale(1); }\n}\n\n@keyframes oa-fade-in {\n from { opacity: 0; transform: translateY(6px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n@keyframes oa-slide-in {\n from { opacity: 0; transform: translateY(16px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n@keyframes oa-breathe {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.04); }\n}\n\n@keyframes oa-idle-blink {\n 0%, 42%, 48%, 100% { transform: scaleY(1); }\n 44%, 46% { transform: scaleY(0.05); }\n}\n\n@keyframes oa-turn-left {\n 0%, 100% { transform: rotateY(0); }\n 35%, 65% { transform: rotateY(-30deg); }\n}\n\n@keyframes oa-turn-right {\n 0%, 100% { transform: rotateY(0); }\n 35%, 65% { transform: rotateY(30deg); }\n}\n\n@keyframes oa-nod {\n 0%, 100% { transform: rotateX(0); }\n 30%, 50% { transform: rotateX(25deg); }\n}\n\n@keyframes oa-blink {\n 0%, 18%, 32%, 50%, 100% { transform: scaleY(1); }\n 22%, 28% { transform: scaleY(0.05); }\n 40%, 46% { transform: scaleY(0.05); }\n}\n\n@keyframes oa-closer {\n 0%, 100% { transform: scale(1); }\n 35%, 55% { transform: scale(1.3); }\n}\n';function l(e){return`\n <div class="oa-hero">\n <div class="oa-hero-icon">\n ${n.replace('class="oa-face-svg"','class="oa-face-svg oa-idle"')}\n </div>\n <p class="oa-hero-status">${e}</p>\n <p class="oa-hero-privacy">\n <svg viewBox="0 0 24 24"\n fill="none" stroke="currentColor" stroke-width="1.5">\n <path d="M12 2l8 4v6c0 5.5-3.8 10-8 12\n C7.8 22 4 17.5 4 12V6l8-4z"/>\n <path d="M9 12l2 2 4-4" stroke-width="2"\n stroke-linecap="round" stroke-linejoin="round"/>\n</svg>\n Open-source & privacy-focused.\n No photos or camera data leave your device.\n </p>\n </div>\n `}function c(e,n){return`\n <div class="oa-result ${{fail:"oa-result-fail",retry:"oa-result-retry"}[e]||""}">\n <div class="oa-result-icon">\n ${{fail:"✕",retry:"↻"}[e]||"?"}\n </div>\n <div class="oa-result-text">${n}</div>\n </div>\n `}const d="1.0.0",u="https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.17",p=`${u}/wasm`,h=`${u}/vision_bundle.mjs`,f="https://cdn.jsdelivr.net/gh/justadudewhohacks/face-api.js@master/weights",m=12;let g=0;class v{constructor(e,n){this.id="oa-"+ ++g,this.params=n,this.container=function(e){if("string"==typeof e)return document.querySelector(e);return e}(e),this.anchorElement=null,this.state="idle",this.token=null,this.popup=null,this.shadow=null,this.elements={},this.onChallenge=null,this.onStartClick=null,this.popupFrame=0,this.themeCleanup=null,this.render()}render(){const e=document.createElement("div");e.id=this.id,this.shadow=e.attachShadow({mode:"open"});const n=document.createElement("style");n.textContent=s,this.shadow.appendChild(n);const a=t(this.params.theme);if(e.setAttribute("data-theme",a),this.themeCleanup=o(e,this.params.theme),"invisible"===this.params.size)return e.style.display="none",this.container.appendChild(e),void(this.host=e);const r=document.createElement("div");r.innerHTML=`\n <div class="oa-widget-wrap">\n <div class="oa-checkbox" role="checkbox"\n aria-checked="false" tabindex="0">\n <div class="oa-check-box"></div>\n <div class="oa-label">\n <span class="oa-label-text">${"I am of age"}</span>\n </div>\n <div class="oa-right-section">\n <div class="oa-branding-row">\n <div class="oa-face-icon-wrap">\n <svg viewBox="0 0 100 120"\n fill="none" class="oa-face-icon-svg">\n <ellipse cx="50" cy="55" rx="35" ry="45"\n stroke="currentColor" stroke-width="2"/>\n <ellipse cx="36" cy="45" rx="5" ry="3.5"\n fill="currentColor" class="oa-eye"/>\n <ellipse cx="64" cy="45" rx="5" ry="3.5"\n fill="currentColor" class="oa-eye"/>\n <line x1="50" y1="52" x2="48" y2="62"\n stroke="currentColor" stroke-width="1.65"\n stroke-linecap="round"/>\n <path d="M40 72 Q50 79 60 72"\n stroke="currentColor" stroke-width="1.65"\n stroke-linecap="round" fill="none"/>\n</svg>\n </div>\n <a class="oa-branding-link"\n href="https://github.com/tn3w/OpenAge"\n target="_blank" rel="noopener">OpenAge</a>\n </div>\n <div class="oa-links-row">\n <a href="https://github.com/tn3w/OpenAge"\n target="_blank"\n rel="noopener">Terms</a>\n <a href="https://github.com/tn3w/OpenAge"\n target="_blank"\n rel="noopener">Privacy</a>\n </div>\n </div>\n </div>\n <div class="oa-error-slot"></div>\n </div>\n `,this.shadow.appendChild(r.firstElementChild);const i=this.shadow.querySelector(".oa-checkbox");"compact"===this.params.size&&i.classList.add("oa-compact"),i.addEventListener("click",e=>{e.target.closest("a")||"verified"!==this.state&&"loading"!==this.state&&(this.clearError(),this.startChallenge())}),i.addEventListener("keydown",e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),i.click())}),this.elements.checkbox=i,this.elements.checkBox=this.shadow.querySelector(".oa-check-box"),this.elements.errorSlot=this.shadow.querySelector(".oa-error-slot"),this.container.appendChild(e),this.host=e}startChallenge(){this.setState("loading"),this.onChallenge&&this.onChallenge(this)}createPopupShell(){const e=t(this.params.theme),n=document.createElement("div");n.setAttribute("data-theme",e);const a=n.attachShadow({mode:"open"}),r=document.createElement("style");r.textContent=s,a.appendChild(r);return{popupHost:n,popupShadow:a,themeCleanup:o(n,this.params.theme)}}openPopup(){if(this.popup)return this.getVideo();const e=this.getPopupAnchor();if(!e)return this.openModal();const{popupHost:n,popupShadow:t,themeCleanup:o}=this.createPopupShell(),a=document.createElement("div");a.className="oa-popup",a.innerHTML=this.buildPopupContent(),a.style.visibility="hidden",a.style.pointerEvents="none",t.appendChild(a),document.body.appendChild(n),this.popup={anchor:e,host:n,root:a,themeCleanup:o};return"modal"===y(e.getBoundingClientRect(),a.getBoundingClientRect()).mode?(n.remove(),o?.(),this.popup=null,this.openModal()):(this.bindPopupEvents(a,t),this.updatePopupPosition(),this.startPopupTracking(),this.getVideo())}openModal(){const{popupHost:e,popupShadow:n,themeCleanup:t}=this.createPopupShell(),o=document.createElement("div");o.className="oa-modal-overlay";const a=document.createElement("div");return a.className="oa-modal",a.innerHTML=this.buildPopupContent(),o.appendChild(a),n.appendChild(o),document.body.appendChild(e),o.addEventListener("click",e=>{e.target===o&&this.closePopup()}),this.popup={host:e,root:a,overlay:o,themeCleanup:t},this.bindPopupEvents(a,n),this.getVideo()}getPopupAnchor(){return this.anchorElement||this.elements.checkbox||this.host||null}startPopupTracking(){if(!this.popup||this.popup.overlay)return;const e=()=>{this.schedulePopupPosition()},n=[],t=(t,o)=>{window.addEventListener(t,e,o),n.push(()=>{window.removeEventListener(t,e,o)})};if(t("resize",{passive:!0}),t("scroll",{capture:!0,passive:!0}),window.visualViewport){const t=window.visualViewport;t.addEventListener("resize",e),t.addEventListener("scroll",e),n.push(()=>{t.removeEventListener("resize",e),t.removeEventListener("scroll",e)})}if("function"==typeof ResizeObserver){const t=new ResizeObserver(()=>{e()});t.observe(this.popup.root),t.observe(this.popup.anchor),t.observe(document.documentElement),document.body&&t.observe(document.body),n.push(()=>t.disconnect())}this.popup.cleanup=()=>{this.popupFrame&&(cancelAnimationFrame(this.popupFrame),this.popupFrame=0);for(const e of n)e()}}schedulePopupPosition(){this.popup&&!this.popup.overlay&&(this.popupFrame||(this.popupFrame=requestAnimationFrame(()=>{this.popupFrame=0,this.updatePopupPosition()})))}updatePopupPosition(){if(!this.popup||this.popup.overlay)return;const e=this.getPopupAnchor();if(!e||!e.isConnected)return void this.closePopup();this.popup.anchor=e;const n=y(e.getBoundingClientRect(),this.popup.root.getBoundingClientRect());if("modal"===n.mode)return this.closePopup(),void this.openModal();const t=`${Math.round(n.top)}px`,o=`${Math.round(n.left)}px`;this.popup.root.style.top!==t&&(this.popup.root.style.top=t),this.popup.root.style.left!==o&&(this.popup.root.style.left=o),this.popup.root.dataset.placement=n.placement,this.popup.root.style.visibility="visible",this.popup.root.style.pointerEvents="auto"}buildPopupContent(){return`\n <div class="oa-header">\n <div class="oa-title">\n <a class="oa-logo"\n href="https://github.com/tn3w/OpenAge"\n target="_blank" rel="noopener">\n Open<strong>Age</strong>\n </a>\n <span class="oa-badge">on-device</span>\n </div>\n <button class="oa-close-btn"\n aria-label="Close">\n <svg viewBox="0 0 24 24"\n fill="none" stroke="currentColor" stroke-width="2"\n stroke-linecap="round">\n <line x1="18" y1="6" x2="6" y2="18"/>\n <line x1="6" y1="6" x2="18" y2="18"/>\n</svg>\n </button>\n </div>\n <div class="oa-body">\n ${l("Initializing…")}\n </div>\n <div class="oa-actions oa-hidden">\n <button class="oa-btn oa-start-btn">\n Begin Verification\n </button>\n </div>\n `}bindPopupEvents(e,n){const t=e.querySelector(".oa-close-btn");t&&t.addEventListener("click",()=>{this.closePopup(),this.params.closeCallback?.()});const o=e.querySelector(".oa-start-btn");o&&o.addEventListener("click",()=>{this.onStartClick&&this.onStartClick()}),this.popupElements={body:e.querySelector(".oa-body"),actions:e.querySelector(".oa-actions"),startBtn:o,heroStatus:e.querySelector(".oa-hero-status")}}getVideo(){return this.popup?this.popup.root.querySelector("video"):null}showHero(e){this.popupElements?.body&&(this.popupElements.body.innerHTML=l(e),this.popupElements.heroStatus=this.popupElements.body.querySelector(".oa-hero-status"),this.hideActions())}showReady(){this.setHeroStatus("Ready to verify your age."),this.showActions("Begin Verification")}showCamera(){if(this.popupElements?.body)return this.popupElements.body.innerHTML='\n <div class="oa-video-area">\n <video autoplay playsinline muted></video>\n <div class="oa-face-guide">\n <svg viewBox="0 0 100 120"\n fill="none" class="oa-face-svg">\n <ellipse cx="50" cy="55" rx="35" ry="45"\n stroke="currentColor" stroke-width="1.8"\n stroke-dasharray="6 4"/>\n <ellipse cx="36" cy="45" rx="5" ry="3.5"\n fill="currentColor" class="oa-eye"/>\n <ellipse cx="64" cy="45" rx="5" ry="3.5"\n fill="currentColor" class="oa-eye"/>\n <line x1="50" y1="52" x2="48" y2="62"\n stroke="currentColor" stroke-width="1.5"\n stroke-linecap="round"/>\n <path d="M40 72 Q50 79 60 72"\n stroke="currentColor" stroke-width="1.5"\n stroke-linecap="round" fill="none"/>\n</svg>\n </div>\n <div class="oa-challenge-hud oa-hidden">\n <p class="oa-challenge-text"></p>\n <div class="oa-challenge-bar">\n <div class="oa-challenge-fill"\n style="width:0%"></div>\n </div>\n </div>\n <div class="oa-video-status">\n <p></p>\n </div>\n </div>\n ',this.popupElements.video=this.popupElements.body.querySelector("video"),this.popupElements.faceGuide=this.popupElements.body.querySelector(".oa-face-guide"),this.popupElements.challengeHud=this.popupElements.body.querySelector(".oa-challenge-hud"),this.popupElements.challengeText=this.popupElements.body.querySelector(".oa-challenge-text"),this.popupElements.challengeFill=this.popupElements.body.querySelector(".oa-challenge-fill"),this.popupElements.videoStatus=this.popupElements.body.querySelector(".oa-video-status p"),this.hideActions(),this.popupElements.video}showLiveness(){this.popupElements?.faceGuide&&this.popupElements.faceGuide.classList.remove("oa-hidden"),this.popupElements?.challengeHud&&this.popupElements.challengeHud.classList.remove("oa-hidden")}setHeroStatus(e){this.popupElements?.heroStatus&&(this.popupElements.heroStatus.textContent=e)}setVideoStatus(e){this.popupElements?.videoStatus&&(this.popupElements.videoStatus.textContent=e)}setInstruction(e){this.popupElements?.challengeText&&(this.popupElements.challengeText.textContent=e)}setStatus(e){this.setVideoStatus(e)}setProgress(e){this.popupElements?.challengeFill&&(this.popupElements.challengeFill.style.width=`${Math.round(100*e)}%`)}setTask(e){this.popupElements?.faceGuide&&this.popupElements.faceGuide.setAttribute("data-task",e||"")}showActions(e){this.popupElements?.actions&&(this.popupElements.actions.classList.remove("oa-hidden"),this.popupElements.startBtn&&(this.popupElements.startBtn.textContent=e))}hideActions(){this.popupElements?.actions&&this.popupElements.actions.classList.add("oa-hidden")}showResult(e,n){if("pass"===e)return this.closePopup(),void this.setState("verified");if("fail"!==e){if("retry"===e){if("invisible"!==this.params.size)return this.closePopup(),void this.setState("retry");this.popupElements?.body&&(this.popupElements.body.innerHTML=c(e,n)),this.hideActions(),this.showActions("Try Again")}}else"invisible"===this.params.size?(this.popupElements?.body&&(this.popupElements.body.innerHTML=c(e,n)),this.hideActions(),this.showActions("Try Again")):(this.closePopup(),this.setState("retry"))}showError(){this.setState("retry")}clearError(){this.elements.errorSlot&&(this.elements.errorSlot.innerHTML="")}closePopup(){this.popup&&(this.popup.cleanup?.(),this.popup.themeCleanup?.(),this.popup.host.remove(),this.popup=null,this.popupElements=null,"loading"===this.state&&this.setState("idle"))}setState(e){this.state=e;const n=this.elements.checkbox,t=this.elements.checkBox;if(n&&t)switch(n.classList.remove("oa-loading","oa-verified","oa-failed","oa-retry","oa-expired"),n.setAttribute("aria-checked","false"),t.innerHTML="",e){case"loading":n.classList.add("oa-loading"),t.innerHTML='<span class="oa-spinner"><svg viewBox="0 0 24 24"\n fill="none" stroke="currentColor" stroke-width="2.5">\n <circle cx="12" cy="12" r="10" opacity="0.2"/>\n <path d="M12 2 A10 10 0 0 1 22 12"\n stroke-linecap="round">\n <animateTransform attributeName="transform"\n type="rotate" from="0 12 12" to="360 12 12"\n dur="0.8s" repeatCount="indefinite"/>\n </path>\n</svg></span>';break;case"verified":n.classList.add("oa-verified"),n.setAttribute("aria-checked","true"),t.innerHTML='<svg viewBox="0 0 24 24"\n fill="none" stroke="currentColor" stroke-width="2.5"\n stroke-linecap="round" stroke-linejoin="round">\n <polyline points="20 6 9 17 4 12"/>\n</svg>';break;case"failed":n.classList.add("oa-failed"),t.innerHTML="✕";break;case"retry":n.classList.add("oa-retry"),t.innerHTML='<svg viewBox="0 0 16 16"\n xmlns="http://www.w3.org/2000/svg"\n fill="currentColor">\n <path d="m14.955 7.986.116.01a1 1 0 0 1 .85 1.13 8 8 0 0 1-13.374 4.728l-.84.84c-.63.63-1.707.184-1.707-.707V10h3.987c.89 0 1.337 1.077.707 1.707l-.731.731a6 6 0 0 0 8.347-.264 6 6 0 0 0 1.63-3.33 1 1 0 0 1 1.131-.848zM11.514.813a8 8 0 0 1 1.942 1.336l.837-.837c.63-.63 1.707-.184 1.707.707V6h-3.981c-.89 0-1.337-1.077-.707-1.707l.728-.729a6 6 0 0 0-9.98 3.591 1 1 0 1 1-1.98-.281A8 8 0 0 1 11.514.813"/>\n</svg>';break;case"expired":n.classList.add("oa-expired"),n.setAttribute("aria-checked","false")}}getToken(){return this.token}reset(){this.token=null,this.closePopup(),this.setState("idle")}destroy(){this.closePopup(),this.themeCleanup?.(),this.host?.remove()}}function y(e,n){const t={width:window.innerWidth,height:window.innerHeight},o=Math.max(n.width||0,340),a=Math.max(n.height||0,520);if(o>t.width-24||a>t.height-24)return{mode:"modal"};const r=function(e,n,t){return Math.min(Math.max(m,e),n-t-m)}(e.left+e.width/2-o/2,t.width,o),i=e.bottom+m,s=e.top-a-m;return i+a<=t.height-m||!(s>=m)?{mode:"popup",placement:"below",top:w(i,t.height,a),left:r}:{mode:"popup",placement:"above",top:w(s,t.height,a),left:r}}function w(e,n,t){return Math.min(Math.max(m,e),n-t-m)}let b=null,x=null,k=-1,E=null;async function C(){return E||(E=await import(h),b=E.FaceLandmarker,E)}async function S(e){const n=await C(),t=await n.FilesetResolver.forVisionTasks(p);x&&x.close(),k=-1,x=await b.createFromOptions(t,{baseOptions:{modelAssetBuffer:new Uint8Array(e),delegate:"GPU"},runningMode:"VIDEO",numFaces:2,outputFaceBlendshapes:!0,outputFacialTransformationMatrixes:!0})}function P(e,n){if(!x)return null;const t=function(e){const n=Number.isFinite(e)?e:performance.now(),t=Math.floor(n),o=Math.max(t,k+1);return k=o,o}(n),o=x.detectForVideo(e,t),a=o.faceLandmarks?.length??0;if(0===a)return{faceCount:0,timestampMs:t};const r=o.faceLandmarks[0];return{faceCount:a,timestampMs:t,landmarks:r,blendshapes:A(o.faceBlendshapes?.[0]),headPose:T(o.facialTransformationMatrixes?.[0]),boundingBox:L(r)}}function M(){x&&(x.close(),x=null),k=-1}function A(e){if(!e?.categories)return{};const n={};for(const t of e.categories)n[t.categoryName]=t.score;return n}function T(e){if(!e?.data||e.data.length<16)return{yaw:0,pitch:0,roll:0};const n=e.data,t=180/Math.PI;return{yaw:Math.atan2(n[8],n[10])*t,pitch:Math.asin(-Math.max(-1,Math.min(1,n[9])))*t,roll:Math.atan2(n[1],n[5])*t}}function L(e){let n=1,t=1,o=0,a=0;for(const r of e)r.x<n&&(n=r.x),r.y<t&&(t=r.y),r.x>o&&(o=r.x),r.y>a&&(a=r.y);const r=o-n,i=a-t;return{x:n,y:t,width:r,height:i,area:r*i}}let _=!1,V=null;async function $(){var e;_||(V=await(e="https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/dist/face-api.min.js",new Promise((n,t)=>{if("undefined"==typeof window)return t(new Error("No DOM available"));const o=document.querySelector(`script[src="${e}"]`);if(o)return window.faceapi?n(window.faceapi):void o.addEventListener("load",()=>n(window.faceapi));const a=document.createElement("script");a.src=e,a.crossOrigin="anonymous",a.onload=()=>n(window.faceapi),a.onerror=()=>t(new Error(`Failed to load ${e}`)),document.head.appendChild(a)})),await Promise.all([V.nets.tinyFaceDetector.loadFromUri(f),V.nets.faceLandmark68TinyNet.loadFromUri(f),V.nets.ageGenderNet.loadFromUri(f)]),_=!0)}async function H(e){if(!V)throw new Error("Age estimator not initialized");const n=await V.detectSingleFace(e,new V.TinyFaceDetectorOptions({inputSize:224})).withFaceLandmarks(!0).withAgeAndGender();return n?{age:n.age,gender:n.gender,confidence:n.detection.score}:null}const O=[{id:"turn-left",instruction:"Turn your head to the left",check:e=>j(e,20)},{id:"turn-right",instruction:"Turn your head to the right",check:e=>j(e,-20)},{id:"nod",instruction:"Nod your head down then up",check:e=>function(e){if(e.length<10)return!1;const n=e[0].headPose.pitch;let t=!1,o=!1;for(const a of e){const e=a.headPose.pitch-n;e>15&&(t=!0),t&&Math.abs(e)<8&&(o=!0)}return t&&o}(e)},{id:"blink-twice",instruction:"Blink twice",check:e=>function(e){if(e.length<10)return!1;let n=0,t=!1;for(const o of e){const e=o.blendshapes.eyeBlinkLeft??0,a=o.blendshapes.eyeBlinkRight??0,r=e>.6&&a>.6;r&&!t?(n++,t=!0):r||(t=!1)}return n>=2}(e)},{id:"move-closer",instruction:"Move closer then back",check:e=>function(e){if(e.length<10)return!1;const n=e[0].boundingBox.area;let t=!1,o=!1;for(const a of e){const e=a.boundingBox.area/n;e>1.3&&(t=!0),t&&e<1.15&&(o=!0)}return t&&o}(e)}];function B(e=3){return[...O].sort(()=>Math.random()-.5).slice(0,e)}function R(e,n){if(e.failed||z(e))return;if(e.currentIndex>=e.tasks.length)return;if(!n||0===n.faceCount)return;if(n.faceCount>1)return e.failed=!0,void(e.failReason=null);const t={timestamp:n.timestampMs,headPose:n.headPose,blendshapes:n.blendshapes,boundingBox:n.boundingBox};0===e.history.length&&(e.taskStartTime=n.timestampMs),e.history.push(t);const o=n.timestampMs-e.taskStartTime;if(o>8e3)return void F(e);if(o<500)return;if(e.tasks[e.currentIndex].check(e.history)){if(function(e){if(e.length<5)return!1;const n=[];for(let t=1;t<e.length;t++){const o=Math.abs(e[t].headPose.yaw-e[t-1].headPose.yaw),a=Math.abs(e[t].headPose.pitch-e[t-1].headPose.pitch);n.push(o+a)}if(n.every(e=>e<.1))return!0;const t=n.reduce((e,n)=>e+n,0)/n.length;return n.reduce((e,n)=>e+(n-t)**2,0)/n.length<.01&&t>.5}(e.history))return e.failed=!0,void(e.failReason=null);e.completedTasks++,F(e)}}function z(e){return e.currentIndex>=e.tasks.length}function F(e){e.currentIndex++,e.history=[],e.taskStartTime=0;const n=e.tasks.length-e.currentIndex;e.completedTasks+n>=e.requiredPasses||(e.failed=!0,e.failReason=null)}function j(e,n){if(e.length<5)return!1;const t=e[0].headPose.yaw,o=Math.sign(n),a=Math.abs(n);return e.some(e=>(e.headPose.yaw-t)*o>a)}function q(e){const n="string"==typeof e?e:String.fromCharCode(...new Uint8Array(e));return btoa(n).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function N(e){const n=e+"=".repeat((4-e.length%4)%4),t=atob(n.replace(/-/g,"+").replace(/_/g,"/"));return Uint8Array.from(t,e=>e.charCodeAt(0))}let I=null;async function U(){return I||(I=await async function(){const e=crypto.getRandomValues(new Uint8Array(32));return crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign","verify"])}()),I}async function D(e){const n=await U(),[t,o,a]=e.split(".");if(!t||!o||!a)return null;let r,i;try{i=(new TextEncoder).encode(`${t}.${o}`),r=N(a)}catch{return null}if(!await crypto.subtle.verify("HMAC",n,r,i))return null;const s=JSON.parse((new TextDecoder).decode(N(o)));return s.exp&&s.exp<Date.now()/1e3?null:s}function J(e){const[,n]=e.split(".");return n?JSON.parse((new TextDecoder).decode(N(n))):null}function Y(e,n={}){if("serverless"===e)return{async verify(e){const{estimatedAge:n,livenessOk:t}=e;if(!t)return{success:!1,token:null};const o=await async function(e){const n=await U(),t=q(JSON.stringify({alg:"HS256",typ:"JWT"})),o=q(JSON.stringify({...e,iat:Math.floor(Date.now()/1e3),exp:Math.floor(Date.now()/1e3)+300})),a=(new TextEncoder).encode(`${t}.${o}`);return`${t}.${o}.${q(await crypto.subtle.sign("HMAC",n,a))}`}({estimatedAge:n,livenessOk:!0,mode:"serverless"});return{success:!0,token:o}},close(){}};return function(e,n){let t=null,o=null;return{async createSession(){const o=[];"undefined"!=typeof WebSocket&&o.push("websocket"),o.push("poll");const a=await fetch(`${e}/api/session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sitekey:n.sitekey,action:n.action,supportedTransports:o})});if(!a.ok)throw new Error("Request failed");return t=await a.json(),t},openChannel(){if(!t)throw new Error("No session");o="websocket"===t.transport?function(e,n){const t=e.replace(/^http/,"ws").replace(/\/$/,""),o=new WebSocket(`${t}/api/ws/${n}`);let a=[],r=!1;const i=new Promise((e,n)=>{o.onopen=()=>{e()},o.onerror=()=>{n(new Error("Connection failed"))}});return o.onmessage=e=>{const n=JSON.parse(e.data),t=a.shift();t&&t(n)},o.onclose=()=>{r=!0;for(const e of a)e(null);a=[]},{receive:()=>r?Promise.resolve(null):new Promise(e=>{a.push(e)}),async send(e){await i,o.send(JSON.stringify(e))},async sendAndReceive(e){return await i,o.send(JSON.stringify(e)),this.receive()},close(){r=!0,o.close()}}}(e,t.sessionId):function(e,n){return{async receive(){const t=await fetch(`${e}/api/poll/${n}`);if(!t.ok)throw new Error("Request failed");return t.json()},async send(t){if(!(await fetch(`${e}/api/verify/${n}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).ok)throw new Error("Request failed")},async sendAndReceive(t){const o=await fetch(`${e}/api/verify/${n}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});if(!o.ok)throw new Error("Request failed");return o.json()},close(){}}}(e,t.sessionId)},receive:async()=>o?o.receive():null,async send(e){if(o)return o.send(e)},sendAndReceive:async e=>o?o.sendAndReceive(e):null,async verify(t){if(o)return this.verifyViaChannel(t);const a=await fetch(`${e}/verify`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({response:t.token,sitekey:n.sitekey,action:n.action})});return a.ok?a.json():{success:!1,token:null}},verifyViaChannel:async e=>(await o.send(e),o.receive()),getSession:()=>t,close(){o?.close(),o=null,t=null}}}("custom"===e?n.server:"https://api.openage.dev",n)}let G=null,W=null,Q=null,X=null,K=null,Z=null;async function ee(e){var n;W=e,await(n=e.wasmJs,new Promise((e,t)=>{if(document.querySelector(`script[src="${n}"]`))return e();const o=document.createElement("script");o.src=n,o.onload=e,o.onerror=()=>{t(new Error(`Failed to load ${n}`))},document.head.appendChild(o)}));const t=await import(e.loaderJs);G=await t.initModule(e.wasmBin);const o=e.exports.vm_init;if(0!==G[`_${o}`]())throw new Error("VM init failed");const a=await fetch(e.challengeVmbc);Q=new Uint8Array(await a.arrayBuffer())}function ne(e,n){return e.HEAPU8[n]|e.HEAPU8[n+1]<<8|e.HEAPU8[n+2]<<16|e.HEAPU8[n+3]<<24}function te(e,n){try{delete window[e]}catch(e){}Object.defineProperty(window,e,{get:n,set(){},configurable:!0})}function oe(e){X=e,te("__vmFaceData",()=>X)}function ae(e){K=e,te("__vmChallenge",()=>K)}function re(){if(!G||!Q)throw new Error("VM not loaded");const e=W.exports.vm_exec_bytecode,n=W.exports.vm_free,t=Q.length,o=G._malloc(t);G.HEAPU8.set(Q,o);const a=G._malloc(4),r=G[`_${e}`](o,t,a);if(G._free(o),!r){G._free(a);const e=W.exports.vm_last_error;let n="VM execution failed";if(e){const t=G[`_${e}`]();if(t){const e=G.UTF8ToString(t);e&&(n=e)}}throw new Error(n)}const i=ne(G,a);G._free(a);const s=new Uint8Array(i);return s.set(G.HEAPU8.subarray(r,r+i)),G[`_${n}`](r),s}function ie(e){let n="";for(let t=0;t<e.length;t++)n+=String.fromCharCode(e[t]);return btoa(n)}const se=new Map;async function le(e,n){if(se.has(n))return se.get(n);const t=await async function(e,n){if(!G||!W)throw new Error("VM not loaded");const t=e.models[n];if(!t)throw new Error(`Unknown model: ${n}`);const o=await fetch(t.url);if(!o.ok)throw new Error(`Failed to fetch model: ${n}`);const a=new Uint8Array(await o.arrayBuffer()),r=e.exports.vm_decrypt_blob,i=e.exports.vm_free,s=a.length,l=G._malloc(s);G.HEAPU8.set(a,l);const c=G._malloc(4),d=G[`_${r}`](l,s,c);if(G._free(l),!d)throw G._free(c),new Error(`Decryption failed: ${n}`);const u=ne(G,c);G._free(c);const p=new Uint8Array(u);return p.set(G.HEAPU8.subarray(d,d+u)),G[`_${i}`](d),p.buffer}(e,n);return se.set(n,t),t}let ce=null,de=null;const ue={video:{facingMode:"user",width:{ideal:640},height:{ideal:480}}};function pe(e){return navigator.mediaDevices.getUserMedia(ue).then(n=>(ce=n,e.srcObject=ce,de=e,new Promise(n=>{e.onloadedmetadata=()=>{e.play(),n({width:e.videoWidth,height:e.videoHeight})}})))}function he(){if(!de)return null;const e=document.createElement("canvas");e.width=de.videoWidth,e.height=de.videoHeight;return e.getContext("2d").drawImage(de,0,0),e}function fe(){ce&&(ce.getTracks().forEach(e=>e.stop()),ce=null),de&&(de.srcObject=null,de=null)}const me={"turn-left":"Turn your head left","turn-right":"Turn your head right",nod:"Nod your head","blink-twice":"Blink twice","move-closer":"Move closer then back"};function ge(e){return new Promise(n=>setTimeout(n,e))}async function ve(e,n){const t=e.params.mode||"serverless";if("serverless"!==t&&!e.params.sitekey){const t=new Error("Configuration error");return e.showResult?.("fail","Verification failed"),n.emit("error",t,e.id),void e.params.errorCallback?.(t)}return"serverless"===t?async function(e,n){const t=e.params;let o=0,a=null;try{e.openPopup(),e.setHeroStatus("Loading…"),await Promise.all([C(),$()]),a=await async function(){const e=await fetch("https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task");if(!e.ok)throw new Error("Failed to load face landmarker model");return new Uint8Array(await e.arrayBuffer())}(),e.showReady(),await be(e),await xe(e,a);const r=Y("serverless",t),i=async()=>{e.showLiveness(),e.setInstruction(""),e.setVideoStatus("Verifying…");const n={tasks:B(),currentIndex:0,history:[],taskStartTime:0,completedTasks:0,requiredPasses:2,failed:!1,failReason:null};if(await async function(e,n,t){return new Promise(o=>{const a=()=>{const r=P(e,performance.now());r&&R(n,r),t.setInstruction(function(e){return e.currentIndex>=e.tasks.length?null:e.tasks[e.currentIndex].instruction}(n)||"Done"),t.setTask(function(e){return e.currentIndex>=e.tasks.length?null:e.tasks[e.currentIndex].id}(n)),t.setProgress(function(e){return 0===e.tasks.length?1:Math.min(e.currentIndex/e.tasks.length,1)}(n)),t.setVideoStatus(`Check ${Math.min(n.currentIndex+1,n.tasks.length)} of ${n.tasks.length}`),n.failed||z(n)?o():requestAnimationFrame(a)};requestAnimationFrame(a)})}(e.popupElements.video,n,e),n.failed||!function(e){return e.completedTasks>=e.requiredPasses}(n))return{outcome:"retry"};e.setInstruction("Hold still…"),e.setVideoStatus("Processing…");const t=await async function(e,n){const t=[];for(let o=0;o<e;o++){const a=he();a&&t.push(a),o<e-1&&await ge(n)}return t}(5,200),o=await async function(e){const n=[];for(const t of e){const e=await H(t);e&&n.push(e)}return n}(t),a=function(e){if(!e||0===e.length)return null;const n=e.map(e=>e.age).sort((e,n)=>e-n),t=n.length>=3?n.slice(1,-1):n;return t.reduce((e,n)=>e+n,0)/t.length}(o),i=await r.verify({estimatedAge:a,livenessOk:!0});return{outcome:i.token?"pass":"fail",token:i.token}};let s=await i();for(;"retry"===s.outcome&&o<3;)o++,e.showResult("retry","Please try again"),await be(e),await xe(e,a),s=await i();Ee(),function(e,n,t){const o=e.params;"pass"===t.outcome?(e.token=t.token||null,e.showResult("pass","Verified"),n.emit("verified",t.token,e.id),o.callback?.(t.token)):(e.showResult("fail","Verification failed"),n.emit("error","failed",e.id),o.errorCallback?.("failed"))}(e,n,s)}catch(o){Ee(),console.log("Error during challenge:",o),e.showResult("fail","Verification failed"),n.emit("error",o,e.id),t.errorCallback?.(o)}}(e,n):async function(e,n){const t=e.params;try{e.openPopup(),e.setHeroStatus("Connecting…");const a=Y(t.mode,t),r=await a.createSession();e.setHeroStatus("Loading…"),await ee(r),e.setHeroStatus("Preparing…"),await async function(e){await le(e,"mediapipe")}(r),await C();const i=await async function(e){return le(e,"mediapipe")}(r);await S(i),o={trackFace:()=>{const n=e.popupElements?.video;if(!n)return"null";const t=P(n,performance.now());return t?JSON.stringify({ts:t.timestampMs??performance.now(),faceCount:t.faceCount,headPose:t.headPose||null,blendshapes:t.blendshapes||null,boundingBox:t.boundingBox||null}):"null"},captureFrame:()=>he()?"true":"null"},Z=Object.freeze({...o}),te("__vmBridge",()=>Z),e.showReady(),await be(e);const s=e.showCamera();e.setVideoStatus("Requesting camera…"),await pe(s),function(e){if(Se(),!e?.srcObject)return;const n=document.createElement("video");n.id="__openage_mirror",n.srcObject=e.srcObject,n.autoplay=!0,n.muted=!0,n.playsInline=!0,n.style.cssText="position:fixed;width:1px;height:1px;opacity:0;pointer-events:none;z-index:-1;",document.body.appendChild(n)}(s),e.setVideoStatus("Position your face"),await ke(s,e),e.showLiveness(),a.openChannel();let l=await a.receive();const c=r.rounds;for(let t=0;t<c;t++){if(!l)return Ce(a),e.showResult("fail","Verification failed"),void n.emit("error","failed",e.id);if("verdict"===l.type)return Ce(a),void ye(e,n,l);if("timeout"===l.type)return Ce(a),e.showResult("fail","Verification failed"),void n.emit("error","failed",e.id);e.setVideoStatus(`Step ${t+1} of ${c}`);const o=l.token?.task;e.setInstruction(me[o]??"Look at the camera"),e.setTask(o),e.setProgress(t/c);let r;oe(await we(e)),ae(l.token);try{r=re()}catch{return Ce(a),e.showResult("fail","Verification failed"),void n.emit("error","failed",e.id)}const i={token:l.token,tokenSignature:l.tokenSignature,response:ie(r)},s=await a.sendAndReceive(i);if(!s)return Ce(a),e.showResult("fail","Verification failed"),void n.emit("error","failed",e.id);if(s.complete)return Ce(a),void ye(e,n,s);s.hint&&(e.setVideoStatus(s.hint),await ge(1e3)),l=s.nextChallenge||null}Ce(a)}catch(o){console.log("Error during challenge:",o),Ce(),e.showResult("fail","Verification failed"),n.emit("error",o,e.id),t.errorCallback?.(o)}var o}(e,n)}function ye(e,n,t){const o=t?.verdict||t,a=o?.token||null,r=e.params;if(a)return e.token=a,e.showResult("pass","Verified"),n.emit("verified",a,e.id),void r.callback?.(a);e.showResult("fail","Verification failed"),n.emit("error","failed",e.id),r.errorCallback?.("failed")}async function we(e){const n=e.popupElements?.video,t=[],o=performance.now();for(;performance.now()-o<3e3;){const e=P(n,performance.now());e&&1===e.faceCount&&t.push({ts:e.timestampMs,headPose:e.headPose,blendshapes:e.blendshapes,boundingBox:e.boundingBox}),await ge(100)}return{faceCount:t.length>0?1:0,motionHistory:t}}function be(e){return new Promise(n=>{e.onStartClick=()=>{e.onStartClick=null,n()}})}async function xe(e,n){const t=e.showCamera();e.setVideoStatus("Requesting camera…"),await pe(t),e.setVideoStatus("Preparing…"),await S(n),e.setVideoStatus("Position your face"),await ke(t,e)}function ke(e,n){return new Promise((t,o)=>{const a=function(e,n){let t=0,o=!1;const a=()=>{if(o)return;const r=P(e,performance.now());r&&0!==r.faceCount?r.faceCount>1?(n.onStatus?.("Only one person please"),t=0):(n.onStatus?.("Hold still…"),t++):(n.onStatus?.("Look at the camera"),t=0),t>=10?n.onReady?.():setTimeout(a,100)};return a(),{cancel:()=>{o=!0}}}(e,{onStatus:e=>n.setVideoStatus(e),onReady:()=>t()});setTimeout(()=>{a.cancel(),o(new Error("Positioning timeout"))},3e4)})}function Ee(){fe(),M()}function Ce(e){fe(),Se(),M(),function(){Z=null;try{delete window.__vmBridge}catch(e){}}(),function(){if(!G||!W)return;const e=W.exports.vm_destroy;e&&G[`_${e}`](),G=null,W=null,Q=null,X=null,K=null,Z=null;for(const e of["__vmFaceData","__vmChallenge","__vmBridge"])try{delete window[e]}catch(e){}}(),se.clear(),e?.close()}function Se(){document.getElementById("__openage_mirror")?.remove()}class Pe{constructor(){this.listeners=new Map}on(e,n){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(n),this}off(e,n){const t=this.listeners.get(e);return t&&t.delete(n),this}once(e,n){const t=(...o)=>{this.off(e,t),n(...o)};return t._original=n,this.on(e,t)}emit(e,...n){const t=this.listeners.get(e);if(t)for(const e of[...t])e(...n)}removeAllListeners(e){return e?this.listeners.delete(e):this.listeners.clear(),this}}const Me=new Pe,Ae=new Map;function Te(e){return{mode:"serverless",theme:"auto",size:"normal",minAge:18,..."undefined"!=typeof window&&window.openage||{},...e}}function Le(e,n={}){const t=Te(n),o=new v(e,t);return Ae.set(o.id,o),function(e){e.onChallenge=()=>{Me.emit("opened",e.id),ve(e,Me)}}(o),o.id}function _e(e={}){const n=Te(e),t=function(e){return new v(document.createElement("div"),{...e,size:"invisible"})}(n);return t.anchorElement=n.anchorElement||null,Ae.set(t.id,t),t.onChallenge=()=>{Me.emit("opened",t.id),ve(t,Me)},t.startChallenge(),t.id}function Ve(e,n={}){const t=Te(n),o="string"==typeof e?document.querySelector(e):e;if(!o)throw new Error("OpenAge: element not found");let a=!1,r=null;const i=()=>{r=null},s=e=>{if(a||r)return;e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation?.();const n=_e({...t,anchorElement:o,callback:e=>{t.callback?.(e),i(),(()=>{a=!0;try{o.click()}finally{a=!1}})()},errorCallback:e=>{i(),t.errorCallback?.(e)},closeCallback:()=>{i(),t.closeCallback?.()}});return r=n,n};return o.addEventListener("click",s,!0),()=>{o.removeEventListener("click",s,!0)}}function $e(e){Ae.get(e)?.reset()}function He(e){const n=Ae.get(e);n&&(n.destroy(),Ae.delete(e))}function Oe(e){return Ae.get(e)?.getToken()||null}function Be(e){const n=Ae.get(e);n&&n.startChallenge()}function Re(e={}){return new Promise((n,t)=>{_e({...e,callback:e=>n(e),errorCallback:e=>t("string"==typeof e?new Error(e):e),closeCallback:()=>t(new Error("User dismissed"))})})}function ze(e,n){Me.on(e,n)}function Fe(e,n){Me.off(e,n)}function je(e,n){Me.once(e,n)}function qe(){if("undefined"==typeof document)return;if("explicit"===("undefined"!=typeof window&&window.openage||{}).render)return;const e=document.querySelectorAll(".openage");for(const n of e){const e={sitekey:n.dataset.sitekey,theme:n.dataset.theme,size:n.dataset.size,action:n.dataset.action,mode:n.dataset.mode,server:n.dataset.server};if(n.dataset.callback&&(e.callback=e=>{const t=window[n.dataset.callback];"function"==typeof t&&t(e)}),n.dataset.errorCallback&&(e.errorCallback=e=>{const t=window[n.dataset.errorCallback];"function"==typeof t&&t(e)}),n.dataset.expiredCallback&&(e.expiredCallback=()=>{const e=window[n.dataset.expiredCallback];"function"==typeof e&&e()}),n.dataset.bind){const t=document.getElementById(n.dataset.bind);if(t){Ve(t,e);continue}}Le(n,e)}const n=document.currentScript?.dataset?.onload;n&&"function"==typeof window[n]&&window[n]()}"undefined"!=typeof document&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",qe):qe());const Ne={render:Le,open:_e,bind:Ve,reset:$e,remove:He,getToken:Oe,execute:Be,challenge:Re,on:ze,off:Fe,once:je,verify:D,decode:J,version:d};e.EventEmitter=Pe,e.bind=Ve,e.challenge=Re,e.decode=J,e.default=Ne,e.execute=Be,e.getToken=Oe,e.off=Fe,e.on=ze,e.once=je,e.open=_e,e.remove=He,e.render=Le,e.reset=$e,e.verify=D,e.version=d,Object.defineProperty(e,"__esModule",{value:!0})});
|
|
1
|
+
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).OpenAge={})}(this,function(e){"use strict";const n='<svg viewBox="0 0 100 120"\n fill="none" class="oa-face-svg">\n <ellipse cx="50" cy="55" rx="35" ry="45"\n stroke="currentColor" stroke-width="1.8"/>\n <ellipse cx="36" cy="45" rx="5" ry="3.5"\n fill="currentColor" class="oa-eye"/>\n <ellipse cx="64" cy="45" rx="5" ry="3.5"\n fill="currentColor" class="oa-eye"/>\n <line x1="50" y1="52" x2="48" y2="62"\n stroke="currentColor" stroke-width="1.5"\n stroke-linecap="round"/>\n <path d="M40 72 Q50 79 60 72"\n stroke="currentColor" stroke-width="1.5"\n stroke-linecap="round" fill="none"/>\n</svg>';function t(e){return"light"===e||"dark"===e?e:r()}function o(e,n){if("light"===n||"dark"===n)return;if("undefined"==typeof window)return;const t=()=>{e.setAttribute("data-theme",r())},o=[];for(const e of["(prefers-color-scheme: dark)","(prefers-color-scheme: light)"]){const n=i(e,t);n&&o.push(n)}if("function"==typeof MutationObserver){const e=new MutationObserver(t),n={attributes:!0,attributeFilter:["class","data-theme","style"]};document.documentElement&&e.observe(document.documentElement,n),document.body&&e.observe(document.body,n),o.push(()=>e.disconnect())}return()=>{for(const e of o)e()}}function r(){if("undefined"==typeof window)return"dark";const e=function(){if("undefined"==typeof document)return null;for(const e of[document.documentElement,document.body]){const n=a(e);if(n)return n}return null}();if(e)return e;const n=function(){if("function"!=typeof window.matchMedia)return null;if(window.matchMedia("(prefers-color-scheme: dark)").matches)return"dark";if(window.matchMedia("(prefers-color-scheme: light)").matches)return"light";return null}();return n||"dark"}function a(e){if(!e||"function"!=typeof window.getComputedStyle)return null;const n=function(e){const n=e.getAttribute("data-theme");if("light"===n||"dark"===n)return n;const t=e.className;if("string"!=typeof t)return null;if(/\bdark\b|theme-dark|dark-theme/i.test(t))return"dark";if(/\blight\b|theme-light|light-theme/i.test(t))return"light";return null}(e);if(n)return n;const t=window.getComputedStyle(e),o=function(e){if(!e||"normal"===e)return null;const n=e.toLowerCase(),t=n.includes("dark"),o=n.includes("light");return t&&!o?"dark":o&&!t?"light":null}(t.colorScheme);return o||function(e){if(!e||"transparent"===e)return null;const n=e.match(/rgba?\(([^)]+)\)/i);if(!n)return null;const t=n[1].split(",").slice(0,3).map(e=>Number.parseFloat(e.trim()));if(3!==t.length||t.some(Number.isNaN))return null;const[o,r,a]=t,i=(299*o+587*r+114*a)/1e3;return i<=140?"dark":i>=180?"light":null}(t.backgroundColor)}function i(e,n){if("function"!=typeof window.matchMedia)return null;const t=window.matchMedia(e);return"function"==typeof t.addEventListener?(t.addEventListener("change",n),()=>{t.removeEventListener("change",n)}):"function"==typeof t.addListener?(t.addListener(n),()=>{t.removeListener(n)}):null}const s='\n:host {\n --oa-bg: #0b0d11;\n --oa-surface: #14161d;\n --oa-border: #1f2230;\n --oa-text: #e8e8ed;\n --oa-text-muted: #7a7d8c;\n --oa-accent: #4ae68a;\n --oa-accent-dim: rgba(74, 230, 138, 0.12);\n --oa-danger: #ef4444;\n --oa-warn: #f59e0b;\n --oa-radius: 16px;\n --oa-font: \'DM Sans\', system-ui, -apple-system,\n sans-serif;\n --oa-mono: \'Space Mono\', monospace;\n\n display: block;\n font-family: var(--oa-font);\n color: var(--oa-text);\n line-height: 1.4;\n}\n\n:host([data-theme="light"]) {\n --oa-bg: #f8f9fb;\n --oa-surface: #ffffff;\n --oa-border: #e2e4ea;\n --oa-text: #1a1c24;\n --oa-text-muted: #6b6e7b;\n --oa-accent: #22c55e;\n --oa-accent-dim: rgba(34, 197, 94, 0.1);\n}\n\n* { box-sizing: border-box; margin: 0; padding: 0; }\n\n/* ── Checkbox ────────────────────────────────── */\n\n.oa-checkbox {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n background: var(--oa-surface);\n border: 2px solid var(--oa-border);\n border-radius: var(--oa-radius);\n cursor: pointer;\n user-select: none;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.oa-checkbox:hover {\n border-color: var(--oa-text-muted);\n}\n\n.oa-checkbox.oa-verified {\n border-color: var(--oa-accent);\n cursor: default;\n}\n\n.oa-checkbox.oa-failed {\n border-color: var(--oa-danger);\n}\n\n.oa-checkbox.oa-retry {\n border-color: var(--oa-danger);\n}\n\n.oa-checkbox.oa-expired {\n border-color: var(--oa-warn);\n}\n\n.oa-checkbox.oa-loading {\n align-items: center;\n}\n\n.oa-check-box {\n width: 24px;\n height: 24px;\n border: 2px solid var(--oa-border);\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n transition: all 0.2s;\n}\n\n.oa-loading .oa-check-box {\n border-color: transparent;\n background: transparent;\n}\n\n.oa-loading .oa-check-box .oa-spinner {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n transform: translateY(2px);\n}\n\n.oa-verified .oa-check-box {\n background: var(--oa-accent);\n border-color: var(--oa-accent);\n color: var(--oa-bg);\n}\n\n.oa-failed .oa-check-box {\n background: var(--oa-danger);\n border-color: var(--oa-danger);\n color: white;\n}\n\n.oa-retry .oa-check-box {\n background: var(--oa-danger);\n border-color: var(--oa-danger);\n color: white;\n}\n\n.oa-check-box svg {\n width: 14px;\n height: 14px;\n}\n\n.oa-check-box .oa-spinner svg {\n width: 18px;\n height: 18px;\n}\n\n.oa-label {\n display: flex;\n flex-direction: column;\n gap: 1px;\n flex: 1;\n min-width: 0;\n}\n\n.oa-label-text {\n font-size: 13px;\n font-weight: 600;\n}\n\n.oa-right-section {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n margin-left: auto;\n}\n\n.oa-face-icon-wrap {\n width: 18px;\n height: 22px;\n color: var(--oa-text-muted);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.oa-face-icon-wrap .oa-face-icon-svg {\n width: 100%;\n height: 100%;\n}\n\n.oa-face-icon-wrap .oa-eye {\n transform-box: fill-box;\n transform-origin: center;\n animation: oa-idle-blink 5s ease-in-out infinite;\n}\n\n.oa-face-icon-wrap .oa-face-icon-svg {\n animation: oa-breathe 4s ease-in-out infinite;\n}\n\n.oa-branding-row {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n}\n\n.oa-branding-link {\n font-size: 10px;\n font-weight: 700;\n font-family: var(--oa-mono);\n color: var(--oa-text-muted);\n text-decoration: none;\n letter-spacing: -0.02em;\n cursor: pointer;\n}\n\n.oa-branding-link:hover {\n color: var(--oa-text);\n}\n\n.oa-links-row {\n display: flex;\n gap: 6px;\n justify-content: center;\n}\n\n.oa-links-row a {\n font-size: 9px;\n color: var(--oa-text-muted);\n text-decoration: none;\n opacity: 0.7;\n cursor: pointer;\n}\n\n.oa-links-row a:hover {\n opacity: 1;\n text-decoration: underline;\n}\n\n.oa-compact .oa-label-text {\n font-size: 11px;\n}\n\n.oa-compact .oa-face-icon-wrap {\n width: 16px;\n height: 20px;\n}\n\n/* ── Error banner (inline in checkbox) ───────── */\n\n.oa-error-banner {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n margin-top: 6px;\n background: rgba(239, 68, 68, 0.08);\n border: 1px solid rgba(239, 68, 68, 0.2);\n border-radius: 8px;\n font-size: 12px;\n color: var(--oa-danger);\n animation: oa-fade-in 0.3s ease;\n}\n\n.oa-error-banner button {\n margin-left: auto;\n background: none;\n border: 1px solid var(--oa-danger);\n border-radius: 6px;\n color: var(--oa-danger);\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n cursor: pointer;\n font-family: var(--oa-font);\n flex-shrink: 0;\n}\n\n.oa-error-banner button:hover {\n background: rgba(239, 68, 68, 0.1);\n}\n\n/* ── Popup / Modal shell ─────────────────────── */\n\n.oa-popup {\n position: fixed;\n z-index: 100000;\n background: var(--oa-bg);\n border: 1px solid var(--oa-border);\n border-radius: var(--oa-radius);\n box-shadow: 0 20px 60px rgba(0,0,0,0.4),\n 0 0 0 1px rgba(0,0,0,0.08);\n width: 340px;\n max-height: 90vh;\n overflow: hidden;\n animation: oa-popup-in 0.25s ease;\n display: flex;\n flex-direction: column;\n}\n\n.oa-inline-shell {\n background: var(--oa-bg);\n border: 1px solid var(--oa-border);\n border-radius: var(--oa-radius);\n box-shadow: 0 20px 60px rgba(0,0,0,0.18),\n 0 0 0 1px rgba(0,0,0,0.06);\n width: min(100%, 360px);\n max-width: 100%;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n margin: 0 auto;\n}\n\n.oa-modal-overlay {\n position: fixed;\n inset: 0;\n z-index: 99999;\n background: rgba(0,0,0,0.65);\n display: flex;\n align-items: center;\n justify-content: center;\n animation: oa-fade-in 0.2s ease;\n backdrop-filter: blur(6px);\n}\n\n.oa-modal {\n background: var(--oa-bg);\n border: 1px solid var(--oa-border);\n border-radius: var(--oa-radius);\n width: 360px;\n max-width: 95vw;\n max-height: 95vh;\n overflow: hidden;\n animation: oa-slide-in 0.3s ease;\n display: flex;\n flex-direction: column;\n}\n\n/* ── Header ──────────────────────────────────── */\n\n.oa-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 14px;\n border-bottom: 1px solid var(--oa-border);\n flex-shrink: 0;\n}\n\n.oa-logo {\n font-family: var(--oa-mono);\n font-size: 0.85rem;\n letter-spacing: -0.03em;\n color: var(--oa-text-muted);\n text-decoration: none;\n cursor: pointer;\n}\n\n.oa-logo:hover {\n color: var(--oa-text);\n}\n\n.oa-logo strong {\n color: var(--oa-text);\n font-weight: 700;\n}\n\n.oa-badge {\n font-size: 0.55rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--oa-accent);\n background: var(--oa-accent-dim);\n padding: 2px 6px;\n border-radius: 100px;\n margin-left: 6px;\n}\n\n.oa-title {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.oa-close-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n color: var(--oa-text-muted);\n transition: background 0.15s, color 0.15s;\n}\n\n.oa-close-btn:hover {\n background: var(--oa-border);\n color: var(--oa-text);\n}\n\n.oa-close-btn svg {\n width: 16px;\n height: 16px;\n}\n\n/* ── Body (viewport) ─────────────────────────── */\n\n.oa-body {\n flex: 1;\n position: relative;\n overflow: hidden;\n}\n\n/* ── Hero / start screen ─────────────────────── */\n\n.oa-hero {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n padding: 2rem 1.2rem 1.2rem;\n background: var(--oa-surface);\n border: 1px solid var(--oa-border);\n border-radius: var(--oa-radius);\n margin: 10px;\n animation: oa-fade-in 0.4s ease;\n}\n\n.oa-hero-icon {\n width: 80px;\n height: 96px;\n color: var(--oa-text-muted);\n}\n\n.oa-hero-icon .oa-face-svg.oa-idle {\n animation: oa-breathe 4s ease-in-out infinite;\n}\n\n.oa-hero-icon .oa-eye {\n transform-box: fill-box;\n transform-origin: center;\n animation: oa-idle-blink 5s ease-in-out infinite;\n}\n\n.oa-hero-status {\n color: var(--oa-text-muted);\n font-size: 0.8rem;\n font-weight: 500;\n text-align: center;\n min-height: 1.4em;\n}\n\n.oa-hero-privacy {\n font-size: 0.68rem;\n color: var(--oa-text-muted);\n text-align: center;\n line-height: 1.5;\n max-width: 260px;\n opacity: 0.7;\n}\n\n.oa-hero-privacy svg {\n width: 10px;\n height: 10px;\n vertical-align: -2px;\n margin-right: 2px;\n}\n\n/* ── Start / Retry button ────────────────────── */\n\n.oa-actions {\n display: flex;\n justify-content: center;\n padding: 0 14px 14px;\n flex-shrink: 0;\n}\n\n.oa-btn {\n font-family: var(--oa-font);\n padding: 0.55rem 1.5rem;\n border: none;\n border-radius: 10px;\n font-size: 0.8rem;\n font-weight: 600;\n cursor: pointer;\n background: var(--oa-accent);\n color: var(--oa-bg);\n transition: transform 0.12s, opacity 0.15s;\n width: 100%;\n max-width: 240px;\n}\n\n.oa-btn:hover { opacity: 0.88; }\n.oa-btn:active { transform: scale(0.97); }\n\n/* ── Video area ──────────────────────────────── */\n\n.oa-video-area {\n position: relative;\n width: 100%;\n aspect-ratio: 3/4;\n max-height: 55vh;\n background: var(--oa-surface);\n overflow: hidden;\n border-radius: var(--oa-radius);\n margin: 10px;\n width: calc(100% - 20px);\n border: 1px solid var(--oa-border);\n animation: oa-fade-in 0.3s ease;\n}\n\n.oa-video-area video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n transform: scaleX(-1);\n}\n\n/* ── Face guide overlay ──────────────────────── */\n\n.oa-face-guide {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 80%;\n max-width: 360px;\n aspect-ratio: 5/6;\n perspective: 400px;\n color: rgba(255, 255, 255, 0.45);\n pointer-events: none;\n z-index: 2;\n transition: opacity 0.4s ease;\n}\n\n.oa-face-guide .oa-face-svg {\n width: 100%;\n height: 100%;\n transform-origin: center center;\n filter: drop-shadow(\n 0 0 16px rgba(15, 23, 42, 0.16)\n );\n}\n\n.oa-face-guide .oa-eye {\n transform-box: fill-box;\n transform-origin: center;\n}\n\n.oa-face-guide[data-task="turn-left"] .oa-face-svg {\n animation: oa-turn-left 2s ease-in-out infinite;\n}\n.oa-face-guide[data-task="turn-right"] .oa-face-svg {\n animation: oa-turn-right 2s ease-in-out infinite;\n}\n.oa-face-guide[data-task="nod"] .oa-face-svg {\n animation: oa-nod 2s ease-in-out infinite;\n}\n.oa-face-guide[data-task="blink-twice"] .oa-eye {\n animation: oa-blink 2.4s ease-in-out infinite;\n}\n.oa-face-guide[data-task="move-closer"] .oa-face-svg {\n animation: oa-closer 2.5s ease-in-out infinite;\n}\n\n/* ── Challenge HUD (bottom gradient overlay) ── */\n\n.oa-challenge-hud {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 1rem 0.8rem 0.6rem;\n background: linear-gradient(\n to top,\n rgba(11, 13, 17, 0.92) 0%,\n rgba(11, 13, 17, 0) 100%\n );\n z-index: 3;\n pointer-events: none;\n}\n\n.oa-challenge-text {\n font-size: 0.85rem;\n font-weight: 600;\n text-align: center;\n color: var(--oa-text);\n text-shadow: 0 1px 6px rgba(0, 0, 0, 0.7);\n margin-bottom: 0.4rem;\n}\n\n.oa-challenge-bar {\n height: 3px;\n background: rgba(255, 255, 255, 0.1);\n border-radius: 2px;\n overflow: hidden;\n}\n\n.oa-challenge-fill {\n height: 100%;\n background: var(--oa-accent);\n border-radius: 2px;\n transition: width 0.25s ease;\n}\n\n/* ── Video status (top gradient overlay) ─────── */\n\n.oa-video-status {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n padding: 0.8rem 0.8rem 1.2rem;\n background: linear-gradient(\n to bottom,\n rgba(11, 13, 17, 0.85) 0%,\n rgba(11, 13, 17, 0) 100%\n );\n z-index: 3;\n pointer-events: none;\n}\n\n.oa-video-status p {\n font-size: 0.75rem;\n font-weight: 500;\n text-align: center;\n color: var(--oa-text-muted);\n text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);\n}\n\n/* ── Result area ─────────────────────────────── */\n\n.oa-result {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 0.5rem;\n padding: 2rem 1.2rem;\n background: var(--oa-surface);\n border: 1px solid var(--oa-border);\n border-radius: var(--oa-radius);\n margin: 10px;\n animation: oa-fade-in 0.4s ease;\n}\n\n.oa-result-icon {\n font-size: 2.4rem;\n line-height: 1;\n}\n\n.oa-result-text {\n font-size: 0.85rem;\n font-weight: 500;\n text-align: center;\n}\n\n.oa-result-pass { color: var(--oa-accent); }\n.oa-result-fail { color: var(--oa-danger); }\n.oa-result-retry { color: var(--oa-warn); }\n\n.oa-error-step {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 0.7rem;\n padding: 2rem 1.2rem;\n background: var(--oa-surface);\n border: 1px solid var(--oa-border);\n border-radius: var(--oa-radius);\n margin: 10px;\n animation: oa-fade-in 0.4s ease;\n text-align: center;\n}\n\n.oa-error-step-icon {\n width: 3rem;\n height: 3rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 999px;\n background: rgba(239, 68, 68, 0.12);\n color: var(--oa-danger);\n font-size: 1.7rem;\n line-height: 1;\n}\n\n.oa-error-step-title {\n font-size: 1rem;\n font-weight: 700;\n color: var(--oa-text);\n}\n\n.oa-error-step-message {\n font-size: 0.84rem;\n line-height: 1.5;\n color: var(--oa-text-muted);\n max-width: 260px;\n}\n\n.oa-error-step-countdown {\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--oa-danger);\n}\n\n.oa-hidden { display: none !important; }\n\n/* ── Animations ──────────────────────────────── */\n\n@keyframes oa-popup-in {\n from { opacity: 0; transform: scale(0.95); }\n to { opacity: 1; transform: scale(1); }\n}\n\n@keyframes oa-fade-in {\n from { opacity: 0; transform: translateY(6px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n@keyframes oa-slide-in {\n from { opacity: 0; transform: translateY(16px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n@keyframes oa-breathe {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.04); }\n}\n\n@keyframes oa-idle-blink {\n 0%, 42%, 48%, 100% { transform: scaleY(1); }\n 44%, 46% { transform: scaleY(0.05); }\n}\n\n@keyframes oa-turn-left {\n 0%, 100% { transform: rotateY(0); }\n 35%, 65% { transform: rotateY(-30deg); }\n}\n\n@keyframes oa-turn-right {\n 0%, 100% { transform: rotateY(0); }\n 35%, 65% { transform: rotateY(30deg); }\n}\n\n@keyframes oa-nod {\n 0%, 100% { transform: rotateX(0); }\n 30%, 50% { transform: rotateX(25deg); }\n}\n\n@keyframes oa-blink {\n 0%, 18%, 32%, 50%, 100% { transform: scaleY(1); }\n 22%, 28% { transform: scaleY(0.05); }\n 40%, 46% { transform: scaleY(0.05); }\n}\n\n@keyframes oa-closer {\n 0%, 100% { transform: scale(1); }\n 35%, 55% { transform: scale(1.3); }\n}\n';function l(e){return`\n <div class="oa-hero">\n <div class="oa-hero-icon">\n ${n.replace('class="oa-face-svg"','class="oa-face-svg oa-idle"')}\n </div>\n <p class="oa-hero-status">${e}</p>\n <p class="oa-hero-privacy">\n <svg viewBox="0 0 24 24"\n fill="none" stroke="currentColor" stroke-width="1.5">\n <path d="M12 2l8 4v6c0 5.5-3.8 10-8 12\n C7.8 22 4 17.5 4 12V6l8-4z"/>\n <path d="M9 12l2 2 4-4" stroke-width="2"\n stroke-linecap="round" stroke-linejoin="round"/>\n</svg>\n Open-source & privacy-focused.\n No photos or camera data leave your device.\n </p>\n </div>\n `}function c(e,n){return`\n <div class="oa-result ${{fail:"oa-result-fail",retry:"oa-result-retry"}[e]||""}">\n <div class="oa-result-icon">\n ${{fail:"✕",retry:"↻"}[e]||"?"}\n </div>\n <div class="oa-result-text">${n}</div>\n </div>\n `}const d="1.0.0",u="https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.17",p=`${u}/wasm`,h=`${u}/vision_bundle.mjs`,f="https://cdn.jsdelivr.net/gh/justadudewhohacks/face-api.js@master/weights",m=12;let g=0;class y{constructor(e,n){this.id="oa-"+ ++g,this.params=n,this.container=function(e){if("string"==typeof e)return document.querySelector(e);return e}(e),this.anchorElement=null,this.state="idle",this.token=null,this.popup=null,this.shadow=null,this.elements={},this.popupElements=null,this.onChallenge=null,this.onStartClick=null,this.popupFrame=0,this.themeCleanup=null,this.render()}render(){const e=document.createElement("div");e.id=this.id,this.shadow=e.attachShadow({mode:"open"}),this.host=e;const n=document.createElement("style");n.textContent=s,this.shadow.appendChild(n);const r=t(this.params.theme);if(e.setAttribute("data-theme",r),this.themeCleanup=o(e,this.params.theme),"invisible"===this.params.size)return e.style.display="none",void this.container.appendChild(e);if(this.isInlineLayout())return this.renderInlineShell(),void this.container.appendChild(e);const a=document.createElement("div");a.innerHTML=`\n <div class="oa-widget-wrap">\n <div class="oa-checkbox" role="checkbox"\n aria-checked="false" tabindex="0">\n <div class="oa-check-box"></div>\n <div class="oa-label">\n <span class="oa-label-text">${"I am of age"}</span>\n </div>\n <div class="oa-right-section">\n <div class="oa-branding-row">\n <div class="oa-face-icon-wrap">\n <svg viewBox="0 0 100 120"\n fill="none" class="oa-face-icon-svg">\n <ellipse cx="50" cy="55" rx="35" ry="45"\n stroke="currentColor" stroke-width="2"/>\n <ellipse cx="36" cy="45" rx="5" ry="3.5"\n fill="currentColor" class="oa-eye"/>\n <ellipse cx="64" cy="45" rx="5" ry="3.5"\n fill="currentColor" class="oa-eye"/>\n <line x1="50" y1="52" x2="48" y2="62"\n stroke="currentColor" stroke-width="1.65"\n stroke-linecap="round"/>\n <path d="M40 72 Q50 79 60 72"\n stroke="currentColor" stroke-width="1.65"\n stroke-linecap="round" fill="none"/>\n</svg>\n </div>\n <a class="oa-branding-link"\n href="https://github.com/tn3w/OpenAge"\n target="_blank" rel="noopener">OpenAge</a>\n </div>\n <div class="oa-links-row">\n <a href="https://github.com/tn3w/OpenAge"\n target="_blank"\n rel="noopener">Terms</a>\n <a href="https://github.com/tn3w/OpenAge"\n target="_blank"\n rel="noopener">Privacy</a>\n </div>\n </div>\n </div>\n <div class="oa-error-slot"></div>\n </div>\n `,this.shadow.appendChild(a.firstElementChild);const i=this.shadow.querySelector(".oa-checkbox");"compact"===this.params.size&&i.classList.add("oa-compact"),i.addEventListener("click",e=>{e.target.closest("a")||"verified"!==this.state&&"loading"!==this.state&&(this.clearError(),this.startChallenge())}),i.addEventListener("keydown",e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),i.click())}),this.elements.checkbox=i,this.elements.checkBox=this.shadow.querySelector(".oa-check-box"),this.elements.errorSlot=this.shadow.querySelector(".oa-error-slot"),this.container.appendChild(e)}isInlineLayout(){return"inline"===this.params.layout}renderInlineShell(){const e=document.createElement("div");e.className="oa-inline-shell",e.innerHTML=this.buildPopupContent({closeable:!1}),this.shadow.appendChild(e),this.popup={host:this.host,root:e,inline:!0},this.bindPopupEvents(e)}resetInlineShell(){this.popup?.root&&(this.popup.root.innerHTML=this.buildPopupContent({closeable:!1}),this.bindPopupEvents(this.popup.root))}startChallenge(){this.setState("loading"),this.onChallenge&&this.onChallenge(this)}createPopupShell(){const e=t(this.params.theme),n=document.createElement("div");n.setAttribute("data-theme",e);const r=n.attachShadow({mode:"open"}),a=document.createElement("style");a.textContent=s,r.appendChild(a);return{popupHost:n,popupShadow:r,themeCleanup:o(n,this.params.theme)}}openPopup(){if(this.isInlineLayout())return this.popup||this.renderInlineShell(),this.getVideo();if(this.popup)return this.getVideo();const e=this.getPopupAnchor();if(!e)return this.openModal();const{popupHost:n,popupShadow:t,themeCleanup:o}=this.createPopupShell(),r=document.createElement("div");r.className="oa-popup",r.innerHTML=this.buildPopupContent(),r.style.visibility="hidden",r.style.pointerEvents="none",t.appendChild(r),document.body.appendChild(n),this.popup={anchor:e,host:n,root:r,themeCleanup:o};return"modal"===v(e.getBoundingClientRect(),r.getBoundingClientRect()).mode?(n.remove(),o?.(),this.popup=null,this.openModal()):(this.bindPopupEvents(r,t),this.updatePopupPosition(),this.startPopupTracking(),this.getVideo())}openModal(){const{popupHost:e,popupShadow:n,themeCleanup:t}=this.createPopupShell(),o=document.createElement("div");o.className="oa-modal-overlay";const r=document.createElement("div");return r.className="oa-modal",r.innerHTML=this.buildPopupContent(),o.appendChild(r),n.appendChild(o),document.body.appendChild(e),o.addEventListener("click",e=>{e.target===o&&this.closePopup()}),this.popup={host:e,root:r,overlay:o,themeCleanup:t},this.bindPopupEvents(r,n),this.getVideo()}getPopupAnchor(){return this.anchorElement||this.elements.checkbox||this.host||null}startPopupTracking(){if(!this.popup||this.popup.overlay)return;const e=()=>{this.schedulePopupPosition()},n=[],t=(t,o)=>{window.addEventListener(t,e,o),n.push(()=>{window.removeEventListener(t,e,o)})};if(t("resize",{passive:!0}),t("scroll",{capture:!0,passive:!0}),window.visualViewport){const t=window.visualViewport;t.addEventListener("resize",e),t.addEventListener("scroll",e),n.push(()=>{t.removeEventListener("resize",e),t.removeEventListener("scroll",e)})}if("function"==typeof ResizeObserver){const t=new ResizeObserver(()=>{e()});t.observe(this.popup.root),t.observe(this.popup.anchor),t.observe(document.documentElement),document.body&&t.observe(document.body),n.push(()=>t.disconnect())}this.popup.cleanup=()=>{this.popupFrame&&(cancelAnimationFrame(this.popupFrame),this.popupFrame=0);for(const e of n)e()}}schedulePopupPosition(){this.popup&&!this.popup.overlay&&(this.popupFrame||(this.popupFrame=requestAnimationFrame(()=>{this.popupFrame=0,this.updatePopupPosition()})))}updatePopupPosition(){if(!this.popup||this.popup.overlay)return;const e=this.getPopupAnchor();if(!e||!e.isConnected)return void this.closePopup();this.popup.anchor=e;const n=v(e.getBoundingClientRect(),this.popup.root.getBoundingClientRect());if("modal"===n.mode)return this.closePopup(),void this.openModal();const t=`${Math.round(n.top)}px`,o=`${Math.round(n.left)}px`;this.popup.root.style.top!==t&&(this.popup.root.style.top=t),this.popup.root.style.left!==o&&(this.popup.root.style.left=o),this.popup.root.dataset.placement=n.placement,this.popup.root.style.visibility="visible",this.popup.root.style.pointerEvents="auto"}buildPopupContent({closeable:e=!0}={}){return`\n <div class="oa-header">\n <div class="oa-title">\n <a class="oa-logo"\n href="https://github.com/tn3w/OpenAge"\n target="_blank" rel="noopener">\n Open<strong>Age</strong>\n </a>\n <span class="oa-badge">on-device</span>\n </div>\n ${e?'<button class="oa-close-btn"\n aria-label="Close">\n <svg viewBox="0 0 24 24"\n fill="none" stroke="currentColor" stroke-width="2"\n stroke-linecap="round">\n <line x1="18" y1="6" x2="6" y2="18"/>\n <line x1="6" y1="6" x2="18" y2="18"/>\n</svg>\n </button>':""}\n </div>\n <div class="oa-body">\n ${l("Initializing…")}\n </div>\n <div class="oa-actions oa-hidden">\n <button class="oa-btn oa-start-btn">\n Begin Verification\n </button>\n </div>\n `}bindPopupEvents(e){const n=e.querySelector(".oa-close-btn");n&&n.addEventListener("click",()=>{this.closePopup(),this.params.closeCallback?.()});const t=e.querySelector(".oa-start-btn");t&&t.addEventListener("click",()=>{this.onStartClick&&this.onStartClick()}),this.popupElements={body:e.querySelector(".oa-body"),actions:e.querySelector(".oa-actions"),startBtn:t,heroStatus:e.querySelector(".oa-hero-status")}}getVideo(){return this.popup?this.popup.root.querySelector("video"):null}showHero(e){this.popupElements?.body&&(this.popupElements.body.innerHTML=l(e),this.popupElements.heroStatus=this.popupElements.body.querySelector(".oa-hero-status"),this.hideActions())}showReady(){this.setHeroStatus("Ready to verify your age."),this.showActions("Begin Verification")}showCamera(){if(this.popupElements?.body)return this.popupElements.body.innerHTML='\n <div class="oa-video-area">\n <video autoplay playsinline muted></video>\n <div class="oa-face-guide">\n <svg viewBox="0 0 100 120"\n fill="none" class="oa-face-svg">\n <ellipse cx="50" cy="55" rx="35" ry="45"\n stroke="currentColor" stroke-width="1.8"\n stroke-dasharray="6 4"/>\n <ellipse cx="36" cy="45" rx="5" ry="3.5"\n fill="currentColor" class="oa-eye"/>\n <ellipse cx="64" cy="45" rx="5" ry="3.5"\n fill="currentColor" class="oa-eye"/>\n <line x1="50" y1="52" x2="48" y2="62"\n stroke="currentColor" stroke-width="1.5"\n stroke-linecap="round"/>\n <path d="M40 72 Q50 79 60 72"\n stroke="currentColor" stroke-width="1.5"\n stroke-linecap="round" fill="none"/>\n</svg>\n </div>\n <div class="oa-challenge-hud oa-hidden">\n <p class="oa-challenge-text"></p>\n <div class="oa-challenge-bar">\n <div class="oa-challenge-fill"\n style="width:0%"></div>\n </div>\n </div>\n <div class="oa-video-status">\n <p></p>\n </div>\n </div>\n ',this.popupElements.video=this.popupElements.body.querySelector("video"),this.popupElements.faceGuide=this.popupElements.body.querySelector(".oa-face-guide"),this.popupElements.challengeHud=this.popupElements.body.querySelector(".oa-challenge-hud"),this.popupElements.challengeText=this.popupElements.body.querySelector(".oa-challenge-text"),this.popupElements.challengeFill=this.popupElements.body.querySelector(".oa-challenge-fill"),this.popupElements.videoStatus=this.popupElements.body.querySelector(".oa-video-status p"),this.hideActions(),this.popupElements.video}showLiveness(){this.popupElements?.faceGuide&&this.popupElements.faceGuide.classList.remove("oa-hidden"),this.popupElements?.challengeHud&&this.popupElements.challengeHud.classList.remove("oa-hidden")}setHeroStatus(e){this.popupElements?.heroStatus&&(this.popupElements.heroStatus.textContent=e)}setVideoStatus(e){this.popupElements?.videoStatus&&(this.popupElements.videoStatus.textContent=e)}setInstruction(e){this.popupElements?.challengeText&&(this.popupElements.challengeText.textContent=e)}setStatus(e){this.setVideoStatus(e)}setProgress(e){this.popupElements?.challengeFill&&(this.popupElements.challengeFill.style.width=`${Math.round(100*e)}%`)}setTask(e){this.popupElements?.faceGuide&&this.popupElements.faceGuide.setAttribute("data-task",e||"")}showActions(e){this.popupElements?.actions&&(this.popupElements.actions.classList.remove("oa-hidden"),this.popupElements.startBtn&&(this.popupElements.startBtn.textContent=e))}hideActions(){this.popupElements?.actions&&this.popupElements.actions.classList.add("oa-hidden")}showResult(e,n){if(this.isInlineLayout()){if(!this.popupElements?.body)return;return this.popupElements.body.innerHTML=c(e,n),this.hideActions(),"pass"===e?void this.setState("verified"):(this.setState("fail"===e?"failed":"retry"),void this.showActions("Try Again"))}if("pass"===e)return this.closePopup(),void this.setState("verified");if("fail"!==e){if("retry"===e){if("invisible"!==this.params.size)return this.closePopup(),void this.setState("retry");this.popupElements?.body&&(this.popupElements.body.innerHTML=c(e,n)),this.hideActions(),this.showActions("Try Again")}}else"invisible"===this.params.size?(this.popupElements?.body&&(this.popupElements.body.innerHTML=c(e,n)),this.hideActions(),this.showActions("Try Again")):(this.closePopup(),this.setState("retry"))}showError(e){this.popupElements?.body&&(this.popupElements.body.innerHTML=function(e){return`\n <div class="oa-error-step">\n <div class="oa-error-step-icon">✕</div>\n <div class="oa-error-step-title">Verification stopped</div>\n <div class="oa-error-step-message">${e}</div>\n <div class="oa-error-step-countdown"></div>\n </div>\n `}(e),this.popupElements.errorCountdown=this.popupElements.body.querySelector(".oa-error-step-countdown"),this.hideActions())}setErrorCountdown(e){if(!this.popupElements?.errorCountdown)return;const n=1===e?"second":"seconds";this.popupElements.errorCountdown.textContent=`Closing in ${e} ${n}…`}clearError(){this.elements.errorSlot&&(this.elements.errorSlot.innerHTML="")}closePopup(){if(this.popup){if(this.popup.inline)return this.popup.cleanup?.(),this.popupFrame&&(cancelAnimationFrame(this.popupFrame),this.popupFrame=0),this.resetInlineShell(),void("loading"===this.state&&this.setState("idle"));this.popup.cleanup?.(),this.popup.themeCleanup?.(),this.popup.host.remove(),this.popup=null,this.popupElements=null,"loading"===this.state&&this.setState("idle")}}setState(e){this.state=e;const n=this.elements.checkbox,t=this.elements.checkBox;if(n&&t)switch(n.classList.remove("oa-loading","oa-verified","oa-failed","oa-retry","oa-expired"),n.setAttribute("aria-checked","false"),t.innerHTML="",e){case"loading":n.classList.add("oa-loading"),t.innerHTML='<span class="oa-spinner"><svg viewBox="0 0 24 24"\n fill="none" stroke="currentColor" stroke-width="2.5">\n <circle cx="12" cy="12" r="10" opacity="0.2"/>\n <path d="M12 2 A10 10 0 0 1 22 12"\n stroke-linecap="round">\n <animateTransform attributeName="transform"\n type="rotate" from="0 12 12" to="360 12 12"\n dur="0.8s" repeatCount="indefinite"/>\n </path>\n</svg></span>';break;case"verified":n.classList.add("oa-verified"),n.setAttribute("aria-checked","true"),t.innerHTML='<svg viewBox="0 0 24 24"\n fill="none" stroke="currentColor" stroke-width="2.5"\n stroke-linecap="round" stroke-linejoin="round">\n <polyline points="20 6 9 17 4 12"/>\n</svg>';break;case"failed":n.classList.add("oa-failed"),t.innerHTML="✕";break;case"retry":n.classList.add("oa-retry"),t.innerHTML='<svg viewBox="0 0 16 16"\n xmlns="http://www.w3.org/2000/svg"\n fill="currentColor">\n <path d="m14.955 7.986.116.01a1 1 0 0 1 .85 1.13 8 8 0 0 1-13.374 4.728l-.84.84c-.63.63-1.707.184-1.707-.707V10h3.987c.89 0 1.337 1.077.707 1.707l-.731.731a6 6 0 0 0 8.347-.264 6 6 0 0 0 1.63-3.33 1 1 0 0 1 1.131-.848zM11.514.813a8 8 0 0 1 1.942 1.336l.837-.837c.63-.63 1.707-.184 1.707.707V6h-3.981c-.89 0-1.337-1.077-.707-1.707l.728-.729a6 6 0 0 0-9.98 3.591 1 1 0 1 1-1.98-.281A8 8 0 0 1 11.514.813"/>\n</svg>';break;case"expired":n.classList.add("oa-expired"),n.setAttribute("aria-checked","false")}}getToken(){return this.token}reset(){this.token=null,this.closePopup(),this.setState("idle")}destroy(){this.closePopup(),this.themeCleanup?.(),this.host?.remove()}}function v(e,n){const t={width:window.innerWidth,height:window.innerHeight},o=Math.max(n.width||0,340),r=Math.max(n.height||0,520);if(o>t.width-24||r>t.height-24)return{mode:"modal"};const a=function(e,n,t){return Math.min(Math.max(m,e),n-t-m)}(e.left+e.width/2-o/2,t.width,o),i=e.bottom+m,s=e.top-r-m;return i+r<=t.height-m||!(s>=m)?{mode:"popup",placement:"below",top:w(i,t.height,r),left:a}:{mode:"popup",placement:"above",top:w(s,t.height,r),left:a}}function w(e,n,t){return Math.min(Math.max(m,e),n-t-m)}let b=null,x=null,k=-1,E=null;async function C(){return E||(E=await import(h),b=E.FaceLandmarker,E)}async function S(e){const n=await C(),t=await n.FilesetResolver.forVisionTasks(p);x&&x.close(),k=-1,x=await b.createFromOptions(t,{baseOptions:{modelAssetBuffer:new Uint8Array(e),delegate:"GPU"},runningMode:"VIDEO",numFaces:2,outputFaceBlendshapes:!0,outputFacialTransformationMatrixes:!0})}function P(e,n){if(!x)return null;const t=function(e){const n=Number.isFinite(e)?e:performance.now(),t=Math.floor(n),o=Math.max(t,k+1);return k=o,o}(n),o=x.detectForVideo(e,t),r=o.faceLandmarks?.length??0;if(0===r)return{faceCount:0,timestampMs:t};const a=o.faceLandmarks[0];return{faceCount:r,timestampMs:t,landmarks:a,blendshapes:A(o.faceBlendshapes?.[0]),headPose:T(o.facialTransformationMatrixes?.[0]),boundingBox:L(a)}}function M(){x&&(x.close(),x=null),k=-1}function A(e){if(!e?.categories)return{};const n={};for(const t of e.categories)n[t.categoryName]=t.score;return n}function T(e){if(!e?.data||e.data.length<16)return{yaw:0,pitch:0,roll:0};const n=e.data,t=180/Math.PI;return{yaw:Math.atan2(n[8],n[10])*t,pitch:Math.asin(-Math.max(-1,Math.min(1,n[9])))*t,roll:Math.atan2(n[1],n[5])*t}}function L(e){let n=1,t=1,o=0,r=0;for(const a of e)a.x<n&&(n=a.x),a.y<t&&(t=a.y),a.x>o&&(o=a.x),a.y>r&&(r=a.y);const a=o-n,i=r-t;return{x:n,y:t,width:a,height:i,area:a*i}}let _=!1,$=null;async function H(){var e;_||($=await(e="https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/dist/face-api.min.js",new Promise((n,t)=>{if("undefined"==typeof window)return t(new Error("No DOM available"));const o=document.querySelector(`script[src="${e}"]`);if(o)return window.faceapi?n(window.faceapi):void o.addEventListener("load",()=>n(window.faceapi));const r=document.createElement("script");r.src=e,r.crossOrigin="anonymous",r.onload=()=>n(window.faceapi),r.onerror=()=>t(new Error(`Failed to load ${e}`)),document.head.appendChild(r)})),await Promise.all([$.nets.tinyFaceDetector.loadFromUri(f),$.nets.faceLandmark68TinyNet.loadFromUri(f),$.nets.ageGenderNet.loadFromUri(f)]),_=!0)}async function V(e){if(!$)throw new Error("Age estimator not initialized");const n=await $.detectSingleFace(e,new $.TinyFaceDetectorOptions({inputSize:224})).withFaceLandmarks(!0).withAgeAndGender();return n?{age:n.age,gender:n.gender,confidence:n.detection.score}:null}const O=[{id:"turn-left",instruction:"Turn your head to the left",check:e=>j(e,20)},{id:"turn-right",instruction:"Turn your head to the right",check:e=>j(e,-20)},{id:"nod",instruction:"Nod your head down then up",check:e=>function(e){if(e.length<10)return!1;const n=e[0].headPose.pitch;let t=!1,o=!1;for(const r of e){const e=r.headPose.pitch-n;e>15&&(t=!0),t&&Math.abs(e)<8&&(o=!0)}return t&&o}(e)},{id:"blink-twice",instruction:"Blink twice",check:e=>function(e){if(e.length<10)return!1;let n=0,t=!1;for(const o of e){const e=o.blendshapes.eyeBlinkLeft??0,r=o.blendshapes.eyeBlinkRight??0,a=e>.6&&r>.6;a&&!t?(n++,t=!0):a||(t=!1)}return n>=2}(e)},{id:"move-closer",instruction:"Move closer then back",check:e=>function(e){if(e.length<10)return!1;const n=e[0].boundingBox.area;let t=!1,o=!1;for(const r of e){const e=r.boundingBox.area/n;e>1.3&&(t=!0),t&&e<1.15&&(o=!0)}return t&&o}(e)}];function F(e=3){return[...O].sort(()=>Math.random()-.5).slice(0,e)}function z(e,n){if(e.failed||B(e))return;if(e.currentIndex>=e.tasks.length)return;if(!n||0===n.faceCount)return;if(n.faceCount>1)return e.failed=!0,void(e.failReason=null);const t={timestamp:n.timestampMs,headPose:n.headPose,blendshapes:n.blendshapes,boundingBox:n.boundingBox};0===e.history.length&&(e.taskStartTime=n.timestampMs),e.history.push(t);const o=n.timestampMs-e.taskStartTime;if(o>8e3)return void R(e);if(o<500)return;if(e.tasks[e.currentIndex].check(e.history)){if(function(e){if(e.length<5)return!1;const n=[];for(let t=1;t<e.length;t++){const o=Math.abs(e[t].headPose.yaw-e[t-1].headPose.yaw),r=Math.abs(e[t].headPose.pitch-e[t-1].headPose.pitch);n.push(o+r)}if(n.every(e=>e<.1))return!0;const t=n.reduce((e,n)=>e+n,0)/n.length;return n.reduce((e,n)=>e+(n-t)**2,0)/n.length<.01&&t>.5}(e.history))return e.failed=!0,void(e.failReason=null);e.completedTasks++,R(e)}}function B(e){return e.currentIndex>=e.tasks.length}function R(e){e.currentIndex++,e.history=[],e.taskStartTime=0;const n=e.tasks.length-e.currentIndex;e.completedTasks+n>=e.requiredPasses||(e.failed=!0,e.failReason=null)}function j(e,n){if(e.length<5)return!1;const t=e[0].headPose.yaw,o=Math.sign(n),r=Math.abs(n);return e.some(e=>(e.headPose.yaw-t)*o>r)}function I(e){const n="string"==typeof e?e:String.fromCharCode(...new Uint8Array(e));return btoa(n).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function q(e){const n=e+"=".repeat((4-e.length%4)%4),t=atob(n.replace(/-/g,"+").replace(/_/g,"/"));return Uint8Array.from(t,e=>e.charCodeAt(0))}let N=null;async function U(){return N||(N=await async function(){const e=crypto.getRandomValues(new Uint8Array(32));return crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign","verify"])}()),N}async function D(e){const n=await U(),[t,o,r]=e.split(".");if(!t||!o||!r)return null;let a,i;try{i=(new TextEncoder).encode(`${t}.${o}`),a=q(r)}catch{return null}if(!await crypto.subtle.verify("HMAC",n,a,i))return null;const s=JSON.parse((new TextDecoder).decode(q(o)));return s.exp&&s.exp<Date.now()/1e3?null:s}function J(e){const[,n]=e.split(".");return n?JSON.parse((new TextDecoder).decode(q(n))):null}function Y(e,n={}){if("serverless"===e)return{async verify(e){const{estimatedAge:n,livenessOk:t}=e;if(!t)return{success:!1,token:null};const o=await async function(e){const n=await U(),t=I(JSON.stringify({alg:"HS256",typ:"JWT"})),o=I(JSON.stringify({...e,iat:Math.floor(Date.now()/1e3),exp:Math.floor(Date.now()/1e3)+300})),r=(new TextEncoder).encode(`${t}.${o}`);return`${t}.${o}.${I(await crypto.subtle.sign("HMAC",n,r))}`}({estimatedAge:n,livenessOk:!0,mode:"serverless"});return{success:!0,token:o}},close(){}};return function(e,n){let t=null,o=null;return{async createSession(){const o=[];"undefined"!=typeof WebSocket&&o.push("websocket"),o.push("poll");const r=await fetch(`${e}/api/session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sitekey:n.sitekey,action:n.action,supportedTransports:o})});if(!r.ok)throw new Error("Request failed");return t=await r.json(),t},openChannel(){if(!t)throw new Error("No session");o="websocket"===t.transport?function(e,n){const t=e.replace(/^http/,"ws").replace(/\/$/,""),o=new WebSocket(`${t}/api/ws/${n}`);let r=[],a=!1;const i=new Promise((e,n)=>{o.onopen=()=>{e()},o.onerror=()=>{n(new Error("Connection failed"))}});return o.onmessage=e=>{const n=JSON.parse(e.data),t=r.shift();t&&t(n)},o.onclose=()=>{a=!0;for(const e of r)e(null);r=[]},{receive:()=>a?Promise.resolve(null):new Promise(e=>{r.push(e)}),async send(e){await i,o.send(JSON.stringify(e))},async sendAndReceive(e){return await i,o.send(JSON.stringify(e)),this.receive()},close(){a=!0,o.close()}}}(e,t.sessionId):function(e,n){return{async receive(){const t=await fetch(`${e}/api/poll/${n}`);if(!t.ok)throw new Error("Request failed");return t.json()},async send(t){if(!(await fetch(`${e}/api/verify/${n}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).ok)throw new Error("Request failed")},async sendAndReceive(t){const o=await fetch(`${e}/api/verify/${n}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});if(!o.ok)throw new Error("Request failed");return o.json()},close(){}}}(e,t.sessionId)},receive:async()=>o?o.receive():null,async send(e){if(o)return o.send(e)},sendAndReceive:async e=>o?o.sendAndReceive(e):null,async verify(t){if(o)return this.verifyViaChannel(t);const r=await fetch(`${e}/verify`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({response:t.token,sitekey:n.sitekey,action:n.action})});return r.ok?r.json():{success:!1,token:null}},verifyViaChannel:async e=>(await o.send(e),o.receive()),getSession:()=>t,close(){o?.close(),o=null,t=null}}}("custom"===e?n.server:"https://api.openage.dev",n)}let G=null,W=null,Q=null,X=null,K=null,Z=null;async function ee(e){var n;W=e,await(n=e.wasmJs,new Promise((e,t)=>{if(document.querySelector(`script[src="${n}"]`))return e();const o=document.createElement("script");o.src=n,o.onload=e,o.onerror=()=>{t(new Error(`Failed to load ${n}`))},document.head.appendChild(o)}));const t=await import(e.loaderJs);G=await t.initModule(e.wasmBin);const o=e.exports.vm_init;if(0!==G[`_${o}`]())throw new Error("VM init failed");const r=await fetch(e.challengeVmbc);Q=new Uint8Array(await r.arrayBuffer())}function ne(e,n){return e.HEAPU8[n]|e.HEAPU8[n+1]<<8|e.HEAPU8[n+2]<<16|e.HEAPU8[n+3]<<24}function te(e,n){try{delete window[e]}catch(e){}Object.defineProperty(window,e,{get:n,set(){},configurable:!0})}function oe(e){X=e,te("__vmFaceData",()=>X)}function re(e){K=e,te("__vmChallenge",()=>K)}function ae(){if(!G||!Q)throw new Error("VM not loaded");const e=W.exports.vm_exec_bytecode,n=W.exports.vm_free,t=Q.length,o=G._malloc(t);G.HEAPU8.set(Q,o);const r=G._malloc(4),a=G[`_${e}`](o,t,r);if(G._free(o),!a){G._free(r);const e=W.exports.vm_last_error;let n="VM execution failed";if(e){const t=G[`_${e}`]();if(t){const e=G.UTF8ToString(t);e&&(n=e)}}throw new Error(n)}const i=ne(G,r);G._free(r);const s=new Uint8Array(i);return s.set(G.HEAPU8.subarray(a,a+i)),G[`_${n}`](a),s}function ie(e){let n="";for(let t=0;t<e.length;t++)n+=String.fromCharCode(e[t]);return btoa(n)}const se=new Map;async function le(e,n){if(se.has(n))return se.get(n);const t=await async function(e,n){if(!G||!W)throw new Error("VM not loaded");const t=e.models[n];if(!t)throw new Error(`Unknown model: ${n}`);const o=await fetch(t.url);if(!o.ok)throw new Error(`Failed to fetch model: ${n}`);const r=new Uint8Array(await o.arrayBuffer()),a=e.exports.vm_decrypt_blob,i=e.exports.vm_free,s=r.length,l=G._malloc(s);G.HEAPU8.set(r,l);const c=G._malloc(4),d=G[`_${a}`](l,s,c);if(G._free(l),!d)throw G._free(c),new Error(`Decryption failed: ${n}`);const u=ne(G,c);G._free(c);const p=new Uint8Array(u);return p.set(G.HEAPU8.subarray(d,d+u)),G[`_${i}`](d),p.buffer}(e,n);return se.set(n,t),t}let ce=null,de=null;const ue={video:{facingMode:"user",width:{ideal:640},height:{ideal:480}}};function pe(e){return navigator.mediaDevices.getUserMedia(ue).then(n=>(ce=n,e.srcObject=ce,de=e,new Promise(n=>{e.onloadedmetadata=()=>{e.play(),n({width:e.videoWidth,height:e.videoHeight})}})))}function he(){if(!de)return null;const e=document.createElement("canvas");e.width=de.videoWidth,e.height=de.videoHeight;return e.getContext("2d").drawImage(de,0,0),e}function fe(){ce&&(ce.getTracks().forEach(e=>e.stop()),ce=null),de&&(de.srcObject=null,de=null)}const me={"turn-left":"Turn your head left","turn-right":"Turn your head right",nod:"Nod your head","blink-twice":"Blink twice","move-closer":"Move closer then back"};function ge(e){return new Promise(n=>setTimeout(n,e))}function ye(e){return"inline"===e?.params?.layout}async function ve(e,n){const t=function(e){const n="string"==typeof e?.name?e.name:"",t=`${n} ${"string"==typeof e?.message?e.message:String(e||"")}`.toLowerCase();return"NotFoundError"===n||/request is not allowed by the user agent or the platform in the current context/.test(t)||/requested device not found|device not found|no camera|could not start video source/.test(t)?"No camera available. Plug in a camera and try again.":"NotAllowedError"===n||"PermissionDeniedError"===n||/permission|camera access|access denied/.test(t)?"Camera access was blocked. Allow camera access and try again.":/positioning timeout/.test(t)?"Face positioning timed out. Reopen the check and try again.":"Verification failed. Please try again."}(n);if(ye(e))e.showResult?.("retry",t);else{e.popup||e.openPopup?.(),e.showError?.(t);for(let n=5;n>0&&e.popup;n--)e.setErrorCountdown?.(n),await ge(1e3);e.closePopup?.(),e.setState?.("retry")}}async function we(e,n,t){console.log("Error during challenge:",t),await ve(e,t),n.emit("error",t,e.id),e.params.errorCallback?.(t)}async function be(e,n){const t=e.params.mode||"serverless";if("serverless"!==t&&!e.params.sitekey){const t=new Error("Configuration error");return e.showResult?.("fail","Verification failed"),n.emit("error",t,e.id),void e.params.errorCallback?.(t)}return"serverless"===t?async function(e,n){const t=e.params;let o=0,r=null;try{e.openPopup(),e.setHeroStatus("Loading…"),await Promise.all([C(),H()]),r=await async function(){const e=await fetch("https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task");if(!e.ok)throw new Error("Failed to load face landmarker model");return new Uint8Array(await e.arrayBuffer())}(),ye(e)||(e.showReady(),await Ee(e)),await Ce(e,r);const a=Y("serverless",t),i=async()=>{e.showLiveness(),e.setInstruction(""),e.setVideoStatus("Verifying…");const n={tasks:F(),currentIndex:0,history:[],taskStartTime:0,completedTasks:0,requiredPasses:2,failed:!1,failReason:null};if(await async function(e,n,t){return new Promise(o=>{const r=()=>{const a=P(e,performance.now());a&&z(n,a),t.setInstruction(function(e){return e.currentIndex>=e.tasks.length?null:e.tasks[e.currentIndex].instruction}(n)||"Done"),t.setTask(function(e){return e.currentIndex>=e.tasks.length?null:e.tasks[e.currentIndex].id}(n)),t.setProgress(function(e){return 0===e.tasks.length?1:Math.min(e.currentIndex/e.tasks.length,1)}(n)),t.setVideoStatus(`Check ${Math.min(n.currentIndex+1,n.tasks.length)} of ${n.tasks.length}`),n.failed||B(n)?o():requestAnimationFrame(r)};requestAnimationFrame(r)})}(e.popupElements.video,n,e),n.failed||!function(e){return e.completedTasks>=e.requiredPasses}(n))return{outcome:"retry"};e.setInstruction("Hold still…"),e.setVideoStatus("Processing…");const t=await async function(e,n){const t=[];for(let o=0;o<e;o++){const r=he();r&&t.push(r),o<e-1&&await ge(n)}return t}(5,200),o=await async function(e){const n=[];for(const t of e){const e=await V(t);e&&n.push(e)}return n}(t),r=function(e){if(!e||0===e.length)return null;const n=e.map(e=>e.age).sort((e,n)=>e-n),t=n.length>=3?n.slice(1,-1):n;return t.reduce((e,n)=>e+n,0)/t.length}(o),i=await a.verify({estimatedAge:r,livenessOk:!0});return{outcome:i.token?"pass":"fail",token:i.token}};let s=await i();for(;"retry"===s.outcome&&o<3;)o++,e.showResult("retry","Please try again"),await Ee(e),await Ce(e,r),s=await i();Pe(),function(e,n,t){const o=e.params;"pass"===t.outcome?(e.token=t.token||null,e.showResult("pass","Verified"),n.emit("verified",t.token,e.id),o.callback?.(t.token)):(e.showResult("fail","Verification failed"),n.emit("error","failed",e.id),o.errorCallback?.("failed"))}(e,n,s)}catch(t){Pe(),await we(e,n,t)}}(e,n):async function(e,n){const t=e.params;try{e.openPopup(),e.setHeroStatus("Connecting…");const r=Y(t.mode,t),a=await r.createSession();e.setHeroStatus("Loading…"),await ee(a),e.setHeroStatus("Preparing…"),await async function(e){await le(e,"mediapipe")}(a),await C();const i=await async function(e){return le(e,"mediapipe")}(a);let s;await S(i),o={trackFace:()=>{const n=e.popupElements?.video;if(!n)return"null";const t=P(n,performance.now());return t?JSON.stringify({ts:t.timestampMs??performance.now(),faceCount:t.faceCount,headPose:t.headPose||null,blendshapes:t.blendshapes||null,boundingBox:t.boundingBox||null}):"null"},captureFrame:()=>he()?"true":"null"},Z=Object.freeze({...o}),te("__vmBridge",()=>Z),ye(e)||(e.showReady(),await Ee(e)),s=e.showCamera(),e.setVideoStatus("Requesting camera…"),await pe(s),function(e){if(Ae(),!e?.srcObject)return;const n=document.createElement("video");n.id="__openage_mirror",n.srcObject=e.srcObject,n.autoplay=!0,n.muted=!0,n.playsInline=!0,n.style.cssText="position:fixed;width:1px;height:1px;opacity:0;pointer-events:none;z-index:-1;",document.body.appendChild(n)}(s),e.setVideoStatus("Position your face"),await Se(s,e),e.showLiveness(),r.openChannel();let l=await r.receive();const c=a.rounds;for(let t=0;t<c;t++){if(!l)return Me(r),e.showResult("fail","Verification failed"),void n.emit("error","failed",e.id);if("verdict"===l.type)return Me(r),void xe(e,n,l);if("timeout"===l.type)return Me(r),e.showResult("fail","Verification failed"),void n.emit("error","failed",e.id);e.setVideoStatus(`Step ${t+1} of ${c}`);const o=l.token?.task;e.setInstruction(me[o]??"Look at the camera"),e.setTask(o),e.setProgress(t/c);let a;oe(await ke(e)),re(l.token);try{a=ae()}catch{return Me(r),e.showResult("fail","Verification failed"),void n.emit("error","failed",e.id)}const i={token:l.token,tokenSignature:l.tokenSignature,response:ie(a)},s=await r.sendAndReceive(i);if(!s)return Me(r),e.showResult("fail","Verification failed"),void n.emit("error","failed",e.id);if(s.complete)return Me(r),void xe(e,n,s);s.hint&&(e.setVideoStatus(s.hint),await ge(1e3)),l=s.nextChallenge||null}Me(r)}catch(t){Me(),await we(e,n,t)}var o}(e,n)}function xe(e,n,t){const o=t?.verdict||t,r=o?.token||null,a=e.params;if(r)return e.token=r,e.showResult("pass","Verified"),n.emit("verified",r,e.id),void a.callback?.(r);e.showResult("fail","Verification failed"),n.emit("error","failed",e.id),a.errorCallback?.("failed")}async function ke(e){const n=e.popupElements?.video,t=[],o=performance.now();for(;performance.now()-o<3e3;){const e=P(n,performance.now());e&&1===e.faceCount&&t.push({ts:e.timestampMs,headPose:e.headPose,blendshapes:e.blendshapes,boundingBox:e.boundingBox}),await ge(100)}return{faceCount:t.length>0?1:0,motionHistory:t}}function Ee(e){return new Promise(n=>{e.onStartClick=()=>{e.onStartClick=null,n()}})}async function Ce(e,n){const t=e.showCamera();e.setVideoStatus("Requesting camera…"),await pe(t),e.setVideoStatus("Preparing…"),await S(n),e.setVideoStatus("Position your face"),await Se(t,e)}function Se(e,n){return new Promise((t,o)=>{const r=function(e,n){let t=0,o=!1;const r=()=>{if(o)return;const a=P(e,performance.now());a&&0!==a.faceCount?a.faceCount>1?(n.onStatus?.("Only one person please"),t=0):(n.onStatus?.("Hold still…"),t++):(n.onStatus?.("Look at the camera"),t=0),t>=10?n.onReady?.():setTimeout(r,100)};return r(),{cancel:()=>{o=!0}}}(e,{onStatus:e=>n.setVideoStatus(e),onReady:()=>t()});setTimeout(()=>{r.cancel(),o(new Error("Positioning timeout"))},3e4)})}function Pe(){fe(),M()}function Me(e){fe(),Ae(),M(),function(){Z=null;try{delete window.__vmBridge}catch(e){}}(),function(){if(!G||!W)return;const e=W.exports.vm_destroy;e&&G[`_${e}`](),G=null,W=null,Q=null,X=null,K=null,Z=null;for(const e of["__vmFaceData","__vmChallenge","__vmBridge"])try{delete window[e]}catch(e){}}(),se.clear(),e?.close()}function Ae(){document.getElementById("__openage_mirror")?.remove()}class Te{constructor(){this.listeners=new Map}on(e,n){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(n),this}off(e,n){const t=this.listeners.get(e);return t&&t.delete(n),this}once(e,n){const t=(...o)=>{this.off(e,t),n(...o)};return t._original=n,this.on(e,t)}emit(e,...n){const t=this.listeners.get(e);if(t)for(const e of[...t])e(...n)}removeAllListeners(e){return e?this.listeners.delete(e):this.listeners.clear(),this}}const Le=new Te,_e=new Map;function $e(e){const n="undefined"!=typeof window&&window.openage||{},t=function(e){return"inline"===e||"embed"===e||"embedded"===e?"inline":"widget"}(e.layout??e.display??n.layout??n.display);return{mode:"serverless",theme:"auto",size:"normal",minAge:18,...n,...e,layout:t}}function He(e,n={}){const t=$e(n),o=new y(e,t);return _e.set(o.id,o),function(e){e.onChallenge=()=>{Le.emit("opened",e.id),be(e,Le)},"inline"===e.params.layout&&e.startChallenge()}(o),o.id}function Ve(e={}){const n=$e(e),t=function(e){return new y(document.createElement("div"),{...e,size:"invisible"})}(n);return t.anchorElement=n.anchorElement||null,_e.set(t.id,t),t.onChallenge=()=>{Le.emit("opened",t.id),be(t,Le)},t.startChallenge(),t.id}function Oe(e,n={}){const t=$e(n),o="string"==typeof e?document.querySelector(e):e;if(!o)throw new Error("OpenAge: element not found");let r=!1,a=null;const i=()=>{a=null},s=e=>{if(r||a)return;e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation?.();const n=Ve({...t,anchorElement:o,callback:e=>{t.callback?.(e),i(),(()=>{r=!0;try{o.click()}finally{r=!1}})()},errorCallback:e=>{i(),t.errorCallback?.(e)},closeCallback:()=>{i(),t.closeCallback?.()}});return a=n,n};return o.addEventListener("click",s,!0),()=>{o.removeEventListener("click",s,!0)}}function Fe(e){_e.get(e)?.reset()}function ze(e){const n=_e.get(e);n&&(n.destroy(),_e.delete(e))}function Be(e){return _e.get(e)?.getToken()||null}function Re(e){const n=_e.get(e);n&&n.startChallenge()}function je(e={}){return new Promise((n,t)=>{Ve({...e,callback:e=>n(e),errorCallback:e=>t("string"==typeof e?new Error(e):e),closeCallback:()=>t(new Error("User dismissed"))})})}function Ie(e,n){Le.on(e,n)}function qe(e,n){Le.off(e,n)}function Ne(e,n){Le.once(e,n)}function Ue(){if("undefined"==typeof document)return;if("explicit"===("undefined"!=typeof window&&window.openage||{}).render)return;const e=document.querySelectorAll(".openage");for(const n of e){const e={sitekey:n.dataset.sitekey,theme:n.dataset.theme,size:n.dataset.size,action:n.dataset.action,mode:n.dataset.mode,layout:n.dataset.layout||n.dataset.display,server:n.dataset.server};if(n.dataset.callback&&(e.callback=e=>{const t=window[n.dataset.callback];"function"==typeof t&&t(e)}),n.dataset.errorCallback&&(e.errorCallback=e=>{const t=window[n.dataset.errorCallback];"function"==typeof t&&t(e)}),n.dataset.expiredCallback&&(e.expiredCallback=()=>{const e=window[n.dataset.expiredCallback];"function"==typeof e&&e()}),n.dataset.bind){const t=document.getElementById(n.dataset.bind);if(t){Oe(t,e);continue}}He(n,e)}const n=document.currentScript?.dataset?.onload;n&&"function"==typeof window[n]&&window[n]()}"undefined"!=typeof document&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",Ue):Ue());const De={render:He,open:Ve,bind:Oe,reset:Fe,remove:ze,getToken:Be,execute:Re,challenge:je,on:Ie,off:qe,once:Ne,verify:D,decode:J,version:d};e.EventEmitter=Te,e.bind=Oe,e.challenge=je,e.decode=J,e.default=De,e.execute=Re,e.getToken=Be,e.off=qe,e.on=Ie,e.once=Ne,e.open=Ve,e.remove=ze,e.render=He,e.reset=Fe,e.verify=D,e.version=d,Object.defineProperty(e,"__esModule",{value:!0})});
|
|
2
2
|
//# sourceMappingURL=openage.min.js.map
|