/**
 * Handles toggling the navigation sub-menus.
 * This content is based on a script from w3.org at
 * @link https://www.w3.org/WAI/ARIA/apg/example-index/disclosure/disclosure-navigation-hybrid.html
 * 
 * This content is licensed according to the W3C Software License at
 * @link https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
 *
 * Supplemental JS for the disclosure menu keyboard behavior
 */

( function() {

	'use strict';

	/**
	 * What menus to select.
	 */
	var menuSelector = '.navigation ul.menu';

	/**
	 * Define the class
	 */
	class DisclosureNav {
		constructor(domNode) {
			this.rootNode = domNode;
			this.controlledNodes = [];
			this.openIndex = null;
			this.useArrowKeys = true;
			this.topLevelNodes = [
				...this.rootNode.querySelectorAll(
					// '.main-link, button[aria-expanded][aria-controls]'
					'[role=button][aria-expanded][aria-controls]'
				),
			];

			this.topLevelNodes.forEach((node) => {
				// handle button + menu
				if (
					// node.tagName.toLowerCase() === 'button' &&
					node.getAttribute('role') &&
					node.getAttribute('role').toLowerCase() === 'button' &&
					node.hasAttribute('aria-controls')
				) {
					let menu = node.parentNode.querySelector('ul');

					if ( !menu ) {
						menu = node.parentNode.querySelector('.dropmenu_template');
					}

					if (menu) {
						// save ref controlled menu
						this.controlledNodes.push(menu);

						// collapse menus
						node.setAttribute('aria-expanded', 'false');

						// attach event listeners
						menu.addEventListener('keydown', this.onMenuKeyDown.bind(this));
						node.addEventListener('click', this.onButtonClick.bind(this, node));
						// add event listener for spacebar or enter - call click on node
						node.addEventListener('keydown', function(event){
							if (event.keyCode === 32 || event.keyCode === 13) {
								event.preventDefault();
								event.stopPropagation();
								this.click();
							}
						});
						node.addEventListener('keydown', this.onButtonKeyDown.bind(this));
					}
				}
				// handle links
				else {
					this.controlledNodes.push(null);
					node.addEventListener('keydown', this.onLinkKeyDown.bind(this));
				}
			});

			this.rootNode.addEventListener('focusout', this.onBlur.bind(this));
		}

		controlFocusByKey(keyboardEvent, nodeList, currentIndex) {
			switch (keyboardEvent.key) {
				case 'ArrowUp':
				case 'ArrowLeft':
					keyboardEvent.preventDefault();
					if (currentIndex > -1) {
						var prevIndex = Math.max(0, currentIndex - 1);
						nodeList[prevIndex].focus();
					}
					break;
				case 'ArrowDown':
				case 'ArrowRight':
					keyboardEvent.preventDefault();
					if (currentIndex > -1) {
						var nextIndex = Math.min(nodeList.length - 1, currentIndex + 1);
						nodeList[nextIndex].focus();
					}
					break;
				case 'Home':
					keyboardEvent.preventDefault();
					nodeList[0].focus();
					break;
				case 'End':
					keyboardEvent.preventDefault();
					nodeList[nodeList.length - 1].focus();
					break;
			}
		}

		// public function to close open menu
		close() {
			this.toggleExpand(this.openIndex, false, true);
		}

		onBlur(event) {

			var menuContainsFocus = false;
			var target = event.relatedTarget//?? event.target;

			if ( target && this.openIndex !== null ) {
				menuContainsFocus = this.controlledNodes[this.openIndex].contains( target )
			}
			
			if ( !menuContainsFocus && this.openIndex !== null ) {
				this.toggleExpand(this.openIndex, false, false);
			}
		}

		onButtonClick(event) {
			var button = arguments[0];//event.target;
			var buttonIndex = this.topLevelNodes.indexOf(button);
			var buttonExpanded = button.getAttribute('aria-expanded') === 'true';
			this.toggleExpand(buttonIndex, !buttonExpanded, true);
		}

		onButtonKeyDown(event) {
			var targetButtonIndex = this.topLevelNodes.indexOf(document.activeElement);

			// close on escape
			if (event.key === 'Escape') {
				this.toggleExpand(this.openIndex, false, true);
			}

			// move focus into the open menu if the current menu is open
			else if (
				this.useArrowKeys &&
				this.openIndex === targetButtonIndex &&
				event.key === 'ArrowDown'
			) {
				event.preventDefault();
				this.controlledNodes[this.openIndex].querySelector('a').focus();
			}

			// handle arrow key navigation between top-level buttons, if set
			else if (this.useArrowKeys) {
				this.controlFocusByKey(event, this.topLevelNodes, targetButtonIndex);
			}
		}

		onLinkKeyDown(event) {
			var targetLinkIndex = this.topLevelNodes.indexOf(document.activeElement);

			// handle arrow key navigation between top-level buttons, if set
			if (this.useArrowKeys) {
				this.controlFocusByKey(event, this.topLevelNodes, targetLinkIndex);
			}
		}

		onMenuKeyDown(event) {
			if (this.openIndex === null) {
				return;
			}

			var menuLinks = Array.prototype.slice.call(
				this.controlledNodes[this.openIndex].querySelectorAll('a')
			);
			var currentIndex = menuLinks.indexOf(document.activeElement);

			// close on escape
			if (event.key === 'Escape') {
				this.topLevelNodes[this.openIndex].focus();
				this.toggleExpand(this.openIndex, false, true);
			}

			// handle arrow key navigation within menu links, if set
			else if (this.useArrowKeys) {
				this.controlFocusByKey(event, menuLinks, currentIndex);
			}
		}

		/**
		 * Open or close a submenu.
		 * 
		 * @param {number} index  Index of the menu to toggle.
		 * @param {bool} expanded Whether to open or close the menu
		 * @param {bool} instant  Whether to instantly toggle the menu.
		 *                        @description This is very usefull for blur events where
		 *                        closing the menu after a delay prevents issues especially
		 *                        on mobile devices.
		 */
		toggleExpand(index, expanded, instant) {
			// close open menu, if applicable
			// if (this.openIndex && this.openIndex !== index) {
			// 	this.toggleExpand(this.openIndex, false);
			// }

			// handle menu at called index
			if (this.topLevelNodes[index]) {
				this.openIndex = expanded ? index : null;

				if ( !expanded && !instant ) {
					setTimeout( () => {
						this.topLevelNodes[index].setAttribute('aria-expanded', expanded);
					}, 150 )
				} else {
					this.topLevelNodes[index].setAttribute('aria-expanded', expanded);
				}
				
			}
		}

		updateKeyControls(useArrowKeys) {
			this.useArrowKeys = useArrowKeys;
		}
	}

	/**
	 * Initialize Disclosure Menus
	 */
	window.addEventListener( 'load', function () {

		var menus = [].slice.call( document.querySelectorAll( menuSelector ) );

		if ( menus.length === 0 ) {
			console.warn('EE: Menu not found');
			return;
		}

		var disclosureMenus = [];

		for (var i = 0; i < menus.length; i++) {
			disclosureMenus[i] = new DisclosureNav(menus[i]);
		}
	} )

})();