(function () { if (window.__mobileNavLoaded) return; window.__mobileNavLoaded = true; const path = window.location.pathname; const I = window.IC || function () { return ''; }; const TOPBAR_H = '4.875rem'; // ── CSS ────────────────────────────────────────────────────────────────── const style = document.createElement('style'); style.textContent = ` .mobile-topbar { display: none; position: fixed; top: 0; left: 0; right: 0; height: ${TOPBAR_H}; background: var(--color-card); border-bottom: 1px solid var(--color-secondary); box-shadow: 0 2px 12px rgba(0,0,0,0.4); z-index: 500; align-items: center; padding: 0 4px 0 0.25rem; } .mobile-topbar-logo { position: absolute; left: 50%; transform: translateX(-50%); display: flex; align-items: center; pointer-events: auto; } .mobile-topbar-logo img { height: 3rem; width: auto; } .mobile-topbar-actions { display: flex; align-items: center; margin-left: auto; } .mobile-tb-btn { position: relative; background: none; border: none; color: var(--color-text); font-size: 1.3rem; line-height: 1; padding: 0.55rem 0.6rem; cursor: pointer; display: flex; align-items: center; justify-content: center; border-radius: 12px; text-decoration: none; transition: background 0.12s; flex-shrink: 0; } .mobile-tb-btn:hover { background: var(--color-secondary); } .mobile-tb-badge { position: absolute; top: 3px; right: 3px; background: var(--color-primary); color: #fff; border-radius: 10px; font-size: 0.87rem; font-weight: 700; min-width: 1.65em; padding: 0.15em 0.375em; display: none; line-height: 1.4; text-align: center; } .mobile-tb-avatar { width: 2.7rem; height: 2.7rem; border-radius: 50%; object-fit: cover; } /* ── Mobile Menu Backdrop ── */ .mob-menu-backdrop { display: none; position: fixed; top: ${TOPBAR_H}; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.55); z-index: 998; } .mob-menu-backdrop.open { display: block; } /* ── Mobile Menu Panel ── */ .mob-menu-panel { position: fixed; top: ${TOPBAR_H}; right: 0; bottom: 0; width: min(80%, 360px); background: var(--color-card); border-left: 1px solid var(--color-secondary); z-index: 999; overflow-y: auto; transform: translateX(100%); transition: transform 0.25s ease; } .mob-menu-panel.open { transform: translateX(0); } /* ── Accordion ── */ .mnav-section { border-bottom: 1px solid var(--color-secondary); } .mnav-section:last-child { border-bottom: none; } .mnav-section-header { display: flex; align-items: center; justify-content: space-between; padding: 0.85rem 1.1rem; cursor: pointer; user-select: none; font-size: 0.88rem; font-weight: 600; color: var(--color-text); transition: background 0.12s; } .mnav-section-header:hover { background: var(--color-secondary); } .mnav-section-arrow { font-size: 0.65rem; transition: transform 0.2s; color: var(--color-muted); } .mnav-section.open .mnav-section-arrow { transform: rotate(90deg); } .mnav-section-body { display: none; } .mnav-section.open .mnav-section-body { display: block; } /* ── Menu Links ── */ .mnav-link { display: flex; align-items: center; gap: 0.65rem; padding: 0.6rem 1.1rem 0.6rem 1.5rem; text-decoration: none; color: var(--color-text); font-size: 0.9rem; transition: background 0.12s, color 0.12s; } .mnav-link:hover { background: var(--color-secondary); color: var(--color-primary); } .mnav-link.active { color: var(--color-primary); background: rgba(var(--color-primary-rgb,233,69,96),0.08); } .mnav-icon { width: 1.3rem; text-align: center; font-size: 1rem; flex-shrink: 0; } .mnav-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; } .mnav-link--danger { color: var(--color-primary); } .mnav-link--danger:hover { background: rgba(var(--color-primary-rgb,233,69,96),0.1); color: var(--color-primary); } /* ── Show only on mobile ── */ @media (max-width: 768px) { .mobile-topbar { display: flex; } body.app { padding-top: ${TOPBAR_H}; } } `; document.head.appendChild(style); // ── Helper ─────────────────────────────────────────────────────────────── function lnk(href, iconKey, label, id, badgeId) { const cls = path === href ? ' active' : ''; const idAttr = id ? ` id="${id}"` : ''; const badge = badgeId ? `` : ''; const icon = iconKey ? `${I(iconKey) || ''}` : ``; return `${icon}${label}${badge}`; } // ── Menu-Sektionen ──────────────────────────────────────────────────────── const SECTIONS = [ { label: 'Allgemein', prefixes: ['/userhome.html', '/search.html', '/community/nachrichten.html', '/community/benachrichtigungen.html', '/games/common/einladungen.html'], html: ` ${lnk('/userhome.html', 'HOME', 'Home' )} ${lnk('/search.html', 'SEARCH', 'Suche' )} ${lnk('/community/nachrichten.html', 'MESSAGES', 'Nachrichten', null, 'mnavBadgeMsg' )} ${lnk('/community/benachrichtigungen.html', 'NOTIFICATIONS', 'Benachrichtigungen', null, 'mnavBadgeNotif' )} ${lnk('/games/common/einladungen.html', 'INVITATIONS', 'Einladungen', null, 'mnavBadgeInv' )} `, }, { label: 'Community', prefixes: ['/community/'], html: ` ${lnk('/community/feed.html', 'FEED', 'Feed' )} ${lnk('/community/freunde.html', 'FRIENDS', 'Freunde', null, 'mnavBadgeFriends' )} ${lnk('/community/gruppen.html', 'GROUPS', 'Gruppen', null, 'mnavBadgeGruppen' )} ${lnk('/community/locations.html', 'LOCATION', 'Locations' )} ${lnk('/community/events.html', 'EVENT', 'Veranstaltungen' )} `, }, { label: 'Dating', prefixes: ['/dating/'], html: ` ${I('DATING') || '♥'}Dating ${lnk('/dating/besucher.html', '', 'Besucher')} ${lnk('/dating/likes.html', '', 'Likes' )} ${lnk('/dating/matches.html', '', 'Matches' )} `, }, { label: 'Vanilla Game', prefixes: ['/games/vanilla/'], html: ` ${lnk('/games/vanilla/neuvanilla.html', 'PLAY_NEW', 'Neue Session', 'mnavVanillaNeu' )} ${lnk('/games/vanilla/aufgaben.html', 'CHECK', 'Aufgaben' )} ${lnk('/games/vanilla/toys.html', 'TOYS', 'Toys' )} ${lnk('/games/vanilla/entdecken.html', 'DISCOVER', 'Entdecken' )} `, }, { label: 'BDSM Game', prefixes: ['/games/bdsm/'], html: ` ${lnk('/games/bdsm/neubdsm.html', 'PLAY_NEW', 'Neue Session', 'mnavBdsmNeu' )} ${lnk('/games/bdsm/aufgaben.html', 'CHECK', 'Aufgaben' )} ${lnk('/games/bdsm/toys.html', 'TOYS', 'Toys' )} ${lnk('/games/bdsm/entdecken.html', 'DISCOVER', 'Entdecken' )} `, }, { label: 'Chastity Game', prefixes: ['/games/chastity/'], html: ` ${lnk('/games/chastity/neulock.html', 'NEW_LOCK', 'Neues Lock', 'mnavChastityNeu' )} ${lnk('/games/chastity/communityvotes.html', 'VOTES', 'Community Votes' )} ${lnk('/games/chastity/meine-locks.html', 'LOCK', 'Meine Vorlagen' )} ${lnk('/games/chastity/entdecken-vorlagen.html', 'DISCOVER', 'Entdecken' )} ${lnk('/games/chastity/keyholder-finden.html', 'FRIENDS', 'Keyholder finden' )} ${lnk('/games/chastity/keyholder.html', 'KEY', 'Keyholder' )} ${lnk('/games/chastity/unlock-history.html', 'HISTORY', 'Code-Historie' )} `, }, { label: 'Konto', prefixes: ['/konto/', '/help/'], html: ` ${lnk('/konto/einstellungen.html', 'SETTINGS', 'Einstellungen')} ${lnk('/help/overview.html', 'HELP', 'Hilfe' )} ${I('LOGOUT') || ''}Abmelden `, }, ]; // ── Mobile Topbar ───────────────────────────────────────────────────────── const topbarEl = document.createElement('div'); topbarEl.className = 'mobile-topbar'; topbarEl.id = 'mobileTopbar'; topbarEl.innerHTML = `
${I('MESSAGES')} ${I('NOTIFICATIONS')} ${I('INVITATIONS')} ${I('PROFILE')}
`; document.body.insertAdjacentElement('afterbegin', topbarEl); // ── Overlay ─────────────────────────────────────────────────────────────── const backdropEl = document.createElement('div'); backdropEl.className = 'mob-menu-backdrop'; backdropEl.id = 'mobMenuBackdrop'; const panelEl = document.createElement('div'); panelEl.className = 'mob-menu-panel'; panelEl.id = 'mobMenuPanel'; panelEl.innerHTML = SECTIONS.map(s => { const isOpen = s.prefixes.some(p => path.startsWith(p) || path === p); return ` `; }).join(''); document.body.appendChild(backdropEl); document.body.appendChild(panelEl); // ── Accordion (nur eine Sektion gleichzeitig offen) ────────────────────── panelEl.querySelectorAll('.mnav-section-header').forEach(h => { h.addEventListener('click', () => { const section = h.closest('.mnav-section'); const isOpen = section.classList.contains('open'); panelEl.querySelectorAll('.mnav-section').forEach(s => s.classList.remove('open')); if (!isOpen) section.classList.add('open'); }); }); // ── Open / Close ────────────────────────────────────────────────────────── function openMenu() { panelEl.classList.add('open'); backdropEl.classList.add('open'); } function closeMenu() { panelEl.classList.remove('open'); backdropEl.classList.remove('open'); } document.getElementById('mobMenuToggle').addEventListener('click', e => { e.stopPropagation(); panelEl.classList.contains('open') ? closeMenu() : openMenu(); }); backdropEl.addEventListener('click', closeMenu); document.addEventListener('keydown', e => { if (e.key === 'Escape') closeMenu(); }); panelEl.querySelectorAll('.mnav-link').forEach(l => { l.addEventListener('click', () => { if (l.getAttribute('href') !== '#') closeMenu(); }); }); // ── Badges ──────────────────────────────────────────────────────────────── function setBadge(id, n) { const el = document.getElementById(id); if (!el) return; el.textContent = n > 99 ? '99+' : n; el.style.display = n > 0 ? 'inline-block' : 'none'; } fetch('/social/messages/unread/count').then(r => r.ok ? r.json() : 0).then(n => { setBadge('mobTbMsgBadge', n); setBadge('mnavBadgeMsg', n); }).catch(() => {}); fetch('/notifications/unread/count').then(r => r.ok ? r.json() : 0).then(n => { setBadge('mobTbNotifBadge', n); setBadge('mnavBadgeNotif', n); }).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]) => { const n = l + k + b + v; setBadge('mobTbInvBadge', n); setBadge('mnavBadgeInv', n); }).catch(() => {}); fetch('/social/friends/pending/count').then(r => r.ok ? r.json() : 0) .then(n => setBadge('mnavBadgeFriends', 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('mnavBadgeGruppen', j + rep)).catch(() => {}); // ── User / Dynamische Links ─────────────────────────────────────────────── fetch('/login/me').then(r => r.ok ? r.json() : null).then(async user => { if (!user) return; // Profilbild const profileBtn = document.getElementById('mobTbProfileBtn'); if (profileBtn) { profileBtn.href = '/community/benutzer.html?userId=' + user.userId; if (user.profilePicture) { profileBtn.innerHTML = ``; } } // Admin if (user.admin) { const el = document.getElementById('mnavAdminLink'); if (el) el.style.display = ''; } // Dating const datingLink = document.getElementById('mnavDatingLink'); if (datingLink) { datingLink.href = user.datingAktiv ? '/dating/dating.html' : '/konto/einstellungen.html#sec-dating'; } 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 setHref = (id, h) => { const el = document.getElementById(id); if (el) el.href = h; }; // BDSM try { const r = await fetch('/bdsm/einladung/meine-aktive'); if (r.ok) { const aktiv = await r.json(); hide('mnavBdsmNeu'); hide('mnavBdsmImSpiel'); show('mnavBdsmAktiv'); setHref('mnavBdsmAktiv', aktiv.sessionId ? '/games/bdsm/bdsmingame.html' : '/games/bdsm/neubdsm.html'); } else { const sr = await fetch(`/bdsm?userId=${user.userId}`); if (sr.status === 200) { hide('mnavBdsmNeu'); show('mnavBdsmImSpiel'); } else show('mnavBdsmNeu'); } } catch (_) {} // Vanilla try { const r = await fetch('/vanilla/einladung/meine-aktive'); if (r.ok) { const aktiv = await r.json(); hide('mnavVanillaNeu'); hide('mnavVanillaImSpiel'); show('mnavVanillaAktiv'); setHref('mnavVanillaAktiv', aktiv.sessionId ? '/games/vanilla/vanillaingame.html' : '/games/vanilla/neuvanilla.html'); } else { const sr = await fetch(`/vanilla?userId=${user.userId}`); if (sr.status === 200) { hide('mnavVanillaNeu'); show('mnavVanillaImSpiel'); } else show('mnavVanillaNeu'); } } catch (_) {} // Chastity try { const r = await fetch('/keyholder/mylock'); if (r.ok) { const lock = await r.json(); show('mnavChastityAktiv'); setHref('mnavChastityAktiv', '/games/chastity/activelock.html?lockId=' + lock.lockId); } } catch (_) {} }).catch(() => {}); })();