PageSourceSearch

https://multilogin.com/wp-content/themes/hello-child-com/assets/js/native-header.js?ver=1790346332

js multilogin.com collected 2026-10-02 02:52:36 UTC 22,155 bytes, 685 lines download raw bytes

1/**
2 * File navigation.js.
3 *
4 * Handles toggling the navigation menu for small screens and enables TAB key
5 * navigation support for dropdown menus.
6 */
7( function() {
8	const siteNavigation = document.getElementById( 'site-navigation' );
9
10	// Return early if the navigation doesn't exist.
11	if ( ! siteNavigation ) {
12		return;
13	}
14
15	const button = siteNavigation.getElementsByTagName( 'button' )[ 0 ];
16
17	// Return early if the button doesn't exist.
18	if ( 'undefined' === typeof button ) {
19		return;
20	}
21
22	const menu = siteNavigation.getElementsByTagName( 'ul' )[ 0 ];
23	const mobileQuery = window.matchMedia( '(max-width: 1100px)' );
24
25	// Hide menu toggle button if menu is empty and return early.
26	if ( 'undefined' === typeof menu ) {
27		button.style.display = 'none';
28		return;
29	}
30
31	if ( ! menu.classList.contains( 'nav-menu' ) ) {
32		menu.classList.add( 'nav-menu' );
33	}
34
35	// Toggle the .toggled class and the aria-expanded value each time the button is clicked.
36	button.addEventListener( 'click', function() {
37		setMobileMenuState( button.getAttribute( 'aria-expanded' ) !== 'true' );
38	} );
39
40	const submenuTriggers = siteNavigation.querySelectorAll( '.header-menu__trigger, .header-menu__link--submenu-trigger' );
41	const header = document.getElementById( 'masthead' );
42	const megaShell = header ? header.querySelector( '.header-mega-shell' ) : null;
43	const megaShellInner = megaShell ? megaShell.querySelector( '.header-mega-shell__inner' ) : null;
44	const desktopQuery = window.matchMedia( '(min-width: 1101px)' );
45	const submenuItems = Array.from( siteNavigation.querySelectorAll( '.header-menu > .header-menu__item--has-submenu' ) );
46	const megaItems = [];
47	const disclosureAnimations = new WeakMap();
48	const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
49	let activeMegaIndex = -1;
50	let suppressFocusOpen = false;
51	let closeMegaTimer = 0;
52	let megaAnimationTimer = 0;
53
54	if ( megaShell && megaShellInner && submenuItems.length ) {
55		setupDesktopMegaMenu();
56	}
57
58	for ( const trigger of submenuTriggers ) {
59		trigger.addEventListener( 'click', function( event ) {
60			if ( ! mobileQuery.matches ) {
61				return;
62			}
63
64			if ( 'A' === trigger.tagName ) {
65				event.preventDefault();
66			}
67
68			const menuItem = trigger.closest( '.header-menu__item--has-submenu' );
69
70			if ( ! menuItem ) {
71				return;
72			}
73
74			const isExpanded = trigger.getAttribute( 'aria-expanded' ) === 'true';
75
76			for ( const openItem of siteNavigation.querySelectorAll( '.header-menu__item--submenu-open' ) ) {
77				if ( openItem !== menuItem ) {
78					const openSubmenu = openItem.querySelector( ':scope > .header-mega-menu' );
79					const openTrigger = openItem.querySelector( '.header-menu__trigger, .header-menu__link--submenu-trigger' );
80
81					if ( openSubmenu ) {
82						animateDisclosure( openSubmenu, openItem, 'header-menu__item--submenu-open', false );
83					}
84
85					if ( openTrigger ) {
86						openTrigger.setAttribute( 'aria-expanded', 'false' );
87					}
88				}
89			}
90
91			const submenu = menuItem.querySelector( ':scope > .header-mega-menu' );
92
93			if ( submenu ) {
94				animateDisclosure( submenu, menuItem, 'header-menu__item--submenu-open', ! isExpanded );
95			}
96
97			trigger.setAttribute( 'aria-expanded', isExpanded ? 'false' : 'true' );
98		} );
99	}
100
101	const languageTriggers = document.querySelectorAll( '.site-header__language-trigger' );
102
103	for ( const trigger of languageTriggers ) {
104		trigger.addEventListener( 'click', function() {
105			const switcher = trigger.closest( '.site-header__language-switcher' );
106
107			if ( ! switcher ) {
108				return;
109			}
110
111			const isExpanded = trigger.getAttribute( 'aria-expanded' ) === 'true';
112
113			for ( const openSwitcher of document.querySelectorAll( '.site-header__language-switcher.is-open' ) ) {
114				if ( openSwitcher !== switcher ) {
115					const openList = openSwitcher.querySelector( '.site-header__language-list' );
116					const openTrigger = openSwitcher.querySelector( '.site-header__language-trigger' );
117
118					if ( openList ) {
119						animateDisclosure( openList, openSwitcher, 'is-open', false );
120					}
121
122					if ( openTrigger ) {
123						openTrigger.setAttribute( 'aria-expanded', 'false' );
124					}
125				}
126			}
127
128			const languageList = switcher.querySelector( '.site-header__language-list' );
129
130			if ( mobileQuery.matches && languageList ) {
131				animateDisclosure( languageList, switcher, 'is-open', ! isExpanded );
132			} else {
133				switcher.classList.toggle( 'is-open', ! isExpanded );
134			}
135
136			trigger.setAttribute( 'aria-expanded', isExpanded ? 'false' : 'true' );
137		} );
138	}
139
140	// Remove the .toggled class and set aria-expanded to false when the user clicks outside the navigation.
141	document.addEventListener( 'click', function( event ) {
142		const isClickInside = siteNavigation.contains( event.target );
143
144		if ( ! isClickInside ) {
145			setMobileMenuState( false );
146			closeMegaMenu();
147			closeLanguageMenus();
148			for ( const trigger of submenuTriggers ) {
149				trigger.setAttribute( 'aria-expanded', 'false' );
150			}
151			closeSubmenus();
152		}
153	} );
154
155	for ( const link of menu.querySelectorAll( 'a' ) ) {
156		link.addEventListener( 'click', function() {
157			if ( mobileQuery.matches && ! link.classList.contains( 'header-menu__link--submenu-trigger' ) ) {
158				setMobileMenuState( false );
159			}
160		} );
161	}
162
163	document.addEventListener( 'keydown', function( keyEvent ) {
164		if ( keyEvent.key === 'Escape' ) {
165			setMobileMenuState( false );
166			closeLanguageMenus();
167		}
168	} );
169
170	mobileQuery.addEventListener( 'change', function() {
171		if ( ! mobileQuery.matches ) {
172			setMobileMenuState( false );
173		}
174	} );
175
176	// Get all the link elements within the menu.
177	const links = menu.getElementsByTagName( 'a' );
178
179	// Get all the link elements with children within the menu.
180	const linksWithChildren = menu.querySelectorAll( '.menu-item-has-children > a, .page_item_has_children > a' );
181
182	// Toggle focus each time a menu link is focused or blurred.
183	for ( const link of links ) {
184		link.addEventListener( 'focus', toggleFocus, true );
185		link.addEventListener( 'blur', toggleFocus, true );
186	}
187
188	// Toggle focus each time a menu link with children receive a touch event.
189	for ( const link of linksWithChildren ) {
190		link.addEventListener( 'touchstart', toggleFocus, false );
191	}
192
193	/**
194	 * Sets or removes .focus class on an element.
195	 */
196	function toggleFocus( event ) {
197		// Mobile disclosures are controlled only by their explicit toggle button.
198		if ( mobileQuery.matches ) {
199			return;
200		}
201
202		if ( event.type === 'focus' || event.type === 'blur' ) {
203			let self = this;
204			// Move up through the ancestors of the current link until we hit
205			// .nav-menu. Desktop submenus are relocated out of the menu, so guard
206			// against walking off the top of the tree.
207			while ( self && self.classList && ! self.classList.contains( 'nav-menu' ) ) {
208				// On li elements toggle the class .focus.
209				if ( 'li' === self.tagName.toLowerCase() ) {
210					self.classList.toggle( 'focus' );
211				}
212				self = self.parentNode;
213			}
214		}
215
216		if ( event.type === 'touchstart' ) {
217			const menuItem = this.parentNode;
218			event.preventDefault();
219			for ( const link of menuItem.parentNode.children ) {
220				if ( menuItem !== link ) {
221					link.classList.remove( 'focus' );
222				}
223			}
224			menuItem.classList.toggle( 'focus' );
225		}
226	}
227
228	function animateDisclosure( disclosure, host, openClass, isOpen ) {
229		const reduceMotion = window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches;
230		const previousAnimation = disclosureAnimations.get( disclosure );
231
232		if ( previousAnimation && previousAnimation.frame ) {
233			window.cancelAnimationFrame( previousAnimation.frame );
234		}
235
236		disclosure.ontransitionend = null;
237		disclosure.ontransitioncancel = null;
238
239		if ( reduceMotion ) {
240			host.classList.toggle( openClass, isOpen );
241			disclosure.style.removeProperty( 'height' );
242			disclosureAnimations.delete( disclosure );
243			return;
244		}
245
246		const currentHeight = disclosure.getBoundingClientRect().height;
247		disclosure.style.height = currentHeight + 'px';
248
249		if ( isOpen ) {
250			host.classList.add( openClass );
251		} else {
252			host.classList.remove( openClass );
253		}
254
255		disclosure.getBoundingClientRect();
256
257		const animation = { frame: 0 };
258		disclosureAnimations.set( disclosure, animation );
259		animation.frame = window.requestAnimationFrame( function() {
260			if ( disclosureAnimations.get( disclosure ) !== animation ) {
261				return;
262			}
263
264			disclosure.style.setProperty( 'height', ( isOpen ? disclosure.scrollHeight : 0 ) + 'px', 'important' );
265			animation.frame = 0;
266		} );
267
268		const finishAnimation = function() {
269			if ( disclosureAnimations.get( disclosure ) !== animation ) {
270				return;
271			}
272
273			disclosure.style.removeProperty( 'height' );
274			disclosure.ontransitionend = null;
275			disclosure.ontransitioncancel = null;
276			disclosureAnimations.delete( disclosure );
277		};
278
279		disclosure.ontransitionend = function( transitionEvent ) {
280			if ( 'height' !== transitionEvent.propertyName ) {
281				return;
282			}
283
284			finishAnimation();
285		};
286		disclosure.ontransitioncancel = function( transitionEvent ) {
287			if ( 'height' !== transitionEvent.propertyName ) {
288				return;
289			}
290
291			finishAnimation();
292		};
293	}
294
295	function setMobileMenuState( isOpen ) {
296		siteNavigation.classList.toggle( 'toggled', isOpen );
297		button.setAttribute( 'aria-expanded', isOpen ? 'true' : 'false' );
298		document.body.classList.toggle( 'mobile-menu-open', isOpen && mobileQuery.matches );
299
300		if ( ! isOpen ) {
301			closeLanguageMenus();
302			closeSubmenus();
303		}
304	}
305
306	function closeSubmenus() {
307		for ( const item of siteNavigation.querySelectorAll( '.header-menu__item--submenu-open' ) ) {
308			const submenu = item.querySelector( ':scope > .header-mega-menu' );
309			const trigger = item.querySelector( ':scope > .header-menu__trigger, :scope > .header-menu__link--submenu-trigger' );
310
311			if ( mobileQuery.matches && submenu ) {
312				animateDisclosure( submenu, item, 'header-menu__item--submenu-open', false );
313			} else {
314				item.classList.remove( 'header-menu__item--submenu-open' );
315			}
316
317			if ( trigger ) {
318				trigger.setAttribute( 'aria-expanded', 'false' );
319			}
320		}
321
322		for ( const item of siteNavigation.querySelectorAll( '.header-menu__item.focus' ) ) {
323			item.classList.remove( 'focus' );
324		}
325	}
326
327	function closeLanguageMenus() {
328		for ( const switcher of document.querySelectorAll( '.site-header__language-switcher.is-open' ) ) {
329			const languageList = switcher.querySelector( '.site-header__language-list' );
330			const trigger = switcher.querySelector( '.site-header__language-trigger' );
331
332			if ( mobileQuery.matches && languageList ) {
333				animateDisclosure( languageList, switcher, 'is-open', false );
334			} else {
335				switcher.classList.remove( 'is-open' );
336			}
337
338			if ( trigger ) {
339				trigger.setAttribute( 'aria-expanded', 'false' );
340			}
341		}
342	}
343
344	function setupDesktopMegaMenu() {
345		const plainMenuItems = Array.from( siteNavigation.querySelectorAll( '.header-menu > .header-menu__item:not(.header-menu__item--has-submenu)' ) );
346
347		submenuItems.forEach( function( menuItem, index ) {
348			const submenu = menuItem.querySelector( ':scope > .header-mega-menu' );
349			const trigger = menuItem.querySelector( ':scope > .header-menu__trigger, :scope > .header-menu__link' );
350
351			if ( ! submenu || ! trigger ) {
352				return;
353			}
354
355			const panel = document.createElement( 'div' );
356			panel.className = 'header-mega-panel';
357			panel.dataset.megaIndex = String( index );
358			panel.appendChild( submenu );
359			megaShellInner.appendChild( panel );
360
361			menuItem.dataset.megaIndex = String( index );
362			trigger.setAttribute( 'aria-controls', 'header-mega-panel-' + index );
363			submenu.id = 'header-mega-panel-' + index;
364
365			megaItems.push( {
366				menuItem,
367				panel,
368				submenu,
369				trigger,
370			} );
371
372			menuItem.addEventListener( 'mouseenter', function() {
373				if ( desktopQuery.matches ) {
374					openMegaMenu( index );
375				}
376			} );
377
378			menuItem.addEventListener( 'focusin', function() {
379				if ( desktopQuery.matches && ! suppressFocusOpen ) {
380					openMegaMenu( index );
381				}
382			} );
383
384			/*
385			 * The panel is rendered inside .header-mega-shell, a sibling of the
386			 * header bar rather than a child of this <li>, so the browser's tab
387			 * order walks past every remaining header control before reaching it,
388			 * and each of those stops closes this panel again. Wire the two
389			 * boundaries (trigger -> panel, panel -> rest of the bar) by hand.
390			 */
391			trigger.addEventListener( 'keydown', function( keyEvent ) {
392				if ( ! desktopQuery.matches ) {
393					return;
394				}
395
396				const isConfirmKey = 'Enter' === keyEvent.key || ' ' === keyEvent.key || 'Spacebar' === keyEvent.key;
397				// A trigger with a real URL keeps Enter for navigation; only the
398				// hash-only <button> triggers treat it as "step into the panel".
399				const entersPanel = 'ArrowDown' === keyEvent.key || ( isConfirmKey && 'BUTTON' === trigger.tagName );
400
401				if ( entersPanel ) {
402					keyEvent.preventDefault();
403					openMegaMenu( index );
404					focusFirstInPanel( panel );
405					return;
406				}
407
408				if ( 'Tab' === keyEvent.key && ! keyEvent.shiftKey && activeMegaIndex === index && focusFirstInPanel( panel ) ) {
409					keyEvent.preventDefault();
410				}
411			} );
412
413			panel.addEventListener( 'keydown', function( keyEvent ) {
414				if ( 'Tab' !== keyEvent.key || ! desktopQuery.matches ) {
415					return;
416				}
417
418				const focusables = getPanelFocusables( panel );
419
420				if ( ! focusables.length ) {
421					return;
422				}
423
424				if ( keyEvent.shiftKey && document.activeElement === focusables[ 0 ] ) {
425					keyEvent.preventDefault();
426					trigger.focus();
427					return;
428				}
429
430				if ( ! keyEvent.shiftKey && document.activeElement === focusables[ focusables.length - 1 ] ) {
431					const nextTarget = getNextHeaderBarTabbable( trigger );
432
433					// Nothing follows the trigger in the bar, so let the browser
434					// carry focus on into the page; focusout closes the panel.
435					if ( ! nextTarget ) {
436						return;
437					}
438
439					keyEvent.preventDefault();
440					closeMegaMenu();
441					nextTarget.focus();
442				}
443			} );
444
445			// Focus can also land in the panel from a screen reader's own cursor
446			// or a Shift+Tab out of the header buttons; keep its owner open.
447			panel.addEventListener( 'focusin', function() {
448				if ( desktopQuery.matches && ! suppressFocusOpen ) {
449					openMegaMenu( index );
450				}
451			} );
452		} );
453
454		plainMenuItems.forEach( function( menuItem ) {
455			menuItem.addEventListener( 'mouseenter', function() {
456				if ( desktopQuery.matches ) {
457					closeMegaMenu();
458				}
459			} );
460
461			menuItem.addEventListener( 'focusin', function() {
462				if ( desktopQuery.matches ) {
463					closeMegaMenu();
464				}
465			} );
466		} );
467
468		moveMegaMenus();
469		siteNavigation.addEventListener( 'mouseleave', scheduleMegaClose );
470		megaShell.addEventListener( 'mouseenter', cancelMegaClose );
471		megaShell.addEventListener( 'mouseleave', scheduleMegaClose );
472
473		document.addEventListener( 'keydown', function( keyEvent ) {
474			if ( keyEvent.key !== 'Escape' ) {
475				return;
476			}
477
478			const openItem = megaItems.find( function( item ) {
479				return item.menuItem.dataset.megaIndex === String( activeMegaIndex );
480			} );
481			const wasFocusInside = !! openItem && megaShell.contains( document.activeElement );
482
483			closeMegaMenu();
484
485			if ( wasFocusInside ) {
486				focusWithoutOpening( openItem.trigger );
487			}
488		} );
489
490		// Tabbing or clicking straight out of the header leaves the panel open
491		// otherwise, since the shell sits outside #site-navigation.
492		header.addEventListener( 'focusout', function( focusEvent ) {
493			const nextTarget = focusEvent.relatedTarget;
494
495			// A null relatedTarget also covers clicking dead space inside the open
496			// panel, so only act on a move to another focusable element.
497			if ( ! desktopQuery.matches || ! nextTarget ) {
498				return;
499			}
500
501			if ( siteNavigation.contains( nextTarget ) || megaShell.contains( nextTarget ) ) {
502				return;
503			}
504
505			closeMegaMenu();
506		} );
507
508		desktopQuery.addEventListener( 'change', function() {
509			closeMegaMenu();
510			moveMegaMenus();
511		} );
512	}
513
514	function moveMegaMenus() {
515		for ( const item of megaItems ) {
516			if ( desktopQuery.matches ) {
517				item.submenu.style.removeProperty( 'height' );
518				item.submenu.ontransitionend = null;
519
520				if ( item.submenu.parentElement !== item.panel ) {
521					item.panel.appendChild( item.submenu );
522				}
523			} else if ( item.submenu.parentElement !== item.menuItem ) {
524				item.menuItem.appendChild( item.submenu );
525			}
526		}
527	}
528
529	function openMegaMenu( index ) {
530		const activeItem = megaItems.find( function( item ) {
531			return item.menuItem.dataset.megaIndex === String( index );
532		} );
533
534		if ( ! activeItem ) {
535			return;
536		}
537
538		cancelMegaClose();
539
540		// Focus moving between a trigger and its own panel re-fires focusin; do
541		// not replay the cross-fade for a panel that is already on screen.
542		if ( activeMegaIndex === index ) {
543			return;
544		}
545
546		const direction = -1 === activeMegaIndex || index > activeMegaIndex ? 'next' : 'previous';
547		const previousActiveItem = megaItems.find( function( item ) {
548			return item.menuItem.dataset.megaIndex === String( activeMegaIndex );
549		} );
550
551		if ( megaAnimationTimer ) {
552			window.clearTimeout( megaAnimationTimer );
553			megaAnimationTimer = 0;
554		}
555
556		megaShell.classList.remove( 'header-mega-shell--previous', 'header-mega-shell--next' );
557		megaShell.classList.add( 'header-mega-shell--' + direction, 'header-mega-shell--open' );
558		megaShell.setAttribute( 'aria-hidden', 'false' );
559		megaShell.removeAttribute( 'inert' );
560
561		for ( const item of megaItems ) {
562			const isActive = item === activeItem;
563			item.panel.classList.remove( 'header-mega-panel--entering', 'header-mega-panel--leaving' );
564			item.menuItem.classList.toggle( 'header-menu__item--submenu-open', isActive );
565			item.trigger.setAttribute( 'aria-expanded', isActive ? 'true' : 'false' );
566		}
567
568		if ( previousActiveItem && previousActiveItem !== activeItem ) {
569			previousActiveItem.panel.classList.add( 'header-mega-panel--leaving' );
570			previousActiveItem.panel.classList.remove( 'header-mega-panel--active' );
571		}
572
573		if ( previousActiveItem && previousActiveItem !== activeItem ) {
574			activeItem.panel.classList.add( 'header-mega-panel--entering' );
575			activeItem.panel.getBoundingClientRect();
576		}
577
578		activeItem.panel.classList.add( 'header-mega-panel--active' );
579		activeItem.panel.classList.remove( 'header-mega-panel--entering' );
580		// Round up so the clipped shell does not shave a pixel off the last row.
581		megaShell.style.setProperty( '--header-mega-height', Math.ceil( activeItem.submenu.getBoundingClientRect().height ) + 'px' );
582		activeMegaIndex = index;
583
584		megaAnimationTimer = window.setTimeout( function() {
585			for ( const item of megaItems ) {
586				if ( item !== activeItem ) {
587					item.panel.classList.remove( 'header-mega-panel--active' );
588				}
589
590				item.panel.classList.remove( 'header-mega-panel--entering', 'header-mega-panel--leaving' );
591			}
592			megaAnimationTimer = 0;
593		}, 280 );
594	}
595
596	function isVisibleForFocus( element ) {
597		// display:none elements have no boxes; visibility:hidden ones still do,
598		// and both are skipped by the browser's own tab order.
599		if ( ! element.getClientRects().length ) {
600			return false;
601		}
602
603		return 'hidden' !== window.getComputedStyle( element ).visibility;
604	}
605
606	function getPanelFocusables( panel ) {
607		return Array.from( panel.querySelectorAll( FOCUSABLE_SELECTOR ) ).filter( isVisibleForFocus );
608	}
609
610	function focusFirstInPanel( panel ) {
611		const focusables = getPanelFocusables( panel );
612
613		if ( ! focusables.length ) {
614			return false;
615		}
616
617		focusables[ 0 ].focus();
618		return true;
619	}
620
621	/**
622	 * Whatever natively follows the trigger in the header bar, so leaving a
623	 * panel resumes the tab order where the panel interrupted it.
624	 */
625	function getNextHeaderBarTabbable( fromElement ) {
626		const bar = header ? header.querySelector( '.site-header__bar' ) : null;
627
628		if ( ! bar ) {
629			return null;
630		}
631
632		const tabbables = Array.from( bar.querySelectorAll( FOCUSABLE_SELECTOR ) ).filter( isVisibleForFocus );
633		const currentIndex = tabbables.indexOf( fromElement );
634
635		return -1 === currentIndex ? null : tabbables[ currentIndex + 1 ] || null;
636	}
637
638	/**
639	 * Returning focus to a trigger would re-trigger its focusin and reopen the
640	 * panel we just dismissed, so mute that handler for this one focus move.
641	 */
642	function focusWithoutOpening( element ) {
643		suppressFocusOpen = true;
644		element.focus();
645		window.setTimeout( function() {
646			suppressFocusOpen = false;
647		}, 0 );
648	}
649
650	function scheduleMegaClose() {
651		cancelMegaClose();
652		closeMegaTimer = window.setTimeout( closeMegaMenu, 120 );
653	}
654
655	function cancelMegaClose() {
656		if ( closeMegaTimer ) {
657			window.clearTimeout( closeMegaTimer );
658			closeMegaTimer = 0;
659		}
660	}
661
662	function closeMegaMenu() {
663		cancelMegaClose();
664		activeMegaIndex = -1;
665
666		if ( megaShell ) {
667			megaShell.classList.remove( 'header-mega-shell--open', 'header-mega-shell--previous', 'header-mega-shell--next' );
668			megaShell.setAttribute( 'aria-hidden', 'true' );
669			megaShell.setAttribute( 'inert', '' );
670			megaShell.style.removeProperty( '--header-mega-height' );
671		}
672
673		if ( megaAnimationTimer ) {
674			window.clearTimeout( megaAnimationTimer );
675			megaAnimationTimer = 0;
676		}
677
678		for ( const item of megaItems ) {
679			item.panel.classList.remove( 'header-mega-panel--active', 'header-mega-panel--entering', 'header-mega-panel--leaving' );
680			item.menuItem.classList.remove( 'header-menu__item--submenu-open' );
681			item.trigger.setAttribute( 'aria-expanded', 'false' );
682		}
683	}
684}() );
685

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.