From 2b1e2d5e02fd9689eb3ad8704ee614b7d2ed98b0 Mon Sep 17 00:00:00 2001 From: Mladen Macanovic Date: Thu, 11 Sep 2025 11:21:08 +0200 Subject: [PATCH 1/2] Maintanance: show banner for invalid licenses --- Source/Blazorise/wwwroot/utilities.js | 123 ++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/Source/Blazorise/wwwroot/utilities.js b/Source/Blazorise/wwwroot/utilities.js index 775690fd0a..ba98fe9240 100644 --- a/Source/Blazorise/wwwroot/utilities.js +++ b/Source/Blazorise/wwwroot/utilities.js @@ -333,6 +333,129 @@ export function verifyRsa(publicKey, content, signature) { export function log(message, args) { console.log(message, args); + + const HOST_ID = "blazorise-license-banner-host"; + const GLOBAL = "__blazoriseBannerState__"; + + // Global state (lives until full page refresh) + const st = (window[GLOBAL] ||= { + dismissed: false, + bodyObserver: null, + attrObserver: null + }); + + // If user already dismissed (in this page lifetime), don't show again + if (st.dismissed) { + return; + } + + // Prepare message for HTML (strip %c and escape) + let cleanMessage = typeof message === "string" ? message.replace(/%c/g, "") : String(message); + cleanMessage = cleanMessage + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + + // If banner exists, just update the message + let host = document.getElementById(HOST_ID); + if (host && host.shadowRoot) { + const msgEl = host.shadowRoot.querySelector(".msg"); + if (msgEl) msgEl.innerHTML = cleanMessage; + return; + } + + // Create host + Shadow DOM (isolates styles) + host = document.createElement("div"); + host.id = HOST_ID; + const shadow = host.attachShadow({ mode: "open" }); + + // Styles: Blazorise purple, subtle sizing + const style = document.createElement("style"); + style.textContent = ` +:host { all: initial !important; } +.wrapper { + position: fixed !important; + left: 0 !important; + right: 0 !important; + bottom: 0 !important; + z-index: 2147483647 !important; + padding: 10px 14px !important; + background: #6C63FF !important; /* Blazorise purple */ + color: #FFFFFF !important; + font: 500 14px/1.4 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif !important; + box-shadow: 0 -4px 12px rgba(0,0,0,0.2) !important; + display: flex !important; + align-items: center !important; + gap: .75rem !important; + border-top-left-radius: 6px !important; + border-top-right-radius: 6px !important; +} +.msg { flex: 1 1 auto !important; } +.btn { + appearance: none !important; + border: 1px solid rgba(255,255,255,0.7) !important; + background: transparent !important; + color: #FFFFFF !important; + padding: .3rem .6rem !important; + border-radius: .3rem !important; + font-size: 12px !important; + cursor: pointer !important; + transition: background .2s ease-in-out, color .2s ease-in-out !important; +} +.btn:hover { background: rgba(255,255,255,0.2) !important; } + `.trim(); + + shadow.appendChild(style); + + // Markup + const wrapperElement = document.createElement("div"); + wrapperElement.className = "wrapper"; + + const messageElement = document.createElement("span"); + messageElement.className = "msg"; + messageElement.innerHTML = cleanMessage; + + const button = document.createElement("button"); + button.className = "btn"; + button.type = "button"; + button.textContent = "Dismiss"; + button.addEventListener("click", () => { + // Mark dismissed for current page lifetime and stop observers + st.dismissed = true; + if (st.bodyObserver) try { st.bodyObserver.disconnect(); } catch { } + if (st.attrObserver) try { st.attrObserver.disconnect(); } catch { } + host.remove(); + }); + + wrapperElement.appendChild(messageElement); + wrapperElement.appendChild(button); + shadow.appendChild(wrapperElement); + document.body.appendChild(host); + + // Re-add if removed from body + if (!st.bodyObserver) { + st.bodyObserver = new MutationObserver(() => { + if (st.dismissed) return; + const exists = document.getElementById(HOST_ID); + if (!exists) { + try { document.body.appendChild(host); } catch { } + } + }); + st.bodyObserver.observe(document.body, { childList: true }); + } + + // Undo display:none / hidden + if (!st.attrObserver) { + st.attrObserver = new MutationObserver(() => { + if (st.dismissed) return; + host.style.display = "block"; + host.style.visibility = "visible"; + host.style.opacity = "1"; + }); + st.attrObserver.observe(host, { attributes: true, attributeFilter: ["style", "class", "hidden"] }); + } } export function createEvent(name) { From ecb5b3344e9f94bba374cc23fc28dad2f2fbb717 Mon Sep 17 00:00:00 2001 From: Mladen Macanovic Date: Thu, 11 Sep 2025 11:22:49 +0200 Subject: [PATCH 2/2] Remove comments --- Source/Blazorise/wwwroot/utilities.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/Source/Blazorise/wwwroot/utilities.js b/Source/Blazorise/wwwroot/utilities.js index ba98fe9240..f7eddb93af 100644 --- a/Source/Blazorise/wwwroot/utilities.js +++ b/Source/Blazorise/wwwroot/utilities.js @@ -337,19 +337,16 @@ export function log(message, args) { const HOST_ID = "blazorise-license-banner-host"; const GLOBAL = "__blazoriseBannerState__"; - // Global state (lives until full page refresh) const st = (window[GLOBAL] ||= { dismissed: false, bodyObserver: null, attrObserver: null }); - // If user already dismissed (in this page lifetime), don't show again if (st.dismissed) { return; } - // Prepare message for HTML (strip %c and escape) let cleanMessage = typeof message === "string" ? message.replace(/%c/g, "") : String(message); cleanMessage = cleanMessage .replace(/&/g, "&") @@ -358,7 +355,6 @@ export function log(message, args) { .replace(/"/g, """) .replace(/'/g, "'"); - // If banner exists, just update the message let host = document.getElementById(HOST_ID); if (host && host.shadowRoot) { const msgEl = host.shadowRoot.querySelector(".msg"); @@ -366,12 +362,10 @@ export function log(message, args) { return; } - // Create host + Shadow DOM (isolates styles) host = document.createElement("div"); host.id = HOST_ID; const shadow = host.attachShadow({ mode: "open" }); - // Styles: Blazorise purple, subtle sizing const style = document.createElement("style"); style.textContent = ` :host { all: initial !important; } @@ -382,7 +376,7 @@ export function log(message, args) { bottom: 0 !important; z-index: 2147483647 !important; padding: 10px 14px !important; - background: #6C63FF !important; /* Blazorise purple */ + background: #6C63FF !important; color: #FFFFFF !important; font: 500 14px/1.4 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif !important; box-shadow: 0 -4px 12px rgba(0,0,0,0.2) !important; @@ -409,7 +403,6 @@ export function log(message, args) { shadow.appendChild(style); - // Markup const wrapperElement = document.createElement("div"); wrapperElement.className = "wrapper"; @@ -422,7 +415,6 @@ export function log(message, args) { button.type = "button"; button.textContent = "Dismiss"; button.addEventListener("click", () => { - // Mark dismissed for current page lifetime and stop observers st.dismissed = true; if (st.bodyObserver) try { st.bodyObserver.disconnect(); } catch { } if (st.attrObserver) try { st.attrObserver.disconnect(); } catch { } @@ -434,7 +426,6 @@ export function log(message, args) { shadow.appendChild(wrapperElement); document.body.appendChild(host); - // Re-add if removed from body if (!st.bodyObserver) { st.bodyObserver = new MutationObserver(() => { if (st.dismissed) return; @@ -446,7 +437,6 @@ export function log(message, args) { st.bodyObserver.observe(document.body, { childList: true }); } - // Undo display:none / hidden if (!st.attrObserver) { st.attrObserver = new MutationObserver(() => { if (st.dismissed) return;