217 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			217 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | (function() { | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Debounce | |||
|  | 	 * | |||
|  | 	 * @param {Function} func | |||
|  | 	 * @param {number} wait | |||
|  | 	 * @param {boolean} immediate | |||
|  | 	 */ | |||
|  | 	function debounce(func, wait, immediate) { | |||
|  | 		'use strict'; | |||
|  | 
 | |||
|  | 		var timeout; | |||
|  | 		wait      = (typeof wait !== 'undefined') ? wait : 20; | |||
|  | 		immediate = (typeof immediate !== 'undefined') ? immediate : true; | |||
|  | 
 | |||
|  | 		return function() { | |||
|  | 
 | |||
|  | 			var context = this, args = arguments; | |||
|  | 			var later = function() { | |||
|  | 				timeout = null; | |||
|  | 
 | |||
|  | 				if (!immediate) { | |||
|  | 					func.apply(context, args); | |||
|  | 				} | |||
|  | 			}; | |||
|  | 
 | |||
|  | 			var callNow = immediate && !timeout; | |||
|  | 
 | |||
|  | 			clearTimeout(timeout); | |||
|  | 			timeout = setTimeout(later, wait); | |||
|  | 
 | |||
|  | 			if (callNow) { | |||
|  | 				func.apply(context, args); | |||
|  | 			} | |||
|  | 		}; | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Prepends an element to a container. | |||
|  | 	 * | |||
|  | 	 * @param {Element} container | |||
|  | 	 * @param {Element} element | |||
|  | 	 */ | |||
|  | 	function prependElement(container, element) { | |||
|  | 		if (container.firstChild.nextSibling) { | |||
|  | 			return container.insertBefore(element, container.firstChild.nextSibling); | |||
|  | 		} else { | |||
|  | 			return container.appendChild(element); | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Shows an element by adding a hidden className. | |||
|  | 	 * | |||
|  | 	 * @param {Element} element | |||
|  | 	 */ | |||
|  | 	function showButton(element) { | |||
|  | 		// classList.remove is not supported in IE11
 | |||
|  | 		element.className = element.className.replace('is-empty', ''); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Hides an element by removing the hidden className. | |||
|  | 	 * | |||
|  | 	 * @param {Element} element | |||
|  | 	 */ | |||
|  | 	function hideButton(element) { | |||
|  | 		// classList.add is not supported in IE11
 | |||
|  | 		if (!element.classList.contains('is-empty')) { | |||
|  | 			element.className += ' is-empty'; | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Returns the currently available space in the menu container. | |||
|  | 	 * | |||
|  | 	 * @returns {number} Available space | |||
|  | 	 */ | |||
|  | 	function getAvailableSpace( button, container ) { | |||
|  | 		return container.offsetWidth - button.offsetWidth - 22; | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Returns whether the current menu is overflowing or not. | |||
|  | 	 * | |||
|  | 	 * @returns {boolean} Is overflowing | |||
|  | 	 */ | |||
|  | 	function isOverflowingNavivation( list, button, container ) { | |||
|  | 		return list.offsetWidth > getAvailableSpace( button, container ); | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Set menu container variable | |||
|  | 	 */ | |||
|  | 	var navContainer = document.querySelector('.main-navigation'); | |||
|  | 	var breaks       = []; | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Let’s bail if we our menu doesn't exist | |||
|  | 	 */ | |||
|  | 	if ( ! navContainer ) { | |||
|  | 		return; | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Refreshes the list item from the menu depending on the menu size | |||
|  | 	 */ | |||
|  | 	function updateNavigationMenu( container ) { | |||
|  | 
 | |||
|  | 		/** | |||
|  | 		 * Let’s bail if our menu is empty | |||
|  | 		 */ | |||
|  | 		if ( ! container.parentNode.querySelector('.main-menu[id]') ) { | |||
|  | 			return; | |||
|  | 		} | |||
|  | 
 | |||
|  | 		// Adds the necessary UI to operate the menu.
 | |||
|  | 		var visibleList  = container.parentNode.querySelector('.main-menu[id]'); | |||
|  | 		var hiddenList   = visibleList.parentNode.nextElementSibling.querySelector('.hidden-links'); | |||
|  | 		var toggleButton = visibleList.parentNode.nextElementSibling.querySelector('.main-menu-more-toggle'); | |||
|  | 
 | |||
|  | 		if ( isOverflowingNavivation( visibleList, toggleButton, container ) ) { | |||
|  | 
 | |||
|  | 			// Record the width of the list
 | |||
|  | 			breaks.push( visibleList.offsetWidth ); | |||
|  | 			// Move last item to the hidden list
 | |||
|  | 			prependElement( hiddenList, ! visibleList.lastChild || null === visibleList.lastChild ? visibleList.previousElementSibling : visibleList.lastChild ); | |||
|  | 			// Show the toggle button
 | |||
|  | 			showButton( toggleButton ); | |||
|  | 
 | |||
|  | 		} else { | |||
|  | 
 | |||
|  | 			// There is space for another item in the nav
 | |||
|  | 			if ( getAvailableSpace( toggleButton, container ) > breaks[breaks.length - 1] ) { | |||
|  | 				// Move the item to the visible list
 | |||
|  | 				visibleList.appendChild( hiddenList.firstChild.nextSibling ); | |||
|  | 				breaks.pop(); | |||
|  | 			} | |||
|  | 
 | |||
|  | 			// Hide the dropdown btn if hidden list is empty
 | |||
|  | 			if (breaks.length < 2) { | |||
|  | 				hideButton( toggleButton ); | |||
|  | 			} | |||
|  | 		} | |||
|  | 
 | |||
|  | 		// Recur if the visible list is still overflowing the nav
 | |||
|  | 		if ( isOverflowingNavivation( visibleList, toggleButton, container ) ) { | |||
|  | 			updateNavigationMenu( container ); | |||
|  | 		} | |||
|  | 	} | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Run our priority+ function as soon as the document is `ready` | |||
|  | 	 */ | |||
|  | 	document.addEventListener( 'DOMContentLoaded', function() { | |||
|  | 
 | |||
|  | 		updateNavigationMenu( navContainer ); | |||
|  | 
 | |||
|  | 		// Also, run our priority+ function on selective refresh in the customizer
 | |||
|  | 		var hasSelectiveRefresh = ( | |||
|  | 			'undefined' !== typeof wp && | |||
|  | 			wp.customize && | |||
|  | 			wp.customize.selectiveRefresh && | |||
|  | 			wp.customize.navMenusPreview.NavMenuInstancePartial | |||
|  | 		); | |||
|  | 
 | |||
|  | 		if ( hasSelectiveRefresh ) { | |||
|  | 			// Re-run our priority+ function on Nav Menu partial refreshes
 | |||
|  | 			wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function ( placement ) { | |||
|  | 
 | |||
|  | 				var isNewNavMenu = ( | |||
|  | 					placement && | |||
|  | 					placement.partial.id.includes( 'nav_menu_instance' ) && | |||
|  | 					'null' !== placement.container[0].parentNode && | |||
|  | 					placement.container[0].parentNode.classList.contains( 'main-navigation' ) | |||
|  | 				); | |||
|  | 
 | |||
|  | 				if ( isNewNavMenu ) { | |||
|  | 					updateNavigationMenu( placement.container[0].parentNode ); | |||
|  | 				} | |||
|  | 			}); | |||
|  |         } | |||
|  | 	}); | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Run our priority+ function on load | |||
|  | 	 */ | |||
|  | 	window.addEventListener( 'load', function() { | |||
|  | 		updateNavigationMenu( navContainer ); | |||
|  | 	}); | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Run our priority+ function every time the window resizes | |||
|  | 	 */ | |||
|  | 	var isResizing = false; | |||
|  | 	window.addEventListener( 'resize', | |||
|  | 		debounce( function() { | |||
|  | 			if ( isResizing ) { | |||
|  | 				return; | |||
|  | 			} | |||
|  | 
 | |||
|  | 			isResizing = true; | |||
|  | 			setTimeout( function() { | |||
|  | 				updateNavigationMenu( navContainer ); | |||
|  | 				isResizing = false; | |||
|  | 			}, 150 ); | |||
|  | 		} ) | |||
|  | 	); | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Run our priority+ function | |||
|  | 	 */ | |||
|  | 	updateNavigationMenu( navContainer ); | |||
|  | 
 | |||
|  | })(); |