1/* 2|-------------------------------------------------------------------------- 3| Carousel Vids 4|-------------------------------------------------------------------------- 5*/ 6document.querySelectorAll('.carousel').forEach(function(carousel) { 7 carousel.addEventListener('slide.bs.carousel', function(ev) { 8 var slides = carousel.querySelectorAll('.carousel-item'); 9 var video = slides[ev.to].querySelectorAll('video')[0]; 10 if (video) { 11 try { 12 video.currentTime = 0; 13 video.play(); 14 } catch (err) { 15 console.log(err); 16 } 17 } 18 }); 19}); 20 21 22 23/* 24|-------------------------------------------------------------------------- 25| Carousel section min-height of slides 26|-------------------------------------------------------------------------- 27*/ 28if (window.location.href.indexOf("in_preview_panel") == -1) { 29 const carouselInners = document.querySelectorAll('.carousel-inner'); 30 carouselInners.forEach(carouselInner => { 31 let minHeight = 0; 32 const carouselItems = carouselInner.querySelectorAll('.carousel-item'); 33 carouselItems.forEach(carouselItem => { 34 const height = carouselItem.offsetHeight; 35 if (height > minHeight) { 36 minHeight = height; 37 } 38 }); 39 carouselItems.forEach(carouselItem => { 40 carouselItem.style.minHeight = minHeight + 'px'; 41 }); 42 }); 43} 44 45 46 47/* 48|--------------------------------------------------------------------------
49| search field toggle on desktop 50|-------------------------------------------------------------------------- 51 52if (document.querySelector('.search-button')) { 53 document.querySelector('.search-button').addEventListener('click', function(e) { 54 var searchInput = this.closest('form').querySelector('.search-input'); 55 if(searchInput.value == '') { 56 e.preventDefault(); 57 this.classList.toggle('show-input'); 58 this.closest('form').classList.toggle('show-input'); 59 } 60 }); 61} 62*/ 63 64/* 65|-------------------------------------------------------------------------- 66| target blank for pdf and external links 67|-------------------------------------------------------------------------- 68*/ 69const currentOrigin = window.location.origin; 70const links = document.querySelectorAll('a[href]'); 71links.forEach(link => { 72 const href = link.getAttribute('href'); 73 if (!href) return; 74 let url; 75 try { 76 url = new URL(href, currentOrigin); 77 } catch (e) { 78 return; 79 } 80 const isHttp = url.protocol === 'http:' || url.protocol === 'https:'; 81 const isExternal = isHttp && url.origin !== currentOrigin; 82 const isDocument = url.pathname.startsWith('/documents/') || url.pathname.toLowerCase().endsWith('.pdf'); 83 if (isExternal || isDocument) { 84 link.setAttribute('target', '_blank'); 85 link.setAttribute('rel', 'nofollow noopener'); 86 } 87}); 88 89 90 91/* 92|-------------------------------------------------------------------------- 93| header set appropriate class on scroll 94|-------------------------------------------------------------------------- 95*/ 96var lastScrollTop = window.pageYOffset || document.documentElement.scrollTop || 0; 97document.addEventListener('scroll', function () { 98 var header = document.querySelector('header'); 99 if (!header) return; 100 101 var currentScrollTop = window.pageYOffset || document.documentElement.scrollTop || 0; 102 if (currentScrollTop > lastScrollTop) { 103 header.classList.add('scrolled-down'); 104 } else if (currentScrollTop < lastScrollTop) { 105 header.classList.remove('scrolled-down'); 106 } 107 108 lastScrollTop = Math.max(currentScrollTop, 0); 109}, { passive: true }); 110 111 112/* 113|-------------------------------------------------------------------------- 114| forms 115|-------------------------------------------------------------------------- 116*/ 117// check if there is a form that has an element with class .is-invalid 118// if there is, scroll to it 119try { 120 setTimeout(function() { 121 var firstInvalid = document.querySelector('.is-invalid'); 122 if(firstInvalid) { 123 firstInvalid.scrollIntoView({behavior: "smooth", block: "center"}); 124 } else { 125 var form = document.querySelector('.form-scroll-to-top'); 126 if(form) { 127 form.scrollIntoView({behavior: "smooth", block: "start"}); 128 } 129 } 130 }, 100); 131} catch(error) { 132 // who cares 133} 134 135// because all form might be cached: get the value of the cookie "csrftoken" and set it as the value of the hidden input "csrfmiddlewaretoken" to every form 136try { 137 const csrftoken_from_cookie = (function (name) { 138 const cookies = document.cookie ? document.cookie.split('; ') : []; 139 for (let i = 0; i < cookies.length; i++) { 140 const parts = cookies[i].split('='); 141 if (decodeURIComponent(parts.shift()) === name) { 142 return decodeURIComponent(parts.join('=')); 143 } 144 } 145 return null; 146 })('csrftoken'); 147 148 // check if the cookie is set 149 if (!csrftoken_from_cookie) { 150 throw new Error("CSRF-Token-Cookie nicht gefunden. Abbruch der Verarbeitung."); 151 } 152 153 // get all forms on the page 154 document.querySelectorAll('form').forEach(function(form) { 155 // get the input with name "csrfmiddlewaretoken" 156 var csrfmiddlewaretoken = form.querySelector('input[name="csrfmiddlewaretoken"]'); 157 if (csrfmiddlewaretoken) { 158 // if there is an input with name "csrfmiddlewaretoken" set its value to the value of the cookie 159 csrfmiddlewaretoken.value = csrftoken_from_cookie;
160 console.log('CSRF-Token aktualisiert'); 161 } else { 162 // if there is no input with name "csrfmiddlewaretoken" create one 163 var input = document.createElement('input'); 164 input.type = 'hidden'; 165 input.name = 'csrfmiddlewaretoken'; 166 input.value = csrftoken_from_cookie; 167 form.appendChild(input); 168 console.log('CSRF-Token hinzugefügt'); 169 } 170 }); 171} catch (error) { 172 console.log(error.message); 173} 174 175 176/* 177|-------------------------------------------------------------------------- 178| messages 179|-------------------------------------------------------------------------- 180*/ 181setTimeout(function() { 182 var messagesWrap = document.querySelector('.messages-wrap'); 183 if (messagesWrap) { 184 messagesWrap.classList.add("fade-up"); 185 } 186}, 1500); 187 188 189/* 190|-------------------------------------------------------------------------- 191| tabs 192|-------------------------------------------------------------------------- 193*/ 194function initAllTabsBlocks() { 195 // Suche alle Tab-Wrapper mit id="tabs-..." 196 document.querySelectorAll('[id^="tabs-"]').forEach(function(tabsWrapper) { 197 var tabId = tabsWrapper.id.replace('tabs-', ''); 198 199 // Tabs per URL-Hash aktivieren (nur für diesen Block) 200 function activateTabFromHash() { 201 var hash = window.location.hash.replace('#', ''); 202 if(hash && hash.startsWith('tab-' + tabId + '-')) { 203 var tabBtn = tabsWrapper.querySelector('[data-bs-target="#' + hash + '"]'); 204 if(tabBtn) { 205 var tab = new bootstrap.Tab(tabBtn); 206 tab.show(); 207 // Nach dem Aktivieren zum gesamten Tabs-Block scrollen 208 tabsWrapper.scrollIntoView({behavior: "smooth", block: "start"}); 209 } 210 } 211 } 212 activateTabFromHash(); 213 window.addEventListener('hashchange', activateTabFromHash); 214 215 // Hash beim Tab-Wechsel setzen (nur für diesen Block) 216 tabsWrapper.querySelectorAll('button[data-bs-toggle="tab"]').forEach(function(tabBtn) { 217 tabBtn.addEventListener('shown.bs.tab', function (event) { 218 history.replaceState(null, null, '#' + event.target.getAttribute('data-bs-target').replace('#','')); 219 }); 220 }); 221 }); 222} 223 224document.addEventListener('DOMContentLoaded', initAllTabsBlocks); 225 226 227/* 228|-------------------------------------------------------------------------- 229| accordion 230|-------------------------------------------------------------------------- 231*/ 232function initAllAccordionBlocks() { 233 document.querySelectorAll('[id^="accordion-"]').forEach(function(accordionWrapper) { 234 var accId = accordionWrapper.id.replace('accordion-', ''); 235 236 function activateAccordionFromHash() { 237 var hash = window.location.hash.replace('#', ''); 238 if(hash && hash.startsWith('element-' + accId + '-')) { 239 var collapseElem = accordionWrapper.querySelector('#' + hash); 240 if(collapseElem) { 241 // Panel öffnen (auch wenn schon offen) 242 var bsCollapse = bootstrap.Collapse.getOrCreateInstance(collapseElem, {toggle: true}); 243 bsCollapse.show(); 244 // Zum Accordion-Block scrollen 245 accordionWrapper.scrollIntoView({behavior: "smooth", block: "start"}); 246 } 247 } 248 } 249 activateAccordionFromHash(); 250 window.addEventListener('hashchange', activateAccordionFromHash); 251 252 // Hash beim Ãffnen eines Accordion-Elements setzen 253 accordionWrapper.querySelectorAll('.accordion-button').forEach(function(btn) { 254 btn.addEventListener('click', function (event) { 255 var target = btn.getAttribute('data-bs-target'); 256 if(target) { 257 history.replaceState(null, null, '#' + target.replace('#','')); 258 } 259 }); 260 }); 261 }); 262} 263 264 265/* 266|-------------------------------------------------------------------------- 267| Mega-Menu: mouseenter/mouseleave â mega-open class auf .header-nav 268|-------------------------------------------------------------------------- 269*/ 270function initMegaMenu() { 271 var nav = document.querySelector('.header-nav'); 272 if (!nav) return; 273 274 var megaBg = nav.querySelector('.mega-bg'); 275 var cachedHeight = null; 276 277 function getMegaHeight() { 278 if (cachedHeight !== null) return cachedHeight; 279 // Make subitems temporarily measurable (visibility:hidden preserves layout) 280 var panels = nav.querySelectorAll('.nav-subitems'); 281 var max = 0;
282 panels.forEach(function(el) { 283 if (el.offsetHeight > max) max = el.offsetHeight; 284 }); 285 cachedHeight = max || 220; 286 return cachedHeight; 287 } 288 289 function updateDividerHeights(extraHeight) { 290 nav.querySelectorAll('.navbar-nav.main .nav-item + .nav-item').forEach(function(item) { 291 item.style.setProperty('--divider-height', (item.offsetHeight + extraHeight) + 'px'); 292 }); 293 } 294 295 nav.addEventListener('mouseenter', function() { 296 if (window.innerWidth < 992) return; 297 var h = getMegaHeight(); 298 if (megaBg) megaBg.style.height = h + 'px'; 299 nav.classList.add('mega-open'); 300 updateDividerHeights(h); 301 }); 302 nav.addEventListener('mouseleave', function() { 303 nav.classList.remove('mega-open'); 304 if (megaBg) megaBg.style.height = '0'; 305 updateDividerHeights(0); 306 }); 307} 308document.addEventListener('DOMContentLoaded', initMegaMenu); 309 310 311/* 312|-------------------------------------------------------------------------- 313| Mega-Menu: Touch-Fallback für Tablets â¥992px 314|-------------------------------------------------------------------------- 315*/ 316if ('ontouchstart' in window) { 317 document.querySelectorAll( 318 '.header-nav .navbar-nav.main .nav-item.has-submenu > .nav-link' 319 ).forEach(function (link) { 320 var activated = false; 321 link.addEventListener('click', function (e) { 322 if (!activated) { 323 e.preventDefault(); 324 activated = true; 325 } else { 326 activated = false; 327 } 328 }); 329 // Reset beim Verlassen des Nav-Bereichs 330 link.closest('.navbar-nav').addEventListener('mouseleave', function () { 331 activated = false; 332 }); 333 }); 334} 335 336document.addEventListener('DOMContentLoaded', initAllAccordionBlocks); 337 338 339/* 340|-------------------------------------------------------------------------- 341| FlipCard â Touch-Support 342|-------------------------------------------------------------------------- 343*/ 344document.querySelectorAll('.flip-card').forEach(function(card) { 345 card.addEventListener('click', function() { 346 if ('ontouchstart' in window || navigator.maxTouchPoints > 0) { 347 this.classList.toggle('flipped'); 348 } 349 }); 350}); 351 352 353/* 354|-------------------------------------------------------------------------- 355| ShuffleJS â Lösungen Filter 356|-------------------------------------------------------------------------- 357*/ 358document.addEventListener('DOMContentLoaded', function () { 359 var grid = document.getElementById('solutions-grid'); 360 if (!grid || typeof Shuffle === 'undefined') return; 361 362 var shuffleInstance = new Shuffle(grid, { 363 itemSelector: '.solution-item', 364 sizer: null, 365 }); 366 367 var activeFilters = { 368 beschwerden: 'all', 369 versorgungsarten: 'all', 370 }; 371 372 function applyFilters() { 373 shuffleInstance.filter(function (element) { 374 return Object.entries(activeFilters).every(function (entry) { 375 var key = entry[0], value = entry[1]; 376 if (value === 'all') return true; 377 var groups = JSON.parse(element.dataset[key] || '[]'); 378 return groups.includes(String(value)); 379 }); 380 }); 381 } 382 383 [ 384 ['filter-beschwerde', 'beschwerden'], 385 ['filter-versorgungsart', 'versorgungsarten'], 386 ].forEach(function (pair) { 387 var select = document.getElementById(pair[0]); 388 if (select) { 389 select.addEventListener('change', function () { 390 activeFilters[pair[1]] = select.value; 391 applyFilters(); 392 }); 393 } 394 }); 395}); 396 397 398/* 399|-------------------------------------------------------------------------- 400| ShuffleJS â Standorte Filter 401|-------------------------------------------------------------------------- 402*/ 403(function () { 404 var grid = document.getElementById('standorte-grid'); 405 if (!grid || typeof Shuffle === 'undefined') return; 406 407 var shuffleInstance = new Shuffle(grid, { 408 itemSelector: '.standort-item', 409 sizer: null, 410 }); 411
412 // Re-layout when a card is expanded/collapsed so Shuffle re-measures the 413 // changed height (otherwise expanded cards overlap their neighbours). 414 ['shown.bs.collapse', 'hidden.bs.collapse'].forEach(function (evt) { 415 grid.addEventListener(evt, function () { 416 shuffleInstance.update(); 417 }); 418 }); 419 420 var activeSparten = new Set(); 421 var activeRegionen = new Set(); 422 423 function applyFilter() { 424 shuffleInstance.filter(function (element) { 425 var sparteIds = JSON.parse(element.dataset.sparten || '[]').map(String); 426 var regionIds = JSON.parse(element.dataset.regionen || '[]').map(String); 427 var sparteMatch = activeSparten.size === 0 || 428 Array.from(activeSparten).every(function (id) { return sparteIds.includes(id); }); 429 var regionMatch = activeRegionen.size === 0 || 430 Array.from(activeRegionen).every(function (id) { return regionIds.includes(id); }); 431 return sparteMatch && regionMatch; 432 }); 433 434 document.querySelectorAll('.sparte-card').forEach(function (card) { 435 card.classList.toggle('active', activeSparten.has(String(card.dataset.sparteId))); 436 }); 437 document.querySelectorAll('.region-card').forEach(function (card) { 438 card.classList.toggle('active', activeRegionen.has(String(card.dataset.regionId))); 439 }); 440 } 441 442 document.querySelectorAll('.sparte-card').forEach(function (card) { 443 card.addEventListener('click', function () { 444 var id = String(card.dataset.sparteId); 445 activeSparten.has(id) ? activeSparten.delete(id) : activeSparten.add(id); 446 applyFilter(); 447 if (window.matchMedia('(max-width: 767.98px)').matches) { 448 var scrollTarget = grid.getBoundingClientRect().top + window.pageYOffset -100; 449 window.scrollTo({ top: scrollTarget, behavior: 'smooth' }); 450 } 451 }); 452 }); 453 454 document.querySelectorAll('.region-card').forEach(function (card) { 455 card.addEventListener('click', function () { 456 var id = String(card.dataset.regionId); 457 activeRegionen.has(id) ? activeRegionen.delete(id) : activeRegionen.add(id); 458 applyFilter(); 459 }); 460 }); 461 462 var resetBtn = document.getElementById('standorte-reset'); 463 if (resetBtn) { 464 resetBtn.addEventListener('click', function () { 465 activeSparten.clear(); 466 activeRegionen.clear(); 467 applyFilter(); 468 }); 469 } 470})();
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.