vendor: 1,162 bytes, lines 1-12
1/** 2 * wp-nav-menus.js 3 * https://github.com/cedaro/wp-nav-menus 4 * 5 * @copyright Copyright (c) 2016 Cedaro, LLC 6 * @license MIT 7 * @version 1.0.0 8 */ 9(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.cedaroNavMenu = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 10(function (global){ 11"use strict"; 12var jQuery = (typeof window !== "undefined" ? window['jQuery'] : typeof global !== "undefined" ? global['jQuery'] : null),
13 wpNavMenu = require( './src/entry' ); 14 15if ( jQuery ) { 16 /** 17 * jQuery method to initialize menus. 18 * 19 * @memberOf jQuery.fn 20 * 21 * @param {Object} options Menu options. 22 * @return {jQuery} Chainable jQuery collection. 23 */ 24 jQuery.fn.cedaroNavMenu = function( options ) { 25 return this.each(function() { 26 var menu = wpNavMenu( this, options ); 27 menu.initialize(); 28 }); 29 }; 30} 31 32/** 33 * Export the method for instantiating a new menu. 34 */ 35module.exports = wpNavMenu; 36 37}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 38},{"./src/entry":2}],2:[function(require,module,exports){ 39(function (global){ 40"use strict"; 41var l10n, 42 $ = require( './query' ), 43 customizerPartials = {}, 44 NavMenu = require( './nav-menu.js' ), 45 wp = (typeof window !== "undefined" ? window['wp'] : typeof global !== "undefined" ? global['wp'] : null); 46 47// Merge global localized strings. 48l10n = $.extend({ 49 collapseSubmenu: 'Collapse submenu', 50 expandSubmenu: 'Expand submenu' 51}, global._cedaroNavMenuL10n ); 52 53/** 54 * Initialize a partial placement in the customizer. 55 * 56 * Managing menus in the customizer causes the placement to be replaced for 57 * each change, so it needs to be reinitialized each time to ensure submenus 58 * are accessible. 59 * 60 * @param {wp.customize.selectiveRefresh.Placement} placement Partial placement. 61 */ 62function initializeCustomizePlacement( placement ) { 63 var addedMenu, removedMenu; 64 65 if ( ! ( placement.partial.id in customizerPartials ) ) { 66 return; 67 } 68 69 removedMenu = customizerPartials[ placement.partial.id ]; 70 removedMenu.destroy(); 71 72 // Initialize the new placement. 73 addedMenu = new NavMenu( placement.container[0], removedMenu.options ); 74 addedMenu.initialize(); 75 76 // Synchronize expanded states from the removed menu. 77 placement.removedNodes.find( '.' + removedMenu.options.expandedMenuItemClass ).each(function() { 78 placement.container 79 .find( '#' + this.id ) 80 .find( '> .{class}, > a .{class}'.replace( /{class}/g, removedMenu.options.submenuToggleClass ) ) 81 .trigger( 'click' ); 82 }); 83} 84 85/** 86 * Selective refresh support in the customizer. 87 */ 88if ( wp && 'customize' in wp ) { 89 wp.customize.bind( 'preview-ready', function() { 90 wp.customize.selectiveRefresh.bind( 'partial-content-rendered', initializeCustomizePlacement ); 91 }); 92} 93 94/** 95 * Create a new menu object. 96 * 97 * @param {string|Element} el Selector or HTML element. 98 * @param {object} options Menu options. 99 * @return {NavMenu} Nav menu instance. 100 */ 101module.exports = function( el, options ) { 102 var menu, 103 partialId; 104 105 // Fail gracefully in unsupported browsers. 106 if ( ! ( 'addEventListener' in global ) ) { 107 return; 108 } 109 110 options = options || {}; 111 options.l10n = options.l10n || {}; 112 113 // Merge global localized strings. 114 $.extend( options.l10n, l10n ); 115 116 menu = new NavMenu( el, options ); 117 118 // Attempt to detect the partial id in the customizer and store a 119 // reference so it can be cleaned up during a selective refresh. 120 partialId = menu.el.getAttribute( 'data-customize-partial-id' ); 121 if ( partialId ) { 122 customizerPartials[ partialId ] = menu; 123 } 124 125 return menu; 126}; 127 128}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 129},{"./nav-menu.js":3,"./query":4}],3:[function(require,module,exports){ 130"use strict"; 131var $ = require ( './query' ), 132 document = window.document; 133 134/** 135 * Create a menu instance. 136 * 137 * @class NavMenu 138 * 139 * @param {Element|string} el HTML element or selector. 140 * @param {object} options Menu options. 141 * @param {string} options.activeMenuItemClass HTML class for active menu items. 142 * @param {number} options.breakpoint Breakpoint to switch between mobile and full site mode. 143 * @param {boolean} options.expandCurrentItem Whether to expand the current menu item on initial load. 144 * @param {string} options.expandedMenuItemClass HTML class applied to the menu item that contains an expanded submenu. 145 * @param {string} options.expandedSubmenuClass HTML class applied to expanded submenus. 146 * @param {string} options.expandedSubmenuToggleClass HTML class applied to submenu toggle buttons when the submenu is expanded. 147 * @param {number} options.hoverTimeout Delay in milliseconds before hiding submenus. 148 * @param {object} options.l10n Localized strings specific to this instance. 149 * @param {string} options.submenuToggleClass HTML class for submenu toggle buttons. 150 * @param {string} options.submenuToggleInsert Where to insert submenu toggle buttons. Defaults to before the submenu. 151 * @param {string} options.submenuToggleMode Mode for displaying submenus. 152 */ 153function NavMenu( el, options ) { 154 var menu = this; 155 156 this.el = $( el )[0]; 157 this.options = $.extend( {}, NavMenu.defaults, options ); 158 this.el.setAttribute( 'data-nav-menu-options', JSON.stringify( this.options ) ); 159 160 // Automatically bind all event handlers. 161 Object.keys( Object.getPrototypeOf( this ) ).filter(function( key ) { 162 return 'function' === typeof menu[ key ] && 0 === key.indexOf( 'on' ); 163 }
163).forEach(function( key ) { 164 menu[ key ] = menu[ key ].bind( menu ); 165 }); 166} 167 168NavMenu.defaults = { 169 breakpoint: 768, // disable 170 expandCurrentItem: false, 171 hoverTimeout: 50, 172 l10n: {}, 173 submenuToggleInsert: '', // default|append|false 174 submenuToggleMode: 'hover', // hover|click; @todo hover-tap? 175 176 // Configurable HTML classes. 177 activeMenuItemClass: 'is-active', 178 expandedMenuItemClass: 'is-sub-menu-open', 179 expandedSubmenuClass: 'is-open', 180 expandedSubmenuToggleClass: 'is-open', 181 submenuToggleClass: 'sub-menu-toggle' 182}; 183 184NavMenu.prototype = { 185 _initialized: false, 186 _hasExpandedSubmenu: false, 187 _hoverTimeouts: [], 188 _touchStarted: false, 189 _viewportWidth: null, 190 191 /** 192 * Initialize the menu. 193 * 194 * @return {this} 195 */ 196 initialize: function() { 197 var menu = this; 198 199 if ( this._initialized ) { 200 return; 201 } 202 203 this._initialized = true; 204 205 // Ensure every menu item has an id to manage the hover state timeouts. 206 this.$items = $( 'li', this.el ).each(function( el ) { 207 el.id = $.getUid( el, 'menu-item' ); 208 }); 209 210 this.$submenus = $( 'li ul', this.el ).each(function( el ) { 211 el.setAttribute( 'aria-expanded', 'false' ); 212 }); 213 214 if ( false !== this.options.submenuToggleInsert ) { 215 this.insertSubmenuToggleButtons(); 216 } 217 218 document.addEventListener( 'click', this.onDocumentClick, true ); 219 this.el.addEventListener( 'click', this.onSubmenuToggleClick, false ); 220 221 if ( 'hover' === this.options.submenuToggleMode /*&& 'disable' !== this.options.breakpoint*/ ) { 222 this.el.addEventListener( 'touchstart', this.onMenuLinkTouchStart, false ); 223 this.el.addEventListener( 'mouseover', this.onMenuItemMouseEnter, false ); 224 this.el.addEventListener( 'mouseout', this.onMenuItemMouseLeave, false ); 225 } 226 227 // Use capturing mode to support event delegation. 228 // @link https://developer.mozilla.org/en-US/docs/Web/Events/blur#Event_delegation 229 this.el.addEventListener( 'focus', this.onMenuLinkFocus, true ); 230 this.el.addEventListener( 'blur', this.onMenuLinkBlur, true ); 231 232 // Expand current submenus on initialization. 233 if ( 234 menu.options.expandCurrentItem && 235 ( 'click' === menu.options.submenuToggleMode || menu.isMobile() ) 236 ) { 237 this.$items.filter(function( el ) { 238 return el.classList.contains( 'current-menu-item' ) || el.classList.contains( 'current-menu-ancestor' ); 239 }).each(function( el ) { 240 menu.expandSubmenu( el ); 241 }); 242 } 243 244 return this; 245 }, 246 247 /** 248 * Destroy the menu. 249 * 250 * Cleans up delegated event listeners. 251 * 252 * @todo Consider removing toggle buttons. 253 */ 254 destroy: function() { 255 document.removeEventListener( 'click', this.onDocumentClick, true ); 256 this.el.removeEventListener( 'click', this.onSubmenuToggleClick, false ); 257 this.el.removeEventListener( 'mouseover', this.onMenuItemMouseEnter, false ); 258 this.el.removeEventListener( 'mouseout', this.onMenuItemMouseLeave, false ); 259 this.el.removeEventListener( 'focus', this.onMenuLinkFocus, true ); 260 this.el.removeEventListener( 'blur', this.onMenuLinkBlur, true ); 261 }, 262 263 /** 264 * Whether the menu is in mobile mode. 265 * 266 * @return {boolean} 267 */ 268 isMobile: function() { 269 if ( 'disable' === this.options.breakpoint ) { 270 return true; 271 } 272 273 return this.getViewportWidth() < this.options.breakpoint; 274 }, 275 276 /** 277 * Collapse the submenu in a menu item. 278 * 279 * @param {Element} menuItem Menu item element. 280 */ 281 collapseSubmenu: function( menuItem ) { 282 var button = this.getSubmenuToggle( menuItem ), 283 $submenu = $( '.sub-menu', menuItem ); 284 285 menuItem.classList.remove( this.options.expandedMenuItemClass ); 286 287 if ( button ) { 288 button.classList.remove( this.options.expandedSubmenuToggleClass ); 289 button.setAttribute( 'aria-expanded', 'false' ); 290 $( '.screen-reader-text', button )[0].innerHTML = this.options.l10n.expandSubmenu || 'Expand submenu'; 291 } 292 293 if ( $submenu.length ) { 294 $submenu[0].classList.remove( this.options.expandedSubmenuClass ); 295 $submenu[0].setAttribute( 'aria-expanded', 'false' ); 296 } 297 }, 298 299 /** 300 * Collapse all submenus. 301 */ 302 collapseAllSubmenus: function() { 303 var menu = this; 304 305 this.$items.each(function( el ) { 306 el.classList.remove( menu.options.activeMenuItemClass ); 307 menu.collapseSubmenu( el ); 308 }); 309 }, 310 311 /** 312 * Collapse submenus in branches that don't contain the focused element. 313 * 314 * @param {Element} focusedEl Element that has focus. 315 */ 316 collapseUnrelatedSubmenus: function( focusedEl ) { 317 var menu = this; 318 319 this.$items.each(function( el ) { 320 if ( ! el.contains( focusedEl ) ) { 321 el.classList.remove( menu.options.activeMenuItemClass ); 322 menu.collapseSubmenu( el ); 323 } 324 }); 325 }, 326 327 /** 328 * Expand the submenu in a menu item. 329 * 330 * @param {Element} menuItem Menu item element. 331 */ 332 expandSubmenu: function( menuItem ) {
333 var button = this.getSubmenuToggle( menuItem ), 334 $submenu = $( '.sub-menu', menuItem ); 335 336 menuItem.classList.add( this.options.activeMenuItemClass ); 337 338 if ( ! this.hasSubmenu( menuItem ) ) { 339 return; 340 } 341 342 menuItem.classList.add( this.options.expandedMenuItemClass ); 343 344 if ( button ) { 345 button.classList.add( this.options.expandedSubmenuToggleClass ); 346 button.setAttribute( 'aria-expanded', 'true' ); 347 $( '.screen-reader-text', button )[0].innerHTML = this.options.l10n.collapseSubmenu || 'Collapse submenu'; 348 } 349 350 if ( $submenu.length ) { 351 $submenu[0].classList.add( this.options.expandedSubmenuClass ); 352 $submenu[0].setAttribute( 'aria-expanded', 'true' ); 353 } 354 }, 355 356 /** 357 * Retrieve the viewport width. 358 * 359 * @return {number} 360 */ 361 getViewportWidth: function() { 362 return this._viewportWidth || Math.max( document.documentElement.clientWidth, window.innerWidth || 0 ); 363 }, 364 365 /** 366 * Set the viewport width. 367 * 368 * @param {number} width Width of the viewport. 369 * @return {this} 370 */ 371 setViewportWidth: function( width ) { 372 this._viewportWidth = parseInt( width, 10 ); 373 return this; 374 }, 375 376 /** 377 * Retrieve the submenu toggle button from a menu item. 378 * 379 * @param {Element} menuItem Menu item element. 380 * @return {Element} 381 */ 382 getSubmenuToggle: function( menuItem ) { 383 return $( '.' + this.options.submenuToggleClass, menuItem )[0]; 384 }, 385 386 /** 387 * Whether a menu item has a submenu. 388 * 389 * WordPress adds the 'menu-item-has-children' class to menu items even when 390 * the children are hidden with a depth restriction, so we have to query for 391 * the submenu to see if it exists. 392 * 393 * @param {Element} menuItem Menu item element. 394 * @return {Boolean} 395 */ 396 hasSubmenu: function ( menuItem ) { 397 return $( '.sub-menu', menuItem ).length > 0; //classList.contains( 'menu-item-has-children' ); 398 }, 399 400 /** 401 * Insert toggle buttons in menu items that contain a submenu. 402 */ 403 insertSubmenuToggleButtons: function() { 404 var button, span, 405 menu = this; 406 407 button = document.createElement( 'button' ); 408 button.setAttribute( 'class', menu.options.submenuToggleClass ); 409 button.setAttribute( 'aria-expanded', 'false' ); 410 411 span = document.createElement( 'span' ); 412 span.setAttribute( 'class', 'screen-reader-text' ); 413 span.innerHTML = this.options.l10n.expandSubmenu || 'Expand submenu'; 414 415 button.appendChild( span ); 416 417 this.$submenus.each(function( submenu ) { 418 var buttonInstance = button.cloneNode( true ); 419 420 // Assign an id to each submenu to use in ARIA attributes. 421 if ( ! submenu.id ) { 422 submenu.id = $.getUid( submenu, 'sub-menu' ); 423 } 424 425 buttonInstance.setAttribute( 'aria-controls', submenu.id ); 426 427 if ( 'append' !== menu.options.submenuToggleInsert ) { 428 // Insert the toggle button before the submenu element. 429 submenu.parentElement.insertBefore( buttonInstance, submenu ); 430 return; 431 } 432 433 // Append the toggle button to the parent item anchor element. 434 // This should primarily be used for backward compatibility. 435 $( submenu.parentElement.children ).each(function( el ) { 436 if ( 'A' === el.nodeName ) { 437 el.appendChild( buttonInstance ); 438 return false; 439 } 440 }); 441 }); 442 }, 443 444 isSubmenuExpanded: function( menuItem ) { 445 return menuItem.classList.contains( this.options.expandedMenuItemClass ); 446 }, 447 448 /** 449 * Toggle the submenu in a menu item. 450 * 451 * @param {Element} menuItem Menu item element. 452 */ 453 toggleSubmenu: function( menuItem ) { 454 if ( this.isSubmenuExpanded( menuItem ) ) { 455 this.collapseSubmenu( menuItem ); 456 } else { 457 this.expandSubmenu( menuItem ); 458 } 459 }, 460 461 /** 462 * Collapse all submenus when clicking outside the menu. 463 * 464 * On touch devices without toggle buttons, tapping a link will expand 465 * the submenu. It's not possible to close it without clicking another link 466 * with a submenu, which leads back to the initial problem. 467 * 468 * Submenus also won't be collapsed when the keyboard is used to focus 469 * an element and a click occurs outside the menu. 470 */ 471 onDocumentClick: function( e ) { 472 // Don't do this in click mode or on mobile. 473 if ( 'click' === this.options.submenuToggleMode || this.isMobile() ) { 474 return; 475 } 476 477 if ( this.el === e.target || ! this.el.contains( e.target ) ) { 478 this.collapseAllSubmenus(); 479 } 480 }, 481 482 /** 483 * Handle clicks on submenu toggle buttons. 484 * 485 * Delegated to the menu element, so need to ensure the click is on a 486 * toggle button before handling it. 487 * 488 * @param {MouseEvent} e Mouse event object. 489 */ 490 onSubmenuToggleClick: function( e ) { 491 var menuItem, 492 button = $( e.target ).closest( '.' + this.options.submenuToggleClass, this.el ); 493 494 // Bail if this isn't a click on a toggle button. 495 if ( ! button ) { 496 return; 497 } 498 499 e.preventDefault(); 500 501 menuItem = $( e.target ).closest( 'li', this.el ); 502 this.toggleSubmenu( menuItem ); 503 }, 504 505 /** 506 * Handle touchstart events on menu links. 507 * 508 * On touch devices, make the first click open a submenu if there isn't a 509 * clickable toggle button available. Otherwise, the submenus w
509ouldn't be 510 * accessible on touch devices. 511 * 512 * @param {TouchEvent} e Touch event object. 513 */ 514 onMenuLinkTouchStart: function( e ) { 515 var button, target; 516 517 // Bail if this isn't a touchstart on the anchor element. 518 if ( ! $.isElementOrDescendant( e.target, 'A', this.el ) ) { 519 return; 520 } 521 522 target = $( e.target ).closest( 'li', this.el ); 523 524 // Bail if a target couldn't be found. 525 if ( ! target ) { 526 return; 527 } 528 529 // Bail if there isn't a submenu in this item or it's already expanded. 530 if ( ! this.hasSubmenu( target ) || this.isSubmenuExpanded( target ) ) { 531 return; 532 } 533 534 button = this.getSubmenuToggle( target ); 535 536 // Expand the submenu and disable the click event if a clickable toggle 537 // button isn't available. 538 if ( ! $.isClickable( button ) ) { 539 e.stopImmediatePropagation(); 540 e.preventDefault(); 541 this.expandSubmenu( target ); 542 this.collapseUnrelatedSubmenus( e.target ); 543 } else { 544 this._touchStarted = true; 545 } 546 }, 547 548 /** 549 * Handle mouseenter events on menu items. 550 * 551 * When the mouse enters a menu item: 552 * - Add an 'is-active' class to the menu item to aid in styling. Using 553 * the class should prevent needing to use :hover, which can cause 554 * double tap issues on touch devices. 555 * - Expand the submenu if not on mobile. The toggle button should be 556 * used to control submenus on mobile. 557 * 558 * Mimics mouseenter, but is delegated to the menu element instead of 559 * being bound to each item. The actual mouseenter event cannot be 560 * delegated. 561 * 562 * @link https://developer.mozilla.org/en-US/docs/Web/Events/mouseenter 563 * @link http://stackoverflow.com/a/22444819 564 * @link https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html#//apple_ref/doc/uid/TP40006511-SW7 565 * @link https://patrickhlauke.github.io/touch/tests/results/ 566 * 567 * @param {MouseEvent} e Mouse event object. 568 */ 569 onMenuItemMouseEnter: function( e ) { 570 var related, target, 571 isTouchEvent = this._touchStarted, 572 menu = this; 573 574 // Reset the touch started flag. 575 this._isTouchStarted = false; 576 577 target = $( e.target ).closest( 'li', this.el ); 578 related = $( e.relatedTarget ).closest( 'li', this.el ); 579 580 /* 581 * Bail if a target couldn't be found, or if the cursor is already in 582 * the target element. Handling mouseover events like this mimics 583 * mousenter behavior. 584 */ 585 if ( ! target || related === target ) { 586 return; 587 } 588 589 // Clear the timeout to hide a submenu when re-entering an item. 590 $( e.target ).up( 'li', this.el ).each(function( el ) { 591 clearTimeout( menu._hoverTimeouts[ el.id ] ); 592 el.classList.add( menu.options.activeMenuItemClass ); 593 }); 594 595 // Bail if there isn't a submenu in this item or it's already expanded. 596 if ( ! this.hasSubmenu( target ) || this.isSubmenuExpanded( target ) ) { 597 return; 598 } 599 600 /* 601 * If a submenu has been expanded by a focus event, hovering over 602 * unrelated menu items won't collapse the submenu. This should only 603 * happen when the keyboard is used to focus a link, then the mouse is 604 * used to interact with the menu. It's an edge case an may not be worth 605 * the performance impact of looping through every item on mouseenter to 606 * collapse unrelated submenus. 607 */ 608 // if ( ! this.isMobile() ) { 609 // this.collapseUnrelatedSubmenus( target ); 610 // } 611 612 /* 613 * If this event was triggered by a touchstart event and the target is 614 * an anchor element, don't open the submenu to prevent it from flashing 615 * before the link is followed. Also prevents a double tap from being 616 * required on iOS. 617 */ 618 if ( ! isTouchEvent || ! $.isElementOrDescendant( e.target, 'A', this.el ) ) { 619 // Don't expand on mobile to prevent the menu from jumping. 620 if ( ! this.isMobile() ) { 621 this.expandSubmenu( target ); 622 } 623 } 624 }, 625 626 /** 627 * Handle mouseleave events on menu items. 628 * 629 * When the mouse leaves a menu item, collapse submenus and remove the 630 * 'is-active' class for any menu items that no longer have the cursor 631 * hovering over them. 632 * 633 * Mimics mouseleave, but is delegated to the menu element instead of 634 * being bound to each item. 635 * 636 * @link https://developer.mozilla.org/en-US/docs/Web/Events/mouseleave 637 * 638 * @param {MouseEvent} e Moust event object. 639 */ 640 onMenuItemMouseLeave: function( e ) { 641 var menu = this, 642 isMobile = menu.isMobile(); 643 644 $( e.target ).up( 'li', this.el ).filter(function( el ) { 645 return el !== e.relatedTarget && ! el.contains( e.relatedTarget ); 646 }).each(function( el ) { 647 el.classList.remove( menu.options.activeMenuItemClass ); 648 649 // Bail in mobile mode to prevent the menu from jumping. 650 if ( isMobile ) { 651 return; 652 } 653 654 menu._hoverTimeouts[ el.id ] = setTimeout(function() { 655 menu.collapseSubmenu( el ); 656 }, menu.options.hoverTimeout ); 657 }); 658 }, 659 660 /** 661 * Handle the focus event on elements within the menu. 662 * 663 * Aids keyboard navigation by: 664 * - Collapsing submenus in unrelated items to prevent submenus from 665 * overlapping in full screen mode. This is not done in mobile mode to 666 * prevent the menu from jumping when tabbing through items. 667 * - Adding an 'is-active' class to ancestor menu items when any 668 * descendant is focused. 669 * - Expanding a submenu when an element (link or toggle button) in the 670 * parent menu item receives focus. 671 * 672 * @param {FocusEvent} e Focus event object. 673 */ 674 onMenuLinkFocus: function( e ) { 675 var button, $parents, 676 menu = this; 677 678 // Collapse unrelated submenus except in mobile mode. 679 if ( 'click' !== menu.options.submenuToggleMode && ! menu.isMobile() ) { 680 menu.collapseUnrelatedSubmenus( e.target ); 681 } 682 683 $parents = $( e.target ).parents( 'li', menu.el ).each(function( el ) { 684 el.classList.add( menu.options.activeMenuItemClass ); 685 }); 686 687 // Bail if the target menu item doesn't have a submenu. 688 if ( ! menu.hasSubmenu( $parents[ 0 ] ) ) { 689 return; 690 } 691
692 button = this.getSubmenuToggle( $parents[0] ); 693 694 /* 695 * Expand the submenu if a toggle button isn't visible. 696 * 697 * Also expands parents since keyboards can access elements positioned 698 * off-screen, so reverse tabbing through a menu needs to make sure 699 * parents are visible. 700 */ 701 if ( ! button || ! $.isVisible( button ) ) { 702 $parents.each(function( el ) { 703 menu.expandSubmenu( el ); 704 }); 705 } 706 }, 707 708 /** 709 * Close submenus when focus leaves the containing item. 710 * 711 * Aids keyboard navigation in full screen mode by preventing multiple 712 * expanded submenus from overlapping when tabbing through items. 713 * 714 * The relatedTarget property isn't available in some browsers for focus 715 * events, so it's not possible to grab the focused element in a blur 716 * event handler. For that reason, unrelated submenus are also collapsed in 717 * onMenuLinkFocus. 718 * 719 * @link https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget 720 * 721 * @todo Blur events sent when clicking outside the element the event is 722 * bound to also send null for the relatedTarget. This prevents 723 * clicking anywhere else in the document from collapsing the submenu. 724 * 725 * @param {FocusEvent} e Focus event object. 726 */ 727 onMenuLinkBlur: function( e ) { 728 var menu = this; 729 730 // Remove the activeMenuItemClass from the parent menu items. 731 $( e.target ).parents( 'li', menu.el ).each(function( el ) { 732 el.classList.remove( menu.options.activeMenuItemClass ); 733 }); 734 735 // Bail if the relatedTarget event is null. 736 if ( ! e.relatedTarget ) { 737 return; 738 } 739 740 // Disable on mobile to prevent the menu from jumping while tabbing 741 // through menu items and toggle buttons. 742 if ( 'click' === menu.options.submenuToggleMode || menu.isMobile() ) { 743 return; 744 } 745 746 menu.collapseUnrelatedSubmenus( e.relatedTarget ); 747 } 748}; 749 750module.exports = NavMenu; 751 752},{"./query":4}],4:[function(require,module,exports){ 753"use strict"; 754var _ = require( './utils' ); 755 756function Query( selector, context ) { 757 var i, length; 758 759 if ( selector instanceof Query ) { 760 return selector; 761 } 762 763 if ( ! selector ) { 764 return this; 765 } 766 767 context = context || document; 768 if ( context.length ) { 769 context = context[0]; 770 } 771 772 if ( 'string' === typeof selector ) { 773 selector = context.querySelectorAll( selector ); 774 } 775 776 if ( selector && selector.nodeType ) { 777 this.length = 1; 778 this[0] = selector; 779 } else { 780 selector = Array.prototype.slice.call( selector ); 781 length = this.length = selector.length; 782 783 for ( i = 0; i < length; i++ ) { 784 this[ i ] = selector[ i ]; 785 } 786 } 787} 788 789_.extend( Query.prototype, { 790 length: 0, 791 792 closest: function( selector, context ) { 793 var el = this[0]; 794 context = context || document.body; 795 796 while ( el && el !== context ) { 797 if ( _.matches( el, selector ) ) { 798 return el; 799 } 800 801 el = el.parentElement; 802 } 803 804 return null; 805 }, 806 807 each: function( callback ) { 808 _.each( this, callback ); 809 return this; 810 }, 811 812 filter: function( callback ) { 813 return query( Array.prototype.filter.call( this, callback ) ); 814 }, 815 816 /** 817 * @todo Improve performance: http://ryanmorr.com/abstract-away-the-performance-faults-of-queryselectorall/ 818 */ 819 find: function( selector ) { 820 return query( this[0].querySelectorAll( selector ) ); 821 }, 822 823 parents: function( selector, context ) { 824 var el = this[0], 825 parents = []; 826 827 context = context || document.body; 828 829 while ( el && el.parentElement && el.parentElement !== context ) { 830 if ( _.matches( el.parentElement, selector ) ) { 831 parents.push( el.parentElement ); 832 } 833 el = el.parentElement; 834 } 835 836 return query( parents ); 837 }, 838 839 up: function( selector, context ) { 840 var items = Array.prototype.slice.call( this.parents( selector, context ) ); 841 842 if ( _.matches( this[0], selector ) ) { 843 items.unshift( this[0] ); 844 } 845 846 return query( items ); 847 } 848}); 849 850function query( selector, context ) { 851 return new Query( selector, context ); 852} 853 854module.exports = _.extend( query, _, { 855 isClickable: function( el ) { 856 var styles; 857 858 if ( ! el ) { 859 return false; 860 } 861 862 styles = window.getComputedStyle( el ); 863 return 'none' !== styles.display && 'none' !== styles.pointerEvents; 864 }, 865 866 isElementOrDescendant: function( el, tagName, container ) { 867 return tagName === el.nodeName || null !== query( el ).closest( tagName, container ); 868 }, 869 870 isVisible: function( el ) { 871 return el && 'none' !== window.getComputedStyle( el ).display; 872 } 873}); 874 875},{"./utils":5}],5:[function(require,module,exports){ 876"use strict";
877function each( items, callback ) { 878 var i, 879 length = items.length; 880 881 for ( i = 0; i < length; i++ ) { 882 if ( false === callback.call( items[i], items[i], i ) ) { 883 break; 884 } 885 } 886} 887 888function extend( target ) { 889 var i = 0, 890 length = arguments.length; 891 892 target = target || {}; 893 894 while ( ++i < length ) { 895 if ( ! arguments[ i ] ) { 896 continue; 897 } 898 899 for ( var key in arguments[ i ] ) { 900 if ( arguments[ i ].hasOwnProperty( key ) ) { 901 target[ key ] = arguments[ i ][ key ]; 902 } 903 } 904 } 905 906 return target; 907} 908 909function generateUniqueId( prefix ) { 910 prefix = prefix || ''; 911 if ( '' !== prefix ) { 912 prefix += '-'; 913 } 914 return prefix + Math.random().toString( 36 ).slice( 2, 15 ); 915} 916 917function getUniqueId( el, prefix ) { 918 if ( el.id ) { 919 return el.id; 920 } 921 922 return generateUniqueId( prefix ); 923} 924 925/** 926 * @link https://developer.mozilla.org/en-US/docs/Web/API/Element/matches 927 */ 928function matches( el, selector ) { 929 var fn = el && ( 930 el.matches || 931 el.webkitMatchesSelector || 932 el.mozMatchesSelector || 933 el.msMatchesSelector || 934 el.oMatchesSelector 935 ); 936 937 return !! fn && fn.call( el, selector ); 938} 939 940module.exports = { 941 each: each, 942 extend: extend, 943 getUid: getUniqueId, 944 matches: matches 945}; 946 947},{}]},{},[1])(1) 948});
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.