PageSourceSearch

https://zamek-sandomierz.pl/wp-content/themes/muzeum-child/assets/js/accessibility.js?ver=2.0.0

js zamek-sandomierz.pl collected 2026-10-02 10:55:42 UTC 10,589 bytes, 320 lines download raw bytes

1/**
2 * Muzeum Child - poprawki dostepnosci WCAG 2.1 AA.
3 *
4 * Zakres:
5 *  - naprawa hierarchii naglowkow w banerze (H3 -> span, H2 -> H2 lub H1)
6 *  - przycisk pauzy/odtwarzania dla karuzeli banera (WCAG 2.2.2)
7 *  - aria-hidden i tabindex="-1" na sklonowanych slajdach Owl (WCAG 4.1.2)
8 *  - aria-expanded toggling dla dropdown menu (WCAG 4.1.2, 2.1.1)
9 *  - poprawa atrybutow alt=Image / alt=logo (WCAG 1.1.1)
10 *  - poprawa href="tel:" i "mailto:" w stopce (jakosc danych)
11 *  - rel="noopener noreferrer" na linkach target=_blank
12 *  - aria-expanded na toggler'ach menu/wyszukiwarki
13 *  - usuniecie preloadera po zaladowaniu
14 */
15(function ($) {
16	'use strict';
17
18	var TEXT = (window.muzeumA11yL10n || {});
19	var t = function (key, fallback) {
20		return TEXT[key] || fallback;
21	};
22
23	/**
24	 * 1. Semantyczna korekta naglowkow w banerze (WCAG 1.3.1, 2.4.6).
25	 *    Tytul slajdu <h3> -> <span class="slide-tag">
26	 *    Glowne haslo <h2> pozostaje <h2> (jest juz <h1> screen-reader-text na stronie glownej)
27	 */
28	function fixBannerHeadings() {
29		$('.banner-section .slide-item').each(function () {
30			var $slide = $(this);
31			$slide.find('.content-box > h3').each(function () {
32				var $h3 = $(this);
33				var $span = $('<span class="slide-tag" />').html($h3.html());
34				$h3.replaceWith($span);
35			});
36		});
37	}
38
39	/**
40	 * 2. Przycisk pauzy/odtwarzania karuzeli banera (WCAG 2.2.2 Pause, Stop, Hide).
41	 */
42	function addBannerPauseControl() {
43		var $banner = $('.banner-carousel').first();
44		if (!$banner.length) {
45			return;
46		}
47
48		var playLabel  = t('play', 'Odtworz karuzele');
49		var pauseLabel = t('pause', 'Zatrzymaj karuzele');
50
51		var $controls = $('<div class="banner-carousel-controls" />');
52		var $btn = $(
53			'<button type="button" class="banner-carousel-toggle" aria-pressed="false">' +
54				'<span class="icon" aria-hidden="true"><i class="fa fa-pause"></i></span>' +
55				'<span class="screen-reader-text">' + pauseLabel + '</span>' +
56			'</button>'
57		);
58		$btn.attr('aria-label', pauseLabel);
59		$controls.append($btn);
60		$banner.closest('.banner-section').css('position', 'relative').append($controls);
61
62		var isPaused = false;
63
64		// Uszanuj prefers-reduced-motion: wylacz autoplay od razu.
65		if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
66			$banner.trigger('stop.owl.autoplay');
67			isPaused = true;
68			$btn.attr('aria-pressed', 'true')
69				.attr('aria-label', playLabel)
70				.find('.icon').html('<i class="fa fa-play"></i>');
71			$btn.find('.screen-reader-text').text(playLabel);
72		}
73
74		$btn.on('click', function () {
75			if (isPaused) {
76				$banner.trigger('play.owl.autoplay');
77				$btn.attr('aria-pressed', 'false')
78					.attr('aria-label', pauseLabel)
79					.find('.icon').html('<i class="fa fa-pause"></i>');
80				$btn.find('.screen-reader-text').text(pauseLabel);
81				isPaused = false;
82			} else {
83				$banner.trigger('stop.owl.autoplay');
84				$btn.attr('aria-pressed', 'true')
85					.attr('aria-label', playLabel)
86					.find('.icon').html('<i class="fa fa-play"></i>');
87				$btn.find('.screen-reader-text').text(playLabel);
88				isPaused = true;
89			}
90		});
91
92		// Zatrzymuj autoplay gdy uzytkownik wchodzi fokusem w slajd.
93		$banner.on('focusin', function () {
94			if (!isPaused) {
95				$banner.trigger('stop.owl.autoplay');
96			}
97		});
98		$banner.on('focusout', function () {
99			if (!isPaused) {
100				$banner.trigger('play.owl.autoplay');
101			}
102		});
103	}
104
105	/**
106	 * 3. Klony Owl carousel powinny byc ukryte dla AT (WCAG 4.1.2).
107	 */
108	function hideClonedCarouselSlides() {
109		$('.owl-carousel .owl-item.cloned').attr('aria-hidden', 'true');
110		$('.owl-carousel .owl-item.cloned a, .owl-carousel .owl-item.cloned button').attr('tabindex', '-1');
111
112		// Obsluguje takze dynamicznie zmieniane klony (Owl przebudowuje DOM).
113		if (window.MutationObserver) {
114			var targets = document.querySelectorAll('.owl-carousel');
115			targets.forEach(function (target) {
116				var obs = new MutationObserver(function () {
117					$(target).find('.owl-item.cloned').attr('aria-hidden', 'true');
118					$(target).find('.owl-item.cloned a, .owl-item.cloned button').attr('tabindex', '-1');
119				});
120				obs.observe(target, { childList: true, subtree: true });
121			});
122		}
123	}
124
125	/**
126	 * 4. Poprawa ogolnych atrybutow alt na stronie (WCAG 1.1.1).
127	 *    Zamieniamy wartosci typu "Image", "image", "logo" na sensowne
128	 *    albo na pusty alt (dla obrazow dekoracyjnych w tlach/ksztaltach).
129	 */
130	function fixGenericAltAttributes() {
131		$('img').each(function () {
132			var $img = $(this);
133			var alt = ($img.attr('alt') || '').trim();
134			var src = $img.attr('src') || '';
135
136			if (alt.toLowerCase() === 'image') {
137				// Decorative cues po sciezce: shape/bg/pattern/arrow/decor/separator/line
138				if (/shape|bg[-_]|pattern|arrow|decor|separator|line|dot|spark/i.test(src)) {
139					$img.attr('alt', '').attr('role', 'presentation');
140					return;
141				}
142				// Jesli obrazek jest wewnatrz linku ze znanym tytulem, uzyj go.
143				var $parentLink = $img.closest('a');
144				if ($parentLink.length) {
145					var title = ($parentLink.attr('title') || $parentLink.text() || '').trim();
146					if (title) {
147						$img.attr('alt', title);
148						return;
149					}
150				}
151				// Zaufany figcaption.
152				var $fig = $img.closest('figure').find('figcaption').first();
153				if ($fig.length && $fig.text().trim()) {
154					$img.attr('alt', $fig.text().trim());
155					return;
156				}
157				// Pusty alt -> traktuj jako dekoracyjny.
158				$img.attr('alt', '').attr('role', 'presentation');
159			} else if (alt.toLowerCase() === 'logo') {
160				$img.attr('alt', t('logo_alt', 'Muzeum Zamkowe w Sandomierzu - logo'));
161			}
162		});
163	}
164
165	/**
166	 * 5. aria-expanded toggling dla toggler'ow (menu, wyszukiwarka, submenu).
167	 */
168	function wireAriaExpandedToggles() {
169		// Side menu toggler (hamburger).
170		$('.side-menu__toggler:not(.side-menu__close-btn)').on('click.muzeumA11y', function () {
171			var $self = $(this);
172			var isActive = $('.side-menu__block').hasClass('active');
173			$self.attr('aria-expanded', isActive ? 'true' : 'false');
174		});
175		$('.side-menu__block-overlay, .side-menu__close-btn').on('click.muzeumA11y', function () {
176			$('.side-menu__toggler:not(.side-menu__close-btn)').attr('aria-expanded', 'false');
177		});
178
179		// Search popup toggler.
180		$('.search-popup__toggler').on('click.muzeumA11y', function () {
181			var isActive = $('.search-popup').hasClass('active');
182			$(this).attr('aria-expanded', isActive ? 'true' : 'false');
183			if (isActive) {
184				window.setTimeout(function () {
185					$('#site-search-popup').trigger('focus');
186				}, 50);
187			}
188		});
189		$('.search-popup__overlay').on('click.muzeumA11y', function () {
190			$('.search-popup__toggler').attr('aria-expanded', 'false');
191		});
192		// Zamykanie popupu klawiszem Escape.
193		$(document).on('keydown.muzeumA11y', function (e) {
194			if (e.key === 'Escape' || e.keyCode === 27) {
195				if ($('.search-popup').hasClass('active')) {
196					$('.search-popup').removeClass('active');
197					$('.search-popup__toggler').attr('aria-expanded', 'false').trigger('focus');
198				}
199				if ($('.side-menu__block').hasClass('active')) {
200					$('.side-menu__block').removeClass('active');
201					$('.side-menu__toggler:not(.side-menu__close-btn)').attr('aria-expanded', 'false').trigger('focus');
202				}
203			}
204		});
205
206		// Submenu togglers w menu glownym.
207		$('.main-nav-one .menu-item-has-children > a, .main-nav-one li.dropdown > a').each(function () {
208			var $link = $(this);
209			var $submenu = $link.siblings('ul').first();
210			if (!$submenu.length) return;
211
212			// Dodajemy aria-haspopup i aria-expanded.
213			$link.attr('aria-haspopup', 'true').attr('aria-expanded', 'false');
214
215			$link.on('keydown.muzeumA11y', function (e) {
216				if (e.key === 'Enter' || e.key === ' ' || e.keyCode === 13 || e.keyCode === 32) {
217					if ($link.attr('href') === '#' || !$link.attr('href')) {
218						e.preventDefault();
219					}
220					var expanded = $link.attr('aria-expanded') === 'true';
221					$link.attr('aria-expanded', expanded ? 'false' : 'true');
222				}
223			});
224
225			$link.parent().on('focusin.muzeumA11y mouseenter.muzeumA11y', function () {
226				$link.attr('aria-expanded', 'true');
227			});
228			$link.parent().on('focusout.muzeumA11y mouseleave.muzeumA11y', function () {
229				$link.attr('aria-expanded', 'false');
230			});
231		});
232	}
233
234	/**
235	 * 6. rel="noopener noreferrer" na wszystkich target="_blank" (bezpieczenstwo).
236	 */
237	function addNoopenerToExternalLinks() {
238		$('a[target="_blank"]').each(function () {
239			var $a = $(this);
240			var rel = ($a.attr('rel') || '').split(/\s+/);
241			var needed = ['noopener', 'noreferrer'];
242			needed.forEach(function (r) {
243				if (rel.indexOf(r) === -1) rel.push(r);
244			});
245			$a.attr('rel', rel.filter(Boolean).join(' ').trim());
246		});
247	}
248
249	/**
250	 * 7. Naprawa brednych tel: / mailto: w stopce.
251	 *    (Plugin widget uzywa danych z bazy; tu dokonujemy ostatecznej korekty.)
252	 */
253	function fixFooterContactLinks() {
254		// Widzialny tekst sluzy za zrodlo prawdy dla numeru telefonu.
255		$('a[href^="tel:"]').each(function () {
256			var $a = $(this);
257			var shown = $a.text().replace(/\s+/g, ' ').trim();
258			var digits = shown.replace(/[^\d+]/g, '');
259			if (digits.length >= 9) {
260				if (digits.charAt(0) !== '+' && digits.length === 9) {
261					digits = '+48' + digits;
262				}
263				$a.attr('href', 'tel:' + digits);
264			}
265		});
266
267		// mailto: -> uzyj widocznego adresu e-mail.
268		$('a[href^="mailto:"]').each(function () {
269			var $a = $(this);
270			var shown = $a.text().trim();
271			if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(shown)) {
272				$a.attr('href', 'mailto:' + shown);
273			}
274		});
275	}
276
277	/**
278	 * 8. Preloader nie moze blokowac czytnikow ekranu - usuwamy po load.
279	 */
280	function cleanupPreloader() {
281		$(window).on('load', function () {
282			window.setTimeout(function () {
283				$('.preloader').attr('aria-hidden', 'true').attr('aria-busy', 'false');
284			}, 100);
285		});
286	}
287
288	/**
289	 * 9. Gdy mapa iframe istnieje, upewnij sie ze ma title.
290	 */
291	function ensureMapTitle() {
292		$('iframe[src*="google.com/maps"], iframe[src*="maps.google"]').each(function () {
293			var $ifr = $(this);
294			if (!$ifr.attr('title')) {
295				$ifr.attr('title', t('map_title', 'Mapa dojazdu do Muzeum Zamkowego w Sandomierzu'));
296			}
297		});
298	}
299
300	$(function () {
301		fixBannerHeadings();
302		fixGenericAltAttributes();
303		wireAriaExpandedToggles();
304		addNoopenerToExternalLinks();
305		fixFooterContactLinks();
306		ensureMapTitle();
307		cleanupPreloader();
308	});
309
310	$(window).on('load', function () {
311		// Owl inicjalizuje sie na window.load - te poprawki musza byc pozniej.
312		window.setTimeout(function () {
313			addBannerPauseControl();
314			hideClonedCarouselSlides();
315			fixGenericAltAttributes();
316			addNoopenerToExternalLinks();
317		}, 200);
318	});
319
320})(jQuery);

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.