Uname: Linux p3plzcpnl499967.prod.phx3.secureserver.net 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64
Software: Apache
PHP version: 8.2.30 [ PHP INFO ] PHP os: Linux
Server Ip: 208.109.40.231
Your Ip: 216.73.216.173
User: nayff91c5tsx (10005085) | Group: nayff91c5tsx (10005085)
Safe Mode: OFF
Disable Function:
NONE

name : mobile-menu.js
import { writeElStyle, getElStyleId } from "../utils/anim-util";
import {
	forEachEl,
	getBreakpoints,
	getPureHeight,
	getSiblings,
	listenDocumentEvent,
} from "../utils/dom-util";

/**
 * This module is intended to handle the mobile menu JS functionality.
 *
 * Along with the site.js and desktop-menu.js, this file will be combined to site-min.js file.
 */
export default function setupMobileMenu() {
	let breakpoints = getBreakpoints();

	/**
	 * The menu type. Accepts: 'hamburger', 'default', or 'premium'.
	 *
	 * @type {string}
	 */
	let menuType;

	/**
	 * Initialize the module, call the main functions.
	 *
	 * This function is the only function that should be called on top level scope.
	 * Other functions are called / hooked from this function.
	 */
	function init() {
		// On window resize, get the updated breakpoints.
		window.addEventListener("resize", function (e) {
			breakpoints = getBreakpoints();
		});

		setupMenuType();
		setupHashLinkBehavior();
		setupMobileMenu();
		setupMobileSubmenu();
	}

	/**
	 * Determine the menu type.
	 *
	 * This function will set the value of top level `menuType` variable.
	 * It could be 'hamburger', 'default', or 'premium'.
	 */
	function setupMenuType() {
		let menu = document.querySelector(".wpbf-mobile-menu-hamburger");

		if (menu) {
			menuType = "hamburger";
			return;
		}

		menu = document.querySelector(".wpbf-mobile-menu-default");

		if (menu) {
			menuType = "default";
			return;
		}

		menuType = "premium";
	}

	/**
	 * Setup behavior when clicking links that contain a hash.
	 */
	function setupHashLinkBehavior() {
		/**
		 * On mobile menu item hash link click,
		 * it will either close the mobile menu, or open the submenu if exists.
		 */
		listenDocumentEvent(
			"click",
			".wpbf-mobile-menu a",
			/**
			 * @this {HTMLAnchorElement}
			 */
			function () {
				// Stop if menu type is 'premium'.
				if ("premium" === menuType) return;

				// Stop if href doesn't contain hash.
				if (!this.href.match("#") && !this.href.match("/#")) return;

				const parentNode = this.parentNode;

				const hasSubmenu =
					parentNode instanceof HTMLElement &&
					parentNode.classList.contains("menu-item-has-children");

				// If the link doesn't have sub-menu, then simply close the mobile menu.
				if (!hasSubmenu) {
					closeMobileMenu(menuType);
				} else {
					if (this.closest(".wpbf-mobile-mega-menu")) {
						// But if the link has sub-menu, and its top level parent menu item is a mega menu, then close the mobile menu.
						closeMobileMenu(menuType);
					} else {
						// And if its top level parent menu item is not a mega menu, then toggle it's sub-menu.
						toggleMobileSubmenuOnHashLinkClick(this);
					}
				}
			},
		);
	}

	/**
	 * Toggle submenu on hash link click.
	 *
	 * @param {HTMLElement} link The anchor element of the menu item.
	 */
	function toggleMobileSubmenuOnHashLinkClick(link) {
		let toggles = getSiblings(link, ".wpbf-submenu-toggle");
		if (!toggles.length) return;
		const toggle = toggles[0];

		if (toggle.classList.contains("active")) {
			closeMobileSubmenu(toggle);
		} else {
			openMobileSubmenu(toggle);
		}
	}

	/**
	 * Setup mobile menu for both 'default' and 'hamburger' menu.
	 */
	function setupMobileMenu() {
		/**
		 * On mobile menu toggle click, we re-run the menu type setup and then run the toggling process.
		 * The menu type setup need to be re-run to handle the behavior inside customizer.
		 */
		listenDocumentEvent("click", ".wpbf-mobile-menu-toggle", function () {
			setupMenuType();
			toggleMobileMenu(menuType);
		});

		// On window resize, if the window width is wider than desktop breakpoint, then hide the mobile menu.
		window.addEventListener("resize", function () {
			const windowHeight = document.documentElement.clientHeight;
			const windowWidth = document.documentElement.clientWidth;

			const mobileNavWrapper = document.querySelector(
				".wpbf-mobile-nav-wrapper",
			);

			const mobileNavWrapperHeight =
				mobileNavWrapper && mobileNavWrapper instanceof HTMLElement
					? mobileNavWrapper.offsetHeight
					: 0;

			forEachEl(".wpbf-mobile-menu-container.active nav", function (el) {
				el.style.maxHeight = windowHeight - mobileNavWrapperHeight + "px";
			});

			if (windowWidth > breakpoints.desktop) {
				closeMobileMenu(menuType);
			}
		});
	}

	/**
	 * Toggle the mobile menu to open or close.
	 * This won't run if the menu type is 'premium'.
	 *
	 * @param {string} menuType The menu type. Accepts 'default' or 'hamburger'.
	 */
	function toggleMobileMenu(menuType) {
		if ("premium" === menuType) return;

		// Toggle here is the mobile menu toggle button.
		const toggle = document.querySelector("#wpbf-mobile-menu-toggle");
		if (!toggle) return;

		if (toggle.classList.contains("active")) {
			closeMobileMenu(menuType);
		} else {
			openMobileMenu(menuType);
		}
	}

	/**
	 * Open mobile menu.
	 * This function is only being called inside `toggleMobileMenu` function.
	 *
	 * But let the menuType and toggle checking stay inside.
	 * We may call this function directly outside of `toggleMobileMenu` function in the future.
	 *
	 * @param {string} menuType The menu type. Accepts 'hamburger' or 'default'.
	 */
	function openMobileMenu(menuType) {
		if ("premium" === menuType) return;

		// Toggle here is the mobile menu toggle button.
		const toggle = document.querySelector("#wpbf-mobile-menu-toggle");
		if (!toggle) return;

		const mobileMenu = document.querySelector(".wpbf-mobile-menu-container");

		if (
			mobileMenu &&
			mobileMenu instanceof HTMLElement &&
			!mobileMenu.classList.contains("active")
		) {
			mobileMenu.classList.add("active");

			const pureHeight = getPureHeight(mobileMenu);
			const styleTagId = getElStyleId(mobileMenu);

			const submenuId = styleTagId.replace("wpbf-style-", "");

			writeElStyle(
				mobileMenu,
				`
				#${submenuId}.wpbf-slide-anim {display: block; height: 0; overflow: hidden;}
				#${submenuId}.wpbf-slide-anim.is-expanded {height: ${pureHeight}px;}
				`,
			);

			mobileMenu.classList.add("wpbf-slide-anim");

			setTimeout(function () {
				mobileMenu.classList.add("is-expanded");

				setTimeout(function () {
					writeElStyle(
						mobileMenu,
						`#${submenuId}.wpbf-slide-anim {display: block;}`,
					);
				}, 400);
			}, 1);
		}

		toggle.classList.add("active");
		toggle.setAttribute("aria-expanded", "true");

		if ("hamburger" === menuType) {
			toggle.classList.remove("wpbff-hamburger");
			toggle.classList.add("wpbff-times");
		}
	}

	/**
	 * Close mobile menu.
	 * This function is being called in `toggleMobileMenu` function and in several direct calls.
	 *
	 * @param {string} menuType The menu type. Accepts 'hamburger' or 'default'.
	 */
	function closeMobileMenu(menuType) {
		if ("premium" === menuType) return;

		// Toggle here is the mobile menu toggle button.
		const toggle = document.querySelector("#wpbf-mobile-menu-toggle");
		if (!toggle) return;

		// Because this function is also being called directly in several places, then we need this checking.
		if (!toggle.classList.contains("active")) return;

		const mobileMenu = document.querySelector(".wpbf-mobile-menu-container");

		if (
			mobileMenu &&
			mobileMenu instanceof HTMLElement &&
			mobileMenu.classList.contains("active")
		) {
			const pureHeight = getPureHeight(mobileMenu);
			const styleTagId = getElStyleId(mobileMenu);
			const submenuId = styleTagId.replace("wpbf-style-", "");

			writeElStyle(
				mobileMenu,
				`
				#${submenuId}.wpbf-slide-anim {display: block; height: 0; overflow: hidden;}
				#${submenuId}.wpbf-slide-anim.is-expanded {height: ${pureHeight}px;}
				`,
			);

			setTimeout(function () {
				mobileMenu.classList.remove("active");
				mobileMenu.classList.remove("is-expanded");

				setTimeout(function () {
					mobileMenu.classList.remove("wpbf-slide-anim");
				}, 400);
			}, 1);
		}

		toggle.classList.remove("active");
		toggle.setAttribute("aria-expanded", "false");

		if ("hamburger" === menuType) {
			toggle.classList.remove("wpbff-times");
			toggle.classList.add("wpbff-hamburger");
		}
	}

	/**
	 * Setup mobile sub-menu for both default and hamburger menu.
	 */
	function setupMobileSubmenu() {
		setupMobileSubmenuToggle("default");
		setupMobileSubmenuToggle("hamburger");
	}

	/**
	 * Setup mobile sub-menu toggle.
	 *
	 * @param {string} menuType The menu type. Accepts 'hamburger' or 'default'.
	 */
	function setupMobileSubmenuToggle(menuType) {
		const menuClass =
			menuType === "hamburger"
				? ".wpbf-mobile-menu-hamburger .wpbf-submenu-toggle"
				: ".wpbf-mobile-menu-default .wpbf-submenu-toggle";

		listenDocumentEvent(
			"click",
			menuClass,
			/**
			 * @param {MouseEvent} e - The mouse event.
			 * @this {HTMLElement}
			 */
			function (e) {
				e.preventDefault();
				toggleMobileSubmenu(this);
			},
		);
	}

	/**
	 * Toggle mobile sub-menu to expand/collapse.
	 *
	 * @param {HTMLElement} toggle The submenu's toggle button (the expand/collapse arrow).
	 */
	function toggleMobileSubmenu(toggle) {
		if (toggle.classList.contains("active")) {
			closeMobileSubmenu(toggle);
		} else {
			openMobileSubmenu(toggle);
		}
	}

	/**
	 * Open mobile submenu.
	 *
	 * @param {HTMLElement} toggle The submenu's toggle button (the expand/collapse arrow).
	 */
	function openMobileSubmenu(toggle) {
		const iElms = toggle.querySelectorAll("i");

		iElms.forEach(function (el) {
			el.classList.remove("wpbff-arrow-down");
			el.classList.add("wpbff-arrow-up");
		});

		toggle.classList.add("active");
		toggle.setAttribute("aria-expanded", "true");

		const submenus = getSiblings(toggle, ".sub-menu");

		submenus.forEach(function (submenu) {
			const pureHeight = getPureHeight(submenu);
			const styleTagId = getElStyleId(submenu);
			const submenuId = styleTagId.replace("wpbf-style-", "");

			writeElStyle(
				submenu,
				`
				#${submenuId}.wpbf-slide-anim {display: block; height: 0; overflow: hidden;}
				#${submenuId}.wpbf-slide-anim.is-expanded {height: ${pureHeight}px;}
				`,
			);

			submenu.classList.add("wpbf-slide-anim");

			setTimeout(function () {
				submenu.classList.add("is-expanded");

				setTimeout(function () {
					writeElStyle(
						submenu,
						`#${submenuId}.wpbf-slide-anim {display: block}`,
					);
				}, 400);
			}, 1);
		});

		autoCollapseMobileSubmenus(toggle);
	}

	/**
	 * Close mobile submenu.
	 *
	 * @param {HTMLElement} toggle The submenu's toggle button (the expand/collapse arrow).
	 */
	function closeMobileSubmenu(toggle) {
		const iElms = toggle.querySelectorAll("i");

		iElms.forEach(function (el) {
			el.classList.remove("wpbff-arrow-up");
			el.classList.add("wpbff-arrow-down");
		});

		toggle.classList.remove("active");
		toggle.setAttribute("aria-expanded", "false");

		const submenus = getSiblings(toggle, ".sub-menu");

		submenus.forEach(function (submenu) {
			const pureHeight = getPureHeight(submenu);
			const styleTagId = getElStyleId(submenu);
			const submenuId = styleTagId.replace("wpbf-style-", "");

			writeElStyle(
				submenu,
				`
				#${submenuId}.wpbf-slide-anim {display: block; height: 0; overflow: hidden;}
				#${submenuId}.wpbf-slide-anim.is-expanded {height: ${pureHeight}px;}
				`,
			);

			setTimeout(function () {
				submenu.classList.remove("is-expanded");

				setTimeout(function () {
					submenu.classList.remove("wpbf-slide-anim");
				}, 400);
			}, 1);
		});
	}

	/**
	 * Auto collapse other opened mobile submenu.
	 *
	 * Open the sub-menu of current clicked menu item,
	 * and hide the other sub-menus (with the same level, in the same parent element).
	 *
	 * @param {HTMLElement} toggle The submenu's toggle button (the expand/collapse arrow) of the current clicked menu item.
	 */
	function autoCollapseMobileSubmenus(toggle) {
		const nav = toggle.closest(".wpbf-navigation");

		if (nav) {
			// If wpbf navigation exists but doesn't have mobile submenu collapse class, then stop here.
			if (!nav.classList.contains("wpbf-mobile-sub-menu-auto-collapse")) {
				return;
			}
		}

		/**
		 * The same level menu items in the same parent element.
		 *
		 * @type {HTMLElement[]}
		 */
		let sameLevelItems = [];

		const menuItem = toggle.closest(".menu-item-has-children");

		if (menuItem && menuItem instanceof HTMLElement) {
			sameLevelItems = getSiblings(menuItem, ".menu-item-has-children");
		}

		sameLevelItems.forEach(function (item) {
			const submenuToggle = item.querySelector(".wpbf-submenu-toggle");
			if (!submenuToggle || !(submenuToggle instanceof HTMLElement)) return;
			closeMobileSubmenu(submenuToggle);
		});
	}

	// Run the module.
	init();
}
© 2026 GrazzMean