UNPKG

@privacyportal.org/privacy-kit

Version:

Lightweight and FOSS browser library bringing Privacy Portal features to your website users

511 lines (510 loc) 16.4 kB
/* MIT License - Copyright (c) 2025 Privacy Portal. See full license at https://github.com/privacyportal/privacy-kit/LICENSE. */ var K = Object.defineProperty; var W = (e, t, n) => t in e ? K(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n; var g = (e, t, n) => W(e, typeof t != "symbol" ? t + "" : t, n); const G = { VITE_AUTHORIZATION_URL: "https://app.privacyportal.org/oauth/authorize", VITE_TOKEN_URL: "https://api.privacyportal.org/oauth/token" }, { VITE_AUTHORIZATION_URL: T, VITE_TOKEN_URL: J } = G, m = { SCOPE: ["openid", "email"], RESPONSE_TYPE: "code", RESPONSE_MODE: "web_message" }, Y = "Please try again later."; class l extends Error { constructor({ message: t }) { super(t); } } function y(e) { if (e instanceof l) switch (d.onError) { case "ignore": break; case "alert": { alert(e.message); break; } default: X(d.onError) && d.onError.error(e.message); } } function X(e) { return e != null && typeof e == "object" && typeof e.error == "function"; } class Q { constructor() { g(this, "_client_id"); g(this, "_name_scope_required"); g(this, "_onError"); } get client_id() { return this._client_id; } set client_id(t) { this._client_id = t; } get onError() { return this._onError; } set onError(t) { this._onError = t; } get name_scope_required() { return this._name_scope_required || !1; } set name_scope_required(t) { this._name_scope_required = t; } get redirect_uri() { return window.location.origin; } get authorization_origin() { return new URL(T).origin; } getNameScopeRequired(t) { return (t == null ? void 0 : t.name_scope_required) !== void 0 ? t == null ? void 0 : t.name_scope_required : this.name_scope_required; } getScope(t) { return [ ...m.SCOPE, ...this.getNameScopeRequired(t) ? ["name"] : [] ].join(" "); } createAuthorizationURL(t, n, i) { if (!this.client_id) throw new l({ message: "OAuth client_id not configured." }); const r = new URLSearchParams(); r.set("client_id", this.client_id), r.set("scope", this.getScope(i)), r.set("response_type", m.RESPONSE_TYPE), r.set("response_mode", m.RESPONSE_MODE), r.set("nonce", crypto.randomUUID().substring(4, 18)), r.set("redirect_uri", this.redirect_uri), r.set("state", t), r.set("code_challenge", n), r.set("code_challenge_method", "S256"); const o = new URL(T); return o.search = r.toString(), o.toString(); } } const d = new Q(); function A(e, t) { return (n) => (i) => { var o; const r = t != null && t.shadow ? (o = i == null ? void 0 : i.composedPath()) == null ? void 0 : o[0] : i == null ? void 0 : i.target; if (r) return r[t != null && t.closest ? "closest" : "matches"](e) && n(r); }; } function L(e, t) { return e && t ? (e.value = t, e.dispatchEvent(new Event("paste", { bubbles: !0 })), e.dispatchEvent(new Event("change", { bubbles: !0 })), !0) : !1; } function b(e) { return e.offsetWidth !== 0 || e.offsetHeight !== 0; } function E(e) { return e.map((t) => `${t}:not(:disabled)`).join(", "); } function O(e, t) { e.addEventListener("focusin", () => { t.style.opacity = "1"; }), e.addEventListener("focusout", () => { t.style.opacity = "0"; }), document.addEventListener( "click", () => { t.style.opacity = document.activeElement === e ? "1" : "0"; }, !0 ); } async function I(e) { let t = e.parentElement, n; for (; t; ) { try { n = t.shadowRoot || t.attachShadow({ mode: "open" }); const i = document.createElement("style"); i.textContent = ":host{position:relative;display:block;box-sizing:border-box;margin:0;padding:0;}"; const r = document.createElement("slot"); n.append(i, r); } catch { } if (n) return { ancestor: t, shadowRoot: n }; t = t.parentElement; } throw new Error("Failed to attach HME DOM."); } function U(e, t, n) { let i = 0; const r = () => { cancelAnimationFrame(i), i = requestAnimationFrame(n); }, o = new ResizeObserver(r); o.observe(e), o.observe(t); const a = new FinalizationRegistry((s) => { s.unobserve(e), s.unobserve(t), s.disconnect(); }), c = new WeakRef(e); a.register(c, o); } function h(e, t) { return (t != null && t.toFixed ? e.toFixed(t.toFixed) : e) + "px"; } function ee(e) { const t = e.split(".", 3)[1]; return { ...JSON.parse(atob(t)) }; } function te(e) { const t = e instanceof Uint8Array ? e : new Uint8Array(e); return btoa(String.fromCharCode(...t)); } function B(e) { return ne(te(e)); } function ne(e) { return e.replace(/\//g, "_").replace(/\+/g, "-").replace(/=+$/, ""); } function x(e) { return typeof e == "string" || e instanceof String; } const j = [ "Authentication failed.", Y ].join(" "); function ie(e = 16) { const t = new Uint8Array(e); return crypto.getRandomValues(t), Array.from( t, (n) => n.toString(16).padStart(2, "0") ).join(""); } function re(e = 32) { const t = new Uint8Array(e); return crypto.getRandomValues(t), B(t); } async function oe(e) { const n = new TextEncoder().encode(e), i = await crypto.subtle.digest("SHA-256", n); return B(i); } async function ae(e, t) { try { if (!e) throw new l({ message: "code missing." }); if (!d.client_id) throw new l({ message: "OAuth client_id not configured" }); const n = new URLSearchParams(); return n.set("client_id", d.client_id), n.set("grant_type", "authorization_code"), n.set("code", e), n.set("redirect_uri", d.redirect_uri), n.set("code_verifier", t), (await fetch(J, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: n })).json(); } catch (n) { throw n instanceof l ? n : new l({ message: j }); } } async function ce({ id_token: e, access_token: t }) { if (!e || !t || !x(e) || !x(t)) throw new l({ message: j }); return { id_token: e, access_token: t }; } async function se(e) { try { const t = ie(), n = re(), i = await oe(n), r = d.createAuthorizationURL( t, i, e ), o = window.open(r, "PrivacyPortalSSO", "top=0"); if (!o) throw new l({ message: "Popup blocked. Please allow popups for this site." }); let a, c; return await Promise.race([ new Promise((s, f) => { window.addEventListener( "message", async (v) => { if (v.origin !== d.authorization_origin) return {}; clearTimeout(a), clearInterval(c); const { code: $, state: Z } = v.data; if (t !== Z) return f( new l({ message: "Authentication failed." }) ); const { id_token: C, access_token: R } = await ae( $, n ).catch(f); await ce({ id_token: C, access_token: R }).catch(f), s({ id_token: C, access_token: R }); }, !1 ); }), new Promise((s) => { c = setInterval(() => { o.closed && (clearTimeout(a), clearInterval(c), s(void 0)); }, 1e3); }), new Promise((s, f) => { a = setTimeout(() => { clearInterval(c), f(new l({ message: "Authorization timed out." })); }, 18e4); }) ]); } catch (t) { y(t); } } async function q(e) { const t = await se(e); if (t != null && t.id_token) { const { email: n, name: i } = ee(t.id_token); return { email: n, name: i }; } return {}; } function de(e, t, n) { const i = document.createElementNS("http://www.w3.org/2000/svg", "svg"); e != null && e.width && i.setAttribute("width", e.width), e != null && e.height && i.setAttribute("height", e.height), e != null && e.viewBox && i.setAttribute("viewBox", e.viewBox), n != null && n.fillRule && (i.style.fillRule = n.fillRule), n != null && n.clipRule && (i.style.clipRule = n.clipRule), n != null && n.strokeLinejoin && (i.style.strokeLinejoin = n.strokeLinejoin), n != null && n.strokeMiterlimit && (i.style.strokeMiterlimit = n.strokeMiterlimit); for (const r of t) i.appendChild(r); return i; } function P(e, t) { const n = document.createElementNS("http://www.w3.org/2000/svg", "path"); return n.setAttribute("d", e), t != null && t.fill && (n.style.fill = t.fill), n; } function le() { return de( { width: "100%", height: "100%", viewBox: "0 0 150 150" }, [ P( "M150,22.5C150,10.082 139.918,0 127.5,0L22.5,0C10.082,0 0,10.082 0,22.5L0,127.5C0,139.918 10.082,150 22.5,150L127.5,150C139.918,150 150,139.918 150,127.5L150,22.5Z", { fill: "#333" } ), P( "M112.979,124.728C120.977,123.051 126.993,115.921 126.993,107.39C126.993,102.517 125.03,98.101 121.857,94.897L126.44,90.314L126.45,90.324C130.816,94.695 133.518,100.73 133.518,107.39C133.518,119.687 124.308,129.85 112.414,131.351L114.651,133.588L110.239,138L100.561,128.322L110.239,118.644L114.651,123.056L112.979,124.728ZM106.337,83.425L104.103,81.191L108.515,76.779L118.192,86.457L108.515,96.135L104.103,91.723L105.781,90.045C97.768,91.71 91.737,98.848 91.737,107.39C91.737,112.261 93.698,116.675 96.87,119.879L96.873,119.882L92.391,124.363L92.293,124.469L92.29,124.465C87.918,120.093 85.212,114.055 85.212,107.39C85.212,95.085 94.434,84.916 106.337,83.425ZM78.539,116.812L30.658,116.812C22.834,116.812 16.482,110.46 16.482,102.636L16.482,36.484C16.482,28.661 22.834,22.309 30.658,22.309L117.06,22.309C124.884,22.309 131.236,28.661 131.236,36.484L131.236,82.065C128.437,79.412 125.165,77.253 121.561,75.728L121.561,36.128C121.561,36.128 86.535,62.757 76.366,70.488C74.751,71.716 72.513,71.711 70.903,70.475C60.81,62.728 26.158,36.128 26.158,36.128L26.158,107.005L76.571,107.005C76.719,110.44 77.402,113.736 78.539,116.812ZM109.365,86.454L109.365,86.461L109.369,86.457L109.365,86.454ZM73.627,59.885L111.21,31.984L36.508,31.984L73.627,59.885Z", { fill: "#fff" } ) ], { fillRule: "evenodd", clipRule: "evenodd", strokeLinejoin: "round", strokeMiterlimit: "2" } ); } const z = "firefox", ue = "android"; function H(e, t) { const n = e.userAgent.toLowerCase(); for (const i of t) if (n.indexOf(i) === -1) return !1; return !0; } function fe(e) { return H(e, [z, ue]); } function he(e) { return H(e, [z]); } let N; const F = "*****@pportal.io", _ = "Hide my Email", u = "input[type=text]", V = [ "input[type=email]", `${u}[id*="email" i]`, `${u}[name*="email" i]`, `${u}[name*="username" i]`, `${u}[name*="login" i]`, `${u}[placeholder*="email" i]`, `${u}[placeholder*="e-mail" i]` ], S = V.map( (e) => `${e}:not([data-pp])` ).join(", "), ye = A(S), ge = A( S, { shadow: !0 } ); async function w(e) { e.value = "", N = e; const { email: t } = await q(); L(N, t); } async function p(e) { const t = `pp-${window.crypto.randomUUID().substring(0, 8)}`; if (fe(navigator)) { const { ancestor: n, shadowRoot: i } = await I(e), r = document.createElement("li"); r.innerText = _, r.style.padding = "3px", r.style.cursor = "pointer"; const o = document.createElement("ul"); o.id = t, o.style.display = "block", o.style.position = "absolute", o.style.maxHeight = "300px", o.style.overflowY = "auto", o.style.listStyle = "none", o.style.backgroundColor = "Canvas", o.style.color = "CanvasText", o.style.colorScheme = "light dark", o.style.boxShadow = "0 2px 2px #999", o.style.fontSize = "small", o.style.zIndex = "1000", o.style.opacity = "0", o.style.padding = o.style.margin = "0px", o.appendChild(r); const a = document.createElement("style"); a.textContent = "li:hover{background-color:ButtonFace;color:ButtonText;}", i.append(a, o), U(e, n, () => { const c = e.getBoundingClientRect(), s = n.getBoundingClientRect(); o.style.width = h(c.width), o.style.top = h(c.bottom - s.top, { toFixed: 2 }), o.style.left = h(c.left - s.left, { toFixed: 2 }); }), e.setAttribute("data-pp", ""), r.onmousedown = (c) => { c.preventDefault(); }, r.onclick = async () => { o.style.opacity = "0", await w(e).catch(y); }, O(e, o); } else { const n = document.createElement("option"); n.setAttribute("id", "new-privacy-addr"), n.setAttribute("value", F), n.textContent = _; let i; if (e.hasAttribute("list")) { const r = e.getAttribute("list"); r && (i = document.getElementById(r)); } if (i || (i = document.createElement("datalist"), i.setAttribute("id", t), e.setAttribute("list", t), e.insertAdjacentElement("afterend", i)), i.appendChild(n), e.setAttribute("data-pp", ""), he(navigator)) { const { ancestor: r, shadowRoot: o } = await I(e), a = document.createElement("button"); a.title = _, a.ariaLabel = _, Object.assign(a.style, { position: "absolute", border: "none", borderRadius: "15px", cursor: "pointer", padding: "0", margin: "0", zIndex: "1000", pointerEvents: "auto", right: "8px", opacity: "0" }), a.appendChild(le()), o.append(a), U(e, r, () => { const c = e.getBoundingClientRect(), s = r.getBoundingClientRect(), f = parseFloat( window.getComputedStyle(e).getPropertyValue("padding-right") ) || 0; a.style.height = a.style.width = h( c.height * 0.7, { toFixed: 2 } ), a.style.top = h( c.top - s.top + c.height * 0.15, { toFixed: 2 } ), a.style.right = h( s.right - c.right + Math.max(f, c.height * 0.15), { toFixed: 2 } ); }), a.onmousedown = (c) => { c.preventDefault(); }, a.onclick = async () => { try { a.disabled = !0, await w(e); } catch (c) { y(c); } finally { a.disabled = !1; } }, O(e, a); } } e.addEventListener("input", async () => { try { (e.value === "@" || e.value === F) && await w(e); } catch (n) { y(n); } }); } function k() { for (const e of Array.from( document.querySelectorAll(S) )) b(e) && p(e).catch(console.error); } function _e(e) { e != null && e.addEventListener && e.addEventListener( "focusin", ye((t) => { p(t).catch(console.error); }), !0 ); } function D() { document.addEventListener( "click", ge((e) => { p(e).catch(console.error); }), !0 ); } function M() { [...Array.from(document.querySelectorAll("iframe"))].map((e) => { var t; try { const n = (e == null ? void 0 : e.contentDocument) || ((t = e == null ? void 0 : e.contentWindow) == null ? void 0 : t.document); n != null && n.addEventListener && _e(n); } catch { } }); } function me() { window.addEventListener("load", () => { k(), D(), M(); }), document.readyState === "complete" && (k(), D(), M()); } const we = [ "form button[data-pp-action=subscribe-anonymously]" ], Le = E( we ), be = E(V), Ae = E([ `${u}[id*="name" i]`, `${u}[name*="name" i]` ]), Ee = A( Le, { closest: !0 } ); async function Se(e) { try { const t = e.closest("form"); if (!t) return; const n = [ ...Array.from( t.querySelectorAll(be) ) ].find(b); if (!n) return; const i = [ ...Array.from( t.querySelectorAll(Ae) ) ].find(b); let r; i != null && i.required && (r = { name_scope_required: !0 }); const { email: o, name: a } = await q(r); L(n, o) && (!i || !a || L(i, a)) && t.dispatchEvent(new Event("submit", { cancelable: !0 })) && t.submit(); } catch (t) { y(t); } } function pe(e) { e != null && e.addEventListener && e.addEventListener( "click", Ee((t) => { Se(t); }), !0 ); } function ve() { window.addEventListener("load", () => { pe(document); }); } function Ce(e) { return typeof e == "object" && e !== null; } const Re = { hide_my_email: !0, subscribe_anonymously: !0, on_error: "alert" }; class Oe { static run(t) { const { client_id: n, hide_my_email: i, subscribe_anonymously: r, on_error: o } = { ...Re, ...t }; d.client_id = n, d.onError = o, i && me(), r && (Ce(r) && (d.name_scope_required = r.set_name_field), ve()); } } export { Oe as default };