PageSourceSearch

https://www.nosotrostambienreciclamos.es/wp-content/plugins/suprem…/js/advanced-tabs.js?ver=6ac7b16c908b8

js nosotrostambienreciclamos.es collected 2026-10-08 15:06:55 UTC 7,794 bytes, 286 lines download raw bytes

1/**
2 * Class representing an advanced tabbed interface with support for fully independent nested tabs.
3 */
4class AdvancedTabs {
5	/**
6	 * Create a tabbed interface.
7	 * @param {HTMLElement} container - The container element for the tabs.
8	 */
9	constructor( container ) {
10		this.container = container;
11		this.tabs = container.querySelectorAll(
12			':scope > .dsm-advanced-tabs-wrapper > .dsm-tab'
13		);
14		this.tabsContent = container.querySelectorAll(
15			':scope > .dsm-advanced-tabs-content-wrapper > .dsm-content-wrapper'
16		);
17		this.trigger = container.dataset.trigger;
18		this.animationName = container.dataset.animation;
19		this.innerAnimationName = container.dataset.inner_animation;
20		this.deepLink = container.dataset.deep_link === 'on';
21		this.autoScrollMobile = container.dataset.auto_scroll_mobile === 'on';
22		this.anchorLink = container.dataset.anchor_link === 'on';
23
24		this.init();
25	}
26
27	/**
28	 * Initialize the tabbed interface.
29	 */
30	init() {
31		this.setupEventListeners();
32		this.handleInitialState();
33	}
34
35	/**
36	 * Set up event listeners for the tabs.
37	 */
38	setupEventListeners() {
39		this.tabs.forEach( ( tab ) => {
40			if ( this.trigger === 'click' ) {
41				tab.addEventListener( 'click', ( e ) =>
42					this.handleEvent( e, tab )
43				);
44			} else if ( this.trigger === 'hover' ) {
45				tab.addEventListener( 'mouseover', ( e ) =>
46					this.handleEvent( e, tab )
47				);
48			}
49			tab.addEventListener( 'keydown', ( event ) => {
50				if ( event.key === 'Enter' ) {
51					this.handleEvent( event, tab );
52				}
53			} );
54		} );
55	}
56
57	/**
58	 * Handle the initial state of the tabs.
59	 */
60	handleInitialState() {
61		let activeTab = this.tabs[ 0 ]; // Default to first tab
62
63		// Agar hash majood hai, uske mutabiq tab dhond lein
64		if ( this.deepLink && location.hash ) {
65			const hash = location.hash.substring( 1 );
66			const matchingTab = Array.from( this.tabs ).find( ( tab ) => {
67				const tabTitle = tab.querySelector( '.dsm-title' );
68				return (
69					tabTitle &&
70					tabTitle.textContent
71						.toLowerCase()
72						.replace( /\s+/g, '-' ) === hash
73				);
74			} );
75			if ( matchingTab ) {
76				activeTab = matchingTab;
77			}
78		}
79
80		// CHANGED: Pass "false" so that we do NOT pushState on page load
81		this.setActiveTab( activeTab, false );
82
83		this.anchorInitScroll();
84
85		// Agar hash hai (yaani user ne #kuchHash ke sath page open kiya),
86		// to scroll karein. Agar hash nahin, to skip.
87		if ( this.deepLink && location.hash && activeTab ) {
88			const tabId = activeTab.dataset.tabid.trim();
89			const content = Array.from( this.tabsContent ).find(
90				( content ) => content.dataset.contentid.trim() === tabId
91			);
92
93			if ( content ) {
94				setTimeout( () => {
95					content.scrollIntoView( {
96						behavior: 'smooth',
97						block: 'start',
98						inline: 'center',
99					} );
100				}, 300 ); // Adjust timeout as needed
101			}
102		}
103	}
104
105	/**
106	 * Handle tab events (click or hover).
107	 * @param {Event} event - The event object.
108	 * @param {HTMLElement} tab - The tab element.
109	 */
110	handleEvent( event, tab ) {
111		event.stopPropagation();
112		// CHANGED: On click, let it push the state (default = true)
113		this.setActiveTab( tab );
114		this.autoScrollOnMobile( tab );
115	}
116
117	/**
118	 * Set the active tab.
119	 * @param {HTMLElement} activeTab - The tab to set as active.
120	 * @param {boolean} [shouldPushState=true] - Whether or not to push hash to URL. (CHANGED)
121	 */
122	setActiveTab( activeTab, shouldPushState = true ) {
123		const tabId = activeTab.dataset.tabid.trim();
124		const content = Array.from( this.tabsContent ).find(
125			( content ) => content.dataset.contentid.trim() === tabId
126		);
127
128		if ( ! content ) {
129			console.error(
130				`No matching content found for tab with id: ${ tabId }`
131			);
132			return;
133		}
134
135		// Remove active class from all tabs and content
136		this.tabs.forEach( ( tab ) =>
137			tab.classList.remove( 'dsm-active', this.animationName )
138		);
139		this.tabsContent.forEach( ( content ) => {
140			content.classList.remove( 'dsm-active', this.animationName );
141			const inner = content.querySelector( '.dsm-inner-content-wrapper' );
142			if ( inner ) {
143				inner.classList.remove( 'animated', this.innerAnimationName );
144			}
145		} );
146
147		// Add active class to selected tab and content
148		activeTab.classList.add( 'dsm-active' );
149		content.classList.add( 'dsm-active', this.animationName );
150		const innerContent = content.querySelector(
151			'.dsm-inner-content-wrapper'
152		);
153		if ( innerContent ) {
154			innerContent.classList.add( 'animated', this.innerAnimationName );
155		}
156
157		// Handle deep linking, but ONLY if shouldPushState == true
158		if ( this.deepLink && shouldPushState ) {
159			const tabTitle = activeTab.querySelector( '.dsm-title' );
160			if ( tabTitle ) {
161				const hash = tabTitle.textContent
162					.toLowerCase()
163					.replace( /\s+/g, '-' );
164				history.pushState( null, null, `#${ hash }` );
165				this.container.classList.add( 'dsm-active-deep-link' );
166			}
167		}
168	}
169
170	/**
171	 * Handle auto scroll on mobile.
172	 * @param {HTMLElement} activeTab - The active tab element.
173	 */
174	autoScrollOnMobile( activeTab ) {
175		const tabId = activeTab.dataset.tabid.trim();
176		const content = Array.from( this.tabsContent ).find(
177			( content ) => content.dataset.contentid.trim() === tabId
178		);
179
180		// Handle auto scroll on mobile
181		if ( this.autoScrollMobile && window.innerWidth <= 768 ) {
182			content.scrollIntoView( {
183				behavior: 'smooth',
184				block: 'start',
185				inline: 'nearest',
186			} );
187		}
188	}
189
190	/**
191	 * Scroll to the target element smoothly.
192	 * @param {HTMLElement} target - The target element to scroll to.
193	 */
194	anchorSmoothScroll( target ) {
195		// Check if anchor linking is enabled
196		if ( ! this.anchorLink ) {
197			return;
198		}
199
200		target.scrollIntoView( {
201			behavior: 'smooth',
202			block: 'start',
203		} );
204	}
205
206	/**
207	 * Find the matching tab by hash.
208	 * @param {string} hash - The hash to find the matching tab by.
209	 * @returns {HTMLElement|null} The matching tab or null if no match is found.
210	 */
211	findMatchingTab( hash ) {
212		if ( ! hash ) return null;
213
214		// First try to find tab by matching title
215		const matchingTab = Array.from( this.tabs ).find( ( tab ) => {
216			const tabTitle = tab.querySelector( '.dsm-title' );
217			return (
218				tabTitle &&
219				tabTitle.textContent.toLowerCase().replace( /\s+/g, '-' ) ===
220					hash
221			);
222		} );
223
224		if ( matchingTab ) {
225			this.setActiveTab( matchingTab );
226			return matchingTab;
227		}
228
229		// If no match by title, try matching by class
230		const matchingTabByClass = Array.from( this.tabs ).find( ( tab ) =>
231			tab.classList.contains( hash )
232		);
233
234		if ( matchingTabByClass ) {
235			this.setActiveTab( matchingTabByClass );
236			return matchingTabByClass;
237		}
238
239		return null;
240	}
241
242	/**
243	 * Initialize anchor linking.
244	 */
245	anchorInitScroll() {
246		const links = document.querySelectorAll( 'a' );
247
248		links.forEach( ( link ) => {
249			const href = link.getAttribute( 'href' );
250
251			if ( href?.startsWith( '#' ) && href.length > 1 ) {
252				link.addEventListener( 'click', ( e ) => {
253					const targetId = href.slice( 1 );
254					const matchingTab = this.findMatchingTab( targetId );
255
256					if ( matchingTab ) {
257						e.preventDefault(); // Only prevent default if we found a matching tab
258						this.anchorSmoothScroll( matchingTab );
259					}
260				} );
261			}
262		} );
263	}
264}
265
266// Initialize tabs when the DOM is fully loaded
267window.addEventListener( 'load', () => {
268	const initTabs = ( container ) => {
269		new AdvancedTabs( container );
270
271		// Initialize nested tabs
272		const nestedContainers = container.querySelectorAll(
273			':scope .dsm-content-wrapper .dsm-advanced-tabs-container'
274		);
275		nestedContainers.forEach( ( nestedContainer ) => {
276			initTabs( nestedContainer );
277		} );
278	};
279
280	const topLevelContainers = document.querySelectorAll(
281		'.dsm-advanced-tabs-container:not(.dsm-content-wrapper .dsm-advanced-tabs-container)'
282	);
283	topLevelContainers.forEach( ( container ) => {
284		initTabs( container );
285	} );
286} );

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.