PageSourceSearch

https://groetzinger-reisecenter.de/wp-content/plugins/smg-technology/dist/accessibility.js?ver=7.1

js groetzinger-reisecenter.de collected 2026-10-07 05:33:34 UTC 16,272 bytes, 427 lines download raw bytes

1var lastFocusedBeforeLightbox = null;
2
3document.addEventListener('DOMContentLoaded', () => {
4    enhanceOnclickElements();
5    enhanceOnclickWithChildrenElements();
6    enhanceTextElements();
7    enhanceLinks();
8    enhanceSearchForm();
9    enhanceBusinessHourShortcode();
10    enhanceProductCardsAccessibility();
11    enhanceDestinationCardsAccessibility();
12    setupLightboxFocusHandling();
13    enhanceDcbBoxes();
14    enhanceSMGShortcodes();
15});
16
17function enhanceOnclickElements() {
18    const selector = 'div[onclick], span[onclick], svg[onclick], img[onclick], div.et_clickable';
19    const elements = document.querySelectorAll(selector);
20
21    elements.forEach((el) => {
22        // Tastaturfokus ermöglichen
23        if (!el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0');
24
25        // Rolle setzen
26        if (!el.hasAttribute('role')) el.setAttribute('role', 'button');
27
28        // aria-label setzen (für Screenreader)
29        if (!el.hasAttribute('aria-label')) {
30            const label =
31                el.getAttribute('alt') ||
32                el.getAttribute('title') ||
33                el.innerText?.trim();
34            if (label) el.setAttribute('aria-label', label);
35        }
36
37        // Tastatursteuerung ermöglichen
38        el.addEventListener('keydown', (event) => {
39            if (event.key === 'Enter' || event.key === ' ') {
40                event.preventDefault();
41                el.click();
42            }
43        });
44    });
45}
46
47function enhanceOnclickWithChildrenElements() {
48    const interactiveSelector = 'div[onclick], span[onclick], svg[onclick], img[onclick]';
49    const textSelector = 'p, h1, h2, h3, h4, h5, h6, span';
50
51    const interactiveElements = document.querySelectorAll(interactiveSelector);
52    const textElements = document.querySelectorAll(textSelector);
53
54    // Interaktive Elemente tastaturfähig machen
55    interactiveElements.forEach((el) => {
56        if (!el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0');
57        if (!el.hasAttribute('role')) el.setAttribute('role', 'button');
58
59        if (!el.hasAttribute('aria-label')) {
60            const label =
61                el.getAttribute('alt') ||
62                el.getAttribute('title') ||
63                el.innerText?.trim();
64            if (label) el.setAttribute('aria-label', label);
65        }
66
67        el.addEventListener('keydown', (event) => {
68            if (event.key === 'Enter' || event.key === ' ') {
69                event.preventDefault();
70                el.click();
71            }
72        });
73    });
74
75    // Textbereiche: nur zugänglich, wenn kein Kind interaktiv ist
76    textElements.forEach((el) => {
77        const child = el.querySelector('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
78        const text = el.innerText?.trim();
79
80        if (child) {
81            // Doppelten Fokus verhindern
82            el.removeAttribute('tabindex');
83            el.removeAttribute('role');
84            el.removeAttribute('aria-label');
85            return;
86        }
87
88        if (!text || text.length < 3) return;
89
90        if (!el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0');
91        if (!el.hasAttribute('role')) el.setAttribute('role', 'document');
92        if (!el.hasAttribute('aria-label')) el.setAttribute('aria-label', text);
93    });
94}
95
96function enhanceTextElements() {
97    const textElements = document.querySelectorAll('p, h1, h2, h3, h4, h5, h6, span');
98
99    textElements.forEach((el) => {
100        const interactiveChild = el.querySelector('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
101
102        if (interactiveChild) {
103            // Entferne Fokus vom Elternelement, falls vorhanden
104            el.removeAttribute('tabindex');
105            el.removeAttribute('role');
106            el.removeAttribute('aria-label');
107            return;
108        }
109
110        const text = el.innerText?.trim();
111        if (!text || text.length < 3) return;
112
113        if (!el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0');
114        if (!el.hasAttribute('role')) el.setAttribute('role', 'document');
115        if (!el.hasAttribute('aria-label')) el.setAttribute('aria-label', text);
116    });
117}
118
119
120function enhanceLinks() {
121    document.querySelectorAll('a').forEach((link) => {
122        const onlyImage = link.children.length === 1 && link.firstElementChild.tagName === 'IMG';
123
124        if (onlyImage && !link.hasAttribute('aria-label')) {
125            const img = link.firstElementChild;
126            const alt = img?.getAttribute('alt')?.trim();
127            if (alt) {
128                link.setAttribute('aria-label', alt);
129            }
130        }
131    });
132}
133
134function enhanceSearchForm() {
135    // 1. Labels (Spans) mit Inputs verbinden via aria-labelledby
136    var spans = document.querySelectorAll('.searchform-label[aria-label]');
137    spans.forEach(function (span) {
138        var input = span.closest('.input-inner-wrapper');
139        if (input) input = input.querySelector('input, select, textarea');
140        if (input && !input.hasAttribute('aria-labelledby')) {
141            var id = span.id || 'label-' + Math.random().toString(36).slice(2, 8);
142            span.id = id;
143            input.setAttribute('aria-labelledby', id);
144        }
145    });
146
147    // 2. Counter-Buttons tastaturfähig machen
148    var buttons = document.querySelectorAll('.down_count, .up_count');
149    buttons.forEach(function (el) {
150        if (!el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0');
151        if (!el.hasAttribute('role')) el.setAttribute('role', 'button');
152        if (!el.hasAttribute('aria-label')) {
153            var title = el.getAttribute('title') || el.innerText.trim();
154            if (title) el.setAttribute('aria-label', title);
155        }
156        el.addEventListener('keydown', function (event) {
157            if (event.key === 'Enter' || event.key === ' ') {
158                event.preventDefault();
159                el.click();
160            }
161        });
162    });
163
164    // 3. Reiseart-Buttons aria-pressed setzen
165    var travelButtons = document.querySelectorAll('.booking-type-btn');
166    travelButtons.forEach(function (btn) {
167        btn.setAttribute('aria-pressed', btn.classList.contains('selected') ? 'true' : 'false');
168        btn.addEventListener('click', function () {
169            travelButtons.forEach(function (b) {
170                b.setAttribute('aria-pressed', 'false');
171            });
172            btn.setAttribute('aria-pressed', 'true');
173        });
174    });
175
176    // 4. Inputs mit visuellen Labels beschriften
177    var inputs = document.querySelectorAll('input[data-input], input[readonly], input.travellers-input');
178    inputs.forEach(function (input) {
179        var labelSpan = input.closest('.input-inner-wrapper');
180        if (labelSpan) labelSpan = labelSpan.querySelector('span.searchform-label');
181        if (labelSpan && !input.hasAttribute('aria-label')) {
182            var label = labelSpan.innerText.trim();
183            if (label) input.setAttribute('aria-label', label);
184        }
185    });
186
187    // 5. Auswahlboxen (Select2) mit aria-label
188    var selects = document.querySelectorAll('.select2-selection');
189    selects.forEach(function (el) {
190        var text = el.innerText.trim();
191        if (!el.hasAttribute('aria-label') && text.length > 0) {
192            el.setAttribute('aria-label', 'Auswahl: ' + text);
193        }
194    });
195
196    // 6. Social-Media-Bild-Links beschriften
197    document.querySelectorAll('a').forEach(function (link) {
198        var img = link.querySelector('img[alt]');
199        if (img && !link.hasAttribute('aria-label')) {
200            var alt = img.getAttribute('alt');
201            if (alt) link.setAttribute('aria-label', alt.trim());
202        }
203    });
204}
205
206function enhanceBusinessHourShortcode() {
207    document.querySelectorAll('.business-hour-detail .row').forEach(function (row) {
208        if (!row.hasAttribute('tabindex')) row.setAttribute('tabindex', '0');
209        if (!row.hasAttribute('role')) row.setAttribute('role', 'document');
210
211        var dayEl = row.querySelector('.col-sm-4');
212        var timeEl = row.querySelector('.col-sm-8');
213
214        if (dayEl && timeEl) {
215            var label = dayEl.innerText.trim() + ' ' + timeEl.innerText.trim();
216            row.setAttribute('aria-label', label);
217        }
218    });
219
220    document.querySelectorAll('section.smg-welcome-text').forEach(function (section) {
221        var h2 = section.querySelector('h2');
222        if (h2) {
223            h2.removeAttribute('tabindex');
224            h2.removeAttribute('role');
225            h2.removeAttribute('aria-label');
226        }
227
228        // Prüfe ob schon ein Screenreader-Block vorhanden ist
229        if (section.querySelector('.sr-text')) return;
230
231        // Alle Textknoten nach dem h2 einsammeln
232        var textParts = [];
233        var node = h2?.nextSibling;
234        while (node) {
235            if (node.nodeType === 3 && node.textContent.trim()) {
236                textParts.push(node.textContent.trim());
237            } else if (node.nodeType === 1) {
238                textParts.push(node.innerText.trim());
239            }
240            node = node.nextSibling;
241        }
242
243        var text = textParts.join(' ').trim();
244        if (!text || text.length < 5) return;
245
246        // Neues <div> mit zugänglichem Text einfügen
247        var div = document.createElement('div');
248        div.className = 'sr-text';
249        div.setAttribute('tabindex', '0');
250        div.setAttribute('role', 'document');
251        div.setAttribute('aria-label', text);
252        div.style.position = 'relative'; // Optional: sichtbar positionieren
253        section.appendChild(div);
254    });
255    document.querySelectorAll('.business-hour-detail .mt-3').forEach(function (el) {
256        if (!el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0');
257        if (!el.hasAttribute('role')) el.setAttribute('role', 'note');
258
259        var text = el.innerText.trim();
260        if (text && !el.hasAttribute('aria-label')) {
261            el.setAttribute('aria-label', text);
262        }
263    });
264}
265
266function enhanceProductCardsAccessibility() {
267    const cards = document.querySelectorAll('.product-card');
268
269    cards.forEach((card) => {
270        const cardLink = card.querySelector('a[href]');
271        if (cardLink) {
272            card.setAttribute('tabindex', '0');
273            card.setAttribute('role', 'link');
274
275            const label = Array.from(
276                card.querySelectorAll('.product-title, .product-description, .product-price')
277            ).map(el => el.innerText.trim()).join(' ');
278            card.setAttribute('aria-label', label.replace(/\s+/g, ' '));
279        }
280
281        card.addEventListener('keydown', (e) => {
282            if (e.key === 'Enter' || e.key === ' ') {
283                e.preventDefault();
284                cardLink.click();
285            }
286        });
287
288        // Fokussierbare Textelemente
289        card.querySelectorAll('.product-title, .product-description, .product-price').forEach(el => {
290            el.setAttribute('tabindex', '0');
291            el.setAttribute('role', 'document');
292
293            if (!el.hasAttribute('aria-label')) {
294                const txt = el.innerText.trim();
295                if (txt) el.setAttribute('aria-label', txt);
296            }
297        });
298
299        // Aktionen: bleiben fokussierbar
300        card.querySelectorAll('.contact-share > button, .contact-mail > a, .contact-telephone > a').forEach(el => {
301            el.setAttribute('tabindex', '0');
302            el.setAttribute('role', 'button');
303
304            const label = el.getAttribute('title') || el.getAttribute('href') || el.innerText;
305            if (label && !el.hasAttribute('aria-label')) el.setAttribute('aria-label', label.trim());
306
307            el.querySelectorAll('svg, object').forEach(svg => svg.removeAttribute('tabindex'));
308        });
309
310        // Tooltip-Tags nicht fokussierbar
311        card.querySelectorAll('.tooltip-tags').forEach(tag => {
312            tag.removeAttribute('tabindex');
313            tag.setAttribute('aria-hidden', 'true');
314
315            const focusables = tag.querySelectorAll('svg, object, [tabindex]');
316            focusables.forEach(el => {
317                el.removeAttribute('tabindex');
318                el.setAttribute('aria-hidden', 'true');
319            });
320        });
321    });
322}
323
324function enhanceDestinationCardsAccessibility() {
325    const destinationCards = document.querySelectorAll('.et_pb_column.hover-img');
326
327    destinationCards.forEach(card => {
328        const textElement = card.querySelector('.et_pb_text_inner p');
329
330        if (textElement) {
331            const labelText = textElement.innerText.trim();
332
333            // Fokus auf die ganze Karte
334            card.setAttribute('tabindex', '0');
335            card.setAttribute('role', 'button');
336            card.setAttribute('aria-label', labelText);
337
338            // Event für Enter/Leertaste optional (wenn klickbar)
339            card.addEventListener('keydown', (e) => {
340                if (e.key === 'Enter' || e.key === ' ') {
341                    e.preventDefault();
342                    card.click(); // falls onclick vorhanden
343                }
344            });
345
346            // Verhindere doppelte Fokussierung
347            textElement.setAttribute('aria-hidden', 'true');
348            textElement.removeAttribute('tabindex');
349        }
350    });
351}
352
353
354
355function setupLightboxFocusHandling() {
356    // Alle Lightbox-Links erkennen
357    const lightboxLinks = document.querySelectorAll('[data-lightbox]');
358
359    lightboxLinks.forEach(link => {
360        link.addEventListener('click', () => {
361            // Fokus vor Öffnung speichern
362            lastFocusedBeforeLightbox = document.activeElement;
363        });
364    });
365
366    // ESC schließt Lightbox und setzt Fokus zurück
367    document.addEventListener('keydown', (e) => {
368        if (e.key === 'Escape') {
369            const openLightbox = document.querySelector('.lightbox, .fancybox-container, .lg-show');
370
371            // Prüfen ob eine bekannte Lightbox sichtbar ist
372            if (openLightbox && typeof lastFocusedBeforeLightbox?.focus === 'function') {
373                setTimeout(() => {
374                    lastFocusedBeforeLightbox.focus();
375                }, 300); // Warten bis Lightbox entfernt wurde
376            }
377        }
378    });
379}
380
381function enhanceDcbBoxes() {
382    // DCB Boxes
383    const dcbTagIcons = document.querySelectorAll('.smg_dynamic_content .hotel-icons .tooltip-tags');
384    const selectors = '.smg_dynamic_content .hotel-icons .tooltip-tags object,' +
385        '.smg_dynamic_content .product-card > a,' +
386        '.smg_dynamic_content .product-card .product-content > a,' +
387        '.smg_subpage_link';
388
389    // Product Card Issues in Firefox, resulting to being trapped
390    const wrongIndexes = document.querySelectorAll(selectors);
391
392    dcbTagIcons.forEach(el => {
393        // Tastaturfokus ermöglichen
394        if (!el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0');
395
396        // Rolle setzen
397        if (!el.hasAttribute('role')) el.setAttribute('role', 'img');
398
399        // aria-label setzen (für Screenreader)
400        if (!el.hasAttribute('aria-label')) {
401            const label =
402                el.getAttribute('alt') ||
403                el.getAttribute('title') ||
404                el.getAttribute('data-orginal-title') ||
405                el.innerText?.trim();
406            if (label) el.setAttribute('aria-label', label);
407        }
408
409        if (el.getAttribute('aria-hidden') == true) {
410            el.setAttribute('aria-hidden', false);
411        }
412    });
413
414    wrongIndexes.forEach(el => {
415        if (el.getAttribute('tabindex') === '0' || !el.hasAttribute('tabindex')) {
416            el.setAttribute('tabindex', '-1');
417        }
418    });
419}
420
421function enhanceSMGShortcodes() {
422    const elements = document.querySelectorAll('.contact-widget, .advantage-card-view');
423
424    elements.forEach(el => {
425        if (!el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0');
426    });
427}

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.