PageSourceSearch

https://ortho-team.ch/static/js/global.js

js ortho-team.ch collected 2026-09-28 12:45:50 UTC 17,031 bytes, 470 lines download raw bytes

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.