/** * 🧭 DYNAMIC COURSE SIDEBAR & RESPONSIVE DRAWER SYSTEM 🧭 * ======================================================= * IT-Praxisportal – VollstĂ€ndig responsive Kurs-Sidebar fĂŒr alle 13 Kurse & Web-IDE. */ (function () { 'use strict'; function escapeHtml(str) { if (!str) return ''; return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } 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?v=2.2.3`; 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 += `
`; navHtml += `
${tr.title}
`; navHtml += `
`; (tr.chapters || []).forEach((ch) => { const isActive = (ch.folder === context.chapterFolder); const isSolved = solvedSet.has(ch.folder) || solvedSet.has(`${context.courseId}/${ch.folder}`) || solvedSet.has(`${tr.id}/${ch.folder}`); let linkUrl = ''; if (context.isWorkspace) { linkUrl = `${rootPrefix}workspace.html?course=${context.courseId}&track=${tr.id}&chapter=${ch.folder}`; } else { if (context.courseId === 'python') { linkUrl = `${rootPrefix}${tr.id}/${ch.folder}/index.html`; } else { linkUrl = `${rootPrefix}courses/${context.courseId}/${ch.folder}/index.html`; } } let statusIconHtml = ''; if (isSolved) { statusIconHtml = `✓`; } else if (isActive) { statusIconHtml = `●`; } else { statusIconHtml = ``; } navHtml += ` ${statusIconHtml} ${ch.title} `; }); 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 = `
IT-Praxisportal
PRO LERNPFAD
Kursfortschritt ${solvedCount} / ${totalChapters} (${percent}%)
`; 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); } // ========================================================================== // 🧭 KURS- & MODUL-NAVIGATIONSSYSTEM (Keine langweiligen Hyperlinks!) // ========================================================================== const ORDERED_COURSE_KEYS = [ 'python', 'bash', 'powershell', 'active_directory', 'git', 'dns_records', 'sql', 'html_css', 'javascript', 'java', 'csharp', 'cpp', 'go', 'rust', 'ihk_ap1', 'ihk_ap2_fisi' ]; const courseIdx = ORDERED_COURSE_KEYS.indexOf(context.courseId); const prevCourseKey = courseIdx > 0 ? ORDERED_COURSE_KEYS[courseIdx - 1] : null; const nextCourseKey = (courseIdx >= 0 && courseIdx < ORDERED_COURSE_KEYS.length - 1) ? ORDERED_COURSE_KEYS[courseIdx + 1] : null; const prevCourse = prevCourseKey ? manifest[prevCourseKey] : null; const nextCourse = nextCourseKey ? manifest[nextCourseKey] : null; const allChapters = []; (currentCourse.tracks || []).forEach(tr => { (tr.chapters || []).forEach(ch => { allChapters.push({ trackId: tr.id, folder: ch.folder, title: ch.title, taskFile: ch.taskFile, testFile: ch.testFile }); }); }); const currentChapIdx = allChapters.findIndex(ch => ch.folder === context.chapterFolder); const prevChapter = currentChapIdx > 0 ? allChapters[currentChapIdx - 1] : null; const nextChapter = (currentChapIdx >= 0 && currentChapIdx < allChapters.length - 1) ? allChapters[currentChapIdx + 1] : null; function buildTheoryUrl(cid, tid, folder) { if (cid === 'python') { return `${rootPrefix}${tid || 'lehrpfad_1_grundlagen'}/${folder}/index.html`; } return `${rootPrefix}courses/${cid}/${folder}/index.html`; } function buildFirstLessonTheoryUrl(cid, courseObj) { if (!courseObj || !courseObj.tracks || !courseObj.tracks.length) return `${rootPrefix}dashboard.html`; const t = courseObj.tracks[0]; const f = t.chapters && t.chapters.length ? t.chapters[0].folder : ''; if (!f) return `${rootPrefix}dashboard.html`; return buildTheoryUrl(cid, t.id, f); } function buildWorkspaceUrl(cid, tid, folder) { return `${rootPrefix}workspace.html?course=${encodeURIComponent(cid)}&track=${encodeURIComponent(tid || '')}&chapter=${encodeURIComponent(folder)}`; } function buildFirstLessonWorkspaceUrl(cid, courseObj) { if (!courseObj || !courseObj.tracks || !courseObj.tracks.length) return `${rootPrefix}workspace.html`; const t = courseObj.tracks[0]; const f = t.chapters && t.chapters.length ? t.chapters[0].folder : ''; return buildWorkspaceUrl(cid, t.id, f); } // 1. THEORIE-SEITEN: Prominente Navigationsleiste am Ende der Lerneinheit function renderTheoryNavigator() { const mainEl = document.querySelector('main.container') || document.querySelector('main') || document.querySelector('.container'); if (!mainEl) return; const existingNav = document.getElementById('courseModuleNavigator'); if (existingNav) existingNav.remove(); // Legacy-Buttons ausblenden const legacyActionDivs = mainEl.querySelectorAll('.card > div[style*="flex"]:last-child, main > div[style*="flex"]:last-child'); legacyActionDivs.forEach(div => { if (div.querySelector('a[href*="workspace.html"]') || div.querySelector('a[href*="index.html"]')) { div.style.display = 'none'; } }); const nav = document.createElement('nav'); nav.className = 'course-module-navigator'; nav.id = 'courseModuleNavigator'; nav.setAttribute('aria-label', 'Kurs- und Modulnavigation'); // Linker Button (Vorheriges Modul oder Vorheriger Kurs) let leftBtnHtml = ''; if (prevChapter) { const prevUrl = buildTheoryUrl(context.courseId, prevChapter.trackId, prevChapter.folder); leftBtnHtml = ` ◀ VORHERIGES MODUL ${escapeHtml(prevChapter.title)} `; } else if (prevCourse) { const prevCourseUrl = buildFirstLessonTheoryUrl(prevCourseKey, prevCourse); leftBtnHtml = ` ⏼ VORHERIGER KURS ${escapeHtml(prevCourse.icon || '📚')} ${escapeHtml(prevCourse.title)} `; } else { leftBtnHtml = ` 🏠 START Zum Dashboard `; } // Mittlere Buttons (Dashboard & Web-IDE) const ideUrl = buildWorkspaceUrl(context.courseId, currentTrackId, context.chapterFolder); const centerHtml = `
🏠 Dashboard đŸ’» In Web-IDE lösen →
`; // Rechter Button (NĂ€chstes Modul oder NĂ€chster Kurs) let rightBtnHtml = ''; if (nextChapter) { const nextUrl = buildTheoryUrl(context.courseId, nextChapter.trackId, nextChapter.folder); rightBtnHtml = ` NÄCHSTES MODUL ${escapeHtml(nextChapter.title)} ▶ `; } else if (nextCourse) { const nextCourseUrl = buildFirstLessonTheoryUrl(nextCourseKey, nextCourse); rightBtnHtml = ` NÄCHSTER KURS ${escapeHtml(nextCourse.icon || '🚀')} ${escapeHtml(nextCourse.title)} ⏭ `; } else { rightBtnHtml = ` ABSCHLUSS 🎉 Kurs gemeistert! 🏆 `; } nav.innerHTML = leftBtnHtml + centerHtml + rightBtnHtml; mainEl.appendChild(nav); } // 2. WORKSPACE-PANE: Navigationsbuttons innerhalb des Theorie-Tabs der Web-IDE function renderWorkspaceTheoryInlineNavigator() { const theoriePane = document.getElementById('tab-content-theorie'); if (!theoriePane) return; const existingPaneNav = theoriePane.querySelector('.course-module-navigator'); if (existingPaneNav) existingPaneNav.remove(); const legacyActionDivs = theoriePane.querySelectorAll('.card > div[style*="flex"]:last-child, div[style*="flex"]:last-child'); legacyActionDivs.forEach(div => { if (div.querySelector('a[href*="workspace.html"]') || div.querySelector('a[href*="index.html"]')) { div.style.display = 'none'; } }); const paneNav = document.createElement('nav'); paneNav.className = 'course-module-navigator'; paneNav.style.marginTop = '28px'; paneNav.setAttribute('aria-label', 'Modulnavigation'); // Left let leftHtml = ''; if (prevChapter) { const prevWsUrl = buildWorkspaceUrl(context.courseId, prevChapter.trackId, prevChapter.folder); leftHtml = ` ◀ VORHERIGES MODUL ${escapeHtml(prevChapter.title)} `; } else if (prevCourse) { const prevCourseWsUrl = buildFirstLessonWorkspaceUrl(prevCourseKey, prevCourse); leftHtml = ` ⏼ VORHERIGER KURS ${escapeHtml(prevCourse.icon || '📚')} ${escapeHtml(prevCourse.title)} `; } else { leftHtml = ` 🏠 START Dashboard `; } // Center const theoryFullUrl = buildTheoryUrl(context.courseId, currentTrackId, context.chapterFolder); const centerHtml = `
📖 Vollbild-Theorie ↗
`; // Right let rightHtml = ''; if (nextChapter) { const nextWsUrl = buildWorkspaceUrl(context.courseId, nextChapter.trackId, nextChapter.folder); rightHtml = ` NÄCHSTES MODUL ${escapeHtml(nextChapter.title)} ▶ `; } else if (nextCourse) { const nextCourseWsUrl = buildFirstLessonWorkspaceUrl(nextCourseKey, nextCourse); rightHtml = ` NÄCHSTER KURS ${escapeHtml(nextCourse.icon || '🚀')} ${escapeHtml(nextCourse.title)} ⏭ `; } else { rightHtml = ` ABSCHLUSS 🎉 Kurs gemeistert! 🏆 `; } paneNav.innerHTML = leftHtml + centerHtml + rightHtml; theoriePane.appendChild(paneNav); } // Global fĂŒr dynamisches Nachladen in workspace.html bereitstellen window.renderWorkspaceTheoryNavigator = renderWorkspaceTheoryInlineNavigator; // 3. WORKSPACE: Topbar-Steuerleiste verdrahten function renderWorkspaceNavigation() { const topbarTitle = document.querySelector('.workspace-title'); if (!topbarTitle) return; // 1. Drawer Button "☰ Kapitel" let toggleBtn = document.getElementById('btn-toggle-chapter-drawer'); if (!toggleBtn) { toggleBtn = document.createElement('button'); toggleBtn.type = 'button'; toggleBtn.id = 'btn-toggle-chapter-drawer'; toggleBtn.className = 'workspace-topbar-btn'; toggleBtn.innerHTML = '☰ Kapitel'; toggleBtn.title = 'Kapitel-Übersicht aufklappen'; topbarTitle.insertBefore(toggleBtn, topbarTitle.firstChild); } toggleBtn.onclick = () => { if (sidebar.classList.contains('open')) { closeSidebar(); } else { openSidebar(); } }; // 2. Dashboard Button let dashBtn = document.getElementById('btn-topbar-dashboard'); if (!dashBtn) { dashBtn = document.createElement('a'); dashBtn.href = `${rootPrefix}dashboard.html`; dashBtn.id = 'btn-topbar-dashboard'; dashBtn.className = 'workspace-topbar-btn'; dashBtn.innerHTML = '🏠 Dashboard'; dashBtn.title = 'ZurĂŒck zum Lern-Dashboard'; toggleBtn.insertAdjacentElement('afterend', dashBtn); } // 3. Navigation Cluster let navCluster = document.getElementById('workspace-nav-cluster'); if (!navCluster) { navCluster = document.createElement('div'); navCluster.id = 'workspace-nav-cluster'; navCluster.className = 'workspace-nav-cluster'; navCluster.innerHTML = ` Modul - / - `; dashBtn.insertAdjacentElement('afterend', navCluster); } // Update Badge const badge = document.getElementById('workspace-chapter-badge'); if (badge) { const modNumber = currentChapIdx >= 0 ? currentChapIdx + 1 : 1; badge.innerText = `MODUL ${modNumber} / ${allChapters.length || 16}`; } const btnPrevCourse = document.getElementById('btn-topbar-prev-course'); if (btnPrevCourse) { if (prevCourseKey && prevCourse) { btnPrevCourse.classList.remove('disabled'); btnPrevCourse.disabled = false; btnPrevCourse.title = `Zum vorherigen Kurs: ${prevCourse.title}`; btnPrevCourse.onclick = () => { window.location.href = buildFirstLessonWorkspaceUrl(prevCourseKey, prevCourse); }; } else { btnPrevCourse.classList.add('disabled'); btnPrevCourse.disabled = true; } } const btnPrevChap = document.getElementById('btn-topbar-prev-chapter'); if (btnPrevChap) { if (prevChapter) { btnPrevChap.classList.remove('disabled'); btnPrevChap.disabled = false; btnPrevChap.title = `Vorheriges Modul: ${prevChapter.title}`; btnPrevChap.onclick = () => { window.location.href = buildWorkspaceUrl(context.courseId, prevChapter.trackId, prevChapter.folder); }; } else { btnPrevChap.classList.add('disabled'); btnPrevChap.disabled = true; } } const btnNextChap = document.getElementById('btn-topbar-next-chapter'); if (btnNextChap) { if (nextChapter) { btnNextChap.classList.remove('disabled'); btnNextChap.disabled = false; btnNextChap.title = `NĂ€chstes Modul: ${nextChapter.title}`; btnNextChap.onclick = () => { window.location.href = buildWorkspaceUrl(context.courseId, nextChapter.trackId, nextChapter.folder); }; } else { btnNextChap.classList.add('disabled'); btnNextChap.disabled = true; } } const btnNextCourse = document.getElementById('btn-topbar-next-course'); if (btnNextCourse) { if (nextCourseKey && nextCourse) { btnNextCourse.classList.remove('disabled'); btnNextCourse.disabled = false; btnNextCourse.title = `Zum nĂ€chsten Kurs: ${nextCourse.title}`; btnNextCourse.onclick = () => { window.location.href = buildFirstLessonWorkspaceUrl(nextCourseKey, nextCourse); }; } else { btnNextCourse.classList.add('disabled'); btnNextCourse.disabled = true; } } renderWorkspaceTheoryInlineNavigator(); } if (context.isWorkspace) { renderWorkspaceNavigation(); } else { renderTheoryNavigator(); } // 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(); } })();