1/*** VARIABLES ***/ 2let xs_bkp = getComputedStyle(document.documentElement).getPropertyValue('--xs') || 320, 3sm_bkp = getComputedStyle(document.documentElement).getPropertyValue('--sm') || 576, 4md_bkp = getComputedStyle(document.documentElement).getPropertyValue('--md') || 768, 5lg_bkp = getComputedStyle(document.documentElement).getPropertyValue('--lg') || 992, 6xl_bkp = getComputedStyle(document.documentElement).getPropertyValue('--xl') || 1200, 7 8// Get the header 9headerElem = document.querySelector('.header-basic'), 10navElem = document.querySelector('.nav-basic'), 11navWrapperElem = $(navElem).find('.nav-menu-wrapper'), 12navTitle = document.querySelector('.nav-title'), 13navAdmin = $('.control-menu-container'), 14navAdminHeight = 0, 15sticky; 16 17// Get the offset position of the navbar 18if(!!$(navElem).length){ 19 if(!!$(navAdmin).length){ 20 navAdminHeight = (navAdmin.outerHeight() * 2); 21 } 22 sticky = (window.innerWidth < md_bkp) ? navElem.offsetTop - navAdminHeight : 0; 23} 24 25function clickOutHideHeaderCollapse(e){ 26 if(!!$(headerElem).length){ 27 let targetElem = $('.header-basic .collapse.show'); 28 29 if ( !($(e.target).parents('.header-basic').length && ($(e.target).parents('.collapse.show').length))) 30 { 31 targetElem.collapse("hide"); 32 if(!!$(navElem).length && !!$(navWrapperElem).length) displayOverlay(e); 33 } 34 } 35} 36 37// Add the sticky class to the header when you reach its scroll position. Remove "sticky" when you leave the scroll position 38 39function fixedHeader() { 40 if(sticky != undefined){ 41 if ((window.innerWidth < Number(md_bkp)) && (window.scrollY > sticky)) { 42 $("body").css("padding-top", (sticky + navElem.offsetHeight)); 43 document.body.classList.add('sticky'); 44 } else { 45 $("body").css("padding-top", ""); 46 document.body.classList.remove("sticky"); 47 } 48 } 49} 50 51function displayOverlay(e) { 52 if($('body').hasClass("menu-opened") ){ 53 $("body").removeClass("menu-opened"); 54 $('.nav-menu-wrapper').css('max-height', 'none'); 55 }else{ 56 let heightSignedIn = 0; 57 if($('body').hasClass("has-control-menu") ){ 58 heightSignedIn = parseInt($("#wrapper").css("marginTop").replace("px", "")); 59 } 60 let menuBarHeight = navElem.offsetHeight - document.querySelector('.nav-menu-wrapper').offsetHeight, 61 scrolling = (window.scrollY < sticky) ? navElem.offsetTop - window.scrollY : 0, 62 maxHeightMenu = window.innerHeight - (scrolling + menuBarHeight) - heightSignedIn; 63 $('.nav-menu-wrapper').css('max-height', maxHeightMenu); 64 if(e.target.classList.contains('nav-button')) $("body").addClass("menu-opened"); 65 } 66} 67 68function stopAction(event) { 69 if(($(document).width() < md_bkp)){ 70 event.stopPropagation(); 71 } 72} 73 74 75function focusableElemsOff(){ 76 $('[tabindex]').each(function(e){ 77 let tabVal = $(this).attr('tabindex'); 78 let dataTabindex = $(this).attr('data-tabindex'); 79 80 if (typeof dataTabindex == typeof undefined){ 81 $(this).attr('data-tabindex',tabVal); 82 } 83 84 }); 85 86 $('a, button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])').attr("tabindex", "-1"); 87} 88 89function focusableElemsOn(){ 90 $('[tabindex]').each(function(e){ 91 let tabVal = $(this).data('tabindex'); 92 $(this).attr("tabindex", tabVal); 93 }); 94} 95 96 97function headerInit(){ 98 if(window.innerWidth > md_bkp) $('.header-basic .nav-item').removeClass('open'); 99 if(sticky != undefined) headerElem.style.top = - sticky + 'px'; 100} 101 102function languageMenuButton(){ 103 $(".js-menu-button").click(function () { 104 if ($(this).attr('aria-label') == 'Mostrar\x20Menú\x20Principal') 105 $(this).attr('aria-label', 'Ocultar\x20Menú\x20Principal'); 106 else 107 $(this).attr('aria-label', 'Mostrar\x20Menú\x20Principal'); 108 }); 109} 110 111 112 113function accCarousel(selector) { 114 const carousels = $(selector); 115 carousels.each(function(index) { 116 $(this).on('init reInit afterChange', function(event, slick, currentSlide) { 117 $(this).find(".slick-list").appendTo($(this)); 118 $(this).find(".slick-list").attr('id', 'myCarousel-items' + index ); 119 $(this).find(".slick-arrow").attr('aria-controls', 'myCarousel-items' + index ); 120 $(this).find('.slick-slide').attr('aria-roledescription', 'slide'); 121 $(this).find('.slick-dots').attr('aria-label', 'Slides'); 122 setTimeout(() => { 123 $(this).find('.slick-dots').find("button[aria-selected!='true']").attr('aria-selected', 'false'); 124 }, 1000); 125 }); 126 }); 127} 128 129function accLanguageSelector(orientation = 'vertical') { 130 return new accSelector({ 131 container: '.select-language', 132 button: '.select-language__button', 133 list: '.select-language__list', 134 options: '.select-language__list li', 135 optionId: 'selectLanguageListItem', 136 selectedCssClass: 'active', 137 orientation 138 }); 139} 140 141class accSelector { 142 selectKeys = ['Enter']; 143 closeKeys = ['Escape', 'Tab']; 144 145 optionId; 146 selectedCssClass; 147 index = -1; 148 orientation = 'vertical'; 149 150 constructor(options) { 151 this.container = document.querySelector(options.container); 152 this.button = document.querySelector(options.button); 153 this.list = $(options.list);
154 this.options = document.querySelectorAll(options.options); 155 this.optionId = options.optionId; 156 this.selectedCssClass = options.selectedCssClass || 'active'; 157 this.orientation = options.orientation === 'horizontal' ? 'horizontal' : 'vertical'; 158 159 this.arrowKeys = this.orientation === 'horizontal' 160 ? ['ArrowRight', 'ArrowLeft'] 161 : ['ArrowDown', 'ArrowUp']; 162 163 if (!this.container || !this.button) { 164 return; 165 } 166 167 $(this.options).each((index, opt) => { 168 $(opt).attr('id', this.optionId + index); 169 }); 170 171 $(document.body).on('click', () => { 172 this.list.hasClass('show') && this.closeList(); 173 }); 174 175 this.container.addEventListener('keydown', (e) => { 176 if (this.closeKeys.includes(e.key)) { 177 this.closeList(); 178 } 179 }); 180 181 this.button.addEventListener('keydown', this.onKeyDown.bind(this)); 182 } 183 184 openList() { 185 if (!this.list.hasClass('show')) { 186 this.list.collapse('show'); 187 } 188 } 189 190 closeList() { 191 this.index = -1; 192 this.list.collapse('hide'); 193 this.button.setAttribute('aria-activedescendant', ''); 194 $(this.options).removeClass(this.selectedCssClass); 195 } 196 197 selectItemList(key) { 198 const isForward = key === 'ArrowDown' || key === 'ArrowRight'; 199 const isBackward = key === 'ArrowUp' || key === 'ArrowLeft'; 200 201 if (isForward) { 202 this.index = Math.min(this.index + 1, this.options.length - 1); 203 } else if (isBackward) { 204 this.index = Math.max(this.index - 1, 0); 205 } 206 207 this.button.setAttribute('aria-activedescendant', this.optionId + this.index); 208 $(this.options).removeClass(this.selectedCssClass); 209 $(this.options[this.index]).addClass(this.selectedCssClass); 210 } 211 212 onKeyDown(e) { 213 if (!this.arrowKeys.concat(this.selectKeys).includes(e.key)) { 214 return; 215 } 216 217 e.preventDefault(); 218 this.openList(); 219 220 if (this.arrowKeys.includes(e.key)) { 221 this.selectItemList(e.key); 222 return; 223 } 224 225 if (this.selectKeys.includes(e.key)) { 226 if (this.index === -1) { 227 this.selectItemList(this.arrowKeys[0]); 228 return; 229 } 230 this.options[this.index].querySelector('a').click(); 231 this.closeList(); 232 } 233 } 234} 235 236 237class accDropdown { 238 arrowKeys = ['ArrowDown', 'ArrowUp', 'Home', 'End']; 239 selectKeys = ['Enter']; 240 closeKeys = ['Escape', 'Tab']; 241 242 optionId; 243 selectedCssClass; 244 index = -1; 245 246 constructor(options) { 247 this.container = document.querySelector(options.container); 248 this.button = document.querySelector(options.button); 249 this.listDropdown = $(options.list); 250 this.menuList = document.querySelector(options.list); 251 this.options = document.querySelectorAll(options.options); 252 this.optionId = options.optionId; 253 this.selectedCssClass = options.selectedCssClass || 'active'; 254 this.menuitemNodes = []; 255 this.firstChars = []; 256 257 let nodes = this.options; 258 259 for (let i = 0; i < nodes.length; i++) { 260 let menuitem = nodes[i]; 261 this.menuitemNodes.push(menuitem); 262 menuitem.tabIndex = -1; 263 this.firstChars.push(menuitem.textContent.trim()[0].toLowerCase()); 264 } 265 266 if (!this.container || !this.button) { 267 return; 268 } 269 270 $(this.options).each((index, opt) => { 271 $(opt).attr('id', this.optionId + index); 272 }); 273 274 $(document.body).on('click', (e) => { 275 this.listDropdown.hasClass('show') && this.closeList(); 276 }); 277 278 $(this.options).on("mouseover", function() { 279 $(this).parent().attr("aria-activedescendant", $(this).attr('id')); 280 $(this).siblings().removeClass('active'); 281 $(this).addClass('active'); 282 }); 283 284 this.container.addEventListener('keydown', (e) => { 285 this.closeKeys.includes(e.key) && this.closeList(); 286 }); 287 288 this.button.addEventListener('keydown', this.onKeyDown.bind(this)); 289 290 } 291 292 setFocusByFirstCharacter(char) { 293 let start, index; 294 295 if (char.length > 1) { 296 return; 297 } 298 299 char = char.toLowerCase(); 300 301 start = this.menuitemNodes.indexOf(this.options[this.index]) + 1; 302 303 if (start >= this.menuitemNodes.length) { 304 start = 0; 305 } 306 307 index = this.firstChars.indexOf(char, start); 308 309 if (index === -1) { 310 index = this.firstChars.indexOf(char, 0); 311 } 312 313 if (index > -1) { 314 $(this.menuList).attr("aria-activedescendant", $(this.options[index]).attr('id')); 315 $(this.options).toggleClass(this.selectedCssClass, false); 316 $(this.options[index]).toggleClass(this.selectedCssClass, true); 317 this.index = index; 318 } 319 } 320 321 #openListDropdown() { 322 if (!this.listDropdown.hasClass('show')) { 323 this.listDropdown.collapse('show'); 324 } 325 } 326 327 closeList() { 328 this.index = -1; 329 this.listDropdown.collapse('hide'); 330 this.menuList.setAttribute('aria-activedescendant', ''); 331 $(this.options).toggleClass(this.selectedCssClass, false); 332 } 333 334 selectItemList(key) { 335 if (key === 'ArrowDown') { 336 if (this.index == this.options.length - 1){ 337 this.index = 0; 338 } else { 339 this.index = Math.min(this.index + 1, this.options.length - 1); 340 } 341 } else if(key === 'ArrowUp') { 342 if (this.index == 0){ 343 this.index = this.options.length - 1; 344 } else { 345 this.index = Math.max(this.index - 1, 0); 346 } 347 } 348 if (key === 'Home') { 349 this.index = 0; 350 } 351 if (key === 'End') { 352 this.index = this.options.length - 1; 353 } 354 355 this.menuList.setAttribute('aria-activedescendant', this.optionId + this.index); 356 $(this.options).toggleClass(this.selectedCssClass, false); 357 $(this.options[this.index]).toggleClass(this.selectedCssClass, true); 358 } 359 360 onKeyDown(e) { 361 362 function isPrintableCharacter(str) { 363 return str.length === 1 && str.match(/\S/); 364 } 365 366 if(isPrintableCharacter(e.key)) { 367 this.setFocusByFirstCharacter(e.key);;
368 } 369 370 if (!this.arrowKeys.concat(this.selectKeys).includes(e.key)) { 371 return; 372 } 373 374 e.preventDefault(); 375 this.#openListDropdown(); 376 377 if (this.arrowKeys.includes(e.key)) { 378 379 if (this.index === -1 && e.key == 'ArrowUp'){ 380 this.index = this.options.length - 1; 381 this.menuList.setAttribute('aria-activedescendant', this.optionId + this.index); 382 $(this.options).toggleClass(this.selectedCssClass, false); 383 $(this.options[this.index]).toggleClass(this.selectedCssClass, true); 384 return; 385 } 386 387 this.selectItemList(e.key); 388 return; 389 } 390 391 if (!this.selectKeys.includes(e.key)) { 392 return; 393 } 394 395 if (this.index === -1) { 396 this.selectItemList('ArrowDown'); 397 return; 398 } 399 400 this.options[this.index].querySelector('a').click(); 401 this.closeList(); 402 } 403} 404 405class AccAccordion { 406 constructor(accordionSelector) { 407 this.accordion = $(accordionSelector); 408 this.focusableElements = this.accordion.find('button[data-toggle="collapse"]'); 409 this.firstFocusableElement = this.focusableElements.first(); 410 this.lastFocusableElement = this.focusableElements.last(); 411 412 this.bindEvents(); 413 } 414 415 bindEvents() { 416 this.accordion.on('keydown', (e) => this.onKeyDown(e)); 417 } 418 419 onKeyDown(e) { 420 if (e.key === 'ArrowUp'){ 421 if ($(document.activeElement).is(this.firstFocusableElement)) { 422 e.preventDefault(); 423 this.lastFocusableElement.focus(); 424 } else { 425 e.preventDefault(); 426 $(document.activeElement).closest('.card').prev().find('button[data-toggle="collapse"]').focus(); 427 } 428 } else if (e.key === 'ArrowDown'){ // Solo Tab 429 if ($(document.activeElement).is(this.lastFocusableElement)) { 430 e.preventDefault(); 431 this.firstFocusableElement.focus(); 432 } else { 433 e.preventDefault(); 434 $(document.activeElement).closest('.card').next().find('button[data-toggle="collapse"]').focus(); 435 } 436 } else if (e.key === 'Home') { 437 e.preventDefault(); 438 this.firstFocusableElement.focus(); 439 } else if (e.key === 'End') { 440 e.preventDefault(); 441 this.lastFocusableElement.focus(); 442 } 443 } 444} 445 446class accTablist { 447 constructor(options) { 448 this.container = document.querySelector(options.container); 449 450 this.vertical = options.vertical; 451 this.automatic = options.automatic; 452 this.selectionFn = this.automatic ? 'setSelectedTab' : 'moveFocusToTab'; 453 454 this.firstTab = null; 455 this.lastTab = null; 456 457 this.tabs = Array.from(this.container.querySelectorAll('[role=tab]')); 458 this.tabpanels = new Map(); 459 460 for (const tab of this.tabs) { 461 const tabpanelid = tab.getAttribute('aria-controls'); 462 const tabpanel = document.getElementById(tabpanelid); 463 464 if (tabpanel) { 465 this.tabpanels.set(tabpanelid, tabpanel); 466 } 467 468 tab.tabIndex = -1; 469 tab.setAttribute('aria-selected', 'false'); 470 tab.addEventListener('keydown', this.onKeydown.bind(this)); 471 tab.addEventListener('click', this.onClick.bind(this)); 472 473 if (!this.firstTab) { 474 this.firstTab = tab; 475 } 476 this.lastTab = tab; 477 } 478 479 this.setSelectedTab(this.firstTab, !this.automatic); 480 } 481 482 setSelectedTab(currentTab, setFocus) { 483 if (typeof setFocus !== 'boolean') { 484 setFocus = true; 485 } 486 for (const tab of this.tabs) { 487 488 if (currentTab === tab) { 489 tab.setAttribute('aria-selected', 'true'); 490 tab.removeAttribute('tabindex'); 491 if (setFocus) { 492 tab.focus(); 493 tab.click(); 494 } 495 } else { 496 tab.setAttribute('aria-selected', 'false'); 497 tab.tabIndex = -1; 498 } 499 } 500 } 501 502 setSelectedToPreviousTab(currentTab) { 503 if (currentTab === this.firstTab) { 504 this[this.selectionFn](this.lastTab); 505 } else { 506 const index = this.tabs.indexOf(currentTab); 507 this[this.selectionFn](this.tabs[index - 1]); 508 } 509 } 510 511 setSelectedToNextTab(currentTab) { 512 if (currentTab === this.lastTab) { 513 this[this.selectionFn](this.firstTab); 514 } else { 515 const index = this.tabs.indexOf(currentTab); 516 this[this.selectionFn](this.tabs[index + 1]); 517 } 518 } 519 520 moveFocusToTab(currentTab) { 521 currentTab.focus(); 522 } 523 524 onClick(event) { 525 this.setSelectedTab(event.currentTarget, false); 526 } 527 528 onKeydown(event) { 529 const tgt = event.currentTarget; 530 let flag = false;
531 532 const previousKey = this.vertical ? 'ArrowUp' : 'ArrowLeft'; 533 const nextKey = this.vertical ? 'ArrowDown' : 'ArrowRight'; 534 535 switch (event.key) { 536 case previousKey: 537 this.setSelectedToPreviousTab(tgt); 538 flag = true; 539 break; 540 541 case nextKey: 542 this.setSelectedToNextTab(tgt); 543 flag = true; 544 break; 545 546 case 'Home': 547 this[this.selectionFn](this.firstTab); 548 flag = true; 549 break; 550 551 case 'End': 552 this[this.selectionFn](this.lastTab); 553 flag = true; 554 break; 555 556 default: 557 break; 558 } 559 560 if (flag) { 561 event.stopPropagation(); 562 event.preventDefault(); 563 } 564 } 565} 566 567function navigationCustomMenuDropdown(){ 568 let btnToggle = $('.navbar-custom .menu-btn-toggle'), 569 navBtn = $('.navbar-custom .nav-button'), 570 childMenu = $('.navbar-custom .child-menu'), 571 myMenu = $('.navbar-custom'); 572 573 btnToggle.click(function(){ 574 if (myMenu.hasClass("navbar-third-level")) { 575 if($(this).parent().hasClass("item-menu-2")){ 576 $(this).parent().parent().parent().siblings('.nav-item').find('.child-menu').hide(); 577 $(this).parent().parent().parent().siblings('.nav-item').find('.menu-btn-toggle').removeClass('open'); 578 } 579 580 if($(this).parent().parent().hasClass("navbar-custom")){ 581 $(this).parent().siblings('.nav-item').find('.child-menu').hide(); 582 $(this).parent().siblings('.nav-item').find('.menu-btn-toggle').removeClass('open'); 583 } 584 585 if($(this).hasClass("open")){ 586 $(this).next().find('.child-menu').hide("slow"); 587 $(this).next().find('.menu-btn-toggle').removeClass('open'); 588 } 589 590 $(this).next('.child-menu').slideToggle(); 591 $(this).toggleClass('open'); 592 } else { 593 $(this).parents().siblings('.nav-item').children('.child-menu').hide('slow'); 594 $(this).next('.child-menu').slideToggle(); 595 $(this).toggleClass('open'); 596 btnToggle.not(this).removeClass('open'); 597 } 598 }); 599 600 navBtn.click(function(){ 601 childMenu.hide(); 602 btnToggle.removeClass('open'); 603 }); 604} 605 606function navigationCustomMenuInit(){ 607 let navItem = $('.navbar-custom .nav-item'), 608 childMenu = $('.navbar-custom .child-menu'); 609 610 if($(window).width() > 767){ 611 navItem.addClass('dropdown'); 612 childMenu.removeAttr('style'); 613 }else{ 614 navItem.removeClass('dropdown'); 615 } 616} 617 618function custMenuThirdLevel() { 619 const KEY = { ESC: 27, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40 }; 620 const menuBar = Array.from(document.querySelectorAll(".navbar-third-level > li")); 621 let justOpenedSubmenu = false; 622 const getDirectAnchor = (li) => 623 li.querySelector(':scope > a[role="menuitem"], :scope > a'); 624 const getDirectSubmenu = (li) => 625 li.querySelector(':scope > ul.child-menu'); 626 const getSubmenuItems = (li) => { 627 const ul = getDirectSubmenu(li); 628 return ul ? Array.from(ul.querySelectorAll(':scope > li > a[role="menuitem"], :scope > li > a')) : []; 629 }; 630 631 function openSubMenu(li) { 632 li.classList.add("hover", "open"); 633 const ul = getDirectSubmenu(li); 634 if (ul) { 635 ul.classList.add("open", "show"); 636 ul.setAttribute("aria-expanded", "true"); 637 } 638 } 639 function closeSubMenu(li) { 640 li.classList.remove("hover", "open"); 641 const ul = getDirectSubmenu(li); 642 if (ul) { 643 ul.classList.remove("open", "show"); 644 ul.setAttribute("aria-expanded", "false"); 645 } 646 } 647 function closeOtherSubMenus(currentLi) { 648 menuBar.forEach((li) => { 649 if (li !== currentLi) closeSubMenu(li); 650 }); 651 } 652 653 const focusNext = (items, i) => items[(i + 1) % items.length].focus(); 654 const focusPrev = (items, i) => items[(i - 1 + items.length) % items.length].focus(); 655 656 menuBar.forEach((li) => { 657 const a = getDirectAnchor(li); 658 if (a) a.setAttribute("tabindex", "0"); 659 }); 660 document.querySelectorAll(".link-menu-2, .link-menu-3").forEach((a) => { 661 a.setAttribute("tabindex", "-1"); 662 }); 663 664 menuBar.forEach((li, liIndex) => { 665 const anchor = getDirectAnchor(li); 666 if (!anchor) return; 667 anchor.addEventListener("keydown", (e) => { 668 if (justOpenedSubmenu) { 669 justOpenedSubmenu = false; 670 e.preventDefault(); 671 e.stopPropagation(); 672 return false; 673 } 674 675 switch (e.which) { 676 case KEY.RIGHT: { 677 e.preventDefault(); 678 e.stopPropagation(); 679 const nextIndex = (liIndex + 1) % menuBar.length; 680 const targetLi = menuBar[nextIndex]; 681 closeSubMenu(li); 682 closeOtherSubMenus(targetLi); 683 const targetA = getDirectAnchor(targetLi); 684 if (targetA) targetA.focus(); 685 return false;
686 } 687 688 case KEY.LEFT: { 689 e.preventDefault(); 690 e.stopPropagation(); 691 const prevIndex = (liIndex - 1 + menuBar.length) % menuBar.length; 692 const targetLi = menuBar[prevIndex]; 693 closeSubMenu(li); 694 closeOtherSubMenus(targetLi); 695 const targetA = getDirectAnchor(targetLi); 696 if (targetA) targetA.focus(); 697 return false; 698 } 699 700 case KEY.DOWN: { 701 e.preventDefault(); 702 e.stopPropagation(); 703 const sub = getDirectSubmenu(li); 704 if (sub) { 705 closeOtherSubMenus(li); 706 openSubMenu(li); 707 const items = getSubmenuItems(li); 708 if (items.length) { 709 items[0].focus(); 710 justOpenedSubmenu = true; 711 } 712 } 713 return false; 714 } 715 716 case KEY.UP: { 717 e.preventDefault(); 718 e.stopPropagation(); 719 const sub = getDirectSubmenu(li); 720 if (sub) { 721 closeOtherSubMenus(li); 722 openSubMenu(li); 723 const items = getSubmenuItems(li); 724 if (items.length) { 725 items[items.length - 1].focus(); 726 justOpenedSubmenu = true; 727 } 728 } 729 return false; 730 } 731 732 default: 733 break; 734 } 735 }); 736 }); 737 738 document.querySelectorAll(".link-menu-2, .link-menu-3").forEach((a) => { 739 a.addEventListener("keydown", (e) => { 740 const li = a.parentElement; 741 const ul = li.parentElement; 742 const siblings = Array.from(ul.querySelectorAll(':scope > li > a[role="menuitem"], :scope > li > a')); 743 const idx = siblings.indexOf(a); 744 const isLevel2 = a.classList.contains("link-menu-2"); 745 const isLevel3 = a.classList.contains("link-menu-3"); 746 747 switch (e.which) { 748 case KEY.ESC: { 749 e.preventDefault(); 750 e.stopPropagation(); 751 const parentLi = ul.parentElement; 752 closeSubMenu(parentLi); 753 const trigger = getDirectAnchor(parentLi); 754 if (trigger) trigger.focus(); 755 return false; 756 } 757 758 case KEY.DOWN: { 759 e.preventDefault(); 760 e.stopPropagation(); 761 focusNext(siblings, idx); 762 return false; 763 } 764 765 case KEY.UP: { 766 e.preventDefault(); 767 e.stopPropagation(); 768 focusPrev(siblings, idx); 769 return false; 770 } 771 772 case KEY.RIGHT: { 773 e.preventDefault(); 774 e.stopPropagation(); 775 776 const childMenu = li.querySelector(':scope > ul.child-menu'); 777 if (childMenu) { 778 openSubMenu(li); 779 const childItems = Array.from(childMenu.querySelectorAll(':scope > li > a[role="menuitem"], :scope > li > a')); 780 if (childItems.length) childItems[0].focus(); 781 } 782 else if (isLevel2) { 783 const topLi = ul.parentElement; 784 const mainLis = Array.from(topLi.parentElement.children); 785 const topIndex = mainLis.indexOf(topLi); 786 const nextTopIndex = (topIndex + 1) % mainLis.length; 787 const targetTopLi = mainLis[nextTopIndex]; 788 789 closeSubMenu(topLi); 790 closeOtherSubMenus(targetTopLi); 791 792 const targetTopA = getDirectAnchor(targetTopLi); 793 if (targetTopA) { 794 targetTopA.focus(); 795 justOpenedSubmenu = false; 796 } 797 798 if (getDirectSubmenu(targetTopLi)) { 799 openSubMenu(targetTopLi); 800 } 801 } 802 return false; 803 } 804 805 case KEY.LEFT: { 806 e.preventDefault(); 807 e.stopPropagation(); 808 809 if (isLevel3) { 810 const parentL2Li = ul.parentElement; 811 closeSubMenu(parentL2Li); 812 const l2Anchor = getDirectAnchor(parentL2Li); 813 if (l2Anchor) l2Anchor.focus(); 814 } 815 else { 816 const parentL1Li = ul.parentElement; 817 closeSubMenu(parentL1Li); 818 const l1Anchor = getDirectAnchor(parentL1Li); 819 if (l1Anchor) { 820 l1Anchor.focus(); 821 justOpenedSubmenu = false; 822 } 823 } 824 return false; 825 } 826 default: 827 break; 828 } 829 }); 830 }); 831} 832 833function loadSpinner(){ 834 let loader = document.createElement("div"); 835 loader.id = 'loader'; 836 loader.innerHTML = "<div class='spinner'></div>"; 837 if(!$('body #loader').length){ 838 $('body').append(loader); 839 } 840} 841 842function removeSpinner(){ 843 $('#loader').remove(); 844} 845 846class AccModal { 847 constructor(modalSelector, onSaveCallback = null) { 848 this.modal = $(modalSelector); 849 this.focusableElements = this.modal.find('button, [href], input, select, textarea, [tabindex]:not
849([tabindex="-1"])'); 850 this.firstFocusableElement = this.focusableElements.first(); 851 this.lastFocusableElement = this.focusableElements.last(); 852 this.onSaveCallback = onSaveCallback; 853 854 this.bindEvents(); 855 } 856 857 bindEvents() { 858 this.modal.on('shown.bs.modal', (e) => this.onModalShown(e)); 859 this.modal.on('hidden.bs.modal', (e) => this.onModalHidden(e)); 860 861 this.modal.on('keydown', (e) => this.onKeyDown(e)); 862 863 this.modal.find('[data-action="confirm"]').on('click', () => this.onSave()); 864 } 865 866 onModalShown(e) { 867 this.focusElement = this.modal.find('[data-focus="true"]').first(); 868 869 if (this.focusElement.length && !this.focusableElements.is(this.focusElement)) { 870 this.focusElement.attr('tabindex', '-1'); 871 } 872 873 let dismissElement = this.modal.find('[data-dismiss="modal"]').first(); 874 875 this.toFocusFirst = this.focusElement.length ? this.focusElement : ( dismissElement.length ? dismissElement : this.firstFocusableElement ); 876 877 if (this.toFocusFirst.length) { 878 this.toFocusFirst.focus(); 879 } 880 } 881 882 onModalHidden(e) { //Prevents error by focus in an aria-hidden=true element due to Bootstrap behaviour 883 this.modal.removeAttr("aria-hidden").attr("aria-modal","true"); 884 } 885 886 onKeyDown(e) { 887 if (e.key === 'Tab' || e.keyCode === 9){ 888 if (e.shiftKey) { // Shift + Tab 889 if ($(document.activeElement).is(this.firstFocusableElement)) { 890 e.preventDefault(); 891 this.lastFocusableElement.focus(); 892 } 893 } else { // Solo Tab 894 if ($(document.activeElement).is(this.lastFocusableElement)) { 895 e.preventDefault(); 896 this.firstFocusableElement.focus(); 897 } 898 } 899 } 900 } 901 902 onSave() { 903 if (typeof this.onSaveCallback === 'function') { 904 this.onSaveCallback(); 905 } 906 } 907} 908 909class AccTooltip { 910 constructor(el = '[data-toggle="tooltip"]') { 911 this.el = $(el); 912 913 this.el.tooltip(); 914 915 this.bindEvents(); 916 } 917 918 bindEvents() { 919 this.el.on('keydown', (e) => this.onKeyDown(e)); 920 } 921 922 onKeyDown(e) { 923 if (e.key === 'Escape' || e.keyCode === 27){ 924 this.el.tooltip('hide'); 925 } 926 } 927} 928 929const MJUCookies = (function (){ 930 931 function translationsConfig() { 932 const DEFAULT = 'Es\x20necesario\x20aceptar\x20las\x20cookies\x20para\x20visualizar\x20este\x20contenido'; 933 const NECESSARY = 'Es\x20necesario\x20aceptar\x20las\x20cookies\x20obligatorias\x20para\x20visualizar\x20este\x20contenido'; 934 const FUNCTIONAL = 'Es\x20necesario\x20aceptar\x20las\x20cookies\x20funcionales\x20para\x20visualizar\x20este\x20contenido'; 935 const PERFORMANCE = 'Es\x20necesario\x20aceptar\x20las\x20cookies\x20de\x20rendimiento\x20para\x20visualizar\x20este\x20contenido'; 936 const PERSONALIZATION = 'Es\x20necesario\x20aceptar\x20las\x20cookies\x20de\x20personalización\x20para\x20visualizar\x20este\x20contenido'; 937 938 return { 939 text: { 940 DEFAULT, 941 NECESSARY: NECESSARY !== 'cookies.blocked-content.text.necessary' ? NECESSARY : DEFAULT, 942 FUNCTIONAL: FUNCTIONAL !== 'cookies.blocked-content.text.functional' ? FUNCTIONAL : DEFAULT, 943 PERFORMANCE: PERFORMANCE !== 'cookies.blocked-content.text.performance' ? PERFORMANCE : DEFAULT, 944 PERSONALIZATION: PERSONALIZATION !== 'cookies.blocked-content.text.personalization' ? PERSONALIZATION : DEFAULT 945 }, 946 action: 'Revisar\x20mi\x20elección', 947 accessibility: 'Revisar\x20mi\x20elección\x20de\x20configuración\x20de\x20cookies' 948 } 949 } 950 951 const COOKIE_TYPES = { 952 FUNCTIONAL: 'CONSENT_TYPE_FUNCTIONAL', 953 NECESSARY: 'CONSENT_TYPE_NECESSARY', 954 PERFORMANCE: 'CONSENT_TYPE_PERFORMANCE', 955 PERSONALIZATION: 'CONSENT_TYPE_PERSONALIZATION' 956 }; 957 958 const DEFAULT_OPTS = { 959 type: null, // ['NECESSARY', 'FUNCTIONAL', 'PERFORMANCE', 'PERSONALIZATION'] 960 blockedCookiesMessageHTML: { // {NECESSARY:'', FUNCTIONAL:'', PERFORMANCE:'', PERSONALIZATION:''} 961 default: ` 962 <div class="mj-blocked-cookies"> 963 <div class="mj-blocked-cookies-text text-center"> 964 <p>%TEXT%</p> 965 <button class="btn btn-link mj-blocked-cookies-button" aria-label="%ACCESSIBILITY%"> 966 %ACTION% 967 </button> 968 </div> 969 </div> 970 ` 971 }, 972 blockedCookiesMessageActionSelector: '.mj-blocked-cookies-button', 973 blockedCookiesMessageActionOnClickFn: function() {
974 $('[id$="CookiesBannerPortlet_configurationButton"]').click(); 975 }, 976 cookiesBannerSelector: '.cookies-banner', 977 lang: translationsConfig() 978 }; 979 980 const COOKIE_CONSENT_COOKIE_ATTR = 'data-third-party-cookie'; 981 const COOKIE_CONSENT_MESSAGE_ATTR = 'data-third-party-cookie-message'; 982 const COOKIE_CONSENT_FLIPPED_ATTR = 'data-third-party-cookie-flipped'; 983 984 let opts, cookieTypes, isCheckingCookies, blockedCookiesMessages, cookiesConsentLast; 985 986 function showBlockedCookiesHtml(type, element) { 987 switch (element.tagName.toUpperCase()) { 988 case 'EMBED': case 'IFRAME': case 'IMG': { 989 const blockedCookiesMessage = 990 (opts.blockedCookiesMessageHTML[type] || opts.blockedCookiesMessageHTML.default) 991 .replace(/%TEXT%/g, opts.lang.text[type] || opts.lang.text.DEFAULT) 992 .replace(/%ACTION%/g, opts.lang.action) 993 .replace(/%ACCESSIBILITY%/g, opts.lang.accessibility); 994 995 const node = $(blockedCookiesMessage); 996 blockedCookiesMessages.set(type, (blockedCookiesMessages.get(type) || []).concat(node)); 997 $(element).after(node); 998 break; 999 } 1000 default: 1001 } 1002 } 1003 1004 function hideBlockedCookiesHtml(type) { 1005 for (const node of (blockedCookiesMessages.get(type) || [])) { 1006 node.remove(); 1007 } 1008 blockedCookiesMessages.delete(type); 1009 } 1010 1011 function checkCookiesConsent() { 1012 let consent = new Map(); 1013 Object.entries(cookieTypes).forEach((type) => { 1014 const value = Liferay.Util.Cookie.get(type[1]) === "true"; 1015 consent.set(type[0], value); 1016 }); 1017 return consent; 1018 } 1019 1020 function checkCookies() { 1021 const cookiesConsent = checkCookiesConsent(); 1022 1023 Object.entries(cookieTypes).forEach((type) => { 1024 const cookieConsent = cookiesConsent.get(type[0]); 1025 1026 const selector = `[${COOKIE_CONSENT_COOKIE_ATTR}="${type[1]}"]`; 1027 1028 $(selector).each(function () { 1029 const hasMessage = this.hasAttribute(COOKIE_CONSENT_MESSAGE_ATTR); 1030 const hasFlipped = this.hasAttribute(COOKIE_CONSENT_FLIPPED_ATTR); 1031 1032 const show = !cookieConsent && !hasMessage && !hasFlipped; 1033 const hide = cookieConsent && hasMessage && hasFlipped; 1034 1035 if(show) { 1036 showBlockedCookiesHtml(type[0], this); 1037 this.setAttribute(COOKIE_CONSENT_MESSAGE_ATTR, ''); 1038 } 1039 else if (hide) { 1040 hideBlockedCookiesHtml(type[0]); 1041 this.removeAttribute(COOKIE_CONSENT_MESSAGE_ATTR); 1042 } 1043 }); 1044 }); 1045 1046 cookiesConsentLast = structuredClone(cookiesConsent); 1047 } 1048 1049 function init(_opts) { 1050 opts = $.extend(true, {}, DEFAULT_OPTS, _opts); 1051 1052 if (!$(opts.cookiesBannerSelector).length || isCheckingCookies) return; 1053 1054 blockedCookiesMessages = new Map(); 1055 cookiesConsentLast = new Map(); 1056 1057 cookieTypes = Object.fromEntries( 1058 Object.entries(COOKIE_TYPES).filter(([k]) => !opts.type?.length || opts.type.includes(k)) 1059 ); 1060 1061 $(document).on('click', opts.blockedCookiesMessageActionSelector, opts.blockedCookiesMessageActionOnClickFn); 1062 1063 const observer = new MutationObserver(checkCookies); 1064 1065 observer.observe(document, { 1066 attributes: true, 1067 attributeFilter: [COOKIE_CONSENT_FLIPPED_ATTR], 1068 subtree: true 1069 }); 1070 1071 const checkCookiesInterval = function (interval) { 1072 if (interval < 7000) { 1073 interval += 500; 1074 } 1075 if (isCheckingCookies) { 1076 checkCookies(); 1077 setTimeout(() => checkCookiesInterval(interval), interval); 1078 } 1079 } 1080 1081 isCheckingCookies = true; 1082 1083 checkCookiesInterval(2000); 1084 } 1085 1086 return { init }; 1087 1088})(); 1089 1090$(document).ready(function() { 1091 // Añadiendo la clase custom-facet-collapsed al portlet, en su configuración del aspecto e interacción/Estilos avanzados, conseguiremos que la faceta aparezca collapsada al cargar la página 1092 if($('.custom-facet-collapsed').length){ 1093 $('.custom-facet-collapsed').find('.panel-header-link').attr('aria-expanded', false).addClass('collapsed'); 1094 $('.custom-facet-collapsed').find('.collapse').removeClass('show'); 1095 } 1096}); 1097 1098class accCombobox { 1099 constructor(options) { 1100 this.container = document.querySelector(options.container); 1101 this.button = this.container.querySelector(options.button); 1102 this.list = this.container.querySelector(options.list); 1103 this.options = this.container.querySelectorAll(options.options); 1104 this.optionId = options.optionId; 1105 this.selectedCssClass = options.selectedCssClass || 'active'; 1106 this.index = -1; 1107 this.menuitemNodes = []; 1108 this.firstChars = []; 1109 1110 this.init(); 1111 } 1112
1113 init() { 1114 this.options.forEach((option, index) => { 1115 this.menuitemNodes.push(option); 1116 option.setAttribute("id", `${this.optionId}${index}`); 1117 option.tabIndex = -1; 1118 1119 const text = option.textContent.trim(); 1120 if (text) { 1121 this.firstChars.push(text[0].toLowerCase()); 1122 } 1123 }); 1124 1125 this.addEventListeners(); 1126 1127 } 1128 1129 addEventListeners() { 1130 this.button.addEventListener("click", (event) => { 1131 this.toggleDropdown(); 1132 event.stopImmediatePropagation(); 1133 }); 1134 1135 this.button.addEventListener("keydown", (event) => { 1136 this.onKeyDown(event); 1137 event.stopImmediatePropagation(); 1138 }); 1139 1140 this.options.forEach((option) => { 1141 option.addEventListener("click", (event) => { 1142 this.selectOption(option); 1143 event.stopImmediatePropagation(); 1144 }); 1145 }); 1146 1147 document.addEventListener("click", (event) => { 1148 if (!this.container.contains(event.target)) { 1149 this.closeDropdown(); 1150 } 1151 }); 1152 } 1153 1154 toggleDropdown() { 1155 this.list.classList.contains("show") ? this.closeDropdown() : this.openDropdown(); 1156 } 1157 1158 openDropdown() { 1159 this.list.classList.add("show"); 1160 this.button.setAttribute("aria-expanded", "true"); 1161 } 1162 1163 closeDropdown() { 1164 this.list.classList.remove("show"); 1165 this.button.setAttribute("aria-expanded", "false"); 1166 this.index = -1; 1167 } 1168 1169 updateActiveOption(index) { 1170 this.options.forEach((option, i) => { 1171 option.classList.toggle(this.selectedCssClass, i === index); 1172 }); 1173 this.button.setAttribute("aria-activedescendant", this.options[index]?.id || ""); 1174 this.index = index; 1175 } 1176 1177 setFocusByFirstCharacter(char) { 1178 if (char.length > 1) return; 1179 1180 char = char.toLowerCase(); 1181 let start = this.index + 1 >= this.menuitemNodes.length ? 0 : this.index + 1; 1182 let matchIndex = this.firstChars.indexOf(char, start); 1183 1184 if (matchIndex === -1) { 1185 matchIndex = this.firstChars.indexOf(char, 0); 1186 } 1187 1188 if (matchIndex > -1) { 1189 if (!this.list.classList.contains("show")) this.openDropdown(); 1190 this.updateActiveOption(matchIndex); 1191 } 1192 } 1193 1194 jumpByPage(key) { 1195 if (!this.list.classList.contains("show")) { 1196 this.openDropdown(); 1197 } 1198 1199 let jump = 10; 1200 if (key === "PageUp") { 1201 this.index = Math.max(0, this.index - jump); 1202 } else if (key === "PageDown") { 1203 this.index = Math.min(this.options.length - 1, this.index + jump); 1204 } 1205 1206 this.updateActiveOption(this.index); 1207 } 1208 1209 onKeyDown(event) { 1210 const keys = { ArrowDown: 1, ArrowUp: -1, Home: "first", End: "last" }; 1211 1212 if (event.key.length === 1 && event.key.match(/\S/)) { 1213 this.setFocusByFirstCharacter(event.key); 1214 return; 1215 } 1216 1217 if (event.key in keys) { 1218 event.preventDefault(); 1219 if (!this.list.classList.contains("show")) this.openDropdown(); 1220 1221 if (keys[event.key] === "first") { 1222 this.index = 0; 1223 } 1224 else if (keys[event.key] === "last") { 1225 this.index = this.options.length - 1; 1226 } 1227 else { 1228 this.index = Math.max(0, Math.min(this.index + keys[event.key], this.options.length - 1)); 1229 } 1230 1231 this.updateActiveOption(this.index); 1232 } 1233 1234 if (event.key === "Enter" || event.key === "Space" || (event.altKey && event.key === "ArrowUp") || event.key === " ") { 1235 event.preventDefault(); 1236 if (!this.list.classList.contains("show")) { 1237 this.openDropdown(); 1238 } 1239 if (this.index >= 0) { 1240 this.selectOption(this.options[this.index]); 1241 } 1242 } 1243 1244 if (event.key === "Tab") { 1245 if (this.index >= 0) { 1246 this.selectOption(this.options[this.index]); 1247 } 1248 } 1249 1250 if (event.key === "Escape") { 1251 this.closeDropdown(); 1252 } 1253 1254 if (event.key === "PageUp" || event.key === "PageDown") { 1255 event.preventDefault(); 1256 this.jumpByPage(event.key); 1257 } 1258 } 1259 1260 selectOption(option) { 1261 this.button.innerText = option.innerText; 1262 this.button.setAttribute("aria-activedescendant", option.id); 1263 this.index = Array.from(this.options).indexOf(option); 1264 this.closeDropdown(); 1265 } 1266}
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.