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.