1'use strict'; 2/* eslint-disable */ //TODO: be aware of unused vars 3function tabs_script() { 4 class Tabs { 5 constructor(element) { 6 this.element = element; 7 this.options = JSON.parse( 8 this.element.getAttribute('data-fr-tabs-options') || '{}' 9 ); 10 this.tabLinks = this.element.querySelectorAll('.fr-tabs__link'); 11 this.contentWrapper = this.options.contentOutside 12 ? document.querySelector(this.options.contentOutsideSelector) 13 : this.element.querySelector('.fr-tabs__content-wrapper'); 14 this.contentItems = this.contentWrapper.querySelectorAll( 15 '.fr-tabs__content-item-wrapper' 16 ); 17 this.tabList = this.element.querySelector('.fr-tabs__list'); 18 19 this.initialHashHandled = false; 20 21 this.init(); 22 this.accordionClickListeners = new Map(); 23 this.resizeListenerAdded = false; 24 this.accordionListenerAdded = false; 25 this.clonedContentItems = []; 26 27 if (this.options.isChangeToAccordion) { 28 this.initAccordion(this.options.accordionOnDevice); 29 } 30 31 //const orientationVertical = this.options.isHorizontal === 0; 32 if (this.options.isHorizontal !== 0) { 33 this.tabList.setAttribute('aria-orientation', 'horizontal'); 34 } else { 35 this.tabList.setAttribute('aria-orientation', 'vertical'); 36 } 37 38 // handle initial on page load if option is enabled 39 if (this.options.scrollToHash && location.hash) { 40 this.handleInitialHash(); 41 } 42 } 43 44 init() { 45 this.toggleDirectionClass(); 46 this.tabLinks.forEach((tabLink, index) => { 47 tabLink.addEventListener('click', (event) => { 48 event.preventDefault(); 49 this.switchTab(tabLink, index); 50 }); 51 this.addKeyboardNavigation(tabLink, index); 52 }); 53 54 if (this.options.animate && this.options.animation.target) { 55 this.createAnimatedElement(); 56 window.addEventListener('resize', () => {
57 this.updateBackgroundAnimation(); 58 }); 59 } 60 61 if (this.options.activeTab > this.tabLinks.length - 1) { 62 console.warn( 63 'The activeTab option is higher or lower than the number of tabs. The first tab will be selected instead.' 64 ); 65 } 66 if ( 67 !this.options.activeTab || 68 this.options.activeTab > this.tabLinks.length - 1 || 69 this.options.activeTab < 0 70 ) { 71 this.options.activeTab = 0; 72 } 73 74 this.switchTab( 75 this.tabLinks[this.options.activeTab], 76 Number(this.options.activeTab) 77 ); 78 this.handleScrollToHash(); 79 this.addRoles(); 80 this.addAriaControls(); 81 82 this.clickToCenterTabLink(); 83 84 // TODO removing it for now, lets see if there is an isolated case about it 85 /*this.hideHorizontalScrollbar(); 86 window.addEventListener("resize", () => { 87 this.hideHorizontalScrollbar(); 88 });*/ 89 90 this.setListDirection(); 91 } 92 93 isBuilder() { 94 return document.querySelector('.iframe.mounted') ? true : false; 95 } 96 ///////////////////////////// 97 // Start accordion methods // 98 ///////////////////////////// 99 100 initAccordion(breakpoint) { 101 if (!this.resizeListenerAdded) { 102 const handleResize = () => { 103 if (window.innerWidth <= breakpoint) { 104 if (!this.accordionListenerAdded) { 105 this.switchToAccordion(); 106 this.addAccordionClickListeners(); 107 this.accordionListenerAdded = true; 108 } 109 } else { 110 if (this.accordionListenerAdded) { 111 this.removeAccordionClickListeners(); 112 this.switchToTabs(); 113 this.accordionListenerAdded = false; 114 } 115 } 116 }; 117 118 // Add listeners on initial load 119 handleResize(); 120 121 // Update listeners on resize 122 window.addEventListener('resize', handleResize); 123 124 this.resizeListenerAdded = true; 125 } 126 } 127 128 toggleAccordionClasses(state) { 129 if (state === 'tabs') { 130 this.element.classList.remove('fr-tabs--accordion'); 131 this.element.classList.add('fr-tabs--tabs'); 132 this.tabList.classList.remove('fr-tabs__list--accordion'); 133 this.tabList.classList.add('fr-tabs__list--tabs'); 134 } else if (state === 'accordion') { 135 this.element.classList.add('fr-tabs--accordion'); 136 this.element.classList.remove('fr-tabs--tabs'); 137 this.tabList.classList.add('fr-tabs__list--accordion'); 138 this.tabList.classList.remove('fr-tabs__list--tabs'); 139 } 140 } 141 142 //todo starts here 143 switchToAccordion() { 144 this.removeVisibleContentItems(); 145 this.moveContentItemsToAccordion(); 146 this.toggleAccordionClasses('accordion'); 147 this.previewAllContentItemsInBuilder(); 148 } 149 150 moveContentItemsToAccordion() { 151 const activeTab = this.element.querySelector('.fr-tabs__link.active'); 152 if (activeTab) { 153 const index = Array.from(this.tabLinks).indexOf(activeTab); 154 const contentItem = this.contentItems[index]; 155 156 this.addContentItem(activeTab, contentItem); 157 } else if (this.tabLinks.length > 0) { 158 const firstTab = this.tabLinks[0]; 159 const firstContentItem = this.contentItems[0]; 160 161 this.addContentItem(firstTab, firstContentItem); 162 } 163 } 164 165 switchToTabs() { 166 this.moveContentItemsToTabs(); 167 this.toggleAccordionClasses('tabs'); 168 this.endPreviewAllContentItemsInBuilder(); 169 } 170 171 moveContentItemsToTabs() { 172 this.contentItems.forEach((contentItem) => { 173 // remove all content items first, to ensure correct order 174 contentItem.parentNode?.removeChild(contentItem); 175 }); 176 177 this.contentItems.forEach((contentItem) => { 178 // Move the element 179 this.contentWrapper.appendChild(contentItem); 180 }); 181 } 182 183 previewAllContentItemsInBuilder() { 184 if (this.isBuilder()) { 185 // Clear any previous cloned items 186 this.clonedContentItems.forEach((clone) => { 187 if (clone && clone.parentNode) { 188 clone.parentNode.removeChild(clone); 189 } 190 }); 191 this.clonedContentItems = []; 192 193 // Clone and append each contentItem after its corresponding tabLink 194 this.tabLinks.forEach((tabLink, index) => { 195 if (this.contentItems[index]) { 196 const clone = this.contentItems[index].cloneNode(true); // Deep clone 197 clone.classList.add('fr-builder-preview-content'); // Add unique class to the cloned content 198 tabLink.insertAdjacentElement('afterend', clone); 199 this.clonedContentItems.push(clone); 200 } 201 }); 202 } 203 } 204 205 endPreviewAllContentItemsInBuilder() { 206 if (this.isBuilder()) {
207 this.clonedContentItems.forEach((clone) => { 208 // Remove the cloned contentItem 209 if (clone && clone.parentNode) { 210 clone.parentNode.removeChild(clone); 211 } 212 }); 213 this.clonedContentItems = []; // Reset the array 214 } 215 } 216 217 accordionClickListener(tabLink, index) { 218 return () => { 219 const contentItem = this.contentItems[index]; 220 // const closePreviousAccordion = true; 221 const closePreviousAccordion = this.options.closePreviousAccordion; 222 223 if (closePreviousAccordion) { 224 this.removeVisibleContentItems(); 225 if (this.isNewContentItem(tabLink)) { 226 this.addContentItemWithTransition(tabLink, contentItem); 227 } else { 228 this.removeContentItemWithTransition(tabLink); 229 } 230 } else { 231 if (this.isBuilder()) return; 232 if (this.isNewContentItem(tabLink)) { 233 this.addContentItemWithTransition(tabLink, contentItem); 234 } else { 235 this.removeContentItemWithTransition(tabLink); 236 } 237 } 238 }; 239 } 240 241 addContentItemWithTransition(tabLink, contentItem) { 242 tabLink.parentNode.insertBefore(contentItem, tabLink.nextElementSibling); 243 244 // Force a reflow to get the computed styles 245 window.getComputedStyle(contentItem).height; 246 const contentHeight = contentItem.offsetHeight; 247 248 contentItem.style.height = 0; 249 const animationDuration = this.options.accordionDuration; 250 contentItem.style.transition = `height ${animationDuration}ms ease-out`; 251 setTimeout(() => { 252 contentItem.style.height = contentHeight + 'px'; 253 }, 0); 254 } 255 256 removeContentItemWithTransition(tabLink) { 257 //todo 258 if (this.isBuilder()) { 259 console.log( 260 'In builder mode, skipping removal of cloned content item.' 261 ); 262 return; 263 } 264 265 let content = tabLink.nextElementSibling; 266 267 if (!content.classList.contains('fr-tabs__content-item-wrapper')) { 268 console.error('Next sibling is not a content item'); 269 return; 270 } 271 272 const animationDuration = this.options.accordionDuration; 273 // content.style.transition = "height 0.3s ease-in"; 274 content.style.transition = `height ${animationDuration}ms ease-in`; 275 setTimeout(() => { 276 content.style.height = 0; 277 setTimeout(() => { 278 if (content.parentNode) { 279 content.parentNode.removeChild(content); 280 content.style.height = null; 281 } 282 }, 300); 283 }, 0); 284 } 285 286 removeVisibleContentItems() { 287 const visibleItems = this.tabList.querySelectorAll( 288 '.fr-tabs__content-item-wrapper' 289 ); 290 291 visibleItems.forEach((contentItem) => { 292 const prevElement = contentItem.previousElementSibling; 293 if (prevElement && prevElement.classList.contains('fr-tabs__link')) { 294 this.removeContentItemWithTransition(prevElement); 295 } else { 296 contentItem.remove(); // This should not happen in normal case, but just to be sure not to leave any orphan contentItems 297 } 298 }); 299 } 300 301 isNewContentItem(tabLink) { 302 303 let nextSibling = tabLink.nextSibling; 304 305 while (nextSibling && nextSibling.nodeType !== Node.ELEMENT_NODE) { 306 nextSibling = nextSibling.nextSibling; 307 } 308 309 return ( nextSibling === null || !nextSibling.classList.contains('fr-tabs__content-item-wrapper')); 310 } 311 312 addContentItem(tabLink, contentItem) { 313 tabLink.parentNode.insertBefore(contentItem, tabLink.nextSibling); 314 } 315 316 removeContentItem(tabLink) { 317 tabLink.parentNode.removeChild(tabLink.nextSibling); 318 } 319 320 addAccordionClickListeners() { 321 this.tabLinks.forEach((tabLink, index) => { 322 const listener = this.accordionClickListener(tabLink, index); 323 this.accordionClickListeners.set(tabLink, listener); // save the listener in the map 324 tabLink.addEventListener('click', listener); 325 326 // Adding a keydown event listener for the "Enter" key 327 tabLink.addEventListener('keydown', (event) => { 328 if (event.key === 'Enter' || event.keyCode === 13) { 329 // "Enter" key 330 event.preventDefault(); 331 listener(); // Trigger the accordion toggle logic 332 } 333 }); 334 }); 335 } 336 337 removeAccordionClickListeners() { 338 this.tabLinks.forEach((tabLink) => { 339 const listener = this.accordionClickListeners.get(tabLink); // get the listener from the map 340 if (listener) { 341 tabLink.removeEventListener('click', listener); 342 343 // Remove the keydown event listener for the "Enter" key 344 tabLink.removeEventListener('keydown', (event) => { 345 if (event.key === 'Enter' || event.keyCode === 13) { 346 // "Enter" key 347 event.preventDefault(); 348 listener();
348 // Trigger the accordion toggle logic 349 } 350 }); 351 } 352 }); 353 this.accordionClickListeners.clear(); // clear the map 354 } 355 356 /////////////////////////// 357 // End accordion methods // 358 /////////////////////////// 359 360 /////////////////////// 361 // METHODS 362 /////////////////////// 363 364 setListDirection() { 365 if (this.options.isHorizontal) { 366 this.tabList.classList.add('horizontal'); 367 this.tabList.classList.remove('vertical'); 368 } else { 369 this.tabList.classList.add('vertical'); 370 this.tabList.classList.remove('horizontal'); 371 } 372 } 373 374 toggleDirectionClass() { 375 if (this.options.isHorizontal) { 376 this.tabList.classList.add('fr-tabs__list--horizontal'); 377 this.tabList.classList.remove('fr-tabs__list--vertical'); 378 } else { 379 this.tabList.classList.add('fr-tabs__list--vertical'); 380 this.tabList.classList.remove('fr-tabs__list--horizontal'); 381 } 382 } 383 384 clickToCenterTabLink() { 385 const tabLinksWidth = this.tabList.scrollWidth; 386 const tabsWrapperWidth = this.tabList.offsetWidth; 387 if (tabLinksWidth > tabsWrapperWidth) { 388 this.tabLinks.forEach((tabLink) => { 389 tabLink.addEventListener('click', () => { 390 const tabLinkPosition = tabLink.offsetLeft; 391 const tabLinkWidth = tabLink.offsetWidth; 392 const tabLinkCenter = tabLinkPosition + tabLinkWidth / 2; 393 const scrollAmount = tabLinkCenter - tabsWrapperWidth / 2; 394 this.tabList.scrollTo({ 395 left: scrollAmount, 396 behavior: 'smooth', 397 }); 398 }); 399 }); 400 } 401 } 402 403 // Horizontal scrollbar visibility 404 405 // TODO removing it for now, lets see if there is an isolated case about it 406 /*hideHorizontalScrollbar() { 407 const tabLinksWidth = this.tabList.scrollWidth; 408 const tabsWrapperWidth = this.tabList.offsetWidth; 409 if (tabLinksWidth <= tabsWrapperWidth) { 410 this.tabList.classList.add("fr-tabs__list--no-scroll"); 411 } else { 412 this.tabList.classList.remove("fr-tabs__list--no-scroll"); 413 } 414 }*/ 415 416 // Scroll To hash on load or on click 417 418 handleScrollToHash() { 419 if (!this.options.scrollToHash) return; 420 421 // new: handle click event 422 document.addEventListener('click', (e) => { 423 if (e.target.closest('.fr-tabs__link')) return; 424 425 const link = e.target.closest('a[href^="#"]'); 426 if (!link) return; 427 428 const targetLink = this.element.querySelector(`.fr-tabs__link[id="${link.hash.replace('#', '')}"]`); 429 if (!targetLink) return; 430 431 e.preventDefault(); 432 const tabIndex = Array.from(this.tabLinks).indexOf(targetLink); 433 this.scrollToTabAndActivate(targetLink, tabIndex); 434 435 //update url with hash and id 436 const newUrl = `${window.location.pathname}${window.location.search}${link.hash}`; 437 history.pushState(null, '', newUrl); 438 }); 439 440 // new: set up click handlers for all tab links 441 // this.tabLinks.forEach((tabLink, index) => { 442 // const id = `#${tabLink.id}`; 443 // if (!id) return; 444 445 // tabLink.addEventListener('click', (e) => { 446 // e.preventDefault(); 447 // this.scrollToTabAndActivate(tabLink, index); 448 449 // // update url with the hash 450 // const newUrl = `${window.location.pathname}${window.location.search}${id}`; 451 // history.pushState(null, '', newUrl); 452 // }); 453 // }); 454 this.tabLinks.forEach((tabLink, index) => { 455 tabLink.addEventListener('click', (e) => { 456 e.preventDefault(); 457 // Just switch tabs, no scroll 458 this.switchTab(tabLink, index); 459 460 const id = `#${tabLink.id}`; 461 const newUrl = `${window.location.pathname}${window.location.search}${id}`; 462 history.pushState(null, '', newUrl); 463 }); 464 }); 465 466 // browser back/forward 467 window.addEventListener('popstate', () => { 468 if (!window.location.hash) return; 469 470 const targetLink = this.element.querySelector(`.fr-tabs__link[id="${window.location.hash.replace('#', '')}"]`); 471 if (!targetLink) return; 472 473 const tabIndex = Array.from(this.tabLinks).indexOf(targetLink); 474 this.scrollToTabAndActivate(targetLink, tabIndex); 475 }); 476 } 477 478 handleInitialHash() { 479 if (this.initialHashHandled) return; 480 481 const hash = window.location.hash; 482 const targetLink = this.element.querySelector(`.fr-tabs__link[id="${hash.replace('#', '')}"]`); 483 484 if (!targetLink) return; 485 486 if (document.readyState === 'complete') { 487 this.scrollToInitialHash(targetLink); 488 } else { 489 window.addEventListener('load', () => this.scrollToInitialHash(targetLink)); 490 } 491 492 this.initialHashHandled = true; 493 } 494 495 scrollToInitialHash(tabLink) { 496 const tabIndex = Array.from(this.tabLinks).indexOf(tabLink); 497 this.switchTab(tabLink, tabIndex); 498 499 setTimeout(() => { 500 const totalOffset = this.options.scrollToHashOffset || 0; 501 502 // Check if it's in accordion mode 503 const isAccordion = this.options.isChangeToAccordion && window.innerWidth <= this.options.accordionOnDevice; 504 505 const scrollTarget = isAccordion ? tabLink : this.element; 506 const elementPosition = scrollTarget.getBoundingClientRect().top + window.scrollY - totalOffset; 507 508 if (isAccordion) {
509 const listener = this.accordionClickListeners.get(tabLink); 510 if (listener) { 511 listener(); 512 } 513 } 514 515 window.scrollTo({ 516 top: Math.max(0, elementPosition), 517 behavior: 'instant' 518 }); 519 }, this.options.animation?.duration || 300); 520 } 521 522 scrollToTabAndActivate(tabLink, index) { 523 this.switchTab(tabLink, index); 524 525 const totalOffset = this.options.scrollToHashOffset || 0; //TODO andre: there's no setting for Offset 526 527 // Check if we're in accordion mode 528 const isAccordion = this.options.isChangeToAccordion && 529 window.innerWidth <= this.options.accordionOnDevice; 530 531 // Scroll to tab if accordion, otherwise scroll to container 532 const scrollTarget = isAccordion ? tabLink : this.element; 533 const elementPosition = scrollTarget.getBoundingClientRect().top + window.scrollY - totalOffset; 534 535 if (isAccordion) { 536 const listener = this.accordionClickListeners.get(tabLink); 537 if (listener) { 538 listener(); 539 } 540 } 541 542 window.scrollTo({ 543 top: Math.max(0, elementPosition), 544 behavior: 'smooth' 545 }); 546 } 547 548 // // TODO : duration time for scrolling doesn't work 549 // scrollToTabAndActivate(tabLink, index) { 550 // this.switchTab(tabLink, index); 551 // const offsetPosition = 552 // tabLink.offsetTop - this.options.scrollToHashOffset; 553 // scrollTo(offsetPosition, 500, easeOutCuaic); 554 // } 555 556 557 // Keyboard navigation in nav and inside content 558 559 addKeyboardNavigation(tabLink, index) { 560 tabLink.addEventListener('keydown', (event) => { 561 let newIndex; 562 switch (event.key) { 563 case 'ArrowLeft': // Applies to horizontal tabs 564 if (this.options.isHorizontal) { 565 event.preventDefault(); 566 newIndex = index > 0 ? index - 1 : this.tabLinks.length - 1; 567 this.switchTab(this.tabLinks[newIndex], newIndex); 568 this.tabLinks[newIndex].focus(); 569 } 570 break; 571 case 'ArrowRight': // Applies to horizontal tabs 572 if (this.options.isHorizontal) { 573 event.preventDefault(); 574 newIndex = index < this.tabLinks.length - 1 ? index + 1 : 0; 575 this.switchTab(this.tabLinks[newIndex], newIndex); 576 this.tabLinks[newIndex].focus(); 577 578 } 579 break; 580 case 'ArrowDown': // applies to the Vertical tabs 581 if (!this.options.isHorizontal) { 582 event.preventDefault(); 583 newIndex = index < this.tabLinks.length - 1 ? index + 1 : 0; 584 this.switchTab(this.tabLinks[newIndex], newIndex); 585 this.tabLinks[newIndex].focus(); 586 } 587 break; 588 case 'ArrowUp': // Applies to the Vertical tabs 589 if (!this.options.isHorizontal) { 590 event.preventDefault(); 591 newIndex = index > 0 ? index - 1 : this.tabLinks.length - 1; 592 this.switchTab(this.tabLinks[newIndex], newIndex); 593 this.tabLinks[newIndex].focus(); 594 } 595 break; 596 } 597 }); 598 } 599 600 // Animation methods 601 createAnimatedElement() { 602 const activeTab = this.options.activeTab; 603 604 this.animatedElement = document.createElement('div'); 605 this.animatedElement.className = 'fr-tabs__animation'; 606 this.animatedElement.setAttribute('aria-hidden', 'true'); 607 this.tabList.appendChild(this.animatedElement); 608 609 if (this.tabLinks && activeTab < this.tabLinks.length) { 610 this.positionAnimatedElement(this.tabLinks[activeTab]); 611 } 612 } 613 614 positionAnimatedElement(activeTabElement) { 615 setTimeout(() => { 616 const tabWidth = activeTabElement.offsetWidth; 617 const tabLeft = activeTabElement.offsetLeft; 618 const tabHeight = activeTabElement.offsetHeight; 619 const tabTop = activeTabElement.offsetTop; 620 621 this.animatedElement.style.width = `${tabWidth}px`; 622 this.animatedElement.style.left = `${tabLeft}px`; 623 this.animatedElement.style.height = `${tabHeight}px`; 624 this.animatedElement.style.top = `${tabTop}px`; 625 626 const duration = this.options.animation.duration || 300; 627 this.animatedElement.style.transition = `all ${duration}ms ease-in-out`; 628 }, 0); 629 } 630 631 handleActiveLinkAnimation(newActiveTab) { 632 const isAccordion = this.tabList.classList.contains( 633 'fr-tabs__list--accordion' 634 ); 635 if (!this.options.isChangeToAccordion) { 636 this.tabLinks.forEach((tabLink) => { 637 tabLink.classList.remove('fr-tabs__link--trans'); 638 }); 639 newActiveTab.classList.add('fr-tabs__link--trans'); 640 } 641 642 this.positionAnimatedElement(newActiveTab); 643 644 const duration = isAccordion ? 0 : this.options.animation.duration; 645 if (this.options.animation.duration) { 646 this.tabLinks.forEach((tabLink) => { 647 // tabLink.style.transition = `all ${this.options.animation.duration}ms ease-in-out`; 648 tabLink.style.transition = `all ${duration}ms ease-in-out`; 649 }); 650 } 651 } 652
653 updateBackgroundAnimation() { 654 // Only proceed if there's a background animation 655 if (!this.animatedElement) { 656 return; 657 } 658 659 const activeTab = this.element.querySelector('.fr-tabs__link.active'); 660 if (activeTab) { 661 this.handleActiveLinkAnimation(activeTab); 662 } 663 } 664 665 // Accessibility 666 667 addRoles() { 668 this.tabList.setAttribute('role', 'tablist'); 669 this.tabLinks.forEach((tabLink) => { 670 tabLink.setAttribute('role', 'tab'); 671 }); 672 this.contentItems.forEach((contentItem) => { 673 contentItem.setAttribute('role', 'tabpanel'); 674 }); 675 } 676 677 addAriaControls() { 678 this.tabLinks.forEach((tabLink, index) => { 679 if (tabLink.id) return; 680 tabLink.id = `fr-tabs__link-${index}`; 681 }); 682 this.contentItems.forEach((contentItem, index) => { 683 if (contentItem.id) return; 684 contentItem.id = `fr-tabs__panel-${index}`; 685 }); 686 687 this.tabLinks.forEach((tabLink, index) => { 688 const tabPanelId = this.contentItems[index].id; 689 tabLink.setAttribute('aria-controls', tabPanelId); 690 }); 691 this.contentItems.forEach((contentItem, index) => { 692 const tabPanelId = this.tabLinks[index].id; 693 contentItem.setAttribute('aria-labelledby', tabPanelId); 694 }); 695 } 696 697 // Main Tabs Switch Method 698 699 // switchTab(newActiveTab, index) { 700 // if (newActiveTab.closest('.fr-tabs') !== this.element) { 701 // return; 702 // } 703 704 // this.tabLinks.forEach((tabLink) => { 705 // const isActive = tabLink === newActiveTab; 706 707 // if (isActive) { 708 // tabLink.setAttribute('aria-selected', isActive); 709 // tabLink.setAttribute('tabindex', '0'); 710 // tabLink.classList.toggle('active', isActive); 711 // } else { 712 // tabLink.removeAttribute('aria-selected', isActive); 713 // tabLink.setAttribute('tabindex', '-1'); 714 // tabLink.classList.toggle('active', isActive); 715 // } 716 // }); 717 718 // this.contentItems.forEach((contentItem, contentIndex) => { 719 // const isActiveContent = contentIndex === index; 720 // contentItem.classList.toggle('active', isActiveContent); 721 // contentItem.setAttribute('tabindex', isActiveContent ? '0' : '-1'); 722 // }); 723 724 // if (this.options.animate) { 725 // this.handleActiveLinkAnimation(newActiveTab); 726 // } 727 // } 728 729 switchTab(newActiveTab, index) { 730 if (newActiveTab.closest('.fr-tabs') !== this.element) { 731 return; 732 } 733 734 // deactivate all tabs 735 this.tabLinks.forEach((tabLink) => { 736 tabLink.setAttribute('aria-selected', 'false'); 737 tabLink.setAttribute('tabindex', '-1'); 738 tabLink.classList.remove('active'); 739 }); 740 741 // activate the selected tab 742 newActiveTab.setAttribute('aria-selected', 'true'); 743 newActiveTab.setAttribute('tabindex', '0'); 744 newActiveTab.classList.add('active'); 745 746 // hanlde content visibility 747 this.contentItems.forEach((contentItem, contentIndex) => { 748 const isActiveContent = contentIndex === index; 749 contentItem.classList.toggle('active', isActiveContent); 750 contentItem.setAttribute('tabindex', isActiveContent ? '0' : '-1'); 751 }); 752 753 if (this.options.animate) { 754 this.handleActiveLinkAnimation(newActiveTab); 755 } 756 } 757 } 758 759 // Initialize the Tabs component for each fr-tabs element on the page 760 document.querySelectorAll('.fr-tabs').forEach((element) => { 761 new Tabs(element); 762 }); 763 764 // helpers 765 766 function scrollTo(c, e, d) { 767 d || (d = easeOutCuaic); 768 var a = document.documentElement; 769 if (0 === a.scrollTop) { 770 var b = a.scrollTop; 771 ++a.scrollTop; 772 a = b + 1 === a.scrollTop-- ? a : document.body; 773 } 774 b = a.scrollTop; 775 0 >= e || 776 ('object' === typeof b && (b = b.offsetTop), 777 'object' === typeof c && (c = c.offsetTop), 778 (function (a, b, c, f, d, e, h) { 779 function g() { 780 0 > f || 1 < f || 0 >= d 781 ? (a.scrollTop = c) 782 : ((a.scrollTop = b - (b - c) * h(f)), 783 (f += d * e), 784 setTimeout(g, e)); 785 } 786 g(); 787 })(a, b, c, 0, 1 / e, 20, d)); 788 } 789 function easeOutCuaic(t) { 790 t--; 791 return t * t * t + 1; 792 } 793} 794 795document.addEventListener('DOMContentLoaded', function (e) { 796 bricksIsFrontend && tabs_script(); 797});
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.