PageSourceSearch

https://sempresister.com/wp-content/plugins/elemental/elementor-wi…/elemental-menu/js/frontend.js?ver=0.0.10

js sempresister.com collected 2026-10-05 01:03:30 UTC 6,679 bytes, 202 lines download raw bytes

1/**
2 * Elemental Menu - off-canvas mobile behavior.
3 *
4 * Open/close the off-canvas panel, drill into submenus (each level swipes in
5 * from the right) and step back out. No dependencies. Each widget instance is
6 * initialized once (guarded), and re-initialized in the Elementor editor.
7 */
8( function () {
9	'use strict';
10
11	var PANEL = '.elemental-menu__panel.elemental-menu__container';
12	var SUBMENU = '.elemental-menu__panel-body .elemental-menu__panel';
13
14	function initMenu( root ) {
15		if ( ! root || root.dataset.elementalMenuInit === '1' ) {
16			return;
17		}
18
19		var panel = root.querySelector( PANEL );
20		if ( ! panel ) {
21			return;
22		}
23
24		root.dataset.elementalMenuInit = '1';
25
26		// Custom breakpoint: the tablet/mobile presets collapse to the off-canvas
27		// menu via static CSS media queries, but a user-defined width (Layout >
28		// Breakpoint > Custom) can't be a static media query. When present, match
29		// it here and toggle `.is-collapsed`, which triggers the same mobile mode.
30		const breakpoint = root.getAttribute( 'data-elemental-breakpoint' );
31		if ( breakpoint ) {
32			const breakpointPx = parseInt( breakpoint, 10 );
33			if ( breakpointPx > 0 && window.matchMedia ) {
34				const mql = window.matchMedia( '(max-width: ' + breakpointPx + 'px)' );
35				const applyBreakpoint = function () {
36					root.classList.toggle( 'is-collapsed', mql.matches );
37				};
38				applyBreakpoint();
39				// addEventListener is the modern API; addListener is the fallback
40				// for older Safari where MediaQueryList predates EventTarget.
41				if ( mql.addEventListener ) {
42					mql.addEventListener( 'change', applyBreakpoint );
43				} else if ( mql.addListener ) {
44					mql.addListener( applyBreakpoint );
45				}
46			}
47		}
48
49		// Scroll state: when enabled (data-scroll-state), toggle `.is-scrolled` on
50		// the root as soon as the page is scrolled down at all. Purely a styling
51		// hook (the Scroll State controls recolor the desktop bar); no positioning.
52		if ( '1' === root.dataset.scrollState ) {
53			var ticking = false;
54			var syncScrolled = function () {
55				ticking = false;
56				var scrolled = ( window.pageYOffset || document.documentElement.scrollTop || 0 ) > 0;
57				root.classList.toggle( 'is-scrolled', scrolled );
58			};
59			var onScroll = function () {
60				if ( ! ticking ) {
61					ticking = true;
62					window.requestAnimationFrame( syncScrolled );
63				}
64			};
65			window.addEventListener( 'scroll', onScroll, { passive: true } );
66			// Set the initial state (e.g. the page loaded already scrolled).
67			syncScrolled();
68		}
69
70		var backdrop = root.querySelector( '.elemental-menu__backdrop' );
71
72		function resetLevels() {
73			panel.querySelectorAll( SUBMENU + '.is-active' ).forEach( function ( submenu ) {
74				submenu.classList.remove( 'is-active' );
75			} );
76		}
77
78		function openPanel() {
79			panel.classList.add( 'is-open' );
80			panel.setAttribute( 'aria-hidden', 'false' );
81			document.body.classList.add( 'elemental-menu-open' );
82
83			root.querySelectorAll( '.elemental-menu__open-button' ).forEach( function ( btn ) {
84				btn.setAttribute( 'aria-expanded', 'true' );
85			} );
86
87			if ( backdrop ) {
88				backdrop.hidden = false;
89				// Next frame so the opacity transition runs.
90				window.requestAnimationFrame( function () {
91					backdrop.classList.add( 'is-visible' );
92				} );
93			}
94		}
95
96		function closePanel() {
97			resetLevels();
98			panel.classList.remove( 'is-open' );
99			panel.setAttribute( 'aria-hidden', 'true' );
100			document.body.classList.remove( 'elemental-menu-open' );
101
102			root.querySelectorAll( '.elemental-menu__open-button' ).forEach( function ( btn ) {
103				btn.setAttribute( 'aria-expanded', 'false' );
104			} );
105
106			if ( backdrop ) {
107				backdrop.classList.remove( 'is-visible' );
108				backdrop.hidden = true;
109			}
110		}
111
112		root.querySelectorAll( '.elemental-menu__open-button' ).forEach( function ( btn ) {
113			btn.setAttribute( 'aria-expanded', 'false' );
114			btn.addEventListener( 'click', function ( e ) {
115				e.preventDefault();
116				openPanel();
117			} );
118		} );
119
120		panel.querySelectorAll( '.elemental-menu__close-button' ).forEach( function ( btn ) {
121			btn.addEventListener( 'click', function ( e ) {
122				e.preventDefault();
123				closePanel();
124			} );
125		} );
126
127		if ( backdrop ) {
128			backdrop.addEventListener( 'click', closePanel );
129		}
130
131		// Drill into a submenu, or step back out.
132		panel.addEventListener( 'click', function ( e ) {
133			var back = e.target.closest( '.elemental-menu__back' );
134			if ( back && panel.contains( back ) ) {
135				e.preventDefault();
136				// Step back one level: deactivate the deepest active submenu. Nested
137				// levels follow their parents in document order, so the last active
138				// one is the deepest.
139				var actives = panel.querySelectorAll( SUBMENU + '.is-active' );
140				if ( actives.length ) {
141					actives[ actives.length - 1 ].classList.remove( 'is-active' );
142				}
143				return;
144			}
145
146			var link = e.target.closest( 'a.elemental-menu__link, a.elemental-menu__sublink' );
147			if ( link && panel.contains( link ) ) {
148				var li = link.closest( 'li' );
149				var submenu = li ? li.querySelector( ':scope > .elemental-menu__panel' ) : null;
150				if ( submenu ) {
151					// Parent item: drill in instead of navigating.
152					e.preventDefault();
153					submenu.classList.add( 'is-active' );
154				}
155			}
156		} );
157
158		// Escape closes the panel.
159		document.addEventListener( 'keydown', function ( e ) {
160			if ( 'Escape' === e.key && panel.classList.contains( 'is-open' ) ) {
161				closePanel();
162			}
163		} );
164
165		// If the viewport grows past the breakpoint while the panel is open, the
166		// panel is hidden by the media query but the backdrop + scroll-lock would
167		// persist - force a clean close so the page stays interactive.
168		window.addEventListener( 'resize', function () {
169			if ( panel.classList.contains( 'is-open' ) && 'none' === window.getComputedStyle( panel ).display ) {
170				closePanel();
171			}
172		} );
173	}
174
175	function initAll( context ) {
176		( context || document ).querySelectorAll( '.elemental-menu' ).forEach( initMenu );
177	}
178
179	if ( 'loading' === document.readyState ) {
180		document.addEventListener( 'DOMContentLoaded', function () {
181			initAll();
182		} );
183	} else {
184		initAll();
185	}
186
187	// Elementor editor: (re)initialize when the widget is rendered in the preview.
188	window.addEventListener( 'elementor/frontend/init', function () {
189		if ( window.elementorFrontend && window.elementorFrontend.hooks ) {
190			window.elementorFrontend.hooks.addAction(
191				'frontend/element_ready/elemental-elemental-menu.default',
192				function ( $scope ) {
193					var el = $scope && $scope[0] ? $scope[0] : $scope;
194					if ( ! el || ! el.querySelector ) {
195						return;
196					}
197					initMenu( el.querySelector( '.elemental-menu' ) || el );
198				}
199			);
200		}
201	} );
202} )();

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.