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.