/**
 * Project: Total WordPress Theme.
 *
 * @license All rights reserved.
 */

/* jshint esversion: 6 */
var wpex = {};

( function( l10n ) {

	'use strict';

	wpex = {

		/**
		 * Main init function.
		 */
		init: function() {
			this.config();
			this.replaceNoJsClass();
			this.bindEvents();
		},

		/**
		 * Define vars for caching.
		 */
		config: function() {
			this.config = {
				localScrollOffset: 0,
				localScrollSections: [],
				passiveListeners: this.passiveListenersSupport()
			};
		},

		/**
		 * Replaces the "wpex-no-js" body class with "wpex-js".
		 */
		replaceNoJsClass: function() {
			var bodyClass = document.body.className;
			bodyClass = bodyClass.replace(/wpex-no-js/, 'wpex-js');
			document.body.className = bodyClass;
		},

		/**
		 * Bind Events.
		 */
		bindEvents: function() {
			var self = this;

			/*** Fires as soon as the document is loaded ***/
			self.domReady( function() {

				document.body.classList.add( 'wpex-docready' );

				if ( self.retinaCheck() ) {
					document.body.classList.add( 'wpex-is-retina' );
				}

				if ( self.mobileCheck() ) {
					document.body.classList.add( 'wpex-is-mobile-device' );
				}

				self.localScrollSections();
				self.megaMenuAddClasses();
				self.dropdownMenuOnclick();
				self.dropdownMenuTouch();
				self.mobileMenu();
				self.hideEditLink();
				self.menuWidgetAccordion();
				self.inlineHeaderLogo();
				self.menuSearch();
				self.skipToContent();
				self.backTopLink();
				self.backTopButton();
				self.goBackButton();
				self.smoothCommentScroll();
				self.toggleElements();
				self.toggleBar();
				self.localScrollLinks();
				self.customSelects();
				self.hoverStyles();
				self.overlaysMobileSupport();
				self.accessability();

			} );

			/*** Fires once the window has been loaded ***/
			window.addEventListener( 'load', function() {

				document.body.classList.add( 'wpex-window-loaded' );

				// Main.
				self.megaMenusWidth();
				self.megaMenusTop();
				self.parallax();
				self.stickyTopBar();
				self.vcTabsTogglesJS();
				self.headerOverlayOffset(); // Add before sticky header ( important ).
				self.equalHeights();
				self.localScrollHighlight();

				// Sticky functions.
				self.stickyHeaderMenu(); // must go before stickyHeader to prevent issues with dyn-styles
				self.stickyHeader();

				// Set localScrollOffset after site is loaded to make sure it includes dynamic items including sticky elements.
				self.parseLocalScrollOffset( 'init' );

				// Run methods after sticky header.
				self.footerReveal(); // Footer Reveal => Must run before fixed footer!!!
				self.fixedFooter(); // Fixed Footer => Must run after footerReveal!!!

				// Scroll to hash (must be last).
				if ( l10n.scrollToHash ) {
					window.setTimeout( function() {
						self.scrollToHash( self );
					}, parseInt( l10n.scrollToHashTimeout ) );
				}

			} );

			/*** Fires on window resize ***/
			window.addEventListener( 'resize', function() {
				self.parseLocalScrollOffset( 'resize' ); //update offsets incase elements changed size.
			} );
		},

		/**
		 * Onclick dropdown menu.
		 */
		dropdownMenuOnclick: function() {

			/*
			// @todo should we implement this?
			document.querySelectorAll( '.wpex-dropdown-menu--onclick .menu-item-has-children > a' ).forEach( function( element ) {
				if ( element.closest( '.megamenu__inner-ul' ) ) {
					return;
				}
				element.setAttribute( 'aria-expanded', 'false' );
				if ( l10n.dropdownMenuAriaLabel ) {
					var ariaLabel = l10n.dropdownMenuAriaLabel;
					element.setAttribute( 'aria-label', ariaLabel.replace( '%s', element.textContent.trim() ) );
				}
			} );*/

			document.addEventListener( 'click', function( event ) {

				var target = event.target;

				if ( ! target.closest( '.wpex-dropdown-menu--onclick .menu-item-has-children > a' ) ) {
					document.querySelectorAll( '.wpex-dropdown-menu--onclick .menu-item-has-children' ).forEach( function( element ) {
						element.classList.remove( 'wpex-active' );
						//element.querySelector( 'a' ).setAttribute( 'aria-expanded', 'false' );
					} );
					return;
				}

				document.querySelectorAll( '.wpex-dropdown-menu--onclick .menu-item-has-children' ).forEach( function( element ) {
					if ( ! element.contains( target ) ) {
						element.classList.remove( 'wpex-active' );
						//element.querySelector( 'a' ).setAttribute( 'aria-expanded', 'false' );
					}
				} );

				var li = target.closest( '.menu-item-has-children' );
				var a = target.closest( 'a' );

				if ( li.classList.contains( 'wpex-active' ) ) {
					li.classList.remove( 'wpex-active' );
					//a.setAttribute( 'aria-expanded', 'false' );
					if ( '#' === a.getAttribute( 'href' ) ) {
						event.preventDefault();
					}
				} else {
					li.classList.add( 'wpex-active' );
					//a.setAttribute( 'aria-expanded', 'true' );
					event.preventDefault();
				}

			} );

			document.addEventListener( 'keydown', function( event ) {
				var code = event.keyCode || event.which;
				var target = event.target.closest( '.wpex-dropdown-menu--onclick .menu-item-has-children.wpex-active' );
				if ( 27 !== code || ! target ) {
					return;
				}
				target.classList.remove( 'wpex-active' );
				//target.querySelector( 'a' ).setAttribute( 'aria-expanded', 'false' );
			} );

			document.querySelectorAll( '.wpex-dropdown-menu--onclick .sub-menu' ).forEach( function( dropdown ) {
				dropdown.addEventListener( 'keydown', function( event ) {
					var code = event.keyCode || event.which;
					if ( 27 === code ) {
						var closestParent = event.target.closest( '.menu-item-has-children.wpex-active' );
						if ( closestParent ) {
							closestParent.classList.remove( 'wpex-active' );
							var closestParentA = closestParent.querySelector( 'a' );
							//closestParentA.setAttribute( 'aria-expanded', 'false' );
							closestParentA.focus();
							event.stopPropagation();
						}
					}
				} );
			} );

		},

		/*
		 * Dropdown menu touch support.
		 */
		dropdownMenuTouch: function() {
			var self     = this;
			var dragging = false;

			document.querySelectorAll( '.wpex-dropdown-menu--onhover .menu-item-has-children > a' ).forEach( function( element ) {
				element.addEventListener( 'touchend', function( event ) {
					if ( dragging ) {
						// hide dropdowns here?
						return;
					}

					var parent = element.closest( '.menu-item-has-children' );

					if ( parent.classList.contains( 'wpex-touched' ) ) {
						return; // already open allow clicking.
					}

					event.preventDefault();

					parent.classList.add( 'wpex-touched' );
				} );

				element.addEventListener( 'touchmove', function( event ) {
					dragging = true;
				}, self.config.passiveListeners ? { passive: true } : false );

				element.addEventListener( 'touchstart', function( event ) {
					dragging = false;
				}, self.config.passiveListeners ? { passive: true } : false );

			} );

			var clickingOutside = function( event ) {
				var target = event.target;
				// not this code will only run if menus are open so it's efficient.
				document.querySelectorAll( '.menu-item-has-children.wpex-touched' ).forEach( function( element ) {
					if ( element.contains( target ) ) {
						return;
					}
					element.classList.remove( 'wpex-touched' );
				} );
			};

			document.addEventListener( 'touchstart', clickingOutside, self.config.passiveListeners ? { passive: true } : false );
			document.addEventListener( 'touchmove', clickingOutside, self.config.passiveListeners ? { passive: true } : false );
		},

		/**
		 * Add "megamenu__inner-ul" classname to megamenu inner UL elements.
		 */
		megaMenuAddClasses: function() {
			document.querySelectorAll( '.main-navigation-ul .megamenu > .sub-menu' ).forEach( function( element ) {
				element.querySelectorAll( '.sub-menu' ).forEach( function( element ) {
					element.classList.add( 'megamenu__inner-ul' );
				} );
			} );
		},

		/**
		 * MegaMenus Width.
		 *
		 * @todo Instead of listening on resize perhaps we should run when resize ends.
		 *       since usually dropdowns won't be open while resizing.
		 */
		megaMenusWidth: function() {
			var self = this;

			var init = function() {
				var navWrap = document.querySelector( '#site-navigation-wrap.wpex-stretch-megamenus' );

				if ( ! self.isVisible( navWrap ) ) {
					return;
				}

				var megaMenus = navWrap.querySelectorAll( '.megamenu > ul' );

				if ( ! megaMenus.length ) {
					return;
				}

				var headerInner = document.querySelector( '#site-header-inner.container' );

				if ( ! headerInner ) {
					return;
				}

				var addNegativeMargin = navWrap.classList.contains( 'navbar-style-one' );
				var headerInnerWidth = headerInner.getBoundingClientRect().width;
				if ( addNegativeMargin ) {
					var headerInnerLeft = parseInt( headerInner.getBoundingClientRect().left );
					var navLeft = navWrap.getBoundingClientRect().left;
				}

				megaMenus.forEach( function( element ) {
					element.style.width = headerInnerWidth + 'px';
					if ( addNegativeMargin ) {
						element.style.marginLeft = -(navLeft-headerInnerLeft) + 'px';
					}
				} );
			};

			init();

			window.addEventListener( 'resize', init );
		},

		/**
		 * MegaMenus Top Position.
		 */
		megaMenusTop: function() {
			var self   = this;
			var header = document.querySelector( '#site-header.header-one' );

			if ( ! header ) {
				return;
			}

			var navWrap = header.querySelector( '#site-navigation-wrap.wpex-stretch-megamenus:not(.wpex-flush-dropdowns)' );

			if ( ! this.isVisible( navWrap ) ) {
				return;
			}

			var megaMenus = navWrap.querySelectorAll( '.megamenu > ul');

			if ( ! megaMenus ) {
				return;
			}

			function setTop() {
				if ( ! self.isVisible( navWrap ) ) {
					return;
				}
				var navHeight = navWrap.getBoundingClientRect().height;
				var topVal = ( ( header.getBoundingClientRect().height - navHeight ) / 2 ) + navHeight;
				megaMenus.forEach( function( element ) {
					element.style.top = topVal + 'px';
				} );
			} setTop();

			window.addEventListener( 'scroll', setTop, self.config.passiveListeners ? { passive: true } : false );
			window.addEventListener( 'resize', setTop );

			var menuItems = navWrap.querySelectorAll( '.megamenu > a' );
			menuItems.forEach( function( element ) {
				element.addEventListener( 'mouseenter', setTop, false );
			} );

		},

		/**
		 * Parses Megamenu HTML for mobile.
		 */
		megaMenusMobile: function( menu ) {
			if ( ! menu ) {
				return;
			}

			var megaMenusWithoutHeadings = menu.classList.contains( 'sidr-class-dropdown-menu' ) ? '.sidr-class-megamenu.sidr-class-hide-headings' : '.megamenu.hide-headings';

			// Loop through mega menus with the hide-headings class to remove top level child ul elements.
			menu.querySelectorAll( megaMenusWithoutHeadings ).forEach( function( megamenu ) {

				if ( megamenu.classList.contains( 'show-headings-mobile' ) || megamenu.classList.contains( 'sidr-class-show-headings-mobile' ) ) {
					return;
				}

				// Loop through top level megamenu li elements (aka columns).
				megamenu.querySelectorAll( ':scope > ul > li' ).forEach( function( headingLi ) {

					// Remove the heading link.
					var headingA = headingLi.querySelector( 'a' );
					if ( headingA ) {
						headingA.parentNode.removeChild( headingA );
					}

					// Remove ul wrapper around direct heading li elements.
					var headingUL = headingLi.querySelector( 'ul' );
					if ( headingUL ) {
						headingUL.outerHTML = headingUL.innerHTML;
					}

					// Remove classes.
					headingLi.classList.remove( 'sidr-class-menu-item-has-children' );
					headingLi.classList.remove( 'menu-item-has-children' );

				} );

			} );
		},

		/**
		 * Mobile Menu.
		 */
		mobileMenu: function() {
			this.mobileMenuSidr();
			this.mobileMenuToggle();
			this.mobileMenuFullScreen();
		},

		/**
		 * Mobile Menu.
		 */
		mobileMenuSidr: function() {
			if ( 'undefined' === typeof l10n.sidrSource || 'undefined' === typeof wpexSidr ) {
				return;
			}

			var self          = this;
			var body          = document.body;
			var toggleBtn     = document.querySelector( 'a.mobile-menu-toggle, li.mobile-menu-toggle > a' );
			var hamburgerIcon = toggleBtn ? toggleBtn.querySelector( '.wpex-hamburger-icon--inactive' ) : '';

			// Add dark overlay to content.
			var sidrOverlay = document.querySelector( '.wpex-sidr-overlay' );

			// Create sidr Element.
			wpexSidr.new( 'a.mobile-menu-toggle, li.mobile-menu-toggle > a', {
				name     : 'sidr-main',
				source   : l10n.sidrSource,
				side     : l10n.sidrSide,
				timing   : 'ease-in-out',
				displace : l10n.sidrDisplace,
				speed    : parseInt( l10n.sidrSpeed ),
				renaming : true,
				bind     : 'click',
				onOpen   : function() {

					// Update toggle btn attributes.
					if ( toggleBtn ) {
						toggleBtn.setAttribute( 'aria-expanded', 'true' );
						toggleBtn.classList.add( 'wpex-active' );
						self.setHamburgerIconState( hamburgerIcon );
					}

					// Prevent body scroll.
					if ( l10n.sidrBodyNoScroll ) {
						body.classList.add( 'wpex-noscroll' );
					}

					// Show Overlay.
					if ( sidrOverlay ) {
						sidrOverlay.classList.remove( 'wpex-hidden' );
						sidrOverlay.classList.add( 'wpex-custom-cursor' );
					}

					// Set focus styles.
					self.focusOnElement( document.querySelector( '#sidr-main' ) );

				},
				onClose: function() {

					// Update toggle btn attributes.
					if ( toggleBtn ) {
						toggleBtn.setAttribute( 'aria-expanded', 'false' );
						toggleBtn.classList.remove( 'wpex-active' );
						self.setHamburgerIconState( hamburgerIcon );
					}

					// Remove body noscroll class.
					if ( l10n.sidrBodyNoScroll ) {
						body.classList.remove( 'wpex-noscroll' );
					}

					// Hide overlay.
					if ( sidrOverlay ) {
						sidrOverlay.classList.remove( 'wpex-custom-cursor' );
						sidrOverlay.classList.add( 'wpex-hidden' );
					}

				},
				onCloseEnd: function() {

					// Close active dropdowns.
					document.querySelectorAll( '.sidr-class-menu-item-has-children.active' ).forEach( function( element ) {
						element.classList.remove( 'active' );
						var ul = element.querySelector( 'ul' );
						if ( ul ) {
							ul.style.display = '';
						}
						var link = element.querySelector( 'a' );
						if ( link ) {
							var toggle = link.querySelector( '.wpex-open-submenu' );
							if ( toggle ) {
								toggle.setAttribute( 'aria-label', l10n.i18n.openSubmenu.replace( '%s', link.textContent.trim() ) );
								toggle.setAttribute( 'aria-expanded', 'false' );
							}
						}
					} );

					// Re-trigger stretched rows to prevent issues if browser was resized while
					// sidr was open.
					if ( l10n.sidrDisplace && 'function' === typeof window.vc_rowBehaviour ) {
						setTimeout( window.vc_rowBehaviour );
					}

				}

			} );

			// Cache sidr element.
			var sidr      = document.querySelector( '#sidr-main' );
			var sidrInner = sidr.querySelector( '.sidr-inner' );

			// Add aria label to sidebar
			if ( l10n.mobileMenuAriaLabel ) {
				sidr.setAttribute( 'aria-label', l10n.mobileMenuAriaLabel );
			}

			// Add extra classes to the sidr element.
			if ( 'string' !== typeof l10n.sidrDarkSurface || l10n.sidrDarkSurface ) {
				sidr.classList.add( 'wpex-surface-dark' );
			}
			sidr.classList.add( 'wpex-mobile-menu' ); // @todo add 'wpex-hide-scrollbar' class unless it's an accessibility concern.

			// Insert mobile menu close button.
			// @todo insert using PHP so it can be modified?
			var sidrCloseBtnEl = document.createElement( 'div' );
			sidrCloseBtnEl.className = 'sidr-class-wpex-close';

			var sidrCloseBtnA = document.createElement( 'a' );
			sidrCloseBtnA.href = '#';
			sidrCloseBtnA.setAttribute( 'role', 'button' );
			sidrCloseBtnEl.appendChild( sidrCloseBtnA );

			var sidrClosebtnIcon = document.createElement( 'span' );
			sidrClosebtnIcon.className = 'sidr-class-wpex-close__icon';
			sidrClosebtnIcon.setAttribute( 'aria-hidden', 'true' );
			sidrClosebtnIcon.innerHTML = '&times;';
			sidrCloseBtnA.appendChild( sidrClosebtnIcon );

			var sidrCloseBtnScreenReaderText = document.createElement( 'span' );
			sidrCloseBtnScreenReaderText.className = 'screen-reader-text';
			sidrCloseBtnScreenReaderText.textContent = l10n.mobileMenuCloseAriaLabel;
			sidrCloseBtnA.appendChild( sidrCloseBtnScreenReaderText );

			// Insert close button.
			sidr.insertBefore( sidrCloseBtnEl, sidr.firstChild );

			// Insert mobile menu extras.
			self.insertExtras( document.querySelector( '.wpex-mobile-menu-top' ), sidrInner, 'prepend' );
			self.insertExtras( document.querySelector( '.wpex-mobile-menu-bottom' ), sidrInner, 'append' );

			// Make sure dropdown-menu is included in sidr-main
			// which may not be included in certain header styles like dev header style.
			sidr.querySelectorAll( '.sidr-class-main-navigation-ul' ).forEach( function( navUl ) {
				navUl.classList.add( 'sidr-class-dropdown-menu' );
			} );

			// Loop through sidr mobile menus.
			sidr.querySelectorAll( '.sidr-class-dropdown-menu' ).forEach( function( menu ) {
				// Parse megamenus to move all li elements from "hide-headings" elements to parent UL.
				self.megaMenusMobile( menu );

				// Create menuAccordion.
				self.menuAccordion( menu );
			} );

			/*** Sidr - bind events ***/

			// Sidr close button
			document.addEventListener( 'click', function( event ) {
				if ( ! event.target.closest( '.sidr-class-wpex-close a' ) ) {
					return;
				}
				event.preventDefault();
				wpexSidr.close( 'sidr-main' );
				if ( toggleBtn ) {
					toggleBtn.focus();
				}
			} );

			// Close on resize past mobile menu breakpoint.
			window.addEventListener( 'resize', function() {
				if ( self.viewportWidth() >= l10n.mobileMenuBreakpoint ) {
					wpexSidr.close( 'sidr-main' );
				}
			} );

			// Scroll to local links.
			if ( self.config.localScrollSections ) {
				document.addEventListener( 'click', function( event ) {
					var localScrollItem = event.target.closest( 'li.sidr-class-local-scroll > a' );
					if ( ! localScrollItem ) {
						return;
					}
					var hash = localScrollItem.hash;
					if ( hash && -1 !== self.config.localScrollSections.indexOf( hash ) ) {
						wpexSidr.close( 'sidr-main' );
						self.scrollTo( hash );
						event.preventDefault();
						event.stopPropagation();
					}
				} );
			}

			// Close sidr when clicking on overlay.
			if ( sidrOverlay ) {
				sidrOverlay.addEventListener( 'click', function( event ) {
					wpexSidr.close( 'sidr-main' );
					event.preventDefault();
				} );
			}

			// Close when clicking esc.
			sidr.addEventListener( 'keydown', function( event ) {
				var code = event.keyCode || event.which;
				if ( 27 === code ) {
					wpexSidr.close( 'sidr-main' );
					if ( toggleBtn ) {
						toggleBtn.focus();
					}
				}
			} );

			// Remove mobile menu alt to clean up DOM.
			var mobileMenuAlt = document.querySelector( '#mobile-menu-alternative' );
			if ( mobileMenuAlt ) {
				mobileMenuAlt.parentNode.removeChild( mobileMenuAlt );
			}
		},

		/**
		 * Toggle Mobile Menu.
		 *
		 */
		mobileMenuToggle: function() {
			const self            = this;
			const mobileToggleNav = document.querySelector( '.mobile-toggle-nav' );

			if ( ! mobileToggleNav ) {
				return;
			}

			let mobileMenuContents     = '';
			let isAnimating            = false;
			const mobileToggleNavInner = mobileToggleNav.querySelector( '.mobile-toggle-nav-inner' );
			const mobileToggleNavUl    = mobileToggleNavInner.querySelector( '.mobile-toggle-nav-ul' );
			const isAnimated           = mobileToggleNav.classList.contains( 'mobile-toggle-nav--animate' );

			// Grab all content from menu and add into mobile-toggle-nav element.
			const mobileAlt = document.querySelector( '#mobile-menu-alternative > ul' );
			if ( mobileAlt ) {
				mobileMenuContents   = mobileAlt.innerHTML;
				var mobileMenuParent = document.querySelector( '#mobile-menu-alternative' );
				mobileMenuParent.parentNode.removeChild( mobileMenuParent );
			}

			if ( ! mobileMenuContents ) {
				const navMenuUl = document.querySelector( '.main-navigation-ul' );
				if ( navMenuUl ) {
					mobileMenuContents = navMenuUl.innerHTML;
				}
			}

			// Add menu items to menu.
			mobileToggleNavUl.innerHTML = mobileMenuContents;

			// Move the nav element.
			let appendTo    = mobileToggleNav.dataset.wpexAppendTo;
			let insertAfter = mobileToggleNav.dataset.wpexInsertAfter;

			if ( appendTo ) {
				appendTo = document.querySelector( appendTo );
				if ( appendTo ) {
					appendTo.appendChild( mobileToggleNav );
				}
			} else if ( insertAfter ) {
				insertAfter = document.querySelector( insertAfter );
				if ( insertAfter ) {
					self.insertAfter( mobileToggleNav, insertAfter );
				}
			}

			// Parse megamenus to move all li elements from "hide-headings" elements to parent UL.
			self.megaMenusMobile( mobileToggleNavUl );

			// Remove all element styles and -1 tab index.
			document.querySelectorAll( '.mobile-toggle-nav-ul, .mobile-toggle-nav-ul *' ).forEach( function( element ) {
				element.removeAttribute( 'style' );
				element.removeAttribute( 'id' ); // prevent seo/accessability issues.
			} );

			// Create menuAccordion.
			self.menuAccordion( mobileToggleNav );

			// Cache toggle and hamburger icon elements.
			const toggleBtn = document.querySelector( 'a.mobile-menu-toggle, li.mobile-menu-toggle > a' );
			const hamburgerIcon = toggleBtn ? toggleBtn.querySelector( '.wpex-hamburger-icon--inactive' ) : '';

			// On Show.
			function openToggle() {
				if ( mobileToggleNav.classList.contains( 'visible' ) || isAnimating ) {
					return; // already open.
				}
				if ( isAnimated ) {
					isAnimating = true;
					self.slideDown( mobileToggleNav, 300, function() {
						isAnimating = false;
						self.focusOnElement( mobileToggleNav );
						mobileToggleNav.classList.add( 'visible' );
						document.body.classList.add( 'mobile-toggle-nav-open' );
					} );
				} else {
					mobileToggleNav.classList.add( 'visible' );
					self.focusOnElement( mobileToggleNav );
					document.body.classList.add( 'mobile-toggle-nav-open' );
				}
				if ( toggleBtn ) {
					toggleBtn.classList.add( 'wpex-active' );
					toggleBtn.setAttribute( 'aria-expanded', 'true' );
					self.setHamburgerIconState( hamburgerIcon );
				}
			}

			// On Close.
			function closeToggle() {
				if ( ! mobileToggleNav.classList.contains( 'visible' ) || isAnimating ) {
					return; // already closed.
				}
				if ( isAnimated ) {
					isAnimating = true;
					self.slideUp( mobileToggleNav, 300, function() {
						isAnimating = false;
						mobileToggleNav.classList.remove( 'visible' );
						document.body.classList.remove( 'mobile-toggle-nav-open' );
					} );
				} else {
					mobileToggleNav.classList.remove( 'visible' );
					document.body.classList.remove( 'mobile-toggle-nav-open' );
				}
				mobileToggleNav.querySelectorAll( 'li.active > ul' ).forEach( function( element ) {
					self.slideUp( element );
				} );
				mobileToggleNav.querySelectorAll( '.active' ).forEach( function( element ) {
					element.classList.remove( 'active' );
				} );
				if ( toggleBtn ) {
					toggleBtn.classList.remove( 'wpex-active' );
					toggleBtn.setAttribute( 'aria-expanded', 'false' );
					self.setHamburgerIconState( hamburgerIcon );
				}
			}

			// Show/Hide.
			document.addEventListener( 'click', function( event ) {
				const button = event.target.closest( '.mobile-menu-toggle' );
				if ( ! button ) {
					if ( mobileToggleNav.classList.contains( 'visible' ) && ! event.target.closest( '.mobile-toggle-nav' ) ) {
						closeToggle();
					}
					return;
				}

				event.preventDefault();

				if ( mobileToggleNav.classList.contains( 'wpex-transitioning' ) ) {
					return;
				}

				if ( mobileToggleNav.classList.contains( 'visible' ) ) {
					closeToggle();
				} else {
					openToggle();
				}

			} );

			// Close when clicking esc.
			mobileToggleNav.addEventListener( 'keydown', function( event ) {
				const code = event.keyCode || event.which;
				if ( mobileToggleNav.classList.contains( 'visible' ) && 27 === code ) {
					closeToggle();
					if ( toggleBtn ) {
						toggleBtn.focus();
					}
				}
			} );

			const onResize = function() {
				if ( mobileToggleNav.classList.contains( 'visible' ) && self.viewportWidth() >= l10n.mobileMenuBreakpoint ) {
					closeToggle();
				}
			};

			if ( '9999' !== l10n.mobileMenuBreakpoint ) {
				window.addEventListener( 'resize', onResize );
			}
		},

		/**
		 * Full-Screen Mobile Menu.
		 */
		mobileMenuFullScreen: function() {
			const self = this;
			const nav  = document.querySelector( '.full-screen-overlay-nav' );

			if ( ! nav ) {
				return;
			}

			const toggleBtn = document.querySelector( '.mobile-menu-toggle' );
			const hamburgerIcon = toggleBtn ? toggleBtn.querySelector( '.wpex-hamburger-icon--inactive' ) : '';
			let menuHTML  = null;

			// Get menu contents
			const mobileAlt = document.querySelector( '#mobile-menu-alternative > ul' );
			if ( mobileAlt ) {
				menuHTML = mobileAlt.innerHTML;
				const mobileMenuParent = document.querySelector( '#mobile-menu-alternative' );
				mobileMenuParent.parentNode.removeChild( mobileMenuParent );
			} else {
				const mainMenu = document.querySelector( '#site-navigation .main-navigation-ul' );
				if ( mainMenu ) {
					menuHTML = mainMenu.innerHTML;
				}
			}

			if ( ! menuHTML ) {
				return;
			}

			// Insert nav items.
			const navMenuUl = nav.querySelector( '.full-screen-overlay-nav-menu ul' );
			navMenuUl.innerHTML = menuHTML;

			// Parse megamenus to move all li elements from "hide-headings" elements to parent UL.
			self.megaMenusMobile( navMenuUl );

			// Remove all inline styles and ID's from copied over nav.
			document.querySelectorAll( '.full-screen-overlay-nav-menu, .full-screen-overlay-nav-menu *' ).forEach( function( element ) {
				element.removeAttribute( 'style' );
				element.removeAttribute( 'id' );
			} );

			// Move search into full-screen nav.
			const mobileSearch = nav.querySelector( '#mobile-menu-search' );
			if ( mobileSearch ) {
				const searchLi = document.createElement( 'li' );
					searchLi.className = 'wpex-search';
				navMenuUl.appendChild( searchLi );
				searchLi.appendChild( mobileSearch );
			}

			let isAnimating = false;

			// Add dropdown toggles.
			document.addEventListener( 'click', function( event ) {
				const target = event.target.closest( '.full-screen-overlay-nav-menu li.menu-item-has-children > a' );

				if ( ! target ) {
					return;
				}

				const parent = target.parentNode; // get the li element.

				if ( parent.classList.contains( 'local-scroll' ) ) {
					return;
				}

				if ( isAnimating ) {
					event.preventDefault();
					event.stopPropagation();
					return; // prevent click spam.
				}

				// Hide current element dropdowns only and allow clicking through to the link.
				if ( parent.classList.contains( 'wpex-active' ) ) {
					parent.classList.remove( 'wpex-active' );

					parent.querySelectorAll( 'li' ).forEach( function( element ) {
						element.classList.remove( 'wpex-active' ); // remove all active classes.
					} );

					parent.querySelectorAll( 'ul' ).forEach( function( element ) {
						isAnimating = true;
						self.slideUp( element, 300, function() {
							isAnimating = false;
						} );
					} );

					if ( parent.classList.contains( 'nav-no-click' ) ) {
						event.preventDefault();
						event.stopPropagation();
					}

				}
				// Show current element dropdown and hide all others.
				else {

					// Hide all other elements to create an accordion style toggle.
					nav.querySelectorAll( '.menu-item-has-children' ).forEach( function( element ) {
						if ( element.contains( target ) || ! element.classList.contains( 'wpex-active' ) ) {
							return;
						}
						const dropdown = element.querySelector( ':scope > ul' );
						if ( dropdown ) {
							element.classList.remove( 'wpex-active' ); // remove all active classes.
							isAnimating = true;
							self.slideUp( dropdown, 300, function() {
								isAnimating = false;
							} );
						}
					} );

					// Open dropdown.
					parent.classList.add( 'wpex-active' );
					isAnimating = true;
					self.slideDown( parent.querySelector( ':scope > ul' ), 300, function() {
						isAnimating = false;
					} );

					event.preventDefault();
					event.stopPropagation();
				}

			} );

			// Open nav
			const openNav = function() {
				nav.classList.add( 'visible' );
				nav.setAttribute( 'aria-expanded', 'true' );

				if ( toggleBtn ) {
					toggleBtn.setAttribute( 'aria-expanded', 'true' );
					if ( hamburgerIcon ) {
						self.setHamburgerIconState( hamburgerIcon );
					}
				}

				// Add no scroll class to the body element.
				document.body.classList.add( 'wpex-noscroll' );

				const focus = function( event ) {
					self.focusOnElement( nav );
					nav.removeEventListener( 'transitionend', focus ); // remove event as it keeps triggering.
				};

				// Focus on the menu when opening after transition is complete to prevent issues.
				nav.addEventListener( 'transitionend', focus );

			};

			// Close nav
			const closeNav = function() {
				nav.classList.remove( 'visible' );
				nav.setAttribute( 'aria-expanded', 'false' );
				if ( toggleBtn ) {
					toggleBtn.setAttribute( 'aria-expanded', 'false' );
					if ( hamburgerIcon ) {
						self.setHamburgerIconState( hamburgerIcon );
					}
				}
				nav.querySelectorAll( '.wpex-active' ).forEach( function( element ) {
					element.classList.remove( 'wpex-active' );
					const drops = element.querySelector( ':scope > ul' );
					if ( drops ) {
						drops.style.display = 'none'; // no need to animate when closing.
					}
				} );
				document.body.classList.remove( 'wpex-noscroll' );
			};

			// Hide nav when clicking local scroll links.
			document.addEventListener( 'click', function( event ) {
				const localScrollItem = event.target.closest( '.full-screen-overlay-nav-menu .local-scroll > a' );
				if ( ! localScrollItem ) {
					return;
				}
				const hash = localScrollItem.hash;
				if ( hash && -1 !== self.config.localScrollSections.indexOf( hash ) ) {
					closeNav();
					event.preventDefault();
					event.stopPropagation();
				}
			} );

			// Show.
			if ( toggleBtn ) {
				toggleBtn.addEventListener( 'click', function( event ) {
					const button = event.target.closest( '.mobile-menu-toggle' );
					if ( ! button ) {
						return;
					}
					if ( nav.classList.contains( 'visible' ) ) {
						closeNav();
					} else {
						openNav();
					}
					event.preventDefault();
					event.stopPropagation();
				} );
			}

			// Hide when clicking close button.
			document.addEventListener( 'click', function( event ) {
				if ( ! event.target.closest( '.full-screen-overlay-nav-close' ) ) {
					return;
				}
				closeNav();
				if ( toggleBtn ) {
					toggleBtn.focus();
				}
				event.preventDefault();
				event.stopPropagation();
			} );

			// Close when clicking esc.
			nav.addEventListener( 'keydown', function( event ) {
				const code = event.keyCode || event.which;
				if ( nav.classList.contains( 'visible' ) && 27 === code ) {
					closeNav();
					if ( toggleBtn ) {
						toggleBtn.focus();
					}
				}
			} );

			if ( '9999' !== l10n.mobileMenuBreakpoint ) {
				window.addEventListener( 'resize', function( event ) {
					if ( nav.classList.contains( 'visible' ) && self.viewportWidth() >= l10n.mobileMenuBreakpoint ) {
						closeNav();
					}
				} );
			}

		},

		/**
		 * Header Search.
		 */
		menuSearch: function() {
			var searchWrap = document.querySelector( '.header-searchform-wrap' );

			if ( ! searchWrap ) {
				return;
			}

			var searchInput = searchWrap.querySelector( 'input[type="search"]' );

			if ( ! searchInput ) {
				return;
			}

			if ( searchWrap ) {
				if ( searchWrap.dataset.placeholder ) {
					searchInput.setAttribute( 'placeholder', searchWrap.dataset.placeholder );
				}
				if ( searchWrap.dataset.disableAutocomplete ) {
					searchInput.setAttribute( 'autocomplete', 'off' );
				}
			}

			this.menuSearchDropdown();
			this.menuSearchOverlay();
			this.menuSearchHeaderReplace();

		},

		/**
		 * Dropdown search.
		 */
		menuSearchDropdown: function() {
			var self               = this;
			var searchDropdownForm = document.querySelector( '#searchform-dropdown' );

			if ( ! searchDropdownForm ) {
				return;
			}

			var isOpen      = false;
			var toggleBtn   = null;
			var searchInput = searchDropdownForm.querySelector( 'input[type="search"]' );
			var targets     = 'a.search-dropdown-toggle, a.mobile-menu-search, .wpex-header-search-icon button';

			var show = function() {
				searchDropdownForm.classList.add( 'show' );
				searchDropdownForm.classList.remove( 'wpex-invisible', 'wpex-opacity-0' );

				document.querySelectorAll( targets ).forEach( function( element ) {
					element.setAttribute( 'aria-expanded', 'true' );
					var elementLi = element.closest( 'li' );
					if ( elementLi ) {
						elementLi.classList.add( 'active' );
					}
				} );

				searchInput.value = '';

				if ( 'function' === typeof jQuery ) {
					jQuery( document ).trigger( 'show.wpex.menuSearch' );
				}

				var focus = function( event ) {
					self.focusOnElement( searchDropdownForm, searchInput );
					searchDropdownForm.removeEventListener( 'transitionend', focus ); // remove event as it keeps triggering.
				};

				// Focus on the search when opening after transition is complete to prevent issues.
				searchDropdownForm.addEventListener( 'transitionend', focus );

				isOpen = true;

			};

			var hide = function() {
				searchDropdownForm.classList.remove( 'show' );
				searchDropdownForm.classList.add( 'wpex-invisible', 'wpex-opacity-0' );
				document.querySelectorAll( targets ).forEach( function( element ) {
					element.setAttribute( 'aria-expanded', 'false' );
					var elementLi = element.closest( 'li' );
					if ( elementLi ) {
						elementLi.classList.remove( 'active' );
					}
				} );
				if ( toggleBtn ) {
					toggleBtn.focus();
				}
				isOpen = false;
			};

			document.addEventListener( 'click', function( event ) {
				toggleBtn = event.target.closest( targets );
				if ( ! toggleBtn ) {
					if ( ! event.target.closest( '#searchform-dropdown' ) && isOpen ) {
						hide();
					}
					return;
				}
				event.preventDefault();
				if ( isOpen ) {
					hide();
				} else {
					show();
				}
			} );

			searchDropdownForm.addEventListener( 'keydown', function( event ) {
				var code = event.keyCode || event.which;
				if ( 27 === code && isOpen ) {
					hide();
				}
			} );

		},

		/**
		 * Overlay search.
		 */
		menuSearchOverlay: function() {
			var self          = this;
			var searchOverlay = document.querySelector( '#wpex-searchform-overlay' );

			if ( ! searchOverlay ) {
				return;
			}

			var isOpen      = false;
			var toggleBtn   = null;
			var searchInput = searchOverlay.querySelector( 'input[type="search"]' );
			var targets     = 'a.search-overlay-toggle, a.mobile-menu-search, li.search-overlay-toggle > a, .wpex-header-search-icon button';

			var show = function() {
				searchOverlay.classList.add( 'active' );

				document.querySelectorAll( targets ).forEach( function( element ) {
					element.setAttribute( 'aria-expanded', 'true' );
					var elementLi = element.closest( 'li' );
					if ( elementLi ) {
						elementLi.classList.add( 'active' );
					}
				} );

				searchInput.value = ''; // reset value on re-open.

				if ( 'function' === typeof jQuery ) {
					jQuery( document ).trigger( 'show.wpex.menuSearch' );
				}

				var focus = function( event ) {
					self.focusOnElement( searchOverlay, searchInput );
					searchOverlay.removeEventListener( 'transitionend', focus ); // remove event as it keeps triggering.
				};

				// Focus on the search when opening after transition is complete to prevent issues.
				searchOverlay.addEventListener( 'transitionend', focus );

				isOpen = true;

			};

			var hide = function() {
				searchOverlay.classList.remove( 'active' );
				document.querySelectorAll( targets ).forEach( function( element ) {
					element.setAttribute( 'aria-expanded', 'false' );
					var elementLi = element.closest( 'li' );
					if ( elementLi ) {
						elementLi.classList.remove( 'active' );
					}
				} );
				if ( toggleBtn ) {
					toggleBtn.focus();
				}
				isOpen = false;
			};

			document.addEventListener( 'click', function( event ) {
				var target = event.target.closest( targets );
				if ( ! target ) {
					if ( event.target.closest( '#wpex-searchform-overlay .wpex-close' ) && isOpen ) {
						hide();
					}
					return;
				}
				toggleBtn = target; // store toggle button for future focus.
				event.preventDefault();
				if ( isOpen ) {
					hide();
				} else {
					show();
				}
			} );

			searchOverlay.addEventListener( 'keydown', function( event ) {
				var code = event.keyCode || event.which;
				if ( 27 === code && isOpen ) {
					hide();
				}
			} );

		},

		/**
		 * Header Replace Search.
		 */
		menuSearchHeaderReplace: function() {
			var self          = this;
			var headerInner   = document.querySelector( '#site-header-inner' );
			var headerReplace = document.querySelector( '#searchform-header-replace' );

			if ( ! headerReplace ) {
				return;
			}

			var isOpen      = false;
			var toggleBtn   = null;
			var searchInput = headerReplace.querySelector( 'input[type="search"]' );
			var targets     = 'a.search-header-replace-toggle, a.mobile-menu-search, .wpex-header-search-icon button';

			var show = function() {
				headerReplace.classList.add( 'show' );
				headerReplace.classList.remove( 'wpex-invisible', 'wpex-opacity-0' );
				if ( headerInner ) {
					headerInner.classList.add( 'wpex-overflow-hidden' );
				}

				document.querySelectorAll( targets ).forEach( function( element ) {
					element.setAttribute( 'aria-expanded', 'true' );
					var elementLi = element.closest( 'li' );
					if ( elementLi ) {
						elementLi.classList.add( 'active' );
					}
				} );

				searchInput.value = '';

				if ( 'function' === typeof jQuery ) {
					jQuery( document ).trigger( 'show.wpex.menuSearch' );
				}

				var focus = function( event ) {
					self.focusOnElement( headerReplace, searchInput );
					headerReplace.removeEventListener( 'transitionend', focus ); // remove event as it keeps triggering.
				};

				// Focus on the search when opening after transition is complete to prevent issues.
				headerReplace.addEventListener( 'transitionend', focus );

				isOpen = true;

			};

			var hide = function() {
				headerReplace.classList.remove( 'show' );
				headerReplace.classList.add( 'wpex-invisible', 'wpex-opacity-0' );

				if ( headerInner ) {
					headerInner.classList.remove( 'wpex-overflow-hidden' );
				}

				document.querySelectorAll( targets ).forEach( function( element ) {
					element.setAttribute( 'aria-expanded', 'false' );
					var elementLi = element.closest( 'li' );
					if ( elementLi ) {
						elementLi.classList.remove( 'active' );
					}
				} );
				if ( toggleBtn ) {
					toggleBtn.focus();
				}
				isOpen = false;
			};

			document.addEventListener( 'click', function( event ) {
				var target = event.target.closest( targets );
				if ( ! target ) {
					if ( ! event.target.closest( '#searchform-header-replace .searchform' ) && isOpen ) {
						hide();
					}
					return;
				}
				toggleBtn = target;
				event.preventDefault();
				if ( isOpen ) {
					hide();
				} else {
					show();
				}
			} );

			headerReplace.addEventListener( 'keydown', function( event ) {
				var code = event.keyCode || event.which;
				if ( 27 === code && isOpen ) {
					hide();
				}
			} );

		},

		/**
		 * Automatically add padding to row to offset header.
		 */
		headerOverlayOffset: function() {
			var header = document.querySelector( '#site-header' );

			if ( ! header || ! header.classList.contains( 'overlay-header' ) || header.classList.contains( 'header-fixed-height' ) ) {
				return;
			}

			var headerHeight = header.getBoundingClientRect().height;

			var setHeight = function() {
				headerHeight = header.getBoundingClientRect().height;
				document.querySelectorAll( '.overlay-header-offset-div' ).forEach( function(el) {
					el.style.height = headerHeight + 'px';
				} );
			};

			setHeight();

			window.addEventListener( 'resize', setHeight );
		},

		/**
		 * Hide post edit link.
		 */
		hideEditLink: function() {
			document.addEventListener( 'click', function( event ) {
				var hidePostLink = event.target.closest( 'a.hide-post-edit' );
				if ( hidePostLink ) {
					event.preventDefault();
					var postLinks = hidePostLink.closest( 'div.post-edit' );
					if ( postLinks ) {
						postLinks.parentNode.removeChild( postLinks );
					}
				}
			} );
		},

		/**
		 * Custom menu widget accordion.
		 *
		 * @todo can this be loaded dynamically if the mobile menu widget is on the page?
		 */
		menuWidgetAccordion: function() {
			const self      = this;
			const duration  = 300;
			let isAnimating = false;

			if ( ! l10n.menuWidgetAccordion ) {
				return;
			}

			// Exclude from page animations.

			// Open toggle for active page.
			document.querySelectorAll( '#sidebar .widget_nav_menu .current-menu-ancestor, .widget_nav_menu_accordion .widget_nav_menu .current-menu-ancestor,#sidebar .widget_nav_menu .current-menu-item, .widget_nav_menu_accordion .widget_nav_menu .current-menu-item' ).forEach( function( element ) {
				element.classList.add( 'active' );
			} );

			// Show dropdown.
			var showDropdown = function( dropdown ) {
				isAnimating = true;
				self.slideDown( dropdown, duration, function() {
					isAnimating = false;
				} );
			};

			// Hide dropdown.
			var hideDropdown = function( dropdown ) {
				isAnimating = true;
				self.slideUp( dropdown, duration, function() {
					isAnimating = false;
				} );
			};

			// Listen for click events.
			document.addEventListener( 'click', function( event ) {
				const target = event.target.closest( '#sidebar .widget_nav_menu .wpex-open-submenu, .widget_nav_menu_accordion .widget_nav_menu .wpex-open-submenu' );
				if ( ! target ) {
					return;
				}

				event.preventDefault();
				event.stopPropagation();

				if ( isAnimating ) {
					return; // prevent click spam.
				}

				const li = target.closest( 'li' );
				const menu = target.closest( '.widget_nav_menu' );
				const dropdown = li.querySelector( '.sub-menu' );

				// Hide all other elements to create an accordion style menu.
				menu.querySelectorAll( '.menu-item-has-children' ).forEach( function( element ) {
					if ( element.contains( target ) ) {
						return;
					}
					// Hide all other dropdowns.
					var elementSub = element.querySelector( '.sub-menu' );
					if ( elementSub && element.classList.contains( 'active' ) ) {
						element.classList.remove( 'active' );
						hideDropdown( elementSub );
					}
				} );

				if ( ! dropdown ) {
					return;
				}

				if ( dropdown.classList.contains( 'wpex-transitioning' ) ) {
					return;
				}

				if ( li.classList.contains( 'active' ) ) {
					li.classList.remove( 'active' );
					hideDropdown( dropdown );
				} else {
					li.classList.add( 'active' );
					showDropdown( dropdown );
				}

			} );

		},

		/**
		 * Header 5 - Inline Logo.
		 */
		inlineHeaderLogo: function() {
			var self   = this;
			var header = document.querySelector( '#site-header' );

			if ( ! header || ! header.classList.contains( 'header-five' ) ) {
				return;
			}

			var logo = document.querySelector( '#site-header.header-five #site-header-inner > .header-five-logo' );
			var nav = document.querySelector( '#site-header.header-five .navbar-style-five' );
			var menuLogoLi = null;

			if ( ! logo || ! nav ) {
				return;
			}

			var getLogoBeforeEl = function() {
				var navLiElements = document.querySelectorAll( '.navbar-style-five .main-navigation-ul > li' );
				var navLiElementsVisible = [];

				for ( var i=0; i < navLiElements.length; i++ ) {
					if ( self.isVisible( navLiElements[i] ) ) {
						navLiElementsVisible.push( navLiElements[i] );
					}
				}

				var navLiCount = navLiElementsVisible.length;
				var insertLogoAfterIndex = Math.round( navLiCount / 2 ) - parseInt( l10n.headerFiveSplitOffset );

				return navLiElementsVisible[insertLogoAfterIndex];
			};

			// Insert Logo into Menu.
			var insertLogo = function() {
				var insertLogoBeforeEl = getLogoBeforeEl();
				if ( ! insertLogoBeforeEl ) {
					return;
				}
				if ( self.viewportWidth() > l10n.mobileMenuBreakpoint ) {
					if ( ! menuLogoLi ) {
						menuLogoLi = document.createElement( 'li' );
						menuLogoLi.className = 'menu-item-logo wpex-px-40';
					}
					menuLogoLi.appendChild( logo );
					insertLogoBeforeEl.parentNode.insertBefore( menuLogoLi, insertLogoBeforeEl.nextSibling );
					logo.classList.add( 'display' );
				}
			};

			// Move logo.
			var moveLogo = function() {
				var inlineLogo = document.querySelector( '.menu-item-logo .header-five-logo' );
				if ( self.viewportWidth() <= l10n.mobileMenuBreakpoint ) {
					if ( inlineLogo ) {
						var headerInner = document.querySelector( '#site-header-inner' );
						if ( headerInner ) {
							headerInner.insertBefore( inlineLogo, headerInner.firstChild );
						}
						if ( menuLogoLi ) {
							menuLogoLi.parentNode.removeChild( menuLogoLi );
						}
					}
				} else if ( ! inlineLogo ) {
					insertLogo(); // Insert logo to menu.
				}
			};

			// Run on init.
			insertLogo();

			// Run on resize.
			window.addEventListener( 'resize', moveLogo );

		},

		/**
		 * Prevent offset issues with the skipToContent link.
		 */
		skipToContent: function() {
			var self = this;
			document.addEventListener( 'click', function( event ) {
				if ( ! event.target.classList.contains( 'skip-to-content' ) ) {
					return;
				}
				var target = document.querySelector( event.target.getAttribute( 'href' ) );
				if ( target ) {
					target.setAttribute( 'tabIndex', '-1' );
					self.scrollTo( target, self.offset( target ).top - self.config.localScrollOffset );
					target.focus();
				}
				event.preventDefault();
				event.stopPropagation();
			} );
		},

		/**
		 * Back to top link.
		 */
		backTopLink: function() {
			var self = this;
			document.addEventListener( 'click', function( event ) {
				var target = event.target;

				if ( ! target.closest( 'a#site-scroll-top, a.wpex-scroll-top, .wpex-scroll-top a' ) ) {
					return;
				}

				var mainLink = target.closest( '#site-scroll-top' );

				if ( mainLink ) {
					target = mainLink;
				}

				if ( 0 !== window.scrollY ) {
					var speed = parseInt( target.dataset.scrollSpeed || parseInt( l10n.localScrollSpeed ) );
					var easing = self.getEasing( target.dataset.scrollEasing );
					if ( easing && 'function' === typeof jQuery ) {
						jQuery( 'html, body' ).stop( true, true ).animate( {
							scrollTop: 0
						}, speed, easing );
					} else {
						window.scrollTo( {
							top: 0,
							behavior: 'smooth'
						} );
					}
				}

				event.preventDefault();
				event.stopPropagation();
			} );
		},

		/**
		 * Back to top link.
		 */
		backTopButton: function() {
			var self          = this;
			var backTopButton = document.querySelector( '#site-scroll-top' );

			if ( ! backTopButton ) {
				return;
			}

			var offset = parseInt( backTopButton.dataset.scrollOffset || 100 );

			if ( 0 === offset ) {
				return;
			}

			var showHide = function( event ) {
				if ( window.pageYOffset > offset ) {
					backTopButton.classList.add( 'show');
					backTopButton.classList.remove( 'wpex-invisible', 'wpex-opacity-0' );
				} else {
					backTopButton.classList.remove( 'show' );
					backTopButton.classList.add( 'wpex-invisible', 'wpex-opacity-0' );
				}
			};

			window.addEventListener( 'scroll', showHide, self.config.passiveListeners ? { passive: true } : false );
		},

		/**
		 * Go back button
		 */
		goBackButton: function() {
			document.querySelectorAll( '.wpex-go-back' ).forEach( function( element ) {
				element.addEventListener( 'click', function( event ) {
					event.preventDefault();
					history.back();
				} );
			} );
		},

		/**
		 * Smooth Comment Scroll.
		 */
		smoothCommentScroll: function() {
			var self = this;
			document.addEventListener( 'click', function( event ) {
				if ( ! event.target.closest( '.comments-link' ) ) {
					return;
				}
				var target = document.querySelector( '#comments' );
				if ( ! target ) {
					return;
				}
				self.scrollTo(
					target,
					self.offset( target ).top - self.config.localScrollOffset - 20
				);
				event.preventDefault();
				event.stopPropagation();
			} );
		},

		/**
		 * Toggle Elements.
		 */
		toggleElements: function() {

			var getTargetElement = function( button ) {
				var target = button.getAttribute( 'aria-controls' );
				if ( target ) {
					return document.querySelector( target );
				}
			};

			document.addEventListener( 'click', function( event ) {
				var button = event.target.closest( 'a.wpex-toggle-element-trigger' );

				if ( ! button ) {
					return;
				}

				var hiddenElement = getTargetElement( button );

				if ( ! hiddenElement ) {
					return;
				}

				event.preventDefault();

				var hiddenElementParent = hiddenElement.parentNode;
				var triggerParent = button.closest( '.vc_section' ) || button.closest( '.vc_row' );
				var isOpen = hiddenElement.classList.contains( 'wpex-toggle-element--visible' );
				var isToggleElementContained = false;
				var openText = button.getAttribute( 'data-open-text' );
				var closeText = button.getAttribute( 'data-close-text' );
				var buttonTextEl = button.querySelector( '.theme-button-text' ) || button;

				if ( triggerParent.contains( hiddenElement ) ) {
					isToggleElementContained = true;
				}

				var targetSection = isToggleElementContained ? triggerParent : document;

				// Hide all open elements
				targetSection.querySelectorAll( '.wpex-toggle-element--visible' ).forEach( function( element ) {
					element.classList.remove( 'wpex-toggle-element--visible' );
				} );

				// Reset triggers.
				targetSection.querySelectorAll( 'a.wpex-toggle-element-trigger' ).forEach( function( element ) {
					element.setAttribute( 'aria-expanded', 'false' );
					element.classList.remove( 'active' );
				} );

				var hideElement = function() {
					if ( button.classList.contains( 'vcex-button' ) ) {
						button.classList.remove( 'active' );
					}
					if ( openText && closeText && buttonTextEl ) {
						buttonTextEl.textContent = openText;
					}
					hiddenElement.classList.remove( 'wpex-toggle-element--visible' );
					button.setAttribute( 'aria-expanded', 'false' );
				};

				var showElement = function() {
					if ( button.classList.contains( 'vcex-button' ) ) {
						button.classList.add( 'active' );
					}
					if ( openText && closeText && buttonTextEl ) {
						buttonTextEl.textContent = closeText;
					}
					button.setAttribute( 'aria-expanded', 'true' );
					hiddenElement.classList.add( 'wpex-toggle-element--visible' );

					// Fix issues with toggle elements inside toggle elements (@see vcex_icon_box).
					if ( hiddenElementParent.classList.contains( 'wpex-toggle-element' ) ) {
						hiddenElementParent.classList.add( 'wpex-toggle-element--visible' );
						hiddenElementParent.setAttribute( 'tabIndex', '-1' );
						hiddenElementParent.focus();
					} else {
						hiddenElement.setAttribute( 'tabIndex', '-1' );
						hiddenElement.focus();
					}

					/*var customEvent = new CustomEvent( 'wpex_toggle_element', {
						detail: {},
						bubbles: false,
						cancelable: true,

					} );
					hiddenElement.dispatchEvent( customEvent );*/
					window.dispatchEvent( new Event( 'resize' ) );
				};

				// Display target element
				if ( hiddenElement && hiddenElement.classList.contains( 'wpex-toggle-element' ) ) {
					if ( isOpen ) {
						hideElement();
					} else {
						showElement();
					}
				}
			} );

			// Return focus to tab when hitting the esc button.
			document.addEventListener( 'keydown', function( event ) {
				var toggleElement = event.target.closest( '.wpex-toggle-element--visible' );
				if ( toggleElement && 27 === event.keyCode ) {
					document.querySelectorAll( '.wpex-toggle-element-trigger[aria-expanded="true"]' ).forEach( function( element ) {
						if ( element.getAttribute( 'href' ) === '#' + toggleElement.getAttribute( 'id' ) ) {
							element.focus();
						}
					} );
				}
			} );

		},

		/**
		 * Toggle Bar.
		 */
		toggleBar: function() {
			var toggleBar = document.querySelector( '#toggle-bar-wrap' );

			if ( ! toggleBar ) {
				return;
			}

			var allowToggle = toggleBar.dataset.allowToggle;

			if ( allowToggle && 'false' !== allowToggle ) {
				this.toggleBarToggle( toggleBar );
			} else {
				this.toggleBarDismiss( toggleBar );
			}
		},

		/**
		 * Toggle Bar Toggle button.
		 */
		toggleBarToggle: function( toggleBar ) {
			const rememberState = ( 'true' === toggleBar.dataset.rememberState );
			const toggleButton  = document.querySelector( '#toggle-bar-button' );

			const getState = () => {
				return toggleBar.dataset.state || 'hidden';
			};

			const setCookie = ( state ) => {
				if ( rememberState ) {
					document.cookie = 'total_togglebar_state=' + state + '; path=/; Max-Age=604800; SameSite=Strict; Secure';
				}
			};

			const openBar = () => {
				toggleBar.classList.add( 'active-bar' );
				toggleBar.dataset.state = 'visible';
				if ( toggleButton ) {
					toggleButton.setAttribute( 'aria-expanded', 'true' );
				}
				setCookie( 'visible' );
			};

			const closeBar = () => {
				toggleBar.classList.remove( 'active-bar' );
				toggleBar.dataset.state = 'hidden';
				if ( toggleButton ) {
					toggleButton.setAttribute( 'aria-expanded', 'false' );
				}
				setCookie( 'hidden' );
			};

			const onDocClick = ( event ) => {
				const button = event.target.closest( 'a.toggle-bar-btn, a.togglebar-toggle, .togglebar-toggle > a' );
				if ( ! button ) {
					if ( 'visible' === getState() && toggleBar.classList.contains( 'close-on-doc-click' ) && ! event.target.closest( '#toggle-bar-wrap' ) ) {
						closeBar();
					}
					return;
				}
				if ( 'hidden' === getState() ) {
					openBar();
				} else {
					closeBar();
				}
				event.preventDefault();
				event.stopPropagation();
			};

			document.addEventListener( 'click', onDocClick );
		},

		/**
		 * Toggle Bar Close Button
		 */
		toggleBarDismiss: function( toggleBar ) {
			document.addEventListener( 'click', function( event ) {
				var button = event.target.closest( '.toggle-bar-dismiss__button' );
				if ( ! button ) {
					return;
				}
				toggleBar.parentNode.removeChild( toggleBar );
				if ( 'true' === toggleBar.dataset.rememberState ) {
					document.cookie = 'total_togglebar_state=hidden; path=/; Max-Age=604800; SameSite=Strict; Secure';
				}
				event.preventDefault();
			} );
		},

		/**
		 * Advanced Parallax.
		 */
		parallax: function( context ) {
			var self = this;

			var init = function() {
				document.querySelectorAll( '.wpex-parallax-bg' ).forEach( function( element ) {

					// Disable on mobile.
					if ( element.classList.contains( 'not-mobile' ) && self.mobileCheck() ) {
						return;
					}

					// Parallax settings.
					var startPosition = 0;
					var velocity = element.dataset.velocity;
					var direction = element.dataset.direction;
					var fixed = element.dataset.fixed;
					var offset = null;

					// Get element dimensions and offsets.
					var elemHeight = element.getBoundingClientRect().height;
					var elemTop = self.offset( element ).top;
					var elemBottom = elemTop + element.getBoundingClientRect().height;

					// Get window dimensions and offsets.
					var winTop = self.winScrollTop();
					var winHeight = window.innerHeight;
					var viewTop =  winTop - 20;
					var viewBottom = winTop + winHeight + 20; // (adds 20px leeway)

					// Make sure element is in viewport.
					if ( elemTop >= viewBottom || elemBottom <= viewTop ) {
						return;
					}

					// If the element is below the fold, calculate the background image start position.
					if ( elemTop > winHeight ) {
						if ( 'none' !== direction ) {
							startPosition = ( elemTop - winHeight ) * Math.abs( velocity );
						}
					}

					// Calculate parallax position.
					var position = Math.ceil( startPosition + winTop * velocity );

					// Set background position.
					var xPos = '50%';
					var yPos = '50%';

					switch( direction ) {
						case 'left':
							xPos = position + 'px';
							break;
						case 'right':
							xPos = 'calc(100% + ' + -position + 'px)';
							break;
						case 'down':
							if ( 'true' === fixed ) {
								yPos = 'calc(100% + ' + (-position) + 'px)';
							} else {
								var computedStyles = window.getComputedStyle( element );
								offset = - ( winHeight -
									elemTop -
									elemHeight -
									parseInt( computedStyles.getPropertyValue( 'padding-top' ) ) -
									parseInt( computedStyles.getPropertyValue( 'padding-bottom' ) ) );

								yPos = 'calc(100% + ' + ( offset - winTop - position ) + 'px)';
							}
							break;
						default:
							if ( 'true' === fixed ) {
								yPos = position + 'px';
							} else {
								yPos = ( elemTop - winTop + position ) + 'px';
							}
							break;
					}

					element.style.backgroundPosition = xPos + ' ' + yPos;

				} );
			};

			init(); // run immediately so that bg's look good on load.

			window.addEventListener( 'scroll', init, self.config.passiveListeners ? { passive: true } : false );
		},

		/**
		 * Local Scroll Offset.
		 */
		parseLocalScrollOffset: function( instance ) {
			var self   = this;
			var offset = 0;

			// Return custom offset.
			if ( l10n.localScrollOffset ) {
				self.config.localScrollOffset = l10n.localScrollOffset;
				return self.config.localScrollOffset;
			}

			// Adds extra offset via filter.
			if ( l10n.localScrollExtraOffset ) {
				offset = parseInt( offset ) + parseInt( l10n.localScrollExtraOffset );
			}

			// Fixed header.
			var stickyHeader = document.querySelector( '#site-header.fixed-scroll' );
			if ( stickyHeader ) {

				// Return 0 for small screens if mobile fixed header is disabled.
				if ( ! l10n.hasStickyMobileHeader && self.viewportWidth() < l10n.stickyHeaderBreakPoint ) {
					offset = parseInt( offset ) + 0;
				}

				// Return header height.
				else {

					// Shrink header.
					if ( stickyHeader.classList.contains( 'shrink-sticky-header' ) ) {
						// @todo can we remove the instance and use a different check?
						// Maybe we can add a data attribute to the header like data-sticky-init="false"
						// that we can check.
						if ( 'init' === instance || self.isVisible( stickyHeader ) ) {
							offset = parseInt( offset ) + parseInt( l10n.shrinkHeaderHeight );
						}
					}

					// Standard header.
					else {
						offset = parseInt( offset ) + stickyHeader.getBoundingClientRect().height;
					}

				}

			}

			// Loop through extra items.
			document.querySelectorAll( '.wpex-ls-offset,#wpadminbar,#top-bar-wrap-sticky-wrapper.wpex-can-sticky,#site-navigation-sticky-wrapper.wpex-can-sticky,#wpex-mobile-menu-fixed-top,.vcex-navbar-sticky-offset' ).forEach( function( element ) {
				if ( self.isVisible( element ) ) {
					offset = parseInt( offset ) + element.getBoundingClientRect().height;
				}
			} );

			/**
			 * Remove 1 pixel to prevent cross browser rounding issues (mostly firefox).
			 *
			 * @todo anyway to fix this?
			 */
			offset = offset ? offset - 1 : 0;

			self.config.localScrollOffset = offset;

			return self.config.localScrollOffset;
		},

		/**
		 * Scroll to function.
		 */
		scrollTo: function( hash, offset, callback ) {
			if ( ! hash ) {
				return;
			}

			var self         = this;
			var target       = null;
			var isLsDataLink = false;
			var lsOffset     = self.config.localScrollOffset;
			var lsSpeed      = parseInt( l10n.localScrollSpeed );
			var sections     = document.querySelectorAll( '[data-ls_id]' );
			var localSection = null;
			var easing       = self.getEasing();

			var run = function() {
				if ( easing && 'function' === typeof jQuery ) {
					jQuery( 'html, body' ).stop( true, true ).animate( {
						scrollTop: offset
					}, lsSpeed, easing );
				} else {
					window.scrollTo( {
						top: offset,
						behavior: 'smooth'
					} );
				}
			};

			// Use standard for loop since forEach doesn't support breaks.
			for ( var i=0; i < sections.length; i++ ) {
				if ( hash === sections[i].dataset.ls_id ) {
					localSection = sections[i];
					break;
				}
			}

			if ( localSection ) {
				target = localSection;
				isLsDataLink = true;
			} else if ( 'string' === typeof hash ) {
				if ( self.isSelectorValid( hash ) ) {
					target = document.querySelector( hash );
				}
			} else if ( hash.nodeType ) {
				target = hash;
			}

			// Target check.
			if ( ! target ) {
				return;
			}

			// Change the target to parent tabs when linking to tab item.
			if ( target.classList.contains( 'vc_tta-panel' ) ) {
				var tab = target.closest( '.vc_tta-tabs' );
				if ( tab ) {
					offset = self.offset( tab ).top - lsOffset - 20;
				}
			}

			// Sanitize offset.
			offset = offset || self.offset( target ).top - lsOffset;

			// Update hash.
			if ( l10n.localScrollUpdateHash && 'string' === typeof hash && isLsDataLink ) {
				window.location.hash = hash;
			}

			/**
			 * Mobile toggle Menu needs it's own code so it closes before the event fires
			 * to make sure we end up in the right place.
			 *
			 * @todo move this into it's own function if possible.
			 */
			var mobileToggleNav = document.querySelector( '.mobile-toggle-nav' );
			if ( mobileToggleNav && mobileToggleNav.classList.contains( 'visible' ) ) {

				// Make sure the nav height is removed from the offset height, but only
				// when its not absolutely positioned.
				if ( 'absolute' !== window.getComputedStyle( mobileToggleNav ).position ) {
					offset = offset - mobileToggleNav.getBoundingClientRect().height;
				}

				document.querySelectorAll( 'a.mobile-menu-toggle, li.mobile-menu-toggle > a' ).forEach( function( element ) {
					element.classList.remove( 'wpex-active' );
					element.setAttribute( 'aria-expanded', 'false' );
				} );

				self.setHamburgerIconState();

				if ( mobileToggleNav.classList.contains( 'mobile-toggle-nav--animate' ) ) {
					self.slideUp( mobileToggleNav, 300, function() {
						mobileToggleNav.classList.remove( 'visible' );
						document.body.classList.remove( 'mobile-toggle-nav-open' );
						run();
					} );
				} else {
					mobileToggleNav.classList.remove( 'visible' );
					document.body.classList.remove( 'mobile-toggle-nav-open' );
					run();
				}
			}
			// Scroll to section.
			else {
				run();
			}
		},

		/**
		 * Scroll to Hash.
		 */
		scrollToHash: function( self ) {
			var target;
			var offset = 0;
			var hash   = location.hash;

			if ( '' == hash || '#' === hash || undefined == hash ) {
				return;
			}

			// Scroll to comments.
			if ( '#view_comments' === hash || '#comments_reply' === hash ) {
				target = document.querySelector( '#comments' );
				if ( target ) {
					offset = self.offset( target ).top - self.config.localScrollOffset - 20;
					self.scrollTo( target, offset );
				}
			}

			// Scroll to specific comment, fix for sticky header.
			if ( -1 !== hash.indexOf( 'comment-' ) && document.querySelector( '#site-header.fixed-scroll' ) ) {
				target = document.querySelector( hash );
				if ( target ) {
					offset = self.offset( target ).top - self.config.localScrollOffset - 20;
					self.scrollTo( target, offset );
				}
				return;
			}

			// Custom local scroll using #localscroll-{ID} for targeting elements on the page.
			if ( -1 !== hash.indexOf( 'localscroll-' ) ) {
				hash = hash.replace( 'localscroll-', '' );
			}

			self.scrollTo( hash );
		},

		/**
		 * Local scroll links array.
		 */
		localScrollSections: function() {
			var self  = this;
			var array = [];
			var links = document.querySelectorAll( l10n.localScrollTargets );

			// Loop through links.
			links.forEach( function( link ) {

				var href = link.getAttribute( 'href' );
				var hash = href ? '#' + href.replace( /^.*?(#|$)/, '' ) : null;

				// Hash required.
				if ( hash && '#' !== hash ) {

					// Add data-ls_linkto to the links (used for section highlight).
					if ( ! link.hasAttribute( 'data-ls_linkto' ) ) {
						link.setAttribute( 'data-ls_linkto', hash );
					}

					// Data attribute targets.
					var section = document.querySelector( '[data-ls_id="' + hash + '"]' );
					if ( ! section && 'string' === typeof hash && self.isSelectorValid( hash ) ) {
						section = document.querySelector( hash );
					}

					if ( section && array.indexOf( hash ) == -1 ) {
						array.push( hash );
					}

				}

			} );

			self.config.localScrollSections = array;

			return self.config.localScrollSections;
		},

		/**
		 * Local Scroll link.
		 */
		localScrollLinks: function() {
			var self = this;

			document.addEventListener( 'click', function( event ) {
				var link = event.target.closest( l10n.localScrollTargets );

				if ( ! link ) {
					return;
				}

				var hash = link.dataset.ls_linkto || link.hash; // this.hash needed as fallback.

				// Don't try and scroll to links not in our array of allowed local scroll links.
				if ( ! self.config.localScrollSections || -1 == self.config.localScrollSections.indexOf( hash ) ) {
					return;
				}

				// Remove superfish dropdown class.
				if ( link.closest( '.sfHover' ) ) {
					link.parentNode.classList.remove( 'sfHover' );
				}

				// Scroll to hash.
				self.scrollTo( hash );

				// Return early for certain items.
				if ( link.closest( '.full-screen-overlay-nav-menu .local-scroll > a' ) ) {
					return;
				}

				event.preventDefault();
				event.stopPropagation();
			} );

			// Local Scroll - Woocommerce Reviews.
			document.addEventListener( 'click', function( event ) {
				if ( ! event.target.closest( 'body.single-product .entry-summary a.woocommerce-review-link' ) ) {
					return;
				}

				var target = document.querySelector( '.woocommerce-tabs' );
				var tab = document.querySelector( '.reviews_tab a' );

				if ( ! target || ! tab ) {
					return;
				}

				event.preventDefault();

				tab.click();

				var offset = self.offset( target ).top - self.config.localScrollOffset;
				self.scrollTo( target, offset );
			} );
		},

		/**
		 * Local Scroll Highlight on scroll.
		 *
		 * @todo update to highlight last item when we reach bottom of the page.
		 */
		localScrollHighlight: function() {
			if ( ! l10n.localScrollHighlight ) {
				return;
			}

			var self                = this;
			var localScrollSections = self.config.localScrollSections;

			// Return if there aren't any local scroll items.
			if ( ! localScrollSections.length ) {
				return;
			}

			// Highlight active items.
			function highlightSections() {
				for ( var i=0; i < localScrollSections.length; i++ ) {
					highlightSection( localScrollSections[i] );
				}
			} //highlightSections(); // no need to fire on load.

			function highlightSection( section ) {
				var targetDiv = document.querySelector( '[data-ls_id="' + section + '"]' ) || document.querySelector( section );

				if ( ! targetDiv ) {
					return;
				}

				var highlightSection = false;
				var winTop           = self.winScrollTop();
				var divPos           = self.offset( targetDiv ).top - self.config.localScrollOffset - 1;
				var divHeight        = targetDiv.offsetHeight;
				var highlightLinks   = document.querySelectorAll( '[data-ls_linkto="' + section + '"]' );

				if ( winTop >= divPos && winTop < ( divPos + divHeight ) ) {
					highlightSection = true;
				} else {
					highlightSection = false;
				}

				if ( highlightSection ) {
					targetDiv.classList.add( 'wpex-ls-inview' );

					// prevent any sort of duplicate local scroll active links
					document.querySelectorAll( '.local-scroll.menu-item' ).forEach( function( element ) {
						element.classList.remove( 'current-menu-item' );
					} );
				} else {
					targetDiv.classList.remove( 'wpex-ls-inview' );
				}

				highlightLinks.forEach( function( element ) {
					if ( highlightSection ) {
						element.classList.add( 'active' );
					} else {
						element.classList.remove( 'active' );
					}
					var li = element.closest( 'li' );
					if ( li ) {
						if ( highlightSection ) {
							li.classList.add( 'current-menu-item' );
						} else {
							li.classList.remove( 'current-menu-item' );
						}
					}
				} );

			}

			window.addEventListener( 'scroll', highlightSections, self.config.passiveListeners ? { passive: true } : false );
		},

		/**
		 * Equal heights function.
		 */
		equalHeights: function( context ) {
			if ( 'function' !== typeof window.wpexEqualHeights ) {
				return;
			}

			wpexEqualHeights( '.match-height-grid', '.match-height-content', context );
			wpexEqualHeights( '.match-height-row', '.match-height-content', context );
			wpexEqualHeights( '.vcex-feature-box-match-height', '.vcex-match-height', context );
			wpexEqualHeights( '.blog-equal-heights', '.blog-entry-inner', context );
			wpexEqualHeights( '.vc_row', '.equal-height-column', context );
			wpexEqualHeights( '.vc_row', '.equal-height-content', context );
			wpexEqualHeights( '.wpex-vc-row-columns-match-height', '.vc_column-inner', context ); // @deprecated 4.0
		},

		/**
		 * Footer Reveal Display on Load.
		 */
		footerReveal: function() {
			var self         = this;
			var footerReveal = document.querySelector( '#footer-reveal' );
			var wrap         = document.querySelector( '#wrap' );
			var main         = document.querySelector( '#main' );

			if ( ! footerReveal || ! wrap || ! main ) {
				return;
			}

			function showHide() {
				if ( self.viewportWidth() < 960 ) {
					if ( footerReveal.classList.contains( 'footer-reveal' ) ) {
						footerReveal.classList.remove( 'footer-reveal' );
						footerReveal.classList.add( 'footer-reveal-visible' );
						wrap.style.removeProperty( 'margin-bottom' );
					}
					return;
				}

				var revealHeight = footerReveal.offsetHeight;
				var windowHeight = window.innerHeight;
				var heightCheck = 0;

				if ( footerReveal.classList.contains( 'footer-reveal' ) ) {
					heightCheck = wrap.offsetHeight + self.config.localScrollOffset;
				} else {
					heightCheck = wrap.offsetHeight + self.config.localScrollOffset - revealHeight;
				}

				// Hide the footer.
				if ( ( windowHeight > revealHeight ) && ( heightCheck > windowHeight ) ) {
					if ( footerReveal.classList.contains( 'footer-reveal-visible' ) ) {
						wrap.style.marginBottom = revealHeight + 'px';
						footerReveal.classList.remove( 'footer-reveal-visible' );
						footerReveal.classList.add( 'footer-reveal' );
					}
				}

				// Display the footer.
				else {
					if ( footerReveal.classList.contains( 'footer-reveal' ) ) {
						wrap.style.removeProperty( 'margin-bottom' );
						footerReveal.classList.remove( 'footer-reveal' );
						footerReveal.classList.remove( 'wpex-visible' );
						footerReveal.classList.add( 'footer-reveal-visible' );
					}
				}

			}

			function reveal() {
				if ( ! footerReveal.classList.contains( 'footer-reveal' ) ) {
					return;
				}
				if ( self.scrolledToBottom( main ) ) {
					footerReveal.classList.add( 'wpex-visible' );
				} else {
					footerReveal.classList.remove( 'wpex-visible' );
				}
			}

			// Fire right away.
			showHide();
			reveal();

			// Fire on events.
			window.addEventListener( 'scroll', reveal, self.config.passiveListeners ? { passive: true } : false );
			window.addEventListener( 'resize', showHide );
		},

		/**
		 * Set min height on main container to prevent issue with extra space below footer.
		 */
		fixedFooter: function() {
			if ( ! document.body.classList.contains( 'wpex-has-fixed-footer' ) ) {
				return;
			}

			var main = document.querySelector( '#main' );

			if ( ! main ) {
				return;
			}

			function run() {
				main.style.minHeight = ( main.offsetHeight + ( window.innerHeight - document.documentElement.offsetHeight ) ) + 'px';
			} run();

			window.addEventListener( 'resize', run );
		},

		/**
		 * Custom Selects.
		 */
		customSelects: function( context ) {
			if ( ! context || ! context.childNodes ) {
				context = document;
			}

			var self    = this;
			var selects = context.querySelectorAll( l10n.customSelects );

			selects.forEach( function(element) {
				var parent = element.parentNode;
				if ( parent.classList.contains( 'wpex-select-wrap' ) || parent.classList.contains( 'wpex-multiselect-wrap' ) ) {
					return;
				}
				var elementId = element.id;
				var className = elementId ? ' wpex-' + elementId : '';
				var addIcon   = false;
				if ( self.isVisible( element ) ) { // @todo is this really needed?
					// Wrap the select
					var div = document.createElement( 'div' );
					if ( element.hasAttribute( 'multiple' ) ) {
						div.className = 'wpex-multiselect-wrap' + className + '';
					} else {
						div.className = 'wpex-select-wrap' + className + '';
						addIcon = true;
					}
					element = self.wrap( element, div );
					// Add icon to wrapper
					if ( addIcon && l10n.selectArrowIcon ) {
						var arrow       = document.createElement( 'span' );
						arrow.className = 'wpex-select-arrow';
						var icon        = document.createDocumentFragment();
						var temp        = document.createElement( 'div' );
						temp.innerHTML  = l10n.selectArrowIcon;
						icon.appendChild(temp.firstChild);
						arrow.appendChild( icon );
						div.appendChild( arrow );
					}
				}
			} );
		},

		/**
		 * Inline hover styles.
		 *
		 * @todo switch to using inline style var() where possible.
		 */
		hoverStyles: function() {
			var oldStyle,
				items,
				itemsLength,
				headCSS = '',
				cssObj = {},
				style,
				head;

			oldStyle = document.querySelector( '.wpex-hover-data' ); // querySelector will return null if nothing is found.
			if ( oldStyle ) {
				oldStyle.remove(); // prevent dups in VC front end.
			}

			items       = document.querySelectorAll( '[data-wpex-hover]' );
			itemsLength = items.length;

			// No need to do anything if we don't have any items to style.
			if ( ! itemsLength ) {
				return;
			}

			// use a standard loop so we can access the index for adding unique classnames.
			for (var i = 0; i < itemsLength; i++) {
				parseItem( i );
			}

			if ( cssObj ) {
				for ( var css in cssObj ) {
					if ( cssObj.hasOwnProperty( css ) ) {
						headCSS += cssObj[css] + '{' + css + '}';
					}
				}
			}

			if ( headCSS ) {
				style = document.createElement( 'style' );
				style.classList.add( 'wpex-hover-data' );
				style.appendChild( document.createTextNode( headCSS ) );
				head = document.head || document.getElementsByTagName( 'head' )[0];
				head.appendChild( style );
			}

			function parseItem( index ) {
				var element,
					data,
					uniqueClass,
					classList,
					hoverCSS = '',
					target = '',
					parent;

				element = items[index];
				data = element.dataset.wpexHover;

				if ( ! data ) {
					return;
				}

				data = JSON.parse( data );

				// Remove any wpex-dhover-{int} classname that may have been previously added.
				// This is a fix for AJAX functions and front-end editor edits.
				classList = element.classList;
				for ( var i = 0; i < classList.length; i++ ) {
					if ( -1 !== classList[i].indexOf( 'wpex-dhover-' ) ) {
						element.classList.remove( classList[i] );
					}
				}

				// New unique classname based on index.
				uniqueClass = 'wpex-dhover-' + index;

				if ( data.parent ) {
					parent = element.closest( data.parent );
					if ( parent ) {
						parent.classList.add( uniqueClass + '-p' );
						element.classList.add( uniqueClass );
						target = '.' + uniqueClass + '-p:hover .' + uniqueClass;
					}
				} else {
					element.classList.add( uniqueClass );
					target = '.' + uniqueClass + ':hover';
				}

				for ( var key in data ) {
					if ( data.hasOwnProperty( key ) ) {
						if ( 'target' === key || 'parent' === key ) {
							continue;
						}
						hoverCSS += key + ':' +  data[key] + '!important;';
					}
				}

				if ( hoverCSS ) {
					if ( hoverCSS in cssObj ) {
						cssObj[hoverCSS] = cssObj[hoverCSS] + ',' + target;
					} else {
						cssObj[hoverCSS] = target;
					}
				}
			}
		},

		/**
		 * Overlay Mobile Support.
		 *
		 * @todo move to it's own file and load dynamically?
		 */
		overlaysMobileSupport: function() {
			var self          = this;
			var supportsTouch = (window.matchMedia("(any-pointer: coarse)").matches);

			if ( ! supportsTouch ) {
				return;
			}

			var dragging = false;

			// Remove overlays completely if mobile support is disabled.
			document.querySelectorAll( '.overlay-parent.overlay-hh' ).forEach( function( element ) {
				if ( ! element.classList.contains( 'overlay-ms' ) ) {
					var overlay = element.querySelector( '.theme-overlay' );
					if ( overlay ) {
						element.parentNode.removeChild( element );
					}
				}
			} );

			var hideAllOverlays = function() {
				document.querySelectorAll( '.overlay-parent.wpex-touched, .vcex-image-swap.wpex-touched' ).forEach( function( element ) {
					element.classList.remove( 'wpex-touched' );
				} );
			};

			var touchEnd = function( event ) {
				var element = event.target.closest( '.overlay-parent.overlay-ms.overlay-h, .vcex-image-swap' );

				if ( ! element ) {
					return;
				}

				if ( dragging ) {
					hideAllOverlays();
					return;
				}

				if ( element.classList.contains( 'wpex-touched' ) ) {
					element.classList.remove( 'wpex-touched' );
					return; // overlay is open, allow clicking inside.
				}

				event.preventDefault();

				hideAllOverlays();
				element.classList.add( 'wpex-touched' );
			};

			var touchMove = function( event ) {
				if ( ! event.target.closest( '.wpex-touched' ) ) {
					hideAllOverlays();
				}
				if ( event.target.closest( '.overlay-parent.overlay-ms.overlay-h, .vcex-image-swap' ) ) {
					dragging = true;
				}
			};

			var touchStart = function( event ) {
				if ( ! event.target.closest( '.wpex-touched' ) ) {
					hideAllOverlays();
				}
				if ( event.target.closest( '.overlay-parent.overlay-ms.overlay-h, .vcex-image-swap' ) ) {
					dragging = false;
				}
			};

			// Attach handlers.
			document.addEventListener( 'touchend', touchEnd, false ); // must have false event listeners.
			document.addEventListener( 'touchmove', touchMove, self.config.passiveListeners ? { passive: true } : false );
			document.addEventListener( 'touchstart', touchStart, self.config.passiveListeners ? { passive: true } : false );
		},

		/**
		 * Sticky Topbar.
		 */
		stickyTopBar: function() {
			var self = this;
			var isSticky = false;
			var offset = 0;
			var stickyTopbar = document.querySelector( '#top-bar-wrap.wpex-top-bar-sticky' );
			var brkPoint = l10n.stickyTopBarBreakPoint;

			if ( ! stickyTopbar ) {
				return;
			}

			// Add wrapper.
			var stickyWrap = document.createElement( 'div' );
			stickyWrap.id = 'top-bar-wrap-sticky-wrapper';
			stickyWrap.className = 'wpex-sticky-top-bar-holder not-sticky';

			self.wrap( stickyTopbar, stickyWrap );

			// Get offset.
			const getOffset = () => {
				offset = 0; // Reset offset for resize.

				document.querySelectorAll( '.wpex-sticky-el-offset,#wpex-mobile-menu-fixed-top,#wpadminbar' ).forEach( ( element ) => {
					if ( self.isBeforeElement( element, stickyWrap ) && self.isVisible( element ) && 'fixed' === window.getComputedStyle( element ).position ) {
						offset = offset + element.getBoundingClientRect().height;
					}
				} );

				return offset;
			};

			// Stick the TopBar.
			function setSticky() {
				if ( isSticky ) {
					return;
				}

				// Tweak wrap class.
				stickyWrap.style.height = stickyTopbar.getBoundingClientRect().height + 'px';
				stickyWrap.classList.remove( 'not-sticky' );
				stickyWrap.classList.add( 'is-sticky' );

				// Tweak topbar class.
				stickyTopbar.classList.remove( 'wpex-z-99' );
				stickyTopbar.classList.add( 'wpex-z-999' );

				// Add CSS to topbar.
				stickyTopbar.style.top = getOffset() + 'px';
				stickyTopbar.style.width = stickyWrap.getBoundingClientRect().width + 'px';
				stickyTopbar.style.position = 'fixed'; // don't use CSS class to prevent possible custom CSS issues.

				// Set sticky to true.
				isSticky = true;
			}

			// Unstick the TopBar.
			function destroySticky() {
				if ( ! isSticky ) {
					return;
				}

				// Tweak wrap class.
				stickyWrap.style.height = '';
				stickyWrap.classList.remove( 'is-sticky' );
				stickyWrap.classList.add( 'not-sticky' );

				// Tweak topbar classes.
				stickyTopbar.classList.add( 'wpex-z-99' );
				stickyTopbar.classList.remove( 'wpex-z-999' );

				// Remove topbar css.
				stickyTopbar.style.width = '';
				stickyTopbar.style.top = '';
				stickyTopbar.style.position = '';

				// Set sticky to false.
				isSticky = false;
			}

			// Runs on load and resize.
			function initSticky() {
				if ( ! l10n.hasStickyTopBarMobile && ( self.viewportWidth() < brkPoint ) ) {
					stickyWrap.classList.remove( 'wpex-can-sticky' );
					destroySticky();
					return;
				}

				var windowTop = self.winScrollTop();

				stickyWrap.classList.add( 'wpex-can-sticky' );

				if ( isSticky ) {

					// Update sticky wrapper height incase it changed on resize.
					stickyWrap.style.height = stickyTopbar.getBoundingClientRect().height + 'px';

					// Update topbar top position and width incase it changed on resize.
					stickyTopbar.style.top = getOffset() + 'px';
					stickyTopbar.style.width = stickyWrap.getBoundingClientRect().width + 'px';

				} else {

					// Set sticky based on original offset.
					offset = self.offset( stickyWrap ).top - getOffset();

					// Set or destroy sticky.
					if ( 0 !== windowTop && windowTop > offset ) {
						setSticky();
					} else {
						destroySticky();
					}

				}
			}

			// On scroll actions for sticky topbar.
			function onScroll() {
				var windowTop = self.winScrollTop();

				// Set or destroy sticky based on offset.
				if ( ( 0 !== windowTop ) && ( windowTop >= ( self.offset( stickyWrap ).top - getOffset() ) ) ) {
					setSticky();
				} else {
					destroySticky();
				}
			}

			// Fire on init.
			initSticky();

			// Fire onscroll event.
			window.addEventListener( 'scroll', function() {
				if ( stickyWrap && stickyWrap.classList.contains( 'wpex-can-sticky' ) ) {
					onScroll();
				}
			}, self.config.passiveListeners ? { passive: true } : false );

			// Fire onResize.
			window.addEventListener( 'resize', initSticky );
		},

		/**
		 * Sticky Header Menu.
		 */
		stickyHeaderMenu: function() {
			var self = this;
			var stickyNav = document.querySelector( '#site-navigation-wrap.fixed-nav' );

			if ( ! stickyNav ) {
				return;
			}

			var isSticky = false;
			var header = document.querySelector( '#site-header' );

			// Define sticky wrap.
			var stickyWrap = document.createElement( 'div' );
			stickyWrap.id = 'site-navigation-sticky-wrapper';
			stickyWrap.className = 'wpex-sticky-navigation-holder not-sticky';

			self.wrap( stickyNav, stickyWrap );

			const getOffset = () => {
				const self = this;
				let offset = 0;

				document.querySelectorAll( '.wpex-sticky-el-offset,.wpex-top-bar-sticky,#wpex-mobile-menu-fixed-top,#wpadminbar' ).forEach( ( element ) => {
					if ( self.isBeforeElement( element, stickyWrap ) && self.isVisible( element ) && 'fixed' === window.getComputedStyle( element ).position ) {
						offset = offset + element.getBoundingClientRect().height;
					}
				} );

				if ( l10n.addStickyHeaderOffset ) {
					offset = offset + l10n.addStickyHeaderOffset;
				}

				return offset;
			};

			// Add offsets.
			var stickyWrapTop = self.offset( stickyWrap ).top;
			var setStickyPos = stickyWrapTop - getOffset();

			// Shrink menu function.
			function setSticky() {
				if ( isSticky ) {
					return;
				}

				// Add wrap class and toggle sticky class.
				stickyWrap.style.height = stickyNav.getBoundingClientRect().height + 'px';
				stickyWrap.classList.remove( 'not-sticky' );
				stickyWrap.classList.add( 'is-sticky' );

				// Add CSS to topbar.
				stickyNav.style.top = getOffset() + 'px';
				stickyNav.style.width = stickyWrap.getBoundingClientRect().width + 'px';

				// Remove header dynamic styles.
				if ( header ) {
					header.classList.remove( 'dyn-styles' );
				}

				// Update shrunk var.
				isSticky = true;
			}

			// Un-Shrink header function.
			const destroySticky = () => {
				if ( ! isSticky ) {
					return;
				}

				// Remove sticky wrap height and toggle sticky class.
				stickyWrap.style.height = '';
				stickyWrap.classList.remove( 'is-sticky' );
				stickyWrap.classList.add( 'not-sticky' );

				// Remove navbar width.
				stickyNav.style.top = '';
				stickyNav.style.width = '';

				// Re-add dynamic header styles.
				if ( header ) {
					header.classList.add( 'dyn-styles' );
				}

				// Update shrunk var.
				isSticky = false;
			};

			// On load check.
			function initResizeSetSticky() {
				if ( self.viewportWidth() <= l10n.stickyNavbarBreakPoint ) {
					destroySticky();
					stickyWrap.classList.remove( 'wpex-can-sticky' );
					return;
				}

				var windowTop = self.winScrollTop();

				stickyWrap.classList.add( 'wpex-can-sticky' );

				if ( isSticky ) {
					// Already sticky, lets update height, width and offsets.
					stickyWrap.style.height = stickyNav.getBoundingClientRect().height + 'px';
					stickyNav.style.top = getOffset() + 'px';
					stickyNav.style.width = stickyWrap.getBoundingClientRect().width + 'px';
				} else {
					if ( windowTop >= setStickyPos && 0 !== windowTop ) {
						setSticky();
					} else {
						destroySticky();
					}

				}
			}

			// Sticky check / enable-disable.
			function onScroll() {
				if ( ! stickyWrap.classList.contains( 'wpex-can-sticky' ) ) {
					return;
				}

				var windowTop = self.winScrollTop();

				// Sticky menu.
				if ( 0 !== windowTop && windowTop >= setStickyPos ) {
					setSticky();
				} else {
					destroySticky();
				}
			}

			// Fire on init.
			initResizeSetSticky();

			// Fire onscroll event.
			window.addEventListener( 'scroll', onScroll, self.config.passiveListeners ? { passive: true } : false );

			// Fire onResize.
			window.addEventListener( 'resize', initResizeSetSticky );

			// Fire resize on flip.
			window.addEventListener( 'orientationchange', function() {
				destroySticky();
				initResizeSetSticky();
			} );
		},

		/**
		 * Sticky Header.
		 */
		stickyHeader: function() {
			const self = this;
			let stickyStyle = l10n.stickyHeaderStyle;
			if ( 'standard' !== stickyStyle && 'shrink' !== stickyStyle && 'shrink_animated' !== stickyStyle ) {
				return;
			}

			const header = document.querySelector( '#site-header.fixed-scroll' );

			if ( ! header ) {
				return;
			}

			const mobileOnly = header.classList.contains( 'fixed-scroll--mobile-only' );

			const startPoint = () => {
				var startPosition = l10n.stickyHeaderStartPosition;
				if ( startPosition && ! isNaN( startPosition ) ) {
					return startPosition;
				}
				var el = document.querySelector( startPosition );
				if ( el ) {
					return this.offset( el ).top;
				}
				return 0;
			};

			let isSticky = false;
			let isShrunk = false;

			// Add sticky wrap.
			const stickyWrap = document.createElement( 'div' );
			stickyWrap.id = 'site-header-sticky-wrapper';
			stickyWrap.className = 'wpex-sticky-header-holder not-sticky';

			self.wrap( header, stickyWrap );

			const getOffset = () => {
				let offset = 0;

				document.querySelectorAll( '.wpex-sticky-el-offset,.wpex-top-bar-sticky,#wpex-mobile-menu-fixed-top,#wpadminbar' ).forEach( ( element ) => {
					if ( self.isBeforeElement( element, stickyWrap ) && self.isVisible( element ) && 'fixed' === window.getComputedStyle( element ).position ) {
						offset = offset + element.getBoundingClientRect().height;
					}
				} );

				if ( l10n.addStickyHeaderOffset ) {
					offset = offset + l10n.addStickyHeaderOffset;
				}

				return offset;
			};

			const getBreakpoint = () => {
				let brkPoint = parseInt( l10n.stickyHeaderBreakPoint );
				return mobileOnly ? brkPoint -1 : brkPoint;
			};

			const getBreakpointComparisonOperator = () => {
				return mobileOnly ? '>' : '<';
			};

			// Define main vars for sticky function.
			const brkPoint = getBreakpoint();
			const mobileSupport = l10n.hasStickyMobileHeader;
			const customStart = startPoint();

			// Check if we are on mobile size.
			function pastBreakPoint() {
				switch ( getBreakpointComparisonOperator() ) {
					case '<':
						return ( self.viewportWidth() < brkPoint );
						break;
					case '>':
						return ( self.viewportWidth() > brkPoint );
						break;
				}
			}

			// Check if we are past the header.
			let pastHeaderBottomCheck = 0;
			if ( document.querySelector( '#overlay-header-wrap' ) ) {
				pastHeaderBottomCheck = self.offset( header ).top + header.getBoundingClientRect().height;
			} else {
				pastHeaderBottomCheck = self.offset( stickyWrap ).top + stickyWrap.getBoundingClientRect().height;
			}
			function pastheader() {
				if ( self.winScrollTop() > pastHeaderBottomCheck ) {
					return true;
				}
				return false;
			}

			// Check start position.
			const start_position = () => {
				let startPosition = customStart || self.offset( stickyWrap ).top;
				return startPosition - getOffset();
			};

			const getShrunkDuration = () => {
				let elDuration = window.getComputedStyle( header ).transitionDuration;
				let duration = '';
				if ( ! elDuration || '0s' !== elDuration ) {
					duration = parseFloat( elDuration ) * ( elDuration.indexOf( 'ms' ) >- 1 ? 1 : 1000 );
				}

				if ( ! duration ) {
					duration = 300;
				}
				return duration;
			};

			// Transform.
			const transformPrepare = () => {
				var windowTop = self.winScrollTop();
				if ( isSticky ) {
					header.classList.add( 'transform-go' ); // prevent issues when scrolling.
				}
				if ( windowTop <= 0 ) {
					header.classList.remove( 'transform-prepare' );
				} else if ( pastheader() ) {
					header.classList.add( 'transform-prepare' );
				} else {
					header.classList.remove( 'transform-prepare' );
				}
			};

			// Set sticky.
			const setSticky = () => {
				if ( isSticky ) {
					return;
				}

				// Set wrapper height before toggling sticky classes.
				stickyWrap.style.height = header.getBoundingClientRect().height + 'px';

				// Toggle sticky classes.
				stickyWrap.classList.remove( 'not-sticky' );
				stickyWrap.classList.add( 'is-sticky' );
				header.classList.remove( 'dyn-styles' );

				// Tweak header styles.
				header.style.top = getOffset() + 'px';
				header.style.width = stickyWrap.getBoundingClientRect().width + 'px';

				// Add transform go class.
				if ( header.classList.contains( 'transform-prepare' ) ) {
					header.classList.add( 'transform-go' );
				}

				// Set sticky to true.
				isSticky = true;
			};

			// Shrink/unshrink header.
			const shrink = () => {
				let check = true; // we already check if it's enabled before running this function.

				if ( mobileOnly ) {
					check = true;
				} else if ( pastBreakPoint() ) {
					if ( mobileSupport && l10n.hasStickyMobileHeaderShrink ) {
						check = true;
					} else {
						check = false;
					}
				}

				if ( check && pastheader() ) {
					if ( ! isShrunk && isSticky ) {
						header.classList.add( 'sticky-header-shrunk' );
						isShrunk = true;
					}
				} else {
					header.classList.remove( 'sticky-header-shrunk' );
					isShrunk = false;
				}
			};

			// Destroy actions.
			const destroyActions = () => {

				// Remove sticky wrap height and toggle sticky class.
				stickyWrap.classList.remove( 'is-sticky' );
				stickyWrap.classList.add( 'not-sticky' );

				if ( header.classList.contains( 'shrink-sticky-header' ) ) {
					let duration = getShrunkDuration();
					setTimeout( function() {
						if ( ! isSticky ) {
							stickyWrap.style.height = '';
						}
					}, duration );
				} else {
					stickyWrap.style.height = ''; //@todo remove for shrink as well?
				}

				// Reset header.
				header.classList.add( 'dyn-styles' );
				header.style.width = '';
				header.style.top = '';
				header.classList.remove( 'transform-go' );

				// Set sticky to false.
				isSticky = false;

				// Make sure shrink header is removed.
				header.classList.remove( 'sticky-header-shrunk' ); // Fixes some bugs with really fast scrolling.
				isShrunk = false;

				// Reset sticky wrapper (causes weird animations with shrink sticky)...@todo enable?
				//stickyWrap.style.height = '';
			};

			// Destroy sticky.
			function destroySticky() {
				if ( ! isSticky ) {
					return;
				}

				if ( customStart ) {
					header.classList.remove( 'transform-go' );
					if ( isShrunk ) {
						header.classList.remove( 'sticky-header-shrunk' );
						isShrunk = false;
					}
				} else {
					header.classList.remove( 'transform-prepare' );
				}

				destroyActions();
			}

			// On load check.
			const initResizeSetSticky = () => {
				let windowTop = self.winScrollTop();

				if ( ( mobileOnly || ! mobileSupport ) && pastBreakPoint() ) {
					destroySticky();
					stickyWrap.classList.remove( 'wpex-can-sticky' );
					header.classList.remove( 'transform-prepare' );
					return;
				}

				//header.classList.add( 'transform-go' );
				stickyWrap.classList.add( 'wpex-can-sticky' );

				if ( isSticky ) {

					// Update header height on resize incase it's changed height.
					// Can't update shrink as it may cause issues.
					if ( ! header.classList.contains( 'shrink-sticky-header' ) ) {
						stickyWrap.style.height = header.getBoundingClientRect().height + 'px';
					}

					header.style.top = getOffset() + 'px';
					header.style.width = stickyWrap.getBoundingClientRect().width + 'px';

				} else {

					if ( 0 !== windowTop && windowTop > start_position() ) {
						setSticky();
					} else {
						destroySticky();
					}

				}

				if ( l10n.hasStickyHeaderShrink ) {
					shrink();
				}
			};

			// On scroll function.
			const onScroll = () => {
				let windowTop = self.winScrollTop();

				if ( ! stickyWrap.classList.contains( 'wpex-can-sticky' ) ) {
					return;
				}

				// Animate scroll with custom start.
				if ( customStart ) {
					transformPrepare();
				}

				// Set or destroy sticky.
				if ( 0 != windowTop && windowTop >= start_position() ) {
					setSticky();
				} else {
					destroySticky();
				}

				// Shrink.
				if ( l10n.hasStickyHeaderShrink ) {
					shrink();
				}
			};

			// Fire on init.
			initResizeSetSticky();

			// Fire onscroll event.
			window.addEventListener( 'scroll', onScroll, self.config.passiveListeners ? { passive: true } : false );

			// Fire onResize.
			window.addEventListener( 'resize', initResizeSetSticky );

			// Destroy and run onResize function on orientation change.
			window.addEventListener( 'orientationchange', () => {
				destroySticky();
				initResizeSetSticky();
			} );
		},

		/**
		 * WPBAKERY Slider & Accordions.
		 *
		 * @todo load conditionally if needed.
		 */
		vcTabsTogglesJS: function() {
			if ( ! document.body.classList.contains( 'wpb-js-composer' ) || 'function' !== typeof jQuery ) {
				return;
			}

			jQuery( document ).on( 'afterShow.vc.accordion', function( event, options ) {

				if ( 'undefined' === typeof event ) {
					return;
				}

				var $this = jQuery( event.target );
				var tab   = $this.data( 'vc.accordion' );

				if ( ! tab ) {
					return;
				}

				tab = tab.getTarget();

				if ( ! tab ) {
					return;
				}

				if ( ! tab.length ) {
					return;
				}

				if ( 'function' === typeof jQuery && 'function' === typeof jQuery.fn.sliderPro ) {
					tab.find( '.wpex-slider' ).each( function() {
						if ( jQuery( this ).data( 'sliderPro' ) ) {
							jQuery( this ).sliderPro( 'update' );
						}
					} );
				}

				if ( 'function' === typeof Isotope ) {
					tab.find( '.vcex-isotope-grid, .wpex-masonry-grid, .vcex-navbar-filter-grid' ).each( function() {
						var iso = Isotope.data( this );
						if ( iso ) {
							iso.layout();
						}
					} );
				}

			} );
		},

		/**
		 * Accessability fixes/enhancements.
		 */
		accessability: function() {

			// Add tabindex -1 to nav-no-click links.
			document.querySelectorAll( '#site-navigation li.nav-no-click:not(.menu-item-has-children) > a, .mobile-toggle-nav li.nav-no-click > a, li.sidr-class-nav-no-click > a' ).forEach( function( element ) {
				element.setAttribute( 'tabIndex', '-1' );
			} );

			// Add tabindex -1 to megamenu headings that are using "#" for the href.
			document.querySelectorAll( '#site-navigation li.megamenu > ul.sub-menu > li.menu-item.menu-item-has-children > a' ).forEach( function( megaMenuHeading ) {
				if ( '#' === megaMenuHeading.getAttribute( 'href' ) ) {
					megaMenuHeading.setAttribute( 'tabIndex', '-1' );
				}
			} );

			// Allow for opening WPBakery FAQ elements with the enter button.
			document.querySelectorAll( '.vc_toggle .vc_toggle_title' ).forEach( function( element ) {
				element.setAttribute( 'tabIndex', 0 );
				element.addEventListener( 'keydown', function( event ) {
					var code = event.keyCode || event.which;
					if ( 13 === code ) {
						event.target.click();
					}
				} );
			} );

		},

		/* Helpers.
		------------------------------------------------------------------------------ */

		/**
		 * Is the DOM ready?
		 */
		domReady: function( fn ) {
			if ( typeof fn !== 'function' || 'undefined' === typeof document ) {
				return;
			}

			var readyState = document.readyState;

			// If document is already loaded, run method.
			if ( readyState === 'interactive' || readyState === 'complete' ) {
				return setTimeout( fn ); // Timeout prevents issues with dependencies when using async.
			}

			// Otherwise, wait until document is loaded.
			document.addEventListener( 'DOMContentLoaded', fn, false );
		},

		/**
		 * Retina Check.
		 */
		retinaCheck: function() {
			var mediaQuery = '(-webkit-min-device-pixel-ratio: 1.5), (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (min-resolution: 1.5dppx)';
			if ( window.devicePixelRatio > 1 ) {
				return true;
			}
			if ( window.matchMedia && window.matchMedia( mediaQuery ).matches ) {
				return true;
			}
			return false;
		},

		/**
		 * Mobile Check.
		 */
		mobileCheck: function() {
			if ( /Android|webOS|iPhone|iPad|iPod|iPad Simulator|iPhone Simulator|iPod Simulator|BlackBerry|IEMobile|Opera Mini/i.test( navigator.userAgent ) ) {
				return true;
			}
		},

		/**
		 * Check if passive event listeners are supported by the browser.
		 */
		passiveListenersSupport: function() {
			var supportsPassive = false;
			try {
				var opts = Object.defineProperty({}, 'passive', {
				get: function() {
						supportsPassive = true;
					}
				} );
				window.addEventListener( 'testPassive', null, opts);
				window.removeEventListener( 'testPassive', null, opts);
			} catch (e) {}
			return supportsPassive;
		},

		/**
		 * Get easing value.
		 */
		getEasing: function( customEasing ) {
			if ( ! l10n.localScrollEasing || 'function' !== typeof jQuery || 'undefined' === typeof jQuery.easing || 'function' !== typeof jQuery.easing.jswing ) {
				return;
			}
			var easing = customEasing || l10n.localScrollEasing;
			if ( ! jQuery.easing.hasOwnProperty( easing ) ) {
				easing = 'swing';
			}
			return easing;
		},

		/**
		 * Viewport width.
		 */
		viewportWidth: function() {
			var e = window, a = 'inner';
			if ( ! ( 'innerWidth' in window ) ) {
				a = 'client';
				e = document.documentElement || document.body;
			}
			return e[ a+'Width' ];
		},

		/**
		 * Check if a given selector is valid.
		 */
		isSelectorValid: function( selector ) {
			var queryCheck = function( s ) {
				document.createDocumentFragment().querySelector( s );
			};
			try {
				queryCheck( selector );
			} catch( error ) {
				return false;
			}
			return true;
		},

		/**
		 * SlideUp element.
		 */
		slideUp: function( target, duration, callback ) {
			if ( ! target ) {
				return;
			}

			// Get current element display.
			var display = window.getComputedStyle( target ).display;

			// Element already closed.
			if ( 'none' === display ) {
				return;
			}

			// Allow for CSS defined transition duration.
			var elDuration = window.getComputedStyle( target ).transitionDuration;

			if ( ! elDuration || '0s' !== elDuration ) {
				duration = parseFloat( elDuration ) * ( elDuration.indexOf( 'ms' ) >- 1 ? 1 : 1000 );
			}

			if ( ! duration ) {
				duration = 300;
			}

			// Add classname that we can check to prevent from doing other things during transition.
			target.classList.add( 'wpex-transitioning' );

			// Set transition duration for animation.
			target.style.transitionProperty = 'height, margin, padding';
			target.style.transitionDuration = duration + 'ms';

			// Set element height.
			target.style.height = target.offsetHeight + 'px';
			target.offsetHeight; // get height so that browser re-paints element.
			target.style.overflow = 'hidden';

			// Reset element height.
			target.style.height = 0;
			target.style.paddingTop = 0;
			target.style.paddingBottom = 0;
			target.style.marginTop = 0;
			target.style.marginBottom = 0;

			// Remove properties after animation has finished
			setTimeout( function() {
				target.style.display = 'none';
				target.style.removeProperty( 'height' );
				target.style.removeProperty( 'padding-top' );
				target.style.removeProperty( 'padding-bottom' );
				target.style.removeProperty( 'margin-top' );
				target.style.removeProperty( 'margin-bottom' );
				target.style.removeProperty( 'overflow' );
				target.style.removeProperty( 'transition-duration' );
				target.style.removeProperty( 'transition-property' );
				target.classList.remove( 'wpex-transitioning' );
				if ( callback ) {
					callback();
				}
			}, duration );

		},

		/**
		 * SlideDown element.
		 */
		slideDown: function( target, duration, callback ) {
			if ( ! target ) {
				return;
			}

			// Get current element display.
			var display = window.getComputedStyle( target ).display;

			// Already open.
			if ( 'block' === display ) {
				return;
			}

			// Allow for CSS defined transition duration.
			var elDuration = window.getComputedStyle( target ).transitionDuration;

			if ( ! elDuration || '0s' !== elDuration ) {
				duration = parseFloat( elDuration ) * ( elDuration.indexOf( 'ms' ) >- 1 ? 1 : 1000 );
			}

			if ( ! duration ) {
				duration = 300;
			}

			// Add classname that we can check to prevent from doing other things during transition.
			target.classList.add( 'wpex-transitioning' );

			// Remove inline display if it had previously been toggled.
			target.style.removeProperty( 'display' );

			// Element needs to be visible to calculate height and animate.
			if ( 'none' === display ) {
				display = 'block';
			}

			target.style.display = display;
			target.style.transitionProperty = 'none'; // prevent possible animation when calculating height.

			var height = target.offsetHeight;

			// Reset height so we can animate it.
			target.style.overflow = 'hidden';
			target.style.height = 0;
			target.style.paddingTop = 0;
			target.style.paddingBottom = 0;
			target.style.marginTop = 0;
			target.style.marginBottom = 0;
			target.offsetHeight; // get height so that browser re-paints element.
			target.style.boxSizing = 'border-box';

			// Add transition duration for animation.
			target.style.transitionProperty = 'height, margin, padding';
			target.style.transitionDuration = duration + 'ms';

			// Set element height using a timeout otherwise animation won't work.
			target.style.height = height + 'px';
			target.style.removeProperty( 'padding-top' );
			target.style.removeProperty( 'padding-bottom' );
			target.style.removeProperty( 'margin-top' );
			target.style.removeProperty( 'margin-bottom' );

			// Remove properties after animation has finished.
			setTimeout( function() {
				target.style.removeProperty( 'height' );
				target.style.removeProperty( 'overflow' );
				target.style.removeProperty( 'transition-duration' );
				target.style.removeProperty( 'transition-property' );
				target.classList.remove( 'wpex-transitioning' );
				if ( callback ) {
					callback();
				}
			}, duration );

		},

		/**
		 * Set correct focus states for custom elements.
		 *
		 * @param {HTMLElement} el
		 */
		focusOnElement: function( element, initialFocus ) {
			var self          = this;
			var focusElements = element.querySelectorAll( 'button, [href], input, select, textarea, a,[tabindex]:not([tabindex="-1"])' );

			if ( ! focusElements.length ) {
				return;
			}

			var focus = [];

			for ( var i=0; i < focusElements.length; i++ ) {
				if ( self.isVisible( focusElements[i] ) ){
					focus.push( focusElements[i] );
				}
			}

			if ( ! focus.length ) {
				return;
			}

			var firstFocus = focus[0];
			var lastFocus = focus[focus.length - 1];

			// Add initial focus.
			if ( initialFocus ) {
				initialFocus.focus();
			} else {
				firstFocus.focus();
			}

			// Redirect last tab to first input.
			lastFocus.addEventListener( 'keydown', function( event ) {
				var code = event.keyCode || event.which;
				if ( 9 === code && ! event.shiftKey ) {
					event.preventDefault();
					firstFocus.focus();
				}
			} );

			// Redirect first shift+tab to last input.
			firstFocus.addEventListener( 'keydown', function( event ) {
				var code = event.keyCode || event.which;
				if ( 9 === code && event.shiftKey ) {
					event.preventDefault();
					lastFocus.focus();
				}
			} );

		},

		/**
		 * Wrap an element.
		 */
		wrap: function( element, wrapper ) {
			if ( ! element.childNodes ) {
				element = [element];
			}
			if ( element.nextSibling ) {
				element.parentNode.insertBefore( wrapper, element.nextSibling );
			} else {
				element.parentNode.appendChild( wrapper );
			}
			wrapper.appendChild( element );
		},

		/**
		 * Insert element after another.
		 */
		insertAfter: function( newNode, referenceNode ) {
			referenceNode.parentNode.insertBefore( newNode, referenceNode.nextSibling );
		},

		/**
		 * Get element offset.
		 */
		offset: function( element ) {
			var rect = element.getBoundingClientRect();
			return {
				top: rect.top + this.winScrollTop(),
				left: rect.left + this.winScrollTop(),
			};
		},

		/**
		 * Check if element is visible.
		 */
		isVisible: function( element ) {
			if ( ! element ) {
				return false;
			}
			return !!( element.offsetWidth || element.offsetHeight || element.getClientRects().length );
		},

		/**
		 * Check if element is empty
		 */
		isEmpty: function( element ) {
			return ! element || '' === element.innerHTML;
		},

		/**
		 * Check if an element is before another one.
		 */
		isBeforeElement: function( firstEl, secondEl ) {
			if ( 'wpadminbar' === firstEl.id ) {
				return true; //  incase some plugin moved it's position.
			}
			if ( ! firstEl || ! secondEl ) {
				return;
			}
			const position = firstEl.compareDocumentPosition( secondEl );
			return ( 4 === position );
		},

		/**
		 * Grabs content and inserts into another element.
		 */
		insertExtras: function( element, target, method ) {
			if ( ! element || ! target ) {
				return;
			}
			switch ( method ) {
				case 'append':
					target.appendChild( element );
					break;
				case 'prepend':
					target.insertBefore( element, target.firstChild );
					break;
			}
			element.classList.remove( 'wpex-hidden' );
		},

		/**
		 * Returns the window scrollTop position.
		 */
		winScrollTop: function() {
			var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
			if ( scrollTop < 0 ) {
				scrollTop = 0; // return 0 if negative to prevent issues with elastic scrolling in Safari.
			}
			return scrollTop;
		},

		/**
		 * Check if window has scrolled to bottom of element.
		 */
		scrolledToBottom: function( element ) {
			var check = this.winScrollTop() >= element.offsetTop + element.offsetHeight - window.innerHeight;
			return check;
		},

		/**
		 * Remove class prefix.
		 */
		removeClassPrefix: function( elements, regex, prefix ) {
			elements.forEach( function( element ) {
				var classes = element.classList;
				for ( var j=0; j < classes.length; j++ ) {
					if ( regex.test( classes[j] ) ) {
						var newclass = classes[j].replace( prefix, '' );
						element.classList.replace( classes[j], newclass );
					}
				}
			} );
		},

		/**
		 * Creates accordion menu.
		 */
		menuAccordion: function( menu ) {
			if ( ! menu ) {
				return;
			}

			var self          = this;
			var isAnimating   = false;
			var isMobileMenu  = ( menu.classList.contains( 'mobile-toggle-nav' ) || menu.classList.contains( 'sidr-class-dropdown-menu' ) );

			// Add toggle buttons.
			menu.querySelectorAll( '.menu-item-has-children, .sidr-class-menu-item-has-children' ).forEach( function( menuItem ) {
				var link = menuItem.querySelector( 'a' );
				if ( ! link ) {
					return;
				}
				var toggleBtn = document.createElement( 'button' );

				if ( isMobileMenu ) {
					toggleBtn.className = 'wpex-open-submenu wpex-unstyled-button wpex-flex wpex-items-center wpex-justify-end wpex-absolute wpex-top-0 wpex-right-0 wpex-h-100 wpex-cursor-pointer wpex-opacity-80 wpex-overflow-hidden';
				} else {
					toggleBtn.className = 'wpex-open-submenu wpex-unstyled-button wpex-block';
				}

				toggleBtn.setAttribute( 'aria-haspopup', 'true' );
				toggleBtn.setAttribute( 'aria-expanded', 'false' );
				toggleBtn.setAttribute( 'role', 'button' );
				toggleBtn.setAttribute( 'aria-label', l10n.i18n.openSubmenu.replace( '%s', link.textContent.trim() ) );

				if ( isMobileMenu ) {
					if ( l10n.mobileMenuOpenSubmenuIcon ) {
						const temp = document.createElement( 'div' );
						temp.innerHTML = l10n.mobileMenuOpenSubmenuIcon;
						toggleBtn.appendChild(temp.firstChild);
					}
				} else {
					const toggleBtnIcon = document.createElement( 'span' );
					toggleBtnIcon.className = 'ticon ticon-angle-down';
					toggleBtnIcon.setAttribute( 'aria-hidden', 'true' );
					toggleBtn.appendChild( toggleBtnIcon );
				}
				link.appendChild( toggleBtn );
			} );

			var closeSubmenu = function( submenu ) {
				var li = submenu.closest( 'li.active' );
				li.classList.remove( 'active' );
				var link = li.querySelector( 'a' );
				var toggle = li.querySelector( '.wpex-open-submenu' );
				toggle.setAttribute( 'aria-expanded', 'false' );
				toggle.setAttribute( 'aria-label', l10n.i18n.openSubmenu.replace( '%s', link.textContent.trim() ) );
				isAnimating = true;
				self.slideUp( submenu, null, function() {
					isAnimating = false;
				} );
			};

			// Add click event.
			document.addEventListener( 'click', function( event ) {
				var button = event.target.closest( '.wpex-open-submenu' );
				if ( ! button || ! menu.contains( button ) ) {
					return;
				}

				var li = button.closest( 'li' );

				if ( ! li ) {
					return;
				}

				var ul = li.querySelector( 'ul' );

				if ( ! ul ) {
					return; // no dropdowns.
				}

				var link = li.querySelector( 'a' );

				if ( ! link ) {
					return; // no link.
				}

				event.preventDefault();
				event.stopPropagation(); // !!important!! needed since button is inside link.

				if ( isAnimating ) {
					return; // prevent click spam.
				}

				// Closing.
				if ( li.classList.contains( 'active' ) ) {

					// Close child submenus.
					li.querySelectorAll( 'li.active > ul' ).forEach( function( submenu ) {
						closeSubmenu( submenu );
					} );

					// Close self.
					li.classList.remove( 'active' );
					button.setAttribute( 'aria-expanded', 'false' );
					button.setAttribute( 'aria-label', l10n.i18n.openSubmenu.replace( '%s', link.textContent.trim() ) );
					isAnimating = true;
					self.slideUp( ul, null, function() {
						isAnimating = false;
					} );
				}

				// Opening.
				else {
					button.setAttribute( 'aria-expanded', 'true' );
					button.setAttribute( 'aria-label', l10n.i18n.closeSubmenu.replace( '%s', link.textContent.trim() ) );

					// Close all open submenus that arent parents of this submenu.
					menu.querySelectorAll( 'li.active > ul' ).forEach( function( submenu ) {
						if ( ! submenu.contains( ul ) ) {
							closeSubmenu( submenu );
						}
					} );

					// Open self.
					isAnimating = true;
					self.slideDown( ul, null, function() {
						isAnimating = false;
					} );
					li.classList.add( 'active' );
				}

			} );
		},

		/**
		 * Set hamburger icon state.
		 */
		setHamburgerIconState: function( el ) {
			if ( ! el ) {
				var toggleBtn = document.querySelector( 'a.mobile-menu-toggle, li.mobile-menu-toggle > a' );
				el = toggleBtn ? toggleBtn.querySelector( '.wpex-hamburger-icon' ) : null;
			}
			if ( ! el ) {
				return;
			}
			if ( el.classList.contains( 'wpex-hamburger-icon--active' ) ) {
				el.classList.remove( 'wpex-hamburger-icon--active' );
				el.classList.add( 'wpex-hamburger-icon--inactive' );
			} else if ( el.classList.contains( 'wpex-hamburger-icon--inactive' ) ) {
				el.classList.remove( 'wpex-hamburger-icon--inactive' );
				el.classList.add( 'wpex-hamburger-icon--active' );
			}
		},

		/**
		 * Fallbacks for old functions.
		 *
		 * @todo deprecate
		 */
		lightbox: function( context ) {
			if ( 'function' === typeof window.wpexFancybox ) {
				wpexFancybox();
			}
		},
		sliderPro: function( $context ) {
			if ( 'function' === typeof window.wpexSliderPro ) {
				wpexSliderPro();
			}
		},
		loadMore: function() {
			if ( 'function' === typeof window.wpexLoadMore ) {
				wpexLoadMore();
			}
		}

	};

	// Start things up
	wpex.init();

} ) ( wpex_theme_params );