PageSourceSearch

https://baukunst-nrw.de/js/ObjectDetailSlider.js

js baukunst-nrw.de collected 2026-09-24 21:23:48 UTC 6,800 bytes, 162 lines download raw bytes

1/**
2 * Spezielle Klasse für den Bilder-Slider in der Objekt-Detailansicht.
3 * Nutzt Swiper anstelle von Glide für bessere Unterstützung variabler Breiten.
4 */
5export default class ObjectDetailSlider {
6    constructor(selector = '.object-images-slider') {
7        this.selector = selector;
8        this.container = document.querySelector(selector);
9
10        if (this.container) {
11            this.init();
12        }
13    }
14
15    init() {
16        const thumbContainer = document.querySelector('.object-thumbnails-slider');
17        const slideCount = this.container.querySelectorAll('.swiper-slide').length;
18        const infoContainer = document.querySelector('.image-info-container');
19        const captionEl = document.querySelector('.image-swiper-caption');
20        const photographerEl = document.querySelector('.image-photographer');
21
22        let thumbnailSwiper = null;
23        if (thumbContainer) {
24            thumbnailSwiper = new Swiper(thumbContainer, {
25                spaceBetween: 10,
26                slidesPerView: 'auto',
27                slideToClickedSlide: true,
28                watchSlidesProgress: true,
29                centeredSlides: true,
30                initialSlide: 0,
31                loop: false,
32                centerInsufficientSlides: true,
33                observer: true,
34                observeParents: true,
35            });
36        }
37
38        const updateImageText = (swiper) => {
39            const activeSlide = swiper.slides[swiper.activeIndex];
40            if (!activeSlide) return;
41
42            const caption = activeSlide.getAttribute('data-caption');
43            const photographer = activeSlide.getAttribute('data-photographer');
44            const photographerUrl = activeSlide.getAttribute('data-photographer-url');
45            const wikiurl = activeSlide.getAttribute('data-wikiurl');
46            const wikitypeName = activeSlide.getAttribute('data-wikitype-name');
47            const wikitypeUrl = activeSlide.getAttribute('data-wikitype-url');
48            const currentIndex = swiper.activeIndex + 1;
49            const totalSlides = swiper.slides.length;
50
51            if (captionEl) captionEl.textContent = caption || '';
52            if (photographerEl) {
53                let html = `Bild ${currentIndex}/${totalSlides}`;
54                if (photographer) {
55                    let photographerLabel;
56                    if (wikiurl) {
57                        // Wikipedia-Foto: unverändert, Link bereits am "Foto:"-Label.
58                        photographerLabel = `<a href="${wikiurl}" target="_blank" rel="noopener">Foto:</a> ${photographer}`;
59                    } else if (photographerUrl) {
60                        // Externe Website öffnet in neuem Tab, interne Übersicht im selben Tab.
61                        const isExternal = /^https?:\/\//i.test(photographerUrl);
62                        const targetAttr = isExternal ? ' target="_blank" rel="noopener"' : '';
63                        photographerLabel = `<a href="${photographerUrl}"${targetAttr}>${photographer}</a>`;
64                    } else {
65                        photographerLabel = photographer;
66                    }
67                    html += ` © ${photographerLabel}`;
68                }
69                if (wikitypeName) {
70                    html += `, Lizenz: <a href="${wikitypeUrl}" target="_blank" rel="noopener">${wikitypeName}</a>`;
71                }
72                photographerEl.innerHTML = html;
73            }
74        };
75
76        // Rechter Rand von image-photographer am rechten Bildrand ausrichten
77        const updatePhotographerAlignment = (swiper) => {
78            if (!photographerEl) return;
79            const activeSlide = swiper.slides[swiper.activeIndex];
80            if (!activeSlide) return;
81
82            const slideRect = activeSlide.getBoundingClientRect();
83            const cbRect = photographerEl.parentElement.getBoundingClientRect();
84            const rightPad = Math.max(0, cbRect.right - slideRect.right);
85            photographerEl.style.paddingRight = rightPad + 'px';
86            photographerEl.style.textAlign = 'right';
87        };
88
89        const swiperConfig = {
90            centeredSlides: true,
91            initialSlide: 0,
92            spaceBetween: 20,
93            slidesPerView: 'auto',
94            loop: false,
95            allowTouchMove: slideCount > 1,
96            observer: true,
97            observeParents: true,
98
99            navigation: {
100                nextEl: '.swiper-button-next',
101                prevEl: '.swiper-button-prev',
102            },
103            thumbs: {
104                swiper: thumbnailSwiper,
105            },
106            on: {
107                init: function () {
108                    updateImageText(this);
109                },
110                afterInit: function () {
111                    updatePhotographerAlignment(this);
112                },
113                slideChange: function () {
114                    if (thumbnailSwiper) {
115                        thumbnailSwiper.slideTo(this.activeIndex);
116                    }
117                    if (captionEl) captionEl.textContent = this.slides[this.activeIndex]?.getAttribute('data-caption') || '';
118                    if (photographerEl) photographerEl.style.opacity = '0';
119                },
120                slideChangeTransitionEnd: function () {
121                    updateImageText(this);
122                    updatePhotographerAlignment(this);
123                    if (photographerEl) photographerEl.style.opacity = '1';
124                },
125            }
126        };
127
128        this.swiper = new Swiper(this.container, swiperConfig);
129
130        const forceReset = () => {
131            if (thumbnailSwiper) {
132                thumbnailSwiper.update();
133                thumbnailSwiper.slideTo(0, 0);
134            }
135            if (this.swiper) {
136                this.swiper.update();
137                this.swiper.slideTo(0, 0);
138                updateImageText(this.swiper);
139            }
140        };
141
142        forceReset();
143
144        setTimeout(forceReset, 100);
145        setTimeout(() => {
146            forceReset();
147            updatePhotographerAlignment(this.swiper);
148            this.container.classList.remove('not-initialized');
149
150            if (infoContainer) infoContainer.classList.remove('not-initialized');
151            const thumbWrapper = document.querySelector('.object-thumbnails-wrapper');
152            if (thumbWrapper) thumbWrapper.classList.remove('not-initialized');
153        }, 500);
154
155        window.addEventListener('resize', () => {
156            if (this.swiper) {
157                updateImageText(this.swiper);
158                updatePhotographerAlignment(this.swiper);
159            }
160        });
161    }
162}

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.