UNPKG

@privacyportal.org/privacy-kit

Version:

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

536 lines (535 loc) 16.9 kB
/* MIT License - Copyright (c) 2025 Privacy Portal. See full license at https://github.com/privacyportal/privacy-kit/LICENSE. */ var W = Object.defineProperty; var K = (e, t, n) => t in e ? W(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n; var g = (e, t, n) => K(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: w, VITE_TOKEN_URL: J } = G, L = { 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 h(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(w).origin; } get authorization_url_placeholder() { return `${w}?loading`; } 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 [ ...L.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", L.RESPONSE_TYPE), r.set("response_mode", L.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(w); return o.search = r.toString(), o.toString(); } } const d = new Q(); function E(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 A(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 S(e) { return e.offsetWidth !== 0 || e.offsetHeight !== 0; } function R(e) { return e.map((t) => `${t}:not(:disabled)`).join(", "); } function I(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 U(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 x(e, t, n) { let i = 0; const r = () => { cancelAnimationFrame(i), i = requestAnimationFrame(n); }, o = new ResizeObserver(r); o.observe(e), o.observe(t); const c = new FinalizationRegistry((s) => { s.unobserve(e), s.unobserve(t), s.disconnect(); }), a = new WeakRef(e); c.register(a, o); } function f(e, t) { return (t != null && t.toFixed ? e.toFixed(t.toFixed) : e) + "px"; } function m(e) { const t = window.open( d.authorization_url_placeholder, "PrivacyPortalSSO", "top=0" ); t ? e(t) : h( new l({ message: "Please allow popups in order to use Hide-my-Email." }) ); } 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 j(e) { return ne(te(e)); } function ne(e) { return e.replace(/\//g, "_").replace(/\+/g, "-").replace(/=+$/, ""); } function P(e) { return typeof e == "string" || e instanceof String; } const q = [ "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), j(t); } async function oe(e) { const n = new TextEncoder().encode(e), i = await crypto.subtle.digest("SHA-256", n); return j(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: q }); } } async function ce({ id_token: e, access_token: t }) { if (!e || !t || !P(e) || !P(t)) throw new l({ message: q }); return { id_token: e, access_token: t }; } async function se(e, t) { try { const n = ie(), i = re(), r = await oe(i); e.location.href = d.createAuthorizationURL( n, r, t ); let o, c; return await Promise.race([ new Promise((a, s) => { window.addEventListener( "message", async (_) => { if (_.origin !== d.authorization_origin) return {}; clearTimeout(o), clearInterval(c); const { code: V, state: Z } = _.data; if (n !== Z) return s( new l({ message: "Authentication failed." }) ); const { id_token: O, access_token: T } = await ae( V, i ).catch(s); await ce({ id_token: O, access_token: T }).catch(s), a({ id_token: O, access_token: T }); }, !1 ); }), new Promise((a) => { c = setInterval(() => { e.closed && (clearTimeout(o), clearInterval(c), a(void 0)); }, 1e3); }), new Promise((a, s) => { o = setTimeout(() => { clearInterval(c), s(new l({ message: "Authorization timed out." })); }, 18e4); }) ]); } catch (n) { h(n); } } async function z(e, t) { const n = await se(e, t); if (n != null && n.id_token) { const { email: i, name: r } = ee(n.id_token); return { email: i, name: r }; } 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 N(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" }, [ N( "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" } ), N( "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 H = "firefox", ue = "android", fe = "safari", he = "chrome", _e = "chromium", ge = "criOS"; function C(e, t) { const n = e.userAgent.toLowerCase(); for (const i of t) if (n.indexOf(i) === -1) return !1; return !0; } function ye(e, t) { const n = e.userAgent.toLowerCase(); for (const i of t) if (n.indexOf(i) > -1) return !0; return !1; } function me(e) { return C(e, [H, ue]); } function we(e) { return C(e, [H]); } function Le(e) { return C(e, [fe]) && !ye(e, [ he, _e, ge ]); } let F; const M = "*****@pportal.io", y = "Hide my Email", u = "input[type=text]", $ = [ "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]` ], p = $.map( (e) => `${e}:not([data-pp])` ).join(", "), be = E(p), Ae = E( p, { shadow: !0 } ); async function b(e, t) { e.value = "", F = e; const { email: n } = await z(t); A(F, n); } async function v(e) { const t = `pp-${window.crypto.randomUUID().substring(0, 8)}`; if (me(navigator) || Le(navigator)) { const { ancestor: n, shadowRoot: i } = await U(e), r = document.createElement("li"); r.innerText = y, 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 c = document.createElement("style"); c.textContent = "li:hover{background-color:ButtonFace;color:ButtonText;}", i.append(c, o), x(e, n, () => { const a = e.getBoundingClientRect(), s = n.getBoundingClientRect(); o.style.width = f(a.width), o.style.top = f(a.bottom - s.top, { toFixed: 2 }), o.style.left = f(a.left - s.left, { toFixed: 2 }); }), e.setAttribute("data-pp", ""), r.onmousedown = (a) => { a.preventDefault(); }, r.onclick = () => { m((a) => { o.style.opacity = "0", b(e, a).catch(h); }); }, I(e, o); } else { const n = document.createElement("option"); n.setAttribute("id", "new-privacy-addr"), n.setAttribute("value", M), n.textContent = y; 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", ""), we(navigator)) { const { ancestor: r, shadowRoot: o } = await U(e), c = document.createElement("button"); c.title = y, c.ariaLabel = y, Object.assign(c.style, { position: "absolute", border: "none", borderRadius: "15px", cursor: "pointer", padding: "0", margin: "0", zIndex: "1000", pointerEvents: "auto", right: "8px", opacity: "0" }), c.appendChild(le()), o.append(c), x(e, r, () => { const a = e.getBoundingClientRect(), s = r.getBoundingClientRect(), _ = parseFloat( window.getComputedStyle(e).getPropertyValue("padding-right") ) || 0; c.style.height = c.style.width = f( a.height * 0.7, { toFixed: 2 } ), c.style.top = f( a.top - s.top + a.height * 0.15, { toFixed: 2 } ), c.style.right = f( s.right - a.right + Math.max(_, a.height * 0.15), { toFixed: 2 } ); }), c.onmousedown = (a) => { a.preventDefault(); }, c.onclick = () => { m((a) => { c.disabled = !0, b(e, a).catch(h).finally(() => { c.disabled = !1; }); }); }, I(e, c); } } e.addEventListener("input", () => { (e.value === "@" || e.value === M) && m((n) => { b(e, n).catch(h); }); }); } function D() { for (const e of Array.from( document.querySelectorAll(p) )) S(e) && v(e).catch(console.error); } function Se(e) { e != null && e.addEventListener && e.addEventListener( "focusin", be((t) => { v(t).catch(console.error); }), !0 ); } function k() { document.addEventListener( "click", Ae((e) => { v(e).catch(console.error); }), !0 ); } function B() { [...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 && Se(n); } catch { } }); } function Ee() { window.addEventListener("load", () => { D(), k(), B(); }), document.readyState === "complete" && (D(), k(), B()); } const Re = [ "form button[data-pp-action=subscribe-anonymously]" ], Ce = R( Re ), pe = R($), ve = R([ `${u}[id*="name" i]`, `${u}[name*="name" i]` ]), Oe = E( Ce, { closest: !0 } ); async function Te(e, t) { try { const n = e.closest("form"); if (!n) return; const i = [ ...Array.from( n.querySelectorAll(pe) ) ].find(S); if (!i) return; const r = [ ...Array.from( n.querySelectorAll(ve) ) ].find(S); let o; r != null && r.required && (o = { name_scope_required: !0 }); const { email: c, name: a } = await z(t, o); A(i, c) && (!r || !a || A(r, a)) && n.dispatchEvent(new Event("submit", { cancelable: !0 })) && n.submit(); } catch (n) { h(n); } } function Ie(e) { e != null && e.addEventListener && e.addEventListener( "click", Oe((t) => { m((n) => { Te(t, n); }); }), !0 ); } function Ue() { window.addEventListener("load", () => { Ie(document); }); } function xe(e) { return typeof e == "object" && e !== null; } const Pe = { hide_my_email: !0, subscribe_anonymously: !0, on_error: "alert" }; class Fe { static run(t) { const { client_id: n, hide_my_email: i, subscribe_anonymously: r, on_error: o } = { ...Pe, ...t }; d.client_id = n, d.onError = o, i && Ee(), r && (xe(r) && (d.name_scope_required = r.set_name_field), Ue()); } } export { Fe as default };