1document.addEventListener('DOMContentLoaded', function () { 2 var nav = document.querySelector('.terminklassen-tage-nav'); 3 if (!nav) return; 4 5 /* Aus möglichen Stacking-Contexts des Contents herauslösen. */ 6 if (nav.parentNode !== document.body) { 7 document.body.appendChild(nav); 8 } 9 10 var sections = Array.prototype.slice.call(document.querySelectorAll('.contentMenu[id]')); 11 if (!sections.length) { 12 nav.style.display = 'none'; 13 return; 14 } 15 16 function makeLabel(id) { 17 if (!id) return ''; 18 return id 19 .replace(/[-_]+/g, ' ') 20 .replace(/\b\w/g, function (char) { return char.toUpperCase(); }); 21 } 22 23 var items = sections.map(function (section) { 24 var id = section.getAttribute('id'); 25 var customLabel = section.getAttribute('data-menu-label'); 26 27 return { 28 id: id, 29 label: customLabel && customLabel.trim() ? customLabel.trim() : makeLabel(id), 30 section: section 31 }; 32 }).filter(function (item) { 33 return !!item.id; 34 }); 35 36 if (!items.length) { 37 nav.style.display = 'none'; 38 return; 39 } 40 41 nav.innerHTML = '' 42 + '<div class="terminklassen-control">' 43 + ' <div class="terminklassen-drawer" aria-hidden="true">' 44 + ' <div class="terminklassen-drawer-head">' 45 + ' <strong>Bereiche</strong>' 46 + ' <button type="button" class="terminklassen-drawer-close" aria-label="Menü schlieÃen">Ã</button>' 47 + ' </div>' 48 + ' <div class="terminklassen-drawer-list"></div>' 49 + ' </div>' 50 + ' <div class="terminklassen-radar" tabindex="0" role="button" aria-label="Abschnittsnavigation öffnen">' 51 + ' <div class="terminklassen-radar-ring"></div>' 52 + ' <div class="terminklassen-radar-window">' 53 + ' <button type="button" class="terminklassen-radar-item is-prev"></button>' 54 + ' <button type="button" class="terminklassen-radar-item is-current"></button>' 55 + ' <button type="button" class="terminklassen-radar-item is-next"></button>' 56 + ' </div>' 57 + ' </div>' 58 + '</div>'; 59 60 var control = nav.querySelector('.terminklassen-control'); 61 var radar = nav.querySelector('.terminklassen-radar'); 62 var radarPrev = nav.querySelector('.terminklassen-radar-item.is-prev'); 63 var radarCurrent = nav.querySelector('.terminklassen-radar-item.is-current'); 64 var radarNext = nav.querySelector('.terminklassen-radar-item.is-next'); 65 var drawer = nav.querySelector('.terminklassen-drawer'); 66 var drawerList = nav.querySelector('.terminklassen-drawer-list'); 67 var closeBtn = nav.querySelector('.terminklassen-drawer-close'); 68 69 var currentIndex = 0; 70 var closeTimer = null; 71 var openLockUntil = 0; 72 73 items.forEach(function (item, index) { 74 var link = document.createElement('a'); 75 link.href = '#' + item.id; 76 link.textContent = item.label; 77 link.className = 'terminklassen-drawer-link'; 78 link.dataset.index = String(index); 79 80 link.addEventListener('click', function () { 81 currentIndex = index; 82 updateRadar(true); 83 closeDrawer(); 84 }); 85 86 drawerList.appendChild(link); 87 item.link = link; 88 }); 89 90 function wrapIndex(index) { 91 if (index < 0) return items.length - 1; 92 if (index >= items.length) return 0; 93 return index; 94 } 95 96 function setRadarButton(button, index) { 97 var actualIndex = wrapIndex(index); 98 var item = items[actualIndex]; 99 100 button.textContent = item.label; 101 button.onclick = function (event) { 102 event.stopPropagation(); 103 var target = document.getElementById(item.id); 104 if (target) { 105 target.scrollIntoView({ behavior: 'smooth', block: 'start' }); 106 } 107 }; 108 } 109 110 function updateRadar(animate) { 111 setRadarButton(radarPrev, currentIndex - 1); 112 setRadarButton(radarCurrent, currentIndex); 113 setRadarButton(radarNext, currentIndex + 1); 114 115 items.forEach(function (item, index) { 116 if (item.link) { 117 item.link.classList.toggle('is-active', index === currentIndex); 118 } 119 }); 120 121 if (animate) { 122 radar.classList.remove('is-turning'); 123 void radar.offsetWidth; 124 radar.classList.add('is-turning'); 125 126 setTimeout(function () { 127 radar.classList.remove('is-turning'); 128 }, 360); 129 } 130 } 131 132 function findCurrentIndex() { 133 var marker = window.scrollY + Math.min(240, window.innerHeight * 0.34); 134 var found = 0; 135
136 items.forEach(function (item, index) { 137 if (item.section.offsetTop <= marker) { 138 found = index; 139 } 140 }); 141 142 return found; 143 } 144 145 function openDrawer() { 146 if (closeTimer) { 147 clearTimeout(closeTimer); 148 closeTimer = null; 149 } 150 151 openLockUntil = performance.now() + 900; 152 153 nav.classList.add('is-open'); 154 drawer.setAttribute('aria-hidden', 'false'); 155 156 var activeLink = items[currentIndex].link; 157 if (activeLink) { 158 setTimeout(function () { 159 activeLink.scrollIntoView({ block: 'center' }); 160 }, 80); 161 } 162 } 163 164 function closeDrawer(force) { 165 if (!force && performance.now() < openLockUntil) { 166 scheduleClose(950); 167 return; 168 } 169 170 nav.classList.remove('is-open'); 171 drawer.setAttribute('aria-hidden', 'true'); 172 } 173 174 function scheduleClose(delay) { 175 if (closeTimer) clearTimeout(closeTimer); 176 177 closeTimer = setTimeout(function () { 178 if (!control.matches(':hover') && 179 !drawer.matches(':hover') && 180 !radar.matches(':hover') && 181 !control.matches(':focus-within')) { 182 closeDrawer(false); 183 } 184 }, typeof delay === 'number' ? delay : 1100); 185 } 186 187 radar.addEventListener('pointerenter', openDrawer); 188 drawer.addEventListener('pointerenter', function () { 189 if (closeTimer) clearTimeout(closeTimer); 190 openLockUntil = performance.now() + 350; 191 }); 192 193 radar.addEventListener('click', function () { 194 nav.classList.contains('is-open') ? closeDrawer(true) : openDrawer(); 195 }); 196 197 radar.addEventListener('keydown', function (event) { 198 if (event.key === 'Enter' || event.key === ' ') { 199 event.preventDefault(); 200 openDrawer(); 201 } 202 203 if (event.key === 'Escape') { 204 closeDrawer(); 205 } 206 }); 207 208 control.addEventListener('pointerenter', function () { 209 if (closeTimer) clearTimeout(closeTimer); 210 }); 211 212 control.addEventListener('pointerleave', function () { 213 scheduleClose(1100); 214 }); 215 216 drawer.addEventListener('pointerleave', function () { 217 scheduleClose(1100); 218 }); 219 220 closeBtn.addEventListener('click', function (event) { 221 event.stopPropagation(); 222 closeDrawer(true); 223 }); 224 225 document.addEventListener('keydown', function (event) { 226 if (event.key === 'Escape') closeDrawer(true); 227 }); 228 229 function getFooter() { 230 return document.querySelector('#colophon, footer.site-footer, footer[role="contentinfo"]'); 231 } 232 233 function updateFooterAvoidance() { 234 var footer = getFooter(); 235 var baseBottom = document.body.classList.contains('logged-in') ? 18 : 38; 236 var gap = 14; 237 238 if (!footer) { 239 nav.style.bottom = baseBottom + 'px'; 240 return; 241 } 242 243 var rect = footer.getBoundingClientRect(); 244 245 if (rect.top >= window.innerHeight) { 246 nav.style.bottom = baseBottom + 'px'; 247 return; 248 } 249 250 nav.style.bottom = Math.max( 251 baseBottom, 252 window.innerHeight - rect.top + gap 253 ) + 'px'; 254 } 255 256 function onScroll() { 257 var nextIndex = findCurrentIndex(); 258 259 if (nextIndex !== currentIndex) { 260 currentIndex = nextIndex; 261 updateRadar(true); 262 } 263 264 updateFooterAvoidance(); 265 } 266 267 window.addEventListener('scroll', onScroll, { passive: true }); 268 window.addEventListener('resize', onScroll); 269 270 currentIndex = findCurrentIndex(); 271 updateRadar(false); 272 updateFooterAvoidance(); 273});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.