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( 'masthead' ); 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.getElementsByClassName( 'header-menu-wrapper' )[ 0 ]; 23 24 // Hide menu toggle button if menu is empty and return early. 25 if ( 'undefined' === typeof menu ) { 26 button.style.display = 'none'; 27 return; 28 } 29 30 if ( ! menu.classList.contains( 'nav-menu' ) ) { 31 menu.classList.add( 'nav-menu' ); 32 } 33 34 const mobileQuery = window.matchMedia( '(max-width: 991px)' ); 35 36 function isMobile() { 37 return mobileQuery.matches; 38 } 39 40 41 /* -------------------------------------------------------- 42 OPEN / CLOSE HELPERS â explicit state, never a blind toggle 43 -------------------------------------------------------- */ 44 45 function setExpanded( item, open ) { 46 item.classList.toggle( 'focus', open ); 47 48 const link = item.querySelector( ':scope > a' ); 49 50 if ( link ) { 51 link.setAttribute( 'aria-expanded', open ? 'true' : 'false' ); 52 } 53 } 54 55 function closeDescendants( item ) { 56 item.querySelectorAll( 'li.focus' ).forEach( function( child ) { 57 setExpanded( child, false ); 58 } ); 59 } 60 61 function closeSiblings( item ) { 62 Array.prototype.forEach.call( item.parentNode.children, function( sibling ) { 63 if ( sibling !== item && sibling.classList.contains( 'focus' ) ) { 64 closeDescendants( sibling ); 65 setExpanded( sibling, false ); 66 } 67 } ); 68 } 69 70 function openItem( item ) { 71 closeSiblings( item ); 72 setExpanded( item, true ); 73 } 74 75 function closeItem( item ) { 76 closeDescendants( item ); 77 setExpanded( item, false ); 78 } 79 80 function closeAll() { 81 menu.querySelectorAll( 'li.focus' ).forEach( function( item ) { 82 setExpanded( item, false ); 83 } ); 84 } 85 86 87 /* -------------------------------------------------------- 88 HAMBURGER 89 -------------------------------------------------------- */ 90 91 button.addEventListener( 'click', function() { 92 siteNavigation.classList.toggle( 'toggled' ); 93 menuIconAnimation(); 94 95 const isOpen = siteNavigation.classList.contains( 'toggled' ); 96 97 button.setAttribute( 'aria-expanded', isOpen ? 'true' : 'false' ); 98 99 // Reset submenu state whenever the drawer closes. 100 if ( ! isOpen ) { 101 closeAll(); 102 } 103 } ); 104 105 // Close when clicking outside the navigation. 106 document.addEventListener( 'click', function( event ) { 107 const isClickInside = siteNavigation.contains( event.target ); 108 109 if ( ! isClickInside ) { 110 siteNavigation.classList.remove( 'toggled' ); 111 button.setAttribute( 'aria-expanded', 'false' ); 112 closeAll(); 113 } 114 } ); 115 116 117 /* -------------------------------------------------------- 118 SUBMENUS 119 -------------------------------------------------------- */ 120 121 const links = menu.getElementsByTagName( 'a' ); 122 const linksWithChildren = menu.querySelectorAll( '.menu-item-has-children > a, .page_item_has_children > a' ); 123 124 // Desktop keyboard navigation only. 125 for ( const link of links ) { 126 link.addEventListener( 'focus', toggleFocus, true ); 127 link.addEventListener( 'blur', toggleFocus, true ); 128 } 129 130 // Mobile: click owns the submenu, so touchstart is not registered at all. 131 for ( const link of linksWithChildren ) { 132 link.addEventListener( 'click', function( event ) { 133 if ( ! isMobile() ) { 134 return; 135 } 136 137 event.preventDefault(); 138 event.stopPropagation(); 139 140 const item = this.parentNode; 141 142 if ( item.classList.contains( 'focus' ) ) { 143 closeItem( item ); 144 } else { 145 openItem( item ); 146 } 147 } ); 148 } 149 150 /** 151 * Sets or removes .focus on ancestors of a focused link (desktop only). 152 */ 153 function toggleFocus( event ) { 154 if ( isMobile() ) { 155 return; 156 } 157 158 const open = ( event.type === 'focus' ); 159 let self = this; 160 161 while ( self && ! self.classList.contains( 'nav-menu' ) ) { 162 if ( 'li' === self.tagName.toLowerCase() ) { 163 self.classList.toggle( 'focus', open ); 164 } 165 self = self.parentNode; 166 } 167 } 168 169 // Clear stale state when crossing the breakpoint. 170 mobileQuery.addEventListener( 'change', closeAll ); 171 172 173 /* -------------------------------------------------------- 174 ICON 175 -------------------------------------------------------- */ 176 177 function menuIconAnimation() { 178 if ( typeof gsap === 'undefined' ) { 179 return; 180 } 181 182 const isOpen = siteNavigation.classList.contains( 'toggled' ); 183 184 const hamburger = { 185 top: 'M30 25L10 25', 186 bottom: 'M30 15L10 15', 187 }; 188 189 const close = { 190 top: 'M28.333 11.6666L11.6663 28.3333', 191 bottom: 'M28.333 28.3334L11.6663 11.6667', 192 }; 193 194 gsap.to( '.line-top', { 195 attr: { d: isOpen ? close.top : hamburger.top }, 196 duration: 0.4, 197 ease: 'power3.inOut', 198 } ); 199 200 gsap.to( '.line-bottom', { 201 attr: { d: isOpen ? close.bottom : hamburger.bottom }, 202 duration: 0.4, 203 ease: 'power3.inOut', 204 } ); 205 } 206}() ); 207 208// Hide and show navbar on scroll 209jQuery(function ($) { 210 let lastScroll = 0; 211 const $nav = $("#masthead"); 212 213 $(window).on("scroll", function () {
214 let currentScroll = $(this).scrollTop(); 215 216 // Always show navbar at the top 217 if (currentScroll <= 0) { 218 $nav.removeClass("nav-hide nav-show").addClass("nav-top"); 219 lastScroll = 0; 220 return; 221 } 222 $nav.removeClass("nav-top"); 223 if (currentScroll > lastScroll && currentScroll > 20) { 224 // Scrolling Down 225 $nav.removeClass("nav-show").addClass("nav-hide"); 226 } else if (currentScroll < lastScroll) { 227 // Scrolling Up 228 $nav.removeClass("nav-hide").addClass("nav-show"); 229 } 230 231 lastScroll = currentScroll; 232 }); 233});
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.