1/* 2 * Opravy pÅÃstupnosti podle auditu MIRRI SR (28.â31. 8. 2026). 3 * 4 * NaÄÃtá se za frontend.js (META-JS.php) a necompiluje se webpackem â build 5 * frontendu na souÄasném Node nejde spustit. PÅi upgradu stacku patÅà do 6 * app/assets/js. ES5 + jQuery (globálnà z frontend.js). 7 */ 8(function ($) { 9 'use strict'; 10 11 var MOBILE = '(max-width: 767px)'; 12 13 function isMobile() { 14 return window.matchMedia && window.matchMedia(MOBILE).matches; 15 } 16 17 function isHovered(el) { 18 try { 19 return el.matches ? el.matches(':hover') : false; 20 } catch (e) { 21 return false; 22 } 23 } 24 25 function isEscape(e) { 26 return e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27; 27 } 28 29 /* 30 * Hlavnà menu â KS 2.1.1, 2.5.1, 1.4.13. 31 * 32 * Podmenu se dosud otevÃralo jen pÅes CSS :hover: klávesnicà neÅ¡lo otevÅÃt, 33 * neÅ¡lo zavÅÃt jinak než odjetÃm myšà a pÅi vychýlenà z dráhy se hned zavÅelo. 34 * Každá rozbalovacà položka teÄ dostane tlaÄÃtko (aria-expanded), které 35 * podmenu otevÃrá klikem i klávesnicÃ; Esc zavÃrá. Myšà zůstává otevÃránà 36 * najetÃm, ale se zpoždÄnÃm pÅi odjetà a s možnostà podmenu klikem pÅipnout. 37 */ 38 function initMenu() { 39 var $menu = $('#menu'); 40 var CLOSE_DELAY = 400; 41 var $items; 42 43 if (!$menu.length) { 44 return; 45 } 46 47 $items = $menu.children('li.dropdown').filter(function () { 48 return $(this).children('.dropdown-menu').length > 0; 49 }); 50 51 $items.each(function (i) { 52 var $li = $(this); 53 var $link = $li.children('a').first(); 54 var $sub = $li.children('.dropdown-menu').first(); 55 var id = $sub.attr('id') || ('podmenu-' + (i + 1)); 56 57 $sub.attr('id', id); 58 $link.after( 59 $('<button type="button" class="submenu-toggle" aria-expanded="false"></button>') 60 .attr('aria-controls', id) 61 .append($('<span class="sr-only"></span>').text('Podmenu: ' + $.trim($link.text()))) 62 ); 63 }); 64 65 // Styly v a11y.css vypÃnajà CSS :hover jen pod touto tÅÃdou â bez JS 66 // zůstane menu fungovat postaru. 67 $menu.addClass('a11y-menu'); 68 69 // Na mobilu rozbaloval podmenu klik na nadÅazený odkaz (functions.js). 70 // Tuto roli teÄ má tlaÄÃtko, odkaz normálnÄ vede na svou stránku. 71 // Platà i pro li.dropdown bez vypsaného podmenu (MENU.php dává tÅÃdu 72 // i položce, jejÞ podpoložky jsou vÅ¡echny skryté) â jinak by handler 73 // s return false nechal odkaz na mobilu nefunkÄnÃ. 74 $menu.children('li.dropdown').children('a').off('click'); 75 76 function setOpen($li, open, via) { 77 clearTimeout($li.data('a11yTimer')); 78 $li.toggleClass('is-open', open) 79 .data('a11yVia', open ? via : null) 80 .children('.submenu-toggle').attr('aria-expanded', open ? 'true' : 'false'); 81 if (open) { 82 placeDropdown($li); 83 } 84 } 85 86 // KS 1.4.4, 1.4.10, 1.4.12: pÅi pÅiblÞenà nebo vÄtÅ¡Ãch rozestupech podmenu 87 // (hlavnÄ âO divadle" s dlouhými položkami) vyjÞdÄlo vpravo mimo obrazovku. 88 // NejdÅÃv ho posunout doleva; teprve když je Å¡iršà než obrazovka, omezit 89 // Å¡ÃÅku a nechat položky zalomit. U fixnà hlaviÄky omezit i výšku, jinak 90 // by se spodek podmenu nedal doscrollovat. 91 function placeDropdown($li) { 92 var ul = $li.children('.dropdown-menu')[0]; 93 var gap = 8; 94 var vw, r, li, avail; 95 96 if (!ul) { 97 return; 98 } 99 ul.style.left = ''; 100 ul.style.maxWidth = ''; 101 ul.style.maxHeight = ''; 102 $(ul).removeClass('is-constrained'); 103 if (isMobile()) { 104 return; 105 } 106 vw = document.documentElement.clientWidth; 107 li = $li[0].getBoundingClientRect(); 108 r = ul.getBoundingClientRect(); 109 if (r.width > vw - 2 * gap) { 110 $(ul).addClass('is-constrained'); 111 ul.style.maxWidth = (vw - 2 * gap) + 'px'; 112 r = ul.getBoundingClientRect(); 113 } 114 if (r.right > vw - gap) {
115 ul.style.left = Math.round(vw - gap - r.width - li.left) + 'px'; 116 r = ul.getBoundingClientRect(); 117 } 118 if (r.left < gap) { 119 ul.style.left = Math.round(gap - li.left) + 'px'; 120 r = ul.getBoundingClientRect(); 121 } 122 if ($('#pageHeader').hasClass('fixed')) { 123 avail = window.innerHeight - r.top - gap; 124 if (r.height > avail) { 125 $(ul).addClass('is-constrained'); 126 ul.style.maxHeight = Math.max(120, Math.floor(avail)) + 'px'; 127 } 128 } 129 } 130 131 $(window).on('resize', function () { 132 $items.filter('.is-open').each(function () { 133 placeDropdown($(this)); 134 }); 135 }); 136 137 function closeAll($except) { 138 $items.not($except || $()).filter('.is-open').each(function () { 139 setOpen($(this), false); 140 }); 141 } 142 143 $items.on('click', '.submenu-toggle', function () { 144 var $li = $(this).closest('li'); 145 146 // OtevÅeno najetÃm myšà (a na dotykovém zaÅÃzenà i tapem) â klik 147 // podmenu pÅipne, nezavÅe. 148 if ($li.hasClass('is-open') && $li.data('a11yVia') === 'hover') { 149 $li.data('a11yVia', 'click'); 150 return; 151 } 152 closeAll($li); 153 setOpen($li, !$li.hasClass('is-open'), 'click'); 154 }); 155 156 $items.on('mouseenter', function () { 157 var $li = $(this); 158 159 if (isMobile()) { 160 return; 161 } 162 clearTimeout($li.data('a11yTimer')); 163 if ($li.data('a11yDismissed') || $li.hasClass('is-open')) { 164 return; 165 } 166 closeAll($li); 167 setOpen($li, true, 'hover'); 168 }); 169 170 $items.on('mouseleave', function () { 171 var $li = $(this); 172 173 $li.data('a11yDismissed', false); 174 if (isMobile() || $li.data('a11yVia') !== 'hover') { 175 return; 176 } 177 $li.data('a11yTimer', setTimeout(function () { 178 setOpen($li, false); 179 }, CLOSE_DELAY)); 180 }); 181 182 // ZamÄÅenà odeÅ¡lo mimo položku â zavÅÃt (kromÄ otevÅenà myÅ¡Ã, to Åešà mouseleave). 183 $items.on('focusout', function () { 184 var li = this; 185 186 setTimeout(function () { 187 var $li = $(li); 188 if ($li.hasClass('is-open') && $li.data('a11yVia') !== 'hover' && !$.contains(li, document.activeElement)) { 189 setOpen($li, false); 190 } 191 }, 0); 192 }); 193 194 $(document).on('keydown', function (e) { 195 if (!isEscape(e)) { 196 return; 197 } 198 $items.filter('.is-open').each(function () { 199 var $li = $(this); 200 var hadFocus = $.contains(this, document.activeElement); 201 202 setOpen($li, false); 203 // Zůstává-li kurzor nad položkou, nesmà se hned zase otevÅÃt. 204 $li.data('a11yDismissed', isHovered(this)); 205 if (hadFocus) { 206 $li.children('.submenu-toggle').focus(); 207 } 208 }); 209 }); 210 211 $(document).on('click', function (e) { 212 if (!$(e.target).closest('#menu').length) { 213 closeAll(); 214 } 215 }); 216 217 if (window.matchMedia) { 218 var mq = window.matchMedia(MOBILE); 219 var onChange = function () { closeAll(); }; 220 if (mq.addEventListener) { 221 mq.addEventListener('change', onChange); 222 } else if (mq.addListener) { 223 mq.addListener(onChange); 224 } 225 } 226 } 227 228 /* 229 * Hamburger tlaÄÃtko â KS 4.1.2. Stav otevÅenà se oznamuje pÅes aria-expanded 230 * (název je stálý âHlavné menu", viz LOGO.php). Menu pÅepÃná functions.js, 231 * tady se jen po jeho handleru synchronizuje stav. 232 */ 233 function initNavToggle() { 234 var $btn = $('.navbar-toggle');
235 var $nav = $('#pageHeader .navbar'); 236 237 if (!$btn.length || !$nav.length) { 238 return; 239 } 240 $btn.attr('aria-expanded', $nav.is(':visible') && isMobile() ? 'true' : 'false'); 241 $btn.on('click', function () { 242 $btn.attr('aria-expanded', $nav.is(':visible') ? 'true' : 'false'); 243 }); 244 } 245 246 /* 247 * Odsazenà obsahu pod hlaviÄkou â KS 1.4.4, 1.4.12. 248 * HlaviÄka je position: absolute a obsah odsazuje pevný padding-top <body> 249 * (97 px). Když hlaviÄka pÅi zvÄtÅ¡enà textu nebo rozestupů naroste (menu se 250 * zalomÃ), pÅikryje drobeÄkovou navigaci i nadpis stránky â nález auditu 251 * na /subor. Odsazenà teÄ sleduje skuteÄnou výšku hlaviÄky; jen na 252 * podstránkách (na úvodnà stránce ležà hlaviÄka zámÄrnÄ pÅes carousel) 253 * a mimo kompaktnà režim (.fixed), kdy hlaviÄka z toku vypadne. 254 * Na mobilu je hlaviÄka statická, v toku stránky: tam se neodsazuje nic, 255 * jinak by padding odsunul dolů i hlaviÄku a po rozbalenà hamburgeru by 256 * nad nà zůstala bÃlá plocha o výšce celého menu. 257 */ 258 function initHeaderOffset() { 259 var header = document.getElementById('pageHeader'); 260 var body = document.body; 261 262 if (!header || body.id !== 'subpage') { 263 return; 264 } 265 266 function update() { 267 var fromCss, height, position; 268 269 if ($(header).hasClass('fixed')) { 270 return; 271 } 272 body.style.paddingTop = ''; 273 position = window.getComputedStyle(header).position; 274 if (position !== 'absolute' && position !== 'fixed') { 275 return; 276 } 277 fromCss = parseFloat(window.getComputedStyle(body).paddingTop) || 0; 278 height = header.offsetHeight; 279 if (height > fromCss + 1) { 280 body.style.paddingTop = height + 'px'; 281 } 282 } 283 284 if (window.ResizeObserver) { 285 new window.ResizeObserver(update).observe(header); 286 } 287 $(window).on('resize load', update); 288 update(); 289 } 290 291 /* 292 * Kompaktnà hlaviÄka â KS 2.4.3, 2.4.7. 293 * Po odscrollovánà je hlaviÄka fixnà a schovaná nad obrazovkou (top: -100px); 294 * vyjede jen pÅi scrollu nahoru (functions.js, hideNav). PÅijde-li do nà 295 * zamÄÅenà klávesnicÃ, musà vyjet, jinak je zamÄÅený prvek neviditelný. 296 */ 297 function initHeaderFocus() { 298 var $header = $('#pageHeader'); 299 300 $header.on('focusin', function () { 301 if ($header.hasClass('fixed') && !$header.hasClass('showNav')) { 302 $header.addClass('showNav').removeClass('hideNav'); 303 } 304 }); 305 } 306 307 /* 308 * MÄsÃÄnà posuvnÃk v programu â KS 2.4.3, 2.1.1, 4.1.2. 309 * V poÅadà Tabu bylo vÅ¡ech ~130 mÄsÃců od roku 2015, i když vÄtÅ¡ina ležà 310 * mimo viditelnou Äást posuvnÃku; Å¡ipky byly <a> bez href a bez textu, 311 * takže klávesnicà neÅ¡ly vůbec. PosuvnÃk se pÅi zmÄnÄ mÄsÃce pÅestavuje 312 * (functions.js, programMonthsSlider), proto se úpravy aplikujà znovu 313 * po každé pÅestavbÄ. 314 */ 315 function initProgramMonths() { 316 var box = document.getElementById('contentBox'); 317 var focusedMonth = null; 318 var pending; 319 320 if (!box || !document.getElementById('programMonths')) { 321 return; 322 } 323 324 // Tab jen na mÄsÃce ve viditelné Äásti posuvnÃku. 325 function syncFocusable() { 326 var $slider = $('#programMonths'); 327 var inner = $slider.find('.inner')[0]; 328 var frame; 329 330 if (!inner) { 331 return; 332 } 333 frame = inner.getBoundingClientRect(); 334 $slider.find('.item').each(function () { 335 var r = this.getBoundingClientRect(); 336 var visible = r.width > 0 && r.left >= frame.left - 1 && r.right <= frame.right + 1; 337 if (visible) { 338 this.removeAttribute('tabindex'); 339 } else { 340 this.setAttribute('tabindex', '-1'); 341 } 342 }); 343 $slider.find('.navigation').each(function () { 344 $(this).attr('aria-disabled', $(this).hasClass('disabled') ? 'true' : 'false'); 345 }); 346 } 347 348 function enhance() { 349 var $slider = $('#programMonths'); 350 var api = $slider.data('scrollable'); 351 var $active; 352 353 // PÅepoÄet po každém posunu. onSeek pÅijde až po dojetà animace; 354 // api vzniká znovu pÅi každé pÅestavbÄ posuvnÃku. 355 if (api && api.onSeek && !api.a11yBound) { 356 api.onSeek(syncFocusable); 357 api.a11yBound = true; 358 } 359 360 $slider.find('.navigation.prev').attr({ role: 'button', tabindex: '0', 'aria-label': 'Predchádzajúce mesiace' }); 361 $slider.find('.navigation.next').attr({ role: 'button', tabindex: '0', 'aria-label': 'Nasledujúce mesiace' }); 362 $slider.find('.item').removeAttr('aria-current'); 363 $active = $slider.find('.item.active').attr('aria-current', 'true'); 364 syncFocusable(); 365 366 // PÅestavba posuvnÃku zahodà zamÄÅený mÄsÃc a zamÄÅenà spadne na body â 367 // vrátit ho na stejný (nebo aktivnÃ) mÄsÃc. Jen v tomto pÅÃpadÄ, jinak 368 // by jiné AJAXové zmÄny v #contentBox zamÄÅenà kradly. 369 if (focusedMonth && !document.body.contains(focusedMonth) && 370 (!document.activeElement || document.activeElement === document.body)) { 371 var target = $slider.find('.item[data-month="' + $(focusedMonth).attr('data-month') + '"]')[0] || $active[0]; 372 focusedMonth = null; 373 if (target) {
374 target.focus(); 375 } 376 } 377 } 378 379 $(document).on('keydown', '#programMonths .navigation', function (e) { 380 if (e.key === 'Enter' || e.key === ' ' || e.keyCode === 13 || e.keyCode === 32) { 381 e.preventDefault(); 382 $(this).trigger('click'); 383 } 384 }); 385 $(document).on('focusin', function (e) { 386 focusedMonth = $(e.target).is('#programMonths .item') ? e.target : null; 387 }); 388 $(window).on('resize', function () { 389 setTimeout(syncFocusable, 100); 390 }); 391 392 if (window.MutationObserver) { 393 new MutationObserver(function () { 394 clearTimeout(pending); 395 pending = setTimeout(enhance, 50); 396 }).observe(box, { childList: true, subtree: true }); 397 } 398 // functions.js posune posuvnÃk na aktivnà mÄsÃc až po naÄtenÃ. 399 setTimeout(enhance, 500); 400 enhance(); 401 } 402 403 /* 404 * Programový filtr v postrannÃm panelu â KS 2.4.3, 2.4.7, 4.1.2, 3.3.2. 405 * Odkazy âŽáner", âAutor"⦠otevÃrajà panel, který je v zavÅeném stavu jen 406 * .sr-only: vizuálnÄ skrytý, ale jeho ovládacà prvky zůstávaly v poÅadà 407 * Tabu. ZavÅené panely jsou teÄ inert, odkaz nese aria-expanded, kÅÞek 408 * je tlaÄÃtko a Esc panel zavÅe. Stav pÅepÃná functions.js tÅÃdami, 409 * tady se jen synchronizuje. 410 */ 411 function initProgramFilter() { 412 var $menu = $('#programMenu'); 413 var $items = $menu.find('> .list > .item').filter(function () { 414 return $(this).children('.programMenu').length > 0; 415 }); 416 417 if (!$items.length) { 418 return; 419 } 420 421 function sync() { 422 $items.each(function () { 423 var $item = $(this); 424 var $panel = $item.children('.programMenu'); 425 var open = !$panel.hasClass('sr-only'); 426 427 $item.children('a').attr('aria-expanded', open ? 'true' : 'false'); 428 if (open) { 429 $panel.removeAttr('inert'); 430 } else { 431 // functions.js zavÃrá panel i sám po výbÄru hodnoty â zamÄÅenà 432 // uvnitÅ by s inert spadlo na <body>, vrátit ho na odkaz filtru. 433 if ($.contains($panel[0], document.activeElement)) { 434 $item.children('a').focus(); 435 } 436 $panel.attr('inert', ''); 437 } 438 }); 439 } 440 441 $items.each(function (i) { 442 var $item = $(this); 443 var $link = $item.children('a'); 444 var $panel = $item.children('.programMenu'); 445 var id = $panel.attr('id') || ('programovy-filter-' + (i + 1)); 446 447 $panel.attr('id', id); 448 $link.attr('aria-controls', id); 449 $panel.find('.closeBtn').attr({ role: 'button', tabindex: '0', 'aria-label': 'ZavrieÅ¥' }); 450 // Pole pluginu chosen (autor, herec) nemajà popisek â název filtru. 451 $panel.find('.chosen-container input, select').attr('aria-label', $.trim($link.text())); 452 }); 453 454 $menu.on('keydown', '.closeBtn', function (e) { 455 if (e.key === 'Enter' || e.key === ' ' || e.keyCode === 13 || e.keyCode === 32) { 456 e.preventDefault(); 457 var $link = $(this).closest('.item').children('a'); 458 $(this).trigger('click'); 459 $link.focus(); 460 } 461 }); 462 463 $menu.on('keydown', '.programMenu', function (e) { 464 if (!isEscape(e)) { 465 return; 466 } 467 var $item = $(this).closest('.item'); 468 // Esc v otevÅeném seznamu chosen zavÅe nejdÅÃv seznam. 469 if ($(this).find('.chosen-with-drop').length) { 470 return; 471 } 472 $(this).find('.closeBtn').trigger('click'); 473 $item.children('a').focus(); 474 }); 475 476 if (window.MutationObserver) { 477 new MutationObserver(sync).observe($menu[0], { attributes: true, attributeFilter: ['class'], subtree: true }); 478 } 479 sync(); 480 481 // Mobilnà pÅepÃnaÄ celého filtru (⤠991 px). 482 var $toggle = $('#programMenu-toggle'); 483 if ($toggle.length) { 484 $toggle.attr({ 'aria-controls': 'programMenu', role: 'button' }); 485 var syncToggle = function () { 486 $toggle.attr('aria-expanded', $menu.is(':visible') ? 'true' : 'false'); 487 }; 488 $toggle.on('click', function () { setTimeout(syncToggle, 0); });
489 syncToggle(); 490 } 491 } 492 493 /* 494 * Ãvodnà carousel â KS 2.1.1, 4.1.2. 495 * Náhledy pod bannerem pÅepÃnajà prezentaci, ale byly to <div>y ovladatelné 496 * jen myšà (slick, focusOnSelect). Viditelné náhledy jsou teÄ tlaÄÃtka 497 * s názvem podujatà (data-title z HOMEPAGE-CAROUSEL.php) a aria-current. 498 * Slick pÅi každém posunu pÅepisuje tabindex/aria-hidden, proto se stav 499 * obnovuje po jeho událostech. Klony z nekoneÄného režimu zůstávajà skryté. 500 */ 501 function initCarousel() { 502 var $main = $('#hpCarousel'); 503 var $nav = $('#carouselNav .inner'); 504 505 if (!$main.length || !$nav.length || !$nav.hasClass('slick-initialized')) { 506 return; 507 } 508 509 $('#carouselNav').attr({ role: 'group', 'aria-label': 'Ãvodná prezentácia' }); 510 511 function sync() { 512 var current = $main.find('.slick-current').attr('data-slick-index'); 513 514 $nav.find('.slick-slide').each(function () { 515 var $slide = $(this); 516 var real = !$slide.hasClass('slick-cloned'); 517 // slick 1.8 obaluje každý náhled dalÅ¡Ãmi <div>, data-title je uvnitÅ 518 var title = $slide.attr('data-title') || $slide.find('[data-title]').first().attr('data-title') || 519 $.trim($main.find('.slick-slide[data-slick-index="' + $slide.attr('data-slick-index') + '"] .title').text()); 520 521 if (!real) { 522 $slide.attr({ 'aria-hidden': 'true', tabindex: '-1' }).removeAttr('role aria-label aria-current'); 523 return; 524 } 525 $slide.attr({ role: 'button', 'aria-label': title }) 526 .attr('tabindex', $slide.hasClass('slick-active') ? '0' : '-1') 527 .attr('aria-hidden', $slide.hasClass('slick-active') ? 'false' : 'true'); 528 if ($slide.attr('data-slick-index') === current) { 529 $slide.attr('aria-current', 'true'); 530 } else { 531 $slide.removeAttr('aria-current'); 532 } 533 }); 534 } 535 536 $nav.on('keydown', '.slick-slide[role="button"]', function (e) { 537 if (e.key === 'Enter' || e.key === ' ' || e.keyCode === 13 || e.keyCode === 32) { 538 e.preventDefault(); 539 $main.slick('slickGoTo', parseInt($(this).attr('data-slick-index'), 10)); 540 } 541 }); 542 $main.add($nav).on('afterChange setPosition', function () { 543 setTimeout(sync, 0); 544 }); 545 sync(); 546 } 547 548 /* 549 * Záložky (jquery.tools tabs: detail inscenace, a v obsahu z CMS napÅ. 550 * vÄrnostnà karta) â KS 4.1.2, 2.4.3. 551 * Záložky nemÄly roli ani stav, a zamÄÅenà po otevÅenà záložky Å¡lo nejdÅÃv 552 * pÅes ostatnà záložky a až pak do obsahu. Vzor ARIA tabs: v Tabu je jen 553 * vybraná záložka, mezi záložkami se pÅecházà šipkami (a Home/End), 554 * dalšà Tab vede do obsahu vybrané záložky. PÅepÃnánà dál dÄlá plugin. 555 */ 556 function initTabs() { 557 $('ul.tabs').each(function (g) { 558 var $list = $(this); 559 var $tabs = $list.find('> li > a'); 560 var $panes = $list.nextAll('.panes').first().children('div'); 561 562 // Panelů může být vÃc než záložek (paneVidea se renderuje vždy). 563 if (!$tabs.length || $panes.length < $tabs.length) { 564 return; 565 } 566 567 // Záložka -> panel: podle href="#id", jinak podle poÅadà (jako plugin). 568 function paneOf(i) { 569 var href = $tabs.eq(i).attr('href') || ''; 570 var $byId = href.length > 1 && href.charAt(0) === '#' ? $panes.filter(href) : $(); 571 return $byId.length ? $byId : $panes.eq(i); 572 } 573 574 $list.attr('role', 'tablist').children('li').attr('role', 'presentation'); 575 $tabs.each(function (i) { 576 var $pane = paneOf(i); 577 var paneId = $pane.attr('id') || ('zalozka-' + (g + 1) + '-' + (i + 1)); 578 var tabId = 'tab-' + paneId; 579 580 $pane.attr({ id: paneId, role: 'tabpanel', 'aria-labelledby': tabId }); 581 $(this).attr({ id: tabId, role: 'tab', 'aria-controls': paneId }); 582 }); 583 584 function sync() { 585 $tabs.each(function (i) { 586 var selected = $(this).hasClass('active') || $(this).hasClass('current'); 587 var $pane = paneOf(i); 588 var needsFocus = selected && $.trim($pane.text()) !== '' && 589 !$pane.find('a[href], button, input, select, textarea, [tabindex="0"]').length; 590 591 $(this).attr({ 'aria-selected': selected ? 'true' : 'false', tabindex: selected ? '0' : '-1' }); 592 // Vybraný panel jen s textem musà jÃt zamÄÅit sám, aby na nÄj Tab 593 // ze záložky dosáhl. Prázdný nebo nevybraný panel ne. 594 if (needsFocus) { 595 $pane.attr('tabindex', '0'); 596 } else { 597 $pane.removeAttr('tabindex'); 598 } 599 }); 600 } 601 602 $list.on('keydown', '[role="tab"]', function (e) { 603 var i = $tabs.index(this); 604 var next = null; 605 606 if (e.key === 'ArrowRight' || e.keyCode === 39) { 607 next = (i + 1) % $tabs.length; 608 } else if (e.key === 'ArrowLeft' || e.keyCode === 37) { 609 next = (i - 1 + $tabs.length) % $tabs.length; 610 } else if (e.key === 'Home' || e.keyCode === 36) { 611 next = 0; 612 } else if (e.key === 'End' || e.keyCode === 35) { 613 next = $tabs.length - 1; 614 } else if (e.key === ' ' || e.keyCode === 32) { 615 e.preventDefault(); 616 $(this).trigger('click'); 617 return; 618 } 619 if (next !== null) { 620 e.preventDefault(); 621 $tabs.eq(next).trigger('click').focus(); 622 } 623 }); 624 625 if (window.MutationObserver) { 626 new MutationObserver(sync).observe(this, { attributes: true, attributeFilter: ['class'], subtree: true }); 627 } 628 sync(); 629 }); 630 } 631 632 /* 633 * Kategorie vyhledávánà (predstavenie / ľudia / ostatné) â KS 4.1.2. 634 * Aktivnà kategorie byla oznaÄená jen tÅÃdou. Nejde o záložky s panely, 635 * ale o pÅepÃnaÄ, který mÄnà výsledky (a u #listSearchFilter odeÅ¡le 636 * hledánÃ), proto aria-current, ne role="tab". 637 */ 638 function initSearchCategories() { 639 var $lists = $('#listSearchFilter .list, #searchFilter .category .list'); 640 641 function sync() { 642 $lists.find('.item').each(function () { 643 var $a = $(this).children('a'); 644 if ($(this).hasClass('active')) { 645 $a.attr('aria-current', 'true'); 646 } else { 647 $a.removeAttr('aria-current'); 648 } 649 }); 650 } 651 652 if (!$lists.length) { 653 return; 654 } 655 // Pojmenovat viditelným nadpisem (âOblasÅ¥ hľadania"), kde je. 656 $lists.each(function (i) { 657 var $title = $(this).siblings('.title').first(); 658 if ($title.length) { 659 $title.attr('id', $title.attr('id') || ('kategoria-hladania-' + (i + 1))); 660 $(this).attr('aria-labelledby', $title.attr('id')); 661 } else { 662 $(this).attr('aria-label', 'Kategória vyhľadávania'); 663 } 664 }); 665 $lists.on('click', 'a', function () { 666 setTimeout(sync, 0); 667 }); 668 sync(); 669 } 670 671 /*
672 * Newsletter â KS 3.3.3 (návrh opravy chyby), 3.3.1. 673 * ProhlÞeÄ hlásil chybu jen bublinou, která zmizÃ, a server obecnou 674 * hláškou bez návodu. PÅi neplatné adrese se odeslánà zastavà a pod polem 675 * zůstane text s návodem a vzorem tvaru, propojený pÅes aria-describedby. 676 */ 677 function initNewsletter() { 678 var $form = $('#newsletter'); 679 var $input = $('#newsletter-email'); 680 var $error = $('#newsletter-email-error'); 681 var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@.]{2,}$/; 682 683 if (!$form.length || !$input.length || !$error.length) { 684 return; 685 } 686 $form.attr('novalidate', 'novalidate'); 687 688 function message(value) { 689 if ($.trim(value) === '') { 690 return 'Zadajte e-mailovú adresu, naprÃklad [email protected].'; 691 } 692 if ((value.match(/@/g) || []).length !== 1) { 693 return 'E-mailová adresa musà obsahovaÅ¥ práve jeden znak @, naprÃklad [email protected].'; 694 } 695 if (!EMAIL.test($.trim(value))) { 696 return 'Za znakom @ musà byÅ¥ názov domény s bodkou, naprÃklad [email protected].'; 697 } 698 return ''; 699 } 700 701 function show(text) { 702 if (text) { 703 $error.text(text).removeAttr('hidden'); 704 $input.attr({ 'aria-invalid': 'true', 'aria-describedby': 'newsletter-email-error' }); 705 } else { 706 $error.text('').attr('hidden', 'hidden'); 707 $input.removeAttr('aria-invalid aria-describedby'); 708 } 709 } 710 711 $form.on('submit', function (e) { 712 var text = message($input.val()); 713 show(text); 714 if (text) { 715 e.preventDefault(); 716 $input.focus(); 717 } 718 }); 719 // Po opravÄ chybu hned schovat. 720 $input.on('input', function () { 721 if ($input.attr('aria-invalid') === 'true' && !message($input.val())) { 722 show(''); 723 } 724 }); 725 } 726 727 /* 728 * Fotogalerie (fancybox 2) â KS 3.1.2, 4.1.2. 729 * Ovládacà prvky mÄly anglické titulky âPrevious", âNext", âClose" na 730 * slovenském webu; ÄteÄka je Äetla slovenskou výslovnostÃ. Výchozà šablony 731 * se pÅepÚà pÅed otevÅenÃm galerie â fancybox je Äte až pÅi otevÅenÃ. 732 */ 733 function initLightbox() { 734 var tpl = $.fancybox && $.fancybox.defaults && $.fancybox.defaults.tpl; 735 736 if (!tpl) { 737 return; 738 } 739 tpl.closeBtn = '<a title="ZatvoriÅ¥" aria-label="ZatvoriÅ¥ fotografiu" role="button" class="fancybox-item fancybox-close" href="javascript:;"></a>'; 740 tpl.next = '<a title="Nasledujúca" aria-label="Nasledujúca fotografia" role="button" class="fancybox-nav fancybox-next" href="javascript:;"><span></span></a>'; 741 tpl.prev = '<a title="Predchádzajúca" aria-label="Predchádzajúca fotografia" role="button" class="fancybox-nav fancybox-prev" href="javascript:;"><span></span></a>'; 742 tpl.error = '<p class="fancybox-error">Obsah sa nepodarilo naÄÃtaÅ¥. Skúste to, prosÃm, neskôr.</p>'; 743 } 744 745 $(function () { 746 initMenu(); 747 initLightbox(); 748 initNewsletter(); 749 initNavToggle(); 750 initHeaderFocus(); 751 initHeaderOffset(); 752 initProgramMonths(); 753 initProgramFilter(); 754 initCarousel(); 755 initTabs(); 756 initSearchCategories(); 757 }); 758})(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.