(function() { var STORAGE_KEY = 'crank_user'; var sessionCache = null; var sessionPromise = null; function tKey(key) { return typeof t === 'function' ? t(key) : key; } function tfKey(key, vars) { return typeof tf === 'function' ? tf(key, vars) : key; } function roleLabel(role) { var normalized = String(role || 'viewer').toLowerCase(); if (normalized === 'operator') return tKey('workspace_setup.role.operator'); if (normalized === 'owner') return tKey('workspace_setup.role.owner'); if (normalized === 'admin') return tKey('workspace_setup.role.admin'); if (normalized === 'viewer') return tKey('workspace_setup.role.viewer'); return normalized.replace(/^\w/, function(char) { return char.toUpperCase(); }); } function currentWorkspaceLabel() { if (window.getCurrentWorkspace) { var workspace = window.getCurrentWorkspace(); if (workspace && workspace.name) { return workspace.name; } } try { return localStorage.getItem('crank_workspace_slug') || tKey('settings.nav.workspace'); } catch (_error) { return tKey('settings.nav.workspace'); } } function isLoginPage() { return /\/(?:html\/login\.html|login)\/?$/.test(window.location.pathname); } function loginUrl() { return (window.CrankRoutes && window.CrankRoutes.login) || '/login'; } function homeUrl() { return (window.CrankRoutes && window.CrankRoutes.home) || '/'; } function clearUserMirror() { sessionCache = null; sessionPromise = null; try { localStorage.removeItem(STORAGE_KEY); } catch (_error) {} renderShellIdentity(null); } function primaryMembership(session) { if (!(session && session.memberships && session.memberships.length)) { return null; } if (session.current_workspace_id) { var currentMembership = session.memberships.find(function(item) { return item.workspace && item.workspace.id === session.current_workspace_id; }); if (currentMembership) { return currentMembership; } } if (window.getCurrentWorkspace) { var currentWorkspace = window.getCurrentWorkspace(); if (currentWorkspace) { var matched = session.memberships.find(function(item) { return item.workspace && item.workspace.id === currentWorkspace.id; }); if (matched) { return matched; } } } return session.memberships[0]; } function initials(displayName, email) { var source = safeIdentityText(displayName, 80, '') || safeIdentityText(email, 254, '') || 'Crank'; return source .split(/\s+/) .filter(Boolean) .slice(0, 2) .map(function(part) { return part.charAt(0).toUpperCase(); }) .join('') || 'CR'; } function safeIdentityText(value, maxLength, fallback) { var text = String(value || '') .replace(/[\r\n\t]+/g, ' ') .replace(/\s{2,}/g, ' ') .trim(); if (!text || /<\s*(?:!doctype|html|head|body|nav|script)\b/i.test(text)) { return fallback || ''; } if (text.length > maxLength) { return fallback || ''; } return text; } function persistUserMirror(session) { var membership = primaryMembership(session); var user = { id: session.user.id, name: safeIdentityText(session.user.display_name, 80, 'Crank'), email: safeIdentityText(session.user.email, 254, ''), role: membership ? roleLabel(membership.role) : tKey('workspace_setup.role.viewer'), workspace: membership ? membership.workspace.slug : '', workspaceId: membership ? membership.workspace.id : '', initials: initials(session.user.display_name, session.user.email), }; try { localStorage.setItem(STORAGE_KEY, JSON.stringify(user)); } catch (_error) {} } function mirroredUser() { try { return JSON.parse(localStorage.getItem(STORAGE_KEY)); } catch (_error) { return null; } } function renderShellIdentity(session) { var membership = primaryMembership(session); var fallbackUser = mirroredUser(); var displayName = session && session.user ? (safeIdentityText(session.user.display_name, 80, '') || safeIdentityText(session.user.email, 254, '') || 'Crank') : safeIdentityText(fallbackUser && fallbackUser.name, 80, 'Crank'); var email = session && session.user ? safeIdentityText(session.user.email, 254, '') : safeIdentityText(fallbackUser && fallbackUser.email, 254, ''); var role = membership ? roleLabel(membership.role) : (fallbackUser && fallbackUser.role) || tKey('workspace_setup.role.viewer'); var workspace = membership && membership.workspace ? (membership.workspace.display_name || membership.workspace.slug || membership.workspace.id) : currentWorkspaceLabel(); var avatarValue = initials(displayName, email); document.querySelectorAll('.nav-avatar').forEach(function(element) { element.textContent = avatarValue; }); document.querySelectorAll('.user-dropdown-name').forEach(function(element) { element.textContent = displayName; }); document.querySelectorAll('.user-dropdown-role').forEach(function(element) { element.textContent = role + ' ยท ' + workspace; }); if (window.CrankDiagnostics && typeof window.CrankDiagnostics.ensureShellTestIds === 'function') { window.CrankDiagnostics.ensureShellTestIds(); } } function replaceSession(session) { sessionCache = session; persistUserMirror(session); renderShellIdentity(session); window.dispatchEvent(new CustomEvent('crank:sessionchange', { detail: session })); return session; } async function fetchSession(force) { if (!force && sessionCache) { return sessionCache; } if (!force && sessionPromise) { return sessionPromise; } sessionPromise = window.CrankApi.getSession() .then(function(session) { return replaceSession(session); }) .catch(function(error) { clearUserMirror(); throw error; }) .finally(function() { sessionPromise = null; }); return sessionPromise; } async function guardProtectedPage() { try { return await fetchSession(false); } catch (error) { if (error && error.status === 401) { window.location.replace(loginUrl()); return null; } throw error; } } async function guardLoginPage() { try { await fetchSession(false); window.location.replace(homeUrl()); } catch (error) { if (!error || error.status !== 401) { throw error; } } } async function login(email, password) { var session = await window.CrankApi.login({ email: email, password: password, }); replaceSession(session); window.location.href = homeUrl(); } async function logout() { try { await window.CrankApi.logout(); } finally { clearUserMirror(); window.location.href = loginUrl(); } } function handleUnauthorized() { if (isLoginPage()) { clearUserMirror(); return; } clearUserMirror(); window.location.replace(loginUrl()); } window.CrankAuth = { fetchSession: fetchSession, replaceSession: replaceSession, getCachedSession: function() { return sessionCache; }, renderShellIdentity: function() { renderShellIdentity(sessionCache); }, guardProtectedPage: guardProtectedPage, guardLoginPage: guardLoginPage, login: login, logout: logout, handleUnauthorized: handleUnauthorized, }; window.addEventListener('crank:workspacechange', function() { renderShellIdentity(sessionCache); }); window.addEventListener('crank:sessionchange', function(event) { renderShellIdentity(event.detail || sessionCache); }); document.addEventListener('DOMContentLoaded', function() { renderShellIdentity(sessionCache); }); }());