UNPKG

@enjoys/pinglet

Version:

Lightweight Customizable Web & CustomPush Notification SDK for modern web apps. Supports customizable layouts, secure delivery, and real-time updates via SSE.

556 lines (555 loc) 16.1 kB
!(() => { ((e) => { !(() => { const e = document.createElement("link"); (e.rel = "stylesheet"), (e.href = "https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"), document.head.appendChild(e); const t = document.createElement("style"); (t.innerHTML = '\n [class^="pinglet-"],\n [class*=" pinglet-"] {\n font-family: \'Inter\', sans-serif !important;\n }\n '), document.head.appendChild(t); })(); const t = { style: { color: "#fff", backgroundColor: "#000" }, position: "bottom-left", branding: { html: 'Powered by <a href="https://pinglet.enjoys.in" style="color:#4da6ff;text-decoration:none;" target="_blank">Pinglet</a> - Enjoys', }, duration: 2e3, }; const n = "pinglet-wrapper"; let o = 0; let i = null; const a = { createMediaElement(e, t = "Pinglet Notification") { let n; switch (e.type) { default: (n = document.createElement("img")), (n.src = e.src), (n.alt = t); break; case "video": (n = document.createElement("video")), (n.src = e.src), (n.controls = !0), n.setAttribute("playsinline", ""), n.setAttribute("muted", ""); break; case "audio": (n = document.createElement("audio")), (n.src = e.src), (n.controls = !0); break; case "icon": (n = document.createElement("span")), (n.className = `icon-${e.src}`), n.setAttribute("aria-label", t); } return ( ["image", "video", "audio"].includes(e.type) && (Object.assign(n.style, { width: "100%", maxWidth: "320px", height: "auto", borderRadius: "10px", marginBottom: "8px", display: "block", }), "audio" === e.type && (n.style.height = "40px"), ("video" !== e.type && "image" !== e.type) || ((n.style.height = "180px"), (n.style.objectFit = "cover"))), n ); }, createWrapper(e = "bottom-right") { let t = document.getElementById(n); if (!t) { (t = document.createElement("div")), (t.id = n), Object.assign(t.style, { position: "fixed", zIndex: 99999, display: "flex", flexDirection: "column", gap: "10px", maxWidth: "calc(100vw - 20px)", padding: "12px", pointerEvents: "none", }); const o = { "top-left": { top: "0", left: "0", alignItems: "flex-start" }, "top-right": { top: "0", right: "0", alignItems: "flex-end" }, "bottom-left": { bottom: "0", left: "0", alignItems: "flex-start", }, "bottom-right": { bottom: "0", right: "0", alignItems: "flex-end", }, "top-center": { top: "0", left: "50%", transform: "translateX(-50%)", alignItems: "center", }, "bottom-center": { bottom: "0", left: "50%", transform: "translateX(-50%)", alignItems: "center", }, }; Object.assign(t.style, o[e] || o["bottom-right"]), document.body.appendChild(t); } return t; }, playNotificationSound({ play: e, src: t }) { if (!e || !t) return; new Audio(t).play().catch((e) => {}); }, showToast(e) { const { title: n = "", description: a = "", media: s, buttons: r = [], } = e; const { style: l = {}, position: c, duration: d, branding: p } = t; const m = this.createWrapper(c); const f = document.createElement("div"); (f.className = "pinglet-toast"), o++, Object.assign(f.style, { background: "#1f1f1f", color: "#fff", padding: "16px 20px", borderRadius: "12px", boxShadow: "0 6px 18px rgba(0,0,0,0.25)", fontFamily: "'Inter', sans-serif", fontSize: "14px", lineHeight: "1.5", maxWidth: "360px", minWidth: "280px", pointerEvents: "auto", position: "relative", opacity: "0", transform: "none", transition: "all 0.4s ease", ...(o >= 4 ? { opacity: 0.8, scale: 0.95 } : {}), ...l, }); const g = document.createElement("span"); if ( ((g.innerHTML = "&times;"), Object.assign(g.style, { position: "absolute", top: "6px", right: "10px", cursor: "pointer", fontSize: "18px", color: "#aaa", }), (g.onclick = () => this.removeToast(f)), f.appendChild(g), s) ) { if ("icon" === s.type) { const e = document.createElement("div"); (e.innerHTML = `<span style="font-size:14px;margin-right:8px;">${s.src}</span><strong>${n}</strong>`), f.appendChild(e); } else if ("image" === s.type || "video" === s.type) { const e = this.createMediaElement(s); f.appendChild(e); const t = document.createElement("div"); (t.innerHTML = `<strong>${n}</strong>`), f.appendChild(t); } } else { const e = document.createElement("div"); (e.innerHTML = `<strong>${n}</strong>`), f.appendChild(e); } if (a) { const e = document.createElement("div"); (e.textContent = a), (e.style.color = "#ccc"), f.appendChild(e); } if (r.length) { const e = document.createElement("div"); Object.assign(e.style, { display: "flex", gap: "10px", marginTop: "8px", flexWrap: "wrap", }), r.forEach((t) => { const n = document.createElement("button"); if ( ((n.type = "button"), (n.className = `pinglet-toast-btn-${o}`), (n.textContent = t.text), Object.assign(n.style, { padding: "6px 12px", background: "#333", border: "1px solid #555", color: "#fff", borderRadius: "6px", cursor: "pointer", fontSize: "13px", }), t.onClick) ) { if ("string" === typeof t.onClick) try { t.onClick = new Function(`return (${t.onClick})`)(); } catch (e) { t.onClick = null; } "function" === typeof t.onClick && n.addEventListener("click", t.onClick); } e.appendChild(n); }), f.appendChild(e); } i || ((i = document.createElement("div")), (i.innerHTML = p.html), Object.assign(i.style, { fontSize: "11px", color: "#888", textAlign: "right", fontFamily: "'Inter', sans-serif", marginTop: "8px", })), m.appendChild(f), i && m.appendChild(i), setTimeout(() => { (f.style.opacity = "1"), (f.style.transform = "translateX(0)"); }, 10); let u = setTimeout(() => this.removeToast(f), d); f.addEventListener("mouseenter", () => clearTimeout(u)), f.addEventListener("mouseleave", () => { u = setTimeout(() => removeToast(f), 2e3); }); }, removeToast(e) { e && ((e.style.opacity = "0"), (e.style.transform = "translateX(100%)"), setTimeout(() => { e.remove(), o--, 0 === o && i && (i.remove(), (i = null)); }, 400)); }, }; const s = { init({ endpoint: e, configuredDomain: t, projectIds: n = [] }) { const o = location.hostname.replace(/^www\./, ""); if (0 === t.length) return void this._showPopup( "Domain Configuration Error ", "Please ensure you are running Pinglet on a valid domain.", ); if (0 === n.length) return void this._showPopup( "Project Configuration Error ", "Please add at least one project to your Pinglet configuration.", ); t === o ? n.forEach((t) => { new EventSource( `${e}/sse?projectId=${encodeURIComponent(t)}`, ).onmessage = (e) => { try { const t = JSON.parse(e.data); a.showToast({ title: t.title, description: t.description, media: t.media, buttons: t.buttons, }); } catch (e) {} }; }) : this._showPopup( "Domain Configuration Error ", `Your current domain is not allowed. Please ensure it matches one of the following:\n ${t}`, ); }, _showNotification(e) { const { title: t = "", description: n = "", buttons: o = [], media: i = null, style: a = {}, branding: s = { show: !0, html: 'Powered by <a href="https://pinglet.enjoys.in" style="color:#4da6ff;text-decoration:none;" target="_blank">Pinglet</a> - Enjoys', }, } = e; const r = this._getWrapper(); const l = document.createElement("div"); if ( ((l.className = "pinglet-toast"), Object.assign(l.style, { background: "#1f1f1f", color: "#fff", padding: "16px 20px", borderRadius: "12px", boxShadow: "0 6px 18px rgba(0, 0, 0, 0.25)", fontFamily: "'Inter', sans-serif", minWidth: "260px", maxWidth: "360px", opacity: "0", transform: "translateX(100%)", transition: "opacity 0.3s ease, transform 0.3s ease", display: "flex", flexDirection: "column", gap: "12px", ...a, }), i) ) { if ("icon" === i.type) { const e = document.createElement("div"); Object.assign(e.style, { display: "flex", alignItems: "center", gap: "10px", fontSize: "16px", fontWeight: "600", }); const n = document.createElement("span"); n.textContent = i.src; const o = document.createElement("span"); (o.textContent = t), e.appendChild(n), e.appendChild(o), l.appendChild(e); } else if ("image" === i.type || "video" === i.type) { const e = document.createElement(i.type); Object.assign(e.style, { maxWidth: "100%", borderRadius: "8px" }), (e.src = i.src), "video" === i.type && (e.controls = !0), l.appendChild(e); const n = document.createElement("div"); (n.textContent = t), Object.assign(n.style, { fontSize: "16px", fontWeight: "600" }), l.appendChild(n); } } else { const e = document.createElement("div"); (e.textContent = t), Object.assign(e.style, { fontSize: "16px", fontWeight: "600" }), l.appendChild(e); } if (n) { const e = document.createElement("div"); (e.textContent = n), Object.assign(e.style, { fontSize: "13.5px", color: "#ccc", lineHeight: "1.5", }), l.appendChild(e); } if (Array.isArray(o) && o.length) { const e = document.createElement("div"); Object.assign(e.style, { display: "flex", gap: "10px", marginTop: "6px", }); for (const t of o) { const n = document.createElement("button"); (n.textContent = t.text || "Click"), Object.assign(n.style, { padding: "8px 14px", background: "#333", color: "#fff", border: "1px solid #444", borderRadius: "6px", fontSize: "13px", cursor: "pointer", transition: "background 0.3s", }), (n.onmouseover = () => (n.style.background = "#444")), (n.onmouseout = () => (n.style.background = "#333")), "function" === typeof t.onClick && (n.onclick = t.onClick), e.appendChild(n); } l.appendChild(e); } if ((r.appendChild(l), !1 !== s?.show)) { const e = document.createElement("div"); (e.innerHTML = s?.html || ""), Object.assign(e.style, { fontSize: "11px", color: "#999", marginTop: "4px", textAlign: "right", fontFamily: "'Inter', sans-serif", }), r.appendChild(e), setTimeout(() => { l.remove(), e.remove(); }, 5e3); } else setTimeout(() => l.remove(), 5e3); requestAnimationFrame(() => { (l.style.opacity = "1"), (l.style.transform = "translateX(0)"); }); }, _showPopup( e, t, n = [ { text: "See Docs", onClick: () => window.open("https://pinglet.enjoys.in/docs", "_blank"), }, ], o = "⚠️", ) { const i = "toastContainer"; let a = document.getElementById(i); a || ((a = document.createElement("div")), (a.id = i), Object.assign(a.style, { position: "fixed", bottom: "24px", right: "24px", zIndex: "9999", display: "flex", flexDirection: "column", gap: "4px", alignItems: "flex-end", }), document.body.appendChild(a)); const s = document.createElement("div"); Object.assign(s.style, { background: "#1f1f1f", color: "#fff", padding: "16px 20px", borderRadius: "12px", boxShadow: "0 6px 18px rgba(0, 0, 0, 0.25)", fontFamily: "'Inter', sans-serif", minWidth: "260px", maxWidth: "340px", opacity: "0", transform: "translateX(100%)", transition: "opacity 0.3s ease, transform 0.3s ease", display: "flex", flexDirection: "column", gap: "10px", }); const r = document.createElement("div"); Object.assign(r.style, { display: "flex", alignItems: "center", gap: "10px", fontSize: "16px", fontWeight: "600", }); const l = document.createElement("div"); l.textContent = o; const c = document.createElement("div"); if ( ((c.textContent = e), r.appendChild(l), r.appendChild(c), s.appendChild(r), t) ) { const e = document.createElement("div"); (e.textContent = t), Object.assign(e.style, { fontSize: "13.5px", fontWeight: "400", color: "#ddd", lineHeight: "1.5", }), s.appendChild(e); } if (Array.isArray(n) && n.length) { const e = document.createElement("div"); Object.assign(e.style, { marginTop: "8px", display: "flex", gap: "10px", justifyContent: "flex-start", }); for (const t of n) { const n = document.createElement("button"); (n.textContent = t.text || "Click"), Object.assign(n.style, { padding: "8px 14px", background: "#333", color: "#fff", border: "1px solid #444", borderRadius: "6px", fontSize: "13px", cursor: "pointer", transition: "background 0.3s", }), (n.onmouseover = () => (n.style.background = "#444")), (n.onmouseout = () => (n.style.background = "#333")), "function" === typeof t.onClick && n.addEventListener("click", t.onClick), e.appendChild(n); } s.appendChild(e); } a.appendChild(s); const d = document.createElement("div"); (d.innerHTML = 'Powered by <a href="https://pinglet.enjoys.in" target="_blank" style="color:#4da6ff;text-decoration:none;">Pinglet</a> - Enjoys'), Object.assign(d.style, { fontSize: "11px", color: "#999", marginTop: "4px", textAlign: "right", fontFamily: "'Inter', sans-serif", }), a.appendChild(d), requestAnimationFrame(() => { (s.style.opacity = "1"), (s.style.transform = "translateX(0)"); }), setTimeout(() => { (s.style.opacity = "0"), (s.style.transform = "translateX(100%)"), setTimeout(() => { s.remove(), d.remove(); }, 500); }, 5e3); }, _getWrapper() { let e = document.getElementById("pinglet-wrapper"); return ( e || ((e = document.createElement("div")), (e.id = "pinglet-wrapper"), (e.style = "position:fixed;bottom:20px;right:20px;z-index:99999;display:flex;flex-direction:column;gap:10px;max-width:300px;font-family:sans-serif"), document.body.appendChild(e)), e ); }, _addBranding(e) { if (!document.getElementById("pinglet-brand")) { const t = document.createElement("div"); (t.id = "pinglet-brand"), (t.style = "font-size:10px;text-align:right;color:#aaa;margin-top:8px"), (t.innerHTML = "Powered by <strong>Pinglet</strong> – Enjoys"), e.appendChild(t); } }, }; e.PingletWidget = s; })(window); })();