(function () { const LOGOUT_URL = {{ lookup('sso', application_id, 'logout_url') | to_json }}; const INJECTED_ATTR = "data-injected-logout"; const LOGOUT_SVG = '"; function findIconGroupAnchor() { const banner = document.querySelector( "header, [role='banner'], .navbar, nav.navbar, nav.menu-header", ) || document.querySelector("nav"); if (!banner) return null; let best = null; let bestRight = -1; banner.querySelectorAll("div, ul, nav").forEach((el) => { const directControls = el.querySelectorAll( ":scope > button, :scope > a, :scope > li > button, :scope > li > a", ); if (directControls.length < 2) return; const rect = el.getBoundingClientRect(); if (rect.width === 0 || rect.height === 0) return; if (rect.right > bestRight) { best = el; bestRight = rect.right; } }); return best || banner; } function ensureLogoutIcon() { if (document.querySelector("[" + INJECTED_ATTR + "]")) return; if (!document.body) return; const anchor = findIconGroupAnchor(); if (!anchor) return; // Clone the styling of an existing sibling icon button so the // injected logout matches the role's theme (box, padding, hover // state, border-radius) without us guessing per-app design tokens. const sibling = anchor.querySelector( ":scope > button, :scope > a, :scope > li > button, :scope > li > a", ); const btn = document.createElement( sibling && sibling.tagName === "BUTTON" ? "button" : "a", ); btn.setAttribute(INJECTED_ATTR, "true"); if (btn.tagName === "A") btn.setAttribute("href", LOGOUT_URL); btn.setAttribute("title", "Logout"); btn.setAttribute("aria-label", "Logout"); btn.setAttribute("type", btn.tagName === "BUTTON" ? "button" : ""); if (sibling) { btn.className = sibling.className; const cs = window.getComputedStyle(sibling); btn.style.cssText = [ "display:" + (cs.display === "inline" ? "inline-flex" : cs.display), "align-items:center", "justify-content:center", "width:" + cs.width, "height:" + cs.height, "padding:" + cs.padding, "background:" + cs.backgroundColor, "color:" + cs.color, "border:" + cs.border, "border-radius:" + cs.borderRadius, "cursor:pointer", "text-decoration:none", ].join(";"); } else { btn.style.cssText = [ "display:inline-flex", "align-items:center", "justify-content:center", "width:32px", "height:32px", "padding:4px", "background:rgba(127,127,127,0.18)", "color:inherit", "border:1px solid rgba(127,127,127,0.25)", "border-radius:6px", "cursor:pointer", "text-decoration:none", ].join(";"); } btn.innerHTML = LOGOUT_SVG; btn.addEventListener( "click", (e) => { e.preventDefault(); window.location.href = LOGOUT_URL; }, { capture: true }, ); anchor.appendChild(btn); } function start() { ensureLogoutIcon(); if (!("MutationObserver" in window)) return; new MutationObserver(() => ensureLogoutIcon()).observe(document.body, { childList: true, subtree: true, }); // React-mounted navbars need a retry — single synchronous pass misses them. setTimeout(ensureLogoutIcon, 750); setTimeout(ensureLogoutIcon, 2500); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", start, { once: true }); } else { start(); } })();