/* logoutPatch.js */ (function(global) { /** * Initialize the logout patch script. * @param {string} logoutUrlBase - Base logout URL (e.g., from your OIDC client). * @param {string} webProtocol - Protocol to use (e.g., "https"). * @param {string} primaryDomain - Primary domain (e.g., "example.com"). * @param {boolean} debugMode - If true, debug logging is enabled. */ function initLogoutPatch(logoutUrlBase, webProtocol, primaryDomain, debugMode) { const DEBUG = !!debugMode; function log(reason, el, extra) { if (!DEBUG) return; console.debug('[logoutPatch]', reason, extra || {}, el || null); } const redirectUri = encodeURIComponent(webProtocol + '://' + primaryDomain); const logoutUrl = logoutUrlBase + '?redirect_uri=' + redirectUri; function matchesLogout(str) { const matched = str && /(?:^|\W)log\s*out(?:\W|$)|logout/i.test(str); if (matched) log('matchesLogout', null, { value: str }); return matched; } /** * Returns true if any attribute name or value on the given element * contains the substring "logout" (case-insensitive). * * @param {Element} element – The DOM element to inspect. * @returns {boolean} – True if "logout" appears in any relevant attribute name or value. */ function containsLogoutAttribute(element) { for (const attribute of element.attributes) { const name = attribute.name || ''; const value = attribute.value || ''; // Strong indicator: attribute *name* contains "logout" if (/logout/i.test(name)) { log('containsLogoutAttribute (name match)', element, { attrName: name, attrValue: value }); return true; } // Only consider values of semantic attributes (NOT href/action) if ((name.startsWith('data-') || name.startsWith('aria-')) && /logout/i.test(value)) { log('containsLogoutAttribute (data/aria value match)', element, { attrName: name, attrValue: value }); return true; } } return false; } function matchesTechnicalIndicators(el) { const title = el.getAttribute('title'); const ariaLabel = el.getAttribute('aria-label'); const onclick = el.getAttribute('onclick'); if (matchesLogout(title)) return true; if (matchesLogout(ariaLabel)) return true; if (matchesLogout(onclick)) return true; for (const attr of el.attributes) { if (attr.name.startsWith('data-') && matchesLogout(attr.name + attr.value)) { log('matchesTechnicalIndicators (data-* match)', el, { attrName: attr.name, attrValue: attr.value }); return true; } } if (typeof el.onclick === 'function' && matchesLogout(el.onclick.toString())) { log('matchesTechnicalIndicators (onclick function match)', el); return true; } if (el.tagName.toLowerCase() === 'use') { const href = el.getAttribute('xlink:href') || el.getAttribute('href'); if (matchesLogout(href)) { log('matchesTechnicalIndicators ( href match)', el, { href }); return true; } } return false; } /** * Apply logout redirect behavior to a matching element: * – Installs a capturing click‐handler to force navigation to logoutUrl * – Always sets href/formaction/action to logoutUrl * – Marks the element as patched to avoid double‐binding * * @param {Element} el – The element to override (e.g. ,