PageSourceSearch

https://sydneypetdentistry.com.au/wp-content/plugins/frames-plugin…/Widgets/tabs/js/tabs.js?ver=1787274737

js sydneypetdentistry.com.au collected 2026-10-08 02:16:34 UTC 24,203 bytes, 797 lines download raw bytes

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.