(function () { const path = window.location.pathname; const I = window.IC || function () { return ''; }; // ── CSS ────────────────────────────────────────────────────────────────── const style = document.createElement('style'); style.textContent = ` /* ── Burger-Button ── */ .nav-burger { display: inline-flex; align-items: center; gap: 0.45rem; padding: 0.46rem 0.8rem 0.46rem 0.6rem; background: none; border: 1px solid var(--color-secondary); border-radius: 8px; cursor: pointer; color: var(--color-text); font-size: 0.88rem; font-weight: 600; flex-shrink: 0; transition: border-color 0.15s, color 0.15s; margin-right: 0.5rem; line-height: 1; } .nav-burger:hover { border-color: var(--color-primary); color: var(--color-primary); } .nav-burger-icon { font-size: 1.05rem; line-height: 1; position: relative; display: inline-flex; align-items: center; justify-content: center; width: 1.2em; height: 1.2em; } .nav-burger-icon-menu, .nav-burger-icon-close { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; transition: opacity 0.15s, transform 0.15s; } .nav-burger-icon-close { opacity: 0; transform: rotate(-45deg); } #topbar.nav-menu-open .nav-burger .nav-burger-icon-menu { opacity: 0; transform: rotate(45deg); } #topbar.nav-menu-open .nav-burger .nav-burger-icon-close { opacity: 1; transform: rotate(0deg); } /* ── Backdrop ── */ .nav-backdrop { position: fixed; inset: 0; z-index: 498; background: rgba(0,0,0,0); pointer-events: none; transition: background 0.2s ease; } .nav-backdrop.open { background: rgba(0,0,0,0.55); pointer-events: auto; } /* ── Topbar: untere Rundung entfernen wenn Menü offen ── */ #topbar { transition: border-radius 0.22s ease, border-bottom-color 0.22s ease; } #topbar.nav-menu-open { border-radius: 12px 12px 0 0; border-bottom-color: transparent; } /* ── Dropdown ── */ .nav-dropdown { position: fixed; z-index: 499; background: var(--color-card); border: 1px solid var(--color-secondary); border-top: none; border-radius: 0 0 12px 12px; box-shadow: 0 12px 40px rgba(0,0,0,0.55); overflow-y: auto; max-height: calc(100vh - 80px); visibility: hidden; opacity: 0; transform: translateY(-12px); pointer-events: none; transition: opacity 0.22s ease, transform 0.22s ease, visibility 0s linear 0.22s; } .nav-dropdown.open { visibility: visible; opacity: 1; transform: translateY(0); pointer-events: auto; transition: opacity 0.22s ease, transform 0.22s ease, visibility 0s linear 0s; } /* ── 4-Spalten-Layout ── */ .nav-columns { display: grid; grid-template-columns: repeat(4, 1fr); } .nav-col { border-right: 1px solid var(--color-secondary); min-width: 0; } .nav-col:last-child { border-right: none; } /* Überschrift: auf Desktop ausgeblendet, auf Mobile als Accordion-Toggle */ .nav-col-header { display: none; align-items: center; justify-content: space-between; padding: 0.75rem 1.1rem; font-size: 0.85rem; font-weight: 600; color: var(--color-text); cursor: pointer; } .nav-col-arrow { font-size: 0.65rem; transition: transform 0.2s; } .nav-col-body { padding: 0.35rem 0; } /* ── Nav-Links ── */ .nav-link { display: flex; align-items: center; gap: 0.65rem; padding: 0.55rem 1.1rem; text-decoration: none; color: var(--color-text); font-size: 0.9rem; font-weight: 500; transition: background 0.12s, color 0.12s; white-space: nowrap; position: relative; } .nav-link:hover { background: var(--color-secondary); color: var(--color-primary); } .nav-link.active { color: var(--color-primary); background: rgba(var(--color-primary-rgb,233,69,96),0.08); } .nav-icon { width: 1.3rem; text-align: center; font-size: 1rem; flex-shrink: 0; } .nav-badge { margin-left: auto; background: var(--color-primary); color: #fff; border-radius: 10px; font-size: 0.68rem; font-weight: 700; min-width: 1.2em; padding: 0.1em 0.3em; display: none; } /* ── Games: Accordion innerhalb der Spalte ── */ .nav-game-group { } .nav-game-toggle { display: flex; align-items: center; gap: 0.65rem; padding: 0.55rem 1.1rem; cursor: pointer; font-size: 0.88rem; font-weight: 600; color: var(--color-text); transition: background 0.12s; user-select: none; } .nav-game-toggle:hover { background: var(--color-secondary); } .nav-game-icon { width: 1.3rem; text-align: center; font-size: 1rem; flex-shrink: 0; } .nav-game-arrow { margin-left: auto; font-size: 0.65rem; transition: transform 0.2s; color: var(--color-muted); } .nav-game-group.open .nav-game-arrow { transform: rotate(90deg); } .nav-game-sub { display: none; } .nav-game-group.open .nav-game-sub { display: block; } .nav-game-sub .nav-link { padding-left: 2.6rem; font-size: 0.85rem; } /* ── Footer ── */ .nav-dropdown-footer { border-top: 1px solid var(--color-secondary); padding: 0.55rem 1.25rem; display: flex; gap: 1.25rem; } .nav-dropdown-footer a { font-size: 0.75rem; color: var(--color-muted); text-decoration: none; } .nav-dropdown-footer a:hover { color: var(--color-primary); } /* ── Responsive: Accordion-Modus (< 680px) ── */ @media (max-width: 679px) { .nav-columns { grid-template-columns: 1fr; } .nav-col { border-right: none; border-bottom: 1px solid var(--color-secondary); } .nav-col:last-child { border-bottom: none; } .nav-col-header { display: flex; } .nav-col.col-open .nav-col-arrow { transform: rotate(90deg); } .nav-col-body { display: none; padding: 0; } .nav-col.col-open .nav-col-body { display: block; } } `; document.head.appendChild(style); // ── app-wrapper ────────────────────────────────────────────────────────── const appWrapper = document.createElement('div'); appWrapper.className = 'app-wrapper'; const mainEl = document.querySelector('.main'); if (mainEl) { document.body.insertBefore(appWrapper, mainEl); appWrapper.appendChild(mainEl); } // ── Hilfsfunktionen ────────────────────────────────────────────────────── function esc(s) { return String(s ?? '').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); } function link(href, iconKey, label, id, badgeId) { const cls = path === href ? ' active' : ''; const idAttr = id ? ` id="${id}"` : ''; const badge = badgeId ? `` : ''; return ` ${I(iconKey) || ''} ${label}${badge} `; } function gameGroup(iconKey, label, items) { const isOpen = items.some(it => it.href === path || (it.href !== '#' && path.startsWith(it.href.split('?')[0]))); const sub = items.map(it => { const cls = path === it.href ? ' active' : ''; const idAt = it.id ? ` id="${it.id}"` : ''; return ` ${I(it.icon) || ''} ${it.label} `; }).join(''); return ` `; } function column(id, label, bodyHtml, pathPrefixes) { const isActive = pathPrefixes.some(p => path === p || path.startsWith(p)); return ` `; } // ── Spalten-Inhalt ─────────────────────────────────────────────────────── const onDating = path.startsWith('/dating/'); const col1Html = ` ${link('/userhome.html', 'HOME', 'Home' )} ${link('/search.html', 'SEARCH', 'Suche' )} ${link('/community/nachrichten.html', 'MESSAGES', 'Nachrichten', null, 'navBadgeMsg' )} ${link('/community/benachrichtigungen.html', 'NOTIFICATIONS', 'Benachrichtigungen', null, 'navBadgeNotif' )} ${link('/games/common/einladungen.html', 'INVITATIONS', 'Einladungen', null, 'navBadgeInv' )} `; const col2Html = ` ${link('/community/feed.html', 'FEED', 'Feed' )} ${link('/community/freunde.html', 'FRIENDS', 'Freunde', null, 'navBadgeFriends' )} ${link('/community/gruppen.html', 'GROUPS', 'Gruppen', null, 'navBadgeGruppen' )} ${link('/community/locations.html', 'LOCATION', 'Locations' )} ${link('/community/events.html', 'EVENT', 'Veranstaltungen' )} `; const col3Html = ` ${I('DATING') || '♥'} Dating ${link('/dating/besucher.html', '', 'Besucher')} ${link('/dating/likes.html', '', 'Likes' )} ${link('/dating/matches.html', '', 'Matches' )} `; const col4Html = ` ${gameGroup('VANILLA', 'Vanilla Game', [ { href: '/games/vanilla/neuvanilla.html', icon: 'PLAY_NEW', label: 'Neue Session', id: 'navVanillaNeu' }, { href: '#', icon: 'WAITING', label: 'Aktive Session', id: 'navVanillaAktiv' }, { href: '/games/vanilla/vanillaingame.html', icon: 'PLAY_ACTIVE', label: 'Im Spiel', id: 'navVanillaImSpiel' }, { href: '/games/vanilla/aufgaben.html', icon: 'CHECK', label: 'Aufgaben' }, { href: '/games/vanilla/toys.html', icon: 'TOYS', label: 'Toys' }, { href: '/games/vanilla/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, ])} ${gameGroup('BDSM', 'BDSM Game', [ { href: '/games/bdsm/neubdsm.html', icon: 'PLAY_NEW', label: 'Neue Session', id: 'navBdsmNeu' }, { href: '#', icon: 'WAITING', label: 'Aktive Session', id: 'navBdsmAktiv' }, { href: '/games/bdsm/bdsmingame.html', icon: 'PLAY_ACTIVE', label: 'Im Spiel', id: 'navBdsmImSpiel' }, { href: '/games/bdsm/aufgaben.html', icon: 'CHECK', label: 'Aufgaben' }, { href: '/games/bdsm/toys.html', icon: 'TOYS', label: 'Toys' }, { href: '/games/bdsm/entdecken.html', icon: 'DISCOVER', label: 'Entdecken' }, ])} ${gameGroup('CHASTITY', 'Chastity Game', [ { href: '/games/chastity/neulock.html', icon: 'NEW_LOCK', label: 'Neues Lock', id: 'navChastityNeu' }, { href: '#', icon: 'ACTIVE_LOCK', label: 'Aktives Lock', id: 'navChastityAktiv' }, { href: '/games/chastity/communityvotes.html', icon: 'VOTES', label: 'Community Votes' }, { href: '/games/chastity/meine-locks.html', icon: 'LOCK', label: 'Meine Vorlagen' }, { href: '/games/chastity/entdecken-vorlagen.html', icon: 'DISCOVER', label: 'Entdecken' }, { href: '/games/chastity/keyholder-finden.html', icon: 'FRIENDS', label: 'Keyholder finden' }, { href: '/games/chastity/keyholder.html', icon: 'KEY', label: 'Keyholder' }, { href: '/games/chastity/unlock-history.html', icon: 'HISTORY', label: 'Code-Historie' }, ])} `; // ── Dropdown-HTML ──────────────────────────────────────────────────────── document.body.insertAdjacentHTML('beforeend', ` `); // ── Dropdown positionieren ──────────────────────────────────────────────── function positionDropdown() { const topbar = document.getElementById('topbar'); const dd = document.getElementById('navDropdown'); if (!topbar || !dd) return; const r = topbar.getBoundingClientRect(); dd.style.top = r.bottom + 'px'; dd.style.left = r.left + 'px'; dd.style.right = (window.innerWidth - r.right) + 'px'; dd.style.width = 'auto'; } // ── Öffnen / Schließen ──────────────────────────────────────────────────── function openNav() { if (window.__topbarCloseAll) window.__topbarCloseAll(); positionDropdown(); document.getElementById('navDropdown').classList.add('open'); document.getElementById('navBackdrop').classList.add('open'); document.getElementById('topbar')?.classList.add('nav-menu-open'); } function closeNav() { document.getElementById('navDropdown').classList.remove('open'); document.getElementById('navBackdrop').classList.remove('open'); document.getElementById('topbar')?.classList.remove('nav-menu-open'); } // Topbar-Panels schließen das Nav-Dropdown window.__navClose = closeNav; document.getElementById('navBackdrop').addEventListener('click', closeNav); document.addEventListener('keydown', e => { if (e.key === 'Escape') closeNav(); }); // Links schließen das Menü document.getElementById('navDropdown').querySelectorAll('.nav-link').forEach(l => { l.addEventListener('click', () => { if (l.getAttribute('href') !== '#') closeNav(); }); }); window.addEventListener('resize', () => { if (document.getElementById('navDropdown').classList.contains('open')) positionDropdown(); }); // ── Burger-Button ───────────────────────────────────────────────────────── function injectBurger() { const topbarLeft = document.querySelector('.topbar-left'); if (!topbarLeft) { setTimeout(injectBurger, 30); return; } if (document.getElementById('navBurgerBtn')) return; const btn = document.createElement('button'); btn.className = 'nav-burger'; btn.id = 'navBurgerBtn'; btn.setAttribute('aria-label', 'Menü öffnen'); btn.innerHTML = `${I('MENU') || '≡'}${I('CLOSE') || 'x'}Menü`; topbarLeft.append(btn); btn.addEventListener('click', e => { e.stopPropagation(); const dd = document.getElementById('navDropdown'); dd.classList.contains('open') ? closeNav() : openNav(); }); } setTimeout(injectBurger, 0); // ── Accordion: Spalten-Header (Responsive-Modus) ────────────────────────── document.querySelectorAll('.nav-col .nav-col-header').forEach(header => { header.addEventListener('click', () => { const col = header.closest('.nav-col'); const isOpen = col.classList.contains('col-open'); // alle schließen document.querySelectorAll('.nav-col').forEach(c => c.classList.remove('col-open')); if (!isOpen) col.classList.add('col-open'); }); }); // ── Accordion: Games-Untergruppen (immer aktiv, gegenseitig exklusiv) ──── document.querySelectorAll('.nav-game-toggle').forEach(toggle => { toggle.addEventListener('click', () => { const grp = toggle.closest('.nav-game-group'); const isOpen = grp.classList.contains('open'); document.querySelectorAll('.nav-game-group').forEach(g => g.classList.remove('open')); if (!isOpen) grp.classList.add('open'); }); }); // ── Login-abhängige Elemente ────────────────────────────────────────────── fetch('/login/me') .then(r => r.ok ? r.json() : null) .then(async user => { if (!user) return; const datingLink = document.getElementById('navDatingLink'); if (datingLink) { datingLink.href = user.datingAktiv ? '/dating/dating.html' : '/konto/einstellungen.html#sec-dating'; } if (user.admin) { const el = document.getElementById('navAdminLink'); if (el) el.style.display = ''; } const hide = id => { const el = document.getElementById(id); if (el) el.style.display = 'none'; }; const show = id => { const el = document.getElementById(id); if (el) el.style.display = ''; }; const href = (id, h) => { const el = document.getElementById(id); if (el) el.href = h; }; hide('navVanillaAktiv'); hide('navVanillaImSpiel'); hide('navBdsmAktiv'); hide('navBdsmImSpiel'); hide('navChastityAktiv'); try { const r = await fetch('/bdsm/einladung/meine-aktive'); if (r.ok) { const aktiv = await r.json(); hide('navBdsmNeu'); hide('navBdsmImSpiel'); show('navBdsmAktiv'); href('navBdsmAktiv', aktiv.sessionId ? '/games/bdsm/bdsmingame.html' : '/games/bdsm/neubdsm.html'); } else { const sr = await fetch(`/bdsm?userId=${user.userId}`); if (sr.status === 200) { hide('navBdsmNeu'); show('navBdsmImSpiel'); } else show('navBdsmNeu'); } } catch (_) {} try { const r = await fetch('/vanilla/einladung/meine-aktive'); if (r.ok) { const aktiv = await r.json(); hide('navVanillaNeu'); hide('navVanillaImSpiel'); show('navVanillaAktiv'); href('navVanillaAktiv', aktiv.sessionId ? '/games/vanilla/vanillaingame.html' : '/games/vanilla/neuvanilla.html'); } else { const sr = await fetch(`/vanilla?userId=${user.userId}`); if (sr.status === 200) { hide('navVanillaNeu'); show('navVanillaImSpiel'); } else show('navVanillaNeu'); } } catch (_) {} try { const r = await fetch('/keyholder/mylock'); if (r.ok) { const lock = await r.json(); show('navChastityAktiv'); href('navChastityAktiv', '/games/chastity/activelock.html?lockId=' + lock.lockId); } } catch (_) {} }) .catch(() => {}); // ── Badges ─────────────────────────────────────────────────────────────── function setBadge(id, count) { const el = document.getElementById(id); if (!el) return; el.textContent = count > 99 ? '99+' : count; el.style.display = count > 0 ? 'inline-block' : 'none'; } window.__navSetBadge = function (type, count) { const map = { msg: 'navBadgeMsg', notif: 'navBadgeNotif', inv: 'navBadgeInv' }; if (map[type]) setBadge(map[type], count); }; fetch('/social/messages/unread/count').then(r => r.ok ? r.json() : 0).then(n => setBadge('navBadgeMsg', n)).catch(() => {}); fetch('/notifications/unread/count').then(r => r.ok ? r.json() : 0).then(n => setBadge('navBadgeNotif', n)).catch(() => {}); fetch('/social/friends/pending/count').then(r => r.ok ? r.json() : 0).then(n => setBadge('navBadgeFriends', n)).catch(() => {}); Promise.all([ fetch('/gruppen/requests/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0), fetch('/gruppen/reports/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0) ]).then(([j, rep]) => setBadge('navBadgeGruppen', j + rep)).catch(() => {}); Promise.all([ fetch('/lockee/invitations/mine/count').then(r => r.ok ? r.json() : 0).catch(() => 0), fetch('/keyholder/invitations/mine/count').then(r => r.ok ? r.json() : 0).catch(() => 0), fetch('/bdsm/einladung/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0), fetch('/vanilla/einladung/pending/count').then(r => r.ok ? r.json() : 0).catch(() => 0) ]).then(([l, k, b, v]) => setBadge('navBadgeInv', l + k + b + v)).catch(() => {}); // ── Externe Scripts ─────────────────────────────────────────────────────── function loadScript(src) { const s = document.createElement('script'); s.src = src; document.head.appendChild(s); } loadScript('/js/topbar.js'); loadScript('/js/social-sidebar.js'); loadScript('/js/section-nav.js'); loadScript('/js/mobile-nav.js'); })();