/** * 🧭 DYNAMIC COURSE SIDEBAR & RESPONSIVE DRAWER SYSTEM 🧭 * ======================================================= * IT-Praxisportal – Vollständig responsive Kurs-Sidebar für alle 13 Kurse & Web-IDE. */ (function () { 'use strict'; function getRootPrefix() { const scripts = document.querySelectorAll('script[src*="course_sidebar.js"]'); if (scripts.length > 0) { const src = scripts[scripts.length - 1].getAttribute('src'); const idx = src.indexOf('assets/course_sidebar.js'); if (idx !== -1) { return src.substring(0, idx); } } const p = window.location.pathname; if (p.includes('/courses/')) return '../../../'; if (p.includes('/lehrpfad_')) return '../../'; return ''; } function detectContext() { const isWorkspace = window.location.pathname.endsWith('workspace.html') || !!document.querySelector('.workspace-topbar'); const urlParams = new URLSearchParams(window.location.search); if (isWorkspace) { return { isWorkspace: true, courseId: urlParams.get('course') || 'python', trackId: urlParams.get('track') || '', chapterFolder: urlParams.get('chapter') || '' }; } const path = window.location.pathname; const courseMatch = path.match(/courses\/([^\/]+)\/([^\/]+)/); if (courseMatch) { return { isWorkspace: false, courseId: courseMatch[1], chapterFolder: courseMatch[2], trackId: '' }; } const pythonMatch = path.match(/(lehrpfad_[^\/]+)\/([^\/]+)/); if (pythonMatch) { return { isWorkspace: false, courseId: 'python', trackId: pythonMatch[1], chapterFolder: pythonMatch[2] }; } return { isWorkspace: false, courseId: 'python', chapterFolder: '', trackId: '' }; } function getSolvedChapters() { const solved = new Set(); try { const stateStr = localStorage.getItem('python_gamification_state'); if (stateStr) { const s = JSON.parse(stateStr); if (Array.isArray(s.geloeste_kapitel)) { s.geloeste_kapitel.forEach(id => solved.add(id)); } } const progStr = localStorage.getItem('python_gamification_progress'); if (progStr) { const p = JSON.parse(progStr); if (Array.isArray(p.geloeste_kapitel)) { p.geloeste_kapitel.forEach(id => solved.add(id)); } } } catch (e) { console.warn('Lernfortschritt konnte nicht geladen werden:', e); } return solved; } function initSidebar() { const rootPrefix = getRootPrefix(); function proceed() { if (!window.COURSES_MANIFEST) { const manifestScript = document.createElement('script'); manifestScript.src = `${rootPrefix}assets/courses_manifest.js`; manifestScript.onload = () => buildSidebar(); manifestScript.onerror = () => console.error('Konnte courses_manifest.js nicht laden.'); document.head.appendChild(manifestScript); } else { buildSidebar(); } } if (!window.AUTH) { const authScript = document.createElement('script'); authScript.src = `${rootPrefix}assets/app_auth.js`; authScript.onload = () => proceed(); authScript.onerror = () => proceed(); document.head.appendChild(authScript); } else { proceed(); } } function buildSidebar() { if (!window.COURSES_MANIFEST) return; // Zugangsschutz: Kurse & Web-IDE erfordern Authentifizierung if (window.AUTH && typeof window.AUTH.requireAuth === 'function') { window.AUTH.requireAuth({ allowClose: false, title: '🔒 Registrierung erforderlich', message: 'Um die interaktiven Kursmodule und die Web-IDE zu nutzen, erstelle bitte einen kostenlosen Account oder melde dich an.' }); } // Alte legacy sidebars entfernen falls vorhanden document.querySelectorAll('.app-sidebar, .sidebar-mobile-toggle, .sidebar-overlay').forEach(el => el.remove()); const existingSidebar = document.getElementById('courseSidebar'); if (existingSidebar) existingSidebar.remove(); const existingBackdrop = document.getElementById('courseSidebarBackdrop'); if (existingBackdrop) existingBackdrop.remove(); const existingMobileBtn = document.getElementById('courseSidebarMobileToggle'); if (existingMobileBtn) existingMobileBtn.remove(); const rootPrefix = getRootPrefix(); const context = detectContext(); const manifest = window.COURSES_MANIFEST; const currentCourse = manifest[context.courseId] || manifest['python']; if (!currentCourse) return; const solvedSet = getSolvedChapters(); if (!context.chapterFolder && currentCourse.tracks && currentCourse.tracks[0] && currentCourse.tracks[0].chapters && currentCourse.tracks[0].chapters[0]) { context.chapterFolder = currentCourse.tracks[0].chapters[0].folder; if (!currentTrackId) currentTrackId = currentCourse.tracks[0].id; } // Zähle Kapitel & gelöste Aufgaben für den aktiven Kurs let totalChapters = 0; let solvedCount = 0; let currentTrackId = context.trackId || ''; (currentCourse.tracks || []).forEach(tr => { (tr.chapters || []).forEach(ch => { totalChapters++; const isSolved = solvedSet.has(ch.folder) || solvedSet.has(`${context.courseId}/${ch.folder}`) || solvedSet.has(`${tr.id}/${ch.folder}`); if (isSolved) solvedCount++; if (ch.folder === context.chapterFolder && !currentTrackId) { currentTrackId = tr.id; } }); }); const percent = totalChapters > 0 ? Math.round((solvedCount / totalChapters) * 100) : 0; if (context.isWorkspace) { document.body.classList.add('workspace-page'); } else { document.body.classList.add('has-course-sidebar'); } // Erstelle Sidebar Container const sidebar = document.createElement('aside'); sidebar.className = 'course-sidebar'; sidebar.id = 'courseSidebar'; // Header mit Branding, Close-Button & Dropdown let courseOptionsHtml = ''; const courseKeys = Object.keys(manifest); courseKeys.forEach(cid => { const c = manifest[cid]; const selected = cid === context.courseId ? 'selected' : ''; const icon = c.icon || '📚'; courseOptionsHtml += ``; }); // Navigation Tracks & Chapters generieren let navHtml = ''; (currentCourse.tracks || []).forEach((tr) => { navHtml += `
`; }); // Footer Action (Theorie lesen vs. Web-IDE) let footerActionHtml = ''; if (context.isWorkspace) { let theoryUrl = ''; if (context.courseId === 'python') { theoryUrl = `${rootPrefix}${currentTrackId || 'lehrpfad_1_grundlagen'}/${context.chapterFolder}/index.html`; } else { theoryUrl = `${rootPrefix}courses/${context.courseId}/${context.chapterFolder}/index.html`; } footerActionHtml = ` 📖 Theorie lesen `; } else { const ideUrl = `${rootPrefix}workspace.html?course=${context.courseId}&track=${currentTrackId}&chapter=${context.chapterFolder}`; footerActionHtml = ` 💻 Web-IDE `; } sidebar.innerHTML = ` `; document.body.appendChild(sidebar); // Backdrop erstellen const backdrop = document.createElement('div'); backdrop.className = 'course-sidebar-backdrop'; backdrop.id = 'courseSidebarBackdrop'; document.body.appendChild(backdrop); // Kurswechsler Event Listener const selectEl = document.getElementById('courseSidebarSelect'); if (selectEl) { selectEl.addEventListener('change', (e) => { const newCourseId = e.target.value; if (newCourseId === context.courseId) return; const targetCourse = manifest[newCourseId]; if (!targetCourse || !targetCourse.tracks || !targetCourse.tracks.length) return; const targetTrack = targetCourse.tracks[0]; const targetChapter = targetTrack.chapters && targetTrack.chapters.length ? targetTrack.chapters[0].folder : ''; if (context.isWorkspace) { window.location.href = `${rootPrefix}workspace.html?course=${newCourseId}&track=${targetTrack.id}&chapter=${targetChapter}`; } else { if (newCourseId === 'python') { window.location.href = `${rootPrefix}${targetTrack.id}/${targetChapter}/index.html`; } else { window.location.href = `${rootPrefix}courses/${newCourseId}/${targetChapter}/index.html`; } } }); } // Toggle-Funktionen für Drawer function openSidebar() { sidebar.classList.add('open'); backdrop.classList.add('active'); } function closeSidebar() { sidebar.classList.remove('open'); backdrop.classList.remove('active'); } const closeBtn = document.getElementById('courseSidebarClose'); if (closeBtn) closeBtn.addEventListener('click', closeSidebar); backdrop.addEventListener('click', closeSidebar); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeSidebar(); }); // Mobil-Toggle Button für Leseansicht if (!context.isWorkspace) { const mobileBtn = document.createElement('button'); mobileBtn.type = 'button'; mobileBtn.className = 'course-sidebar-mobile-btn'; mobileBtn.id = 'courseSidebarMobileToggle'; mobileBtn.setAttribute('aria-label', 'Module und Kapitel öffnen'); mobileBtn.innerHTML = `☰ Kapitel (${solvedCount}/${totalChapters})`; mobileBtn.addEventListener('click', () => { if (sidebar.classList.contains('open')) { closeSidebar(); } else { openSidebar(); } }); document.body.appendChild(mobileBtn); } // Workspace: Topbar "☰ Kapitel" Button einbinden if (context.isWorkspace) { const topbarTitle = document.querySelector('.workspace-title'); if (topbarTitle && !document.getElementById('btn-toggle-chapter-drawer')) { const toggleBtn = document.createElement('button'); toggleBtn.type = 'button'; toggleBtn.id = 'btn-toggle-chapter-drawer'; toggleBtn.className = 'btn btn-secondary'; toggleBtn.style.cssText = 'font-size: 0.8rem; padding: 4px 10px; margin-right: 10px; display: inline-flex; align-items: center; gap: 6px; border: 1px solid rgba(255,255,255,0.18); background: rgba(255,255,255,0.08); color: #f8fafc; font-weight: 700; cursor: pointer; border-radius: 6px;'; toggleBtn.innerHTML = '☰ Kapitel'; toggleBtn.title = 'Kapitel-Drawer aufklappen'; toggleBtn.addEventListener('click', () => { if (sidebar.classList.contains('open')) { closeSidebar(); } else { openSidebar(); } }); topbarTitle.insertBefore(toggleBtn, topbarTitle.firstChild); } } // Aktives Kapitel automatisch mittig in der Sidebar sichtbar scrollen setTimeout(() => { const activeItem = document.getElementById('activeSidebarChapter'); if (activeItem) { activeItem.scrollIntoView({ block: 'center', behavior: 'smooth' }); } }, 150); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initSidebar); } else { initSidebar(); } })();