1/** 2 * Polyfill for IE11 - adds NodeList.foreach(). 3 * 4 * @see https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach 5 */ 6if ( window.NodeList && ! NodeList.prototype.forEach ) { 7 NodeList.prototype.forEach = function( callback, thisArg ) { 8 thisArg = thisArg || window; 9 for ( var i = 0; i < this.length; i++ ) { // eslint-disable-line vars-on-top 10 callback.call( thisArg, this[ i ], i, this ); 11 } 12 }; 13} 14 15window.alxMediaMenu = { 16 17 /** 18 * 19 * @param {Object} args - The arguments. 20 * @param {string} args.selector - The navigation selector. 21 * @param {int} args.breakpoint - The breakpoint in pixels. 22 */ 23 init: function( args ) { 24 var self = this, 25 navs = document.querySelectorAll( args.selector ); 26 27 if ( ! navs.length ) { 28 return; 29 } 30 31 navs.forEach( function( nav ) { 32 var menuToggler = nav.querySelector( '.menu-toggle' ); 33 34 // Hide menu toggle button if menu is empty and return early. 35 if ( ! nav.querySelector( 'ul' ) && nav.querySelector( '.menu-toggle' ) ) { 36 nav.querySelector( '.menu-toggle' ).style.display = 'none'; 37 } 38 39 // Add nav-menu class. 40 if ( ! nav.classList.contains( 'nav-menu' ) ) { 41 nav.classList.add( 'nav-menu' ); 42 } 43 44 // Toggle the hover event listeners. 45 self.toggleHoverEventListeners( nav ); 46 47 // Toggle focus classes on links. 48 nav.querySelectorAll( 'a,button' ).forEach( function( link ) { 49 link.addEventListener( 'focus', window.alxMediaMenu.toggleFocus, true ); 50 link.addEventListener( 'blur', window.alxMediaMenu.toggleFocus, true ); 51 }); 52 53 menuToggler.addEventListener( 'click', function() { 54 if ( nav.classList.contains( 'toggled' ) ) { 55 menuToggler.setAttribute( 'aria-expanded', 'false' ); 56 nav.classList.remove( 'toggled' ); 57 } else { 58 menuToggler.setAttribute( 'aria-expanded', 'true' ); 59 nav.classList.add( 'toggled' ); 60 } 61 }); 62 63 // If on mobile nav, close it when clicking outside. 64 // If on desktop, close expanded submenus when clicking outside. 65 document.addEventListener( 'click', function( event ) { 66 if ( ! nav.contains( event.target ) ) { 67 68 // Mobile. 69 nav.classList.remove( 'toggled' ); 70 71 // Desktop. 72 nav.querySelectorAll( 'button.active,.sub-menu.active' ).forEach( function( el ) { 73 el.classList.remove( 'active' ); 74 }); 75 76 menuToggler.setAttribute( 'aria-expanded', 'false' ); 77 } 78 }); 79 }); 80 81 // Toggle mobile classes on initial load. 82 window.alxMediaMenu.toggleMobile( args.selector, args.breakpoint ); 83 84 // Toggle mobile classes on resize. 85 window.addEventListener( 'resize', function() { 86 87 // If timer is null, reset it to our bounceDelay and run, otherwise wait until timer is cleared. 88 if ( ! window.resizeDebouncedTimeout ) { 89 window.resizeDebouncedTimeout = setTimeout( function() { 90 window.resizeDebouncedTimeout = null; 91 window.alxMediaMenu.toggleMobile( args.selector, args.breakpoint ); 92 }, 250 ); 93 } 94 }); 95 96 // Toggle focus classes to allow submenu access on tables. 97 document.querySelectorAll( args.selector ).forEach( function( el ) { 98 window.alxMediaMenu.toggleFocusTouch( el ); 99 }); 100 }, 101 102 /** 103 * Expand a menu item. 104 * 105 * @param {Element} - The menu item (DOM element). 106 * @return {void} 107 */ 108 toggleItem: function( el ) { 109 var parentLi = this.helper.firstAncestorMatch( el, 'li' ), 110 parentUl = this.helper.firstAncestorMatch( el, 'ul' ), 111 ul = parentLi.querySelector( 'ul.sub-menu' ); 112 113 parentLi.classList.remove( 'hover' ); 114 115 ul.setAttribute( 'tabindex', '-1' ); 116 this.helper.toggleClass( ul, 'active' ); 117 this.helper.toggleClass( el, 'active' ); 118 119 // Go one level up in the list, and close other items that are already open. 120 parentUl.querySelectorAll( 'ul.sub-menu' ).forEach( function( subMenu ) { 121 var subMenuButton; 122 if ( ! parentLi.contains( subMenu ) ) { 123 subMenu.classList.remove( 'active' ); 124 subMenuButton = subMenu.parentNode.querySelector( 'button.active' ); 125 if ( subMenuButton ) { 126 subMenuButton.classList.remove( 'active' ); 127 } 128 } 129 }); 130 }, 131 132 /** 133 * Toggles a mobile class to elements matching our selector, 134 * depending on the defined breakpoint. 135 * 136 * @param {string} selector - The elements where we want to toggle our mobile class. 137 * @param {string} className - The class-name we want to toggle. 138 * @param {int} breakpoint - The breakpoint. 139 * @return {void} 140 */ 141 toggleMobile: function( selector, breakpoint ) { 142 var self = this, 143 screenWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, 144 navs = document.body.querySelectorAll( selector ), 145 isMobile; 146 147 breakpoint = breakpoint || 720; 148 isMobile = breakpoint > screenWidth; 149 150 if ( isMobile ) {
151 navs.forEach( function( nav ) { 152 if ( ! nav.classList.contains( 'mobile' ) ) { 153 nav.classList.add( 'mobile' ); 154 self.toggleHoverEventListeners( nav ); 155 } 156 }); 157 } else { 158 navs.forEach( function( nav ) { 159 if ( nav.classList.contains( 'mobile' ) ) { 160 nav.classList.remove( 'mobile' ); 161 self.toggleHoverEventListeners( nav ); 162 } 163 }); 164 } 165 }, 166 167 /** 168 * Add a "hover" class. 169 * 170 * @return {void} 171 */ 172 liMouseEnterEvent: function() { 173 this.classList.add( 'hover' ); 174 }, 175 176 /** 177 * Remove the "hover" class. 178 * 179 * @return {void} 180 */ 181 liMouseLeaveEvent: function() { 182 this.classList.remove( 'hover' ); 183 }, 184 185 /** 186 * 187 * @param {Element} nav - The nav element. 188 * @return {void} 189 */ 190 toggleHoverEventListeners: function( nav ) { 191 if ( nav.classList.contains( 'mobile' ) ) { 192 this.removeHoverEventListeners( nav ); 193 } else { 194 this.addHoverEventListeners( nav ); 195 } 196 }, 197 198 /** 199 * Add event-listeners for hover events. 200 * 201 * @param {Element} nav - The nav element. 202 * @return {void} 203 */
204 addHoverEventListeners: function( nav ) { 205 nav.querySelectorAll( 'li' ).forEach( function( li ) { 206 li.addEventListener( 'mouseenter', window.alxMediaMenu.liMouseEnterEvent ); 207 li.addEventListener( 'mouseleave', window.alxMediaMenu.liMouseLeaveEvent ); 208 }); 209 }, 210 211 /** 212 * Remove event-listeners for hover events. 213 * 214 * @param {Element} nav - The nav element. 215 * @return {void} 216 */ 217 removeHoverEventListeners: function( nav ) { 218 nav.querySelectorAll( 'li' ).forEach( function( li ) { 219 li.removeEventListener( 'mouseenter', window.alxMediaMenu.liMouseEnterEvent ); 220 li.removeEventListener( 'mouseleave', window.alxMediaMenu.liMouseLeaveEvent ); 221 }); 222 }, 223 224 /** 225 * Sets or removes .focus class on an element. 226 * 227 * @return {void} 228 */ 229 toggleFocus: function() { 230 var self = this; 231 232 // Move up through the ancestors of the current link until we hit .nav-menu. 233 while ( -1 === self.className.indexOf( 'nav-menu' ) ) { 234 // On li elements toggle the class .focus. 235 if ( 'li' === self.tagName.toLowerCase() ) { 236 if ( -1 !== self.className.indexOf( 'focus' ) ) { 237 self.className = self.className.replace( ' focus', '' ); 238 } else { 239 self.className += ' focus'; 240 } 241 } 242 243 self = self.parentElement; 244 } 245 }, 246 247 /** 248 * Toggle focus classes to allow submenu access on tables. 249 * 250 * @param {Element} el - The menu element. 251 * @return {void} 252 */ 253 toggleFocusTouch: function( el ) { 254 var touchStartFn, 255 parentLinks = el.querySelectorAll( '.menu-item-has-children > a, .page_item_has_children > a' ); 256 257 if ( 'ontouchstart' in window ) { 258 touchStartFn = function( e ) { 259 var menuItem = this.parentNode; 260 261 if ( ! menuItem.classList.contains( 'focus' ) ) { 262 e.preventDefault(); 263 menuItem.parentNode.children.forEach( function( child ) { 264 if ( menuItem !== child ) { 265 child.classList.remove( 'focus' ); 266 } 267 }); 268 menuItem.classList.add( 'focus' ); 269 } else { 270 menuItem.classList.remove( 'focus' ); 271 } 272 }; 273 274 parentLinks.forEach( function( parentLink ) { 275 parentLink.addEventListener( 'touchstart', touchStartFn, false ); 276 }); 277 } 278 }, 279 280 /** 281 * Helper methods. 282 */ 283 helper: { 284 285 /** 286 * Toggle a class to an element. 287 * 288 * @param {Element} el - The element. 289 * @param {string} className - The class we want to toggle. 290 * @return {void} 291 */ 292 toggleClass: function( el, className ) { 293 if ( el.classList.contains( className ) ) { 294 el.classList.remove( className ); 295 } else { 296 el.classList.add( className ); 297 } 298 }, 299 300 /** 301 * Get the 1st ancestor of an element that matches our selector. 302 * 303 * @param {Element} el - The element. 304 * @param {string} selector - The class we want to toggle. 305 * @return {Element} 306 */ 307 firstAncestorMatch: function( el, selector ) { 308 if ( el.parentNode.matches( selector ) ) { 309 return el.parentNode; 310 } 311 return this.firstAncestorMatch( el.parentNode, selector ); 312 } 313 } 314}; 315 316window.alxMediaMenu.init({ 317 selector: '.main-navigation.nav-menu', 318 breakpoint: 720 319});
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.