文件操作 - core.js
返回文件管理
返回主菜单
删除本文件
文件: /storage/v12552/jimbrandstatter/public_html/wp-content/themes/Total/assets/js/frontend/core.js
编辑文件内容
/* 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 = { localScrollSections: [] }; }, /** * Replaces the "wpex-no-js" body class with "wpex-js". */ replaceNoJsClass: function() { let bodyClass = document.body.className; bodyClass = bodyClass.replace(/wpex-no-js/, 'wpex-js'); document.body.className = bodyClass; }, /** * Bind Events. */ bindEvents: function() { const self = this; self.setScrollBarWidth(); /*** Fires as soon as the document is loaded ***/ self.domReady( () => { 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.inlineHeaderLogo(); self.menuSearch(); self.skipToContent(); self.backTopLink(); self.backTopButton(); self.goBackButton(); self.smoothCommentScroll(); self.dialogs(); self.toggleElements(); self.localScrollLinks(); self.customSelects(); self.hoverStyles(); self.overlaysMobileSupport(); self.accessibility(); } ); /*** Fires once the window has been loaded ***/ window.addEventListener( 'load', () => { document.body.classList.add( 'wpex-window-loaded' ); // Main. self.megaMenusWidth(); self.megaMenusTop(); self.stickyTopBar(); 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(); self.stickyElements(); // 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', () => { self.setScrollBarWidth(); } ); }, /** * Set scrollbar width variable to fix issues with 100vw */ setScrollBarWidth: function() { const self = this; const elements = document.querySelectorAll( '.wpex-vc-full-width-row:not(.wpex-vc-full-width-row--centered)' ); if ( ! elements ) { return; } const scrollbarWidth = self.getScrollBarWidth(); elements.forEach( ( element ) => { element.style.setProperty('--scrollbar-width', `${scrollbarWidth}px`); } ); }, /** * 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', ( event ) => { const 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( ( 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( ( element ) => { if ( ! element.contains( target ) ) { element.classList.remove( 'wpex-active' ); //element.querySelector( 'a' ).setAttribute( 'aria-expanded', 'false' ); } } ); const li = target.closest( '.menu-item-has-children' ); const 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', ( event ) => { const target = event.target.closest( '.wpex-dropdown-menu--onclick .menu-item-has-children.wpex-active' ); if ( 'Escape' !== event.key || ! target ) { return; } target.classList.remove( 'wpex-active' ); //target.querySelector( 'a' ).setAttribute( 'aria-expanded', 'false' ); } ); document.querySelectorAll( '.wpex-dropdown-menu--onclick .sub-menu' ).forEach( ( dropdown ) => { dropdown.addEventListener( 'keydown', ( event ) => { if ( 'Escape' === event.key ) { const closestParent = event.target.closest( '.menu-item-has-children.wpex-active' ); if ( closestParent ) { closestParent.classList.remove( 'wpex-active' ); const closestParentA = closestParent.querySelector( 'a' ); //closestParentA.setAttribute( 'aria-expanded', 'false' ); closestParentA.focus(); event.stopPropagation(); } } } ); } ); }, /* * Dropdown menu touch support. */ dropdownMenuTouch: function() { const self = this; let dragging = false; document.querySelectorAll( '.wpex-dropdown-menu--onhover .menu-item-has-children > a' ).forEach( ( element ) => { element.addEventListener( 'touchend', ( event ) => { if ( dragging ) { // hide dropdowns here? return; } const 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', ( event ) => { dragging = true; }, { passive: true } ); element.addEventListener( 'touchstart', ( event ) => { dragging = false; }, { passive: true } ); } ); const clickingOutside = ( event ) => { const 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( ( element ) => { if ( element.contains( target ) ) { return; } element.classList.remove( 'wpex-touched' ); } ); }; document.addEventListener( 'touchstart', clickingOutside, { passive: true } ); document.addEventListener( 'touchmove', clickingOutside, { passive: true } ); }, /** * Add "megamenu__inner-ul" classname to megamenu inner UL elements. */ megaMenuAddClasses: function() { document.querySelectorAll( '.main-navigation-ul .megamenu > .sub-menu' ).forEach( ( element ) => { element.querySelectorAll( '.sub-menu' ).forEach( ( 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() { const self = this; const init = () => { const navWrap = document.querySelector( '#site-navigation-wrap.wpex-stretch-megamenus' ); let navLeft; let headerInnerLeft; if ( ! self.isVisible( navWrap ) ) { return; } const megaMenus = navWrap.querySelectorAll( '.megamenu:not(.no-stretch) > ul' ); if ( ! megaMenus.length ) { return; } const headerInner = document.querySelector( '#site-header-inner.container' ); if ( ! headerInner ) { return; } const addNegativeMargin = navWrap.classList.contains( 'navbar-style-one' ); const headerInnerWidth = headerInner.getBoundingClientRect().width; if ( addNegativeMargin ) { headerInnerLeft = parseInt( headerInner.getBoundingClientRect().left ); navLeft = navWrap.getBoundingClientRect().left; } megaMenus.forEach( ( element ) => { element.style.width = headerInnerWidth + 'px'; if ( addNegativeMargin ) { element.style.marginLeft = -(navLeft-headerInnerLeft) + 'px'; } } ); }; // Add events. init(); window.addEventListener( 'resize', init ); }, /** * MegaMenus Top Position. */ megaMenusTop: function() { const self = this; const header = document.querySelector( '#site-header.header-one' ); if ( ! header ) { return; } const navWrap = header.querySelector( '#site-navigation-wrap.wpex-stretch-megamenus:not(.wpex-flush-dropdowns)' ); if ( ! this.isVisible( navWrap ) ) { return; } const megaMenus = navWrap.querySelectorAll( '.megamenu > ul'); if ( ! megaMenus ) { return; } const setTop = () => { if ( ! self.isVisible( navWrap ) ) { return; } const navHeight = navWrap.getBoundingClientRect().height; const topVal = ( ( header.getBoundingClientRect().height - navHeight ) / 2 ) + navHeight; megaMenus.forEach( ( element ) => { element.style.top = topVal + 'px'; } ); }; // Add events. setTop(); window.addEventListener( 'scroll', setTop, { passive: true } ); window.addEventListener( 'resize', setTop ); navWrap.querySelectorAll( '.megamenu > a' ).forEach( ( element ) => { element.addEventListener( 'mouseenter', setTop, false ); } ); }, /** * Parses Megamenu HTML for mobile. */ megaMenusMobile: function( menu ) { if ( ! menu ) { return; } const 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( ( 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( ( headingLi ) => { // Remove the heading link. const headingA = headingLi.querySelector( 'a' ); if ( headingA ) { headingA.parentNode.removeChild( headingA ); } // Remove ul wrapper around direct heading li elements. const 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' ); } ); } ); }, /** * Header Search. */ menuSearch: function() { const searchWrap = document.querySelector( '.header-searchform-wrap' ); if ( ! searchWrap ) { return; } const 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() { const self = this; const searchDropdownForm = document.querySelector( '#searchform-dropdown' ); if ( ! searchDropdownForm ) { return; } let isOpen = false; let onCloseFocus = false; let toggleBtn = null; const searchInput = searchDropdownForm.querySelector( 'input[type="search"]' ); const targets = 'a.search-dropdown-toggle, a.mobile-menu-search, .wpex-header-search-icon button'; const show = () => { document.body.classList.add( 'header-search-is-open' ); searchDropdownForm.classList.add( 'show' ); searchDropdownForm.classList.remove( 'wpex-invisible', 'wpex-opacity-0' ); document.querySelectorAll( targets ).forEach( ( 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' ); } const focus = ( 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; onCloseFocus = false; }; const hide = () => { document.body.classList.remove( 'header-search-is-open' ); searchDropdownForm.classList.remove( 'show' ); searchDropdownForm.classList.add( 'wpex-invisible', 'wpex-opacity-0' ); document.querySelectorAll( targets ).forEach( ( element ) => { element.setAttribute( 'aria-expanded', 'false' ); var elementLi = element.closest( 'li' ); if ( elementLi ) { elementLi.classList.remove( 'active' ); } } ); if ( toggleBtn && onCloseFocus ) { toggleBtn.focus(); } isOpen = false; }; const onDocClick = ( event ) => { toggleBtn = event.target.closest( targets ); if ( ! toggleBtn ) { if ( ! event.target.closest( '#searchform-dropdown' ) && isOpen ) { hide(); } return; } event.preventDefault(); if ( isOpen ) { hide(); } else { show(); } }; const onKeyDown = ( event ) => { const key = event.key; if ( 'Escape' === key && isOpen ) { onCloseFocus = true; hide(); } }; // Add events. document.addEventListener( 'click', onDocClick ); searchDropdownForm.addEventListener( 'keydown', onKeyDown ); }, /** * Overlay search. */ menuSearchOverlay: function() { const self = this; const searchOverlay = document.querySelector( '#wpex-searchform-overlay' ); if ( ! searchOverlay ) { return; } let isOpen = false; let toggleBtn = null; let onCloseFocus = false; const searchInput = searchOverlay.querySelector( 'input[type="search"]' ); const targets = 'a.search-overlay-toggle, a.mobile-menu-search, li.search-overlay-toggle > a, .wpex-header-search-icon button'; const setFocus = () => { const focus = ( 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 ); }; const returnFocus = () => { if ( toggleBtn && onCloseFocus ) { toggleBtn.focus(); } }; const show = () => { isOpen = true; onCloseFocus = false; document.body.classList.add( 'header-search-is-open' ); searchOverlay.classList.add( 'active' ); self.htmlNoScroll( 'add' ); document.querySelectorAll( targets ).forEach( ( element ) => { element.setAttribute( 'aria-expanded', 'true' ); const 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' ); } setFocus(); }; const hide = ( event ) => { isOpen = false; document.body.classList.remove( 'header-search-is-open' ); searchOverlay.classList.remove( 'active' ); document.querySelectorAll( targets ).forEach( ( element ) => { element.setAttribute( 'aria-expanded', 'false' ); const elementLi = element.closest( 'li' ); if ( elementLi ) { elementLi.classList.remove( 'active' ); } } ); returnFocus(); // after animation has finished. setTimeout( () => { self.htmlNoScroll( 'remove' ); }, self.getElTransitionDuration( searchOverlay ) ); }; const onDocClick = ( event ) => { const target = event.target.closest( targets ); if ( ! target ) { if ( isOpen && event.target.closest( '#wpex-searchform-overlay .wpex-close' ) ) { hide(); } return; } toggleBtn = target; // store toggle button for future focus. event.preventDefault(); if ( isOpen ) { hide(); } else { show(); } }; const onKeyDown = ( event ) => { const key = event.key; if ( 'Escape' === key && isOpen ) { onCloseFocus = true; return hide(); } else if ( event.target.closest( '#wpex-searchform-overlay .wpex-close' ) && ( 'Space' === key || 'Enter' === key ) ) { onCloseFocus = true; } }; // Add events. document.addEventListener( 'click', onDocClick ); document.addEventListener( 'keydown', onKeyDown ); }, /** * Header Replace Search. */ menuSearchHeaderReplace: function() { const self = this; const headerReplace = document.querySelector( '#searchform-header-replace' ); if ( ! headerReplace ) { return; } let isOpen = false; let onCloseFocus = false; let toggleBtn = null; const headerInner = document.querySelector( '#site-header-inner' ); const slideDown = headerReplace.classList.contains( '-wpex-translate-y-50' ); const searchInput = headerReplace.querySelector( 'input[type="search"]' ); const targets = 'a.search-header-replace-toggle, a.mobile-menu-search, .wpex-header-search-icon button'; const show = () => { document.body.classList.add( 'header-search-is-open' ); headerReplace.classList.add( 'show' ); headerReplace.classList.remove( 'wpex-invisible', 'wpex-opacity-0' ); if ( headerInner ) { headerInner.classList.add( 'wpex-overflow-hidden' ); // headerInner.classList.add( 'wpex-invisible' ); // will break Poppy demo, don't do this - would require extra option. } if ( slideDown ) { headerReplace.classList.remove( '-wpex-translate-y-50' ); } document.querySelectorAll( targets ).forEach( ( element ) => { element.setAttribute( 'aria-expanded', 'true' ); const elementLi = element.closest( 'li' ); if ( elementLi ) { elementLi.classList.add( 'active' ); } } ); searchInput.value = ''; if ( 'function' === typeof jQuery ) { jQuery( document ).trigger( 'show.wpex.menuSearch' ); } const focus = ( 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; onCloseFocus = false; }; const hide = () => { document.body.classList.remove( 'header-search-is-open' ); headerReplace.classList.remove( 'show' ); headerReplace.classList.add( 'wpex-invisible', 'wpex-opacity-0' ); if ( headerInner ) { headerInner.classList.remove( 'wpex-overflow-hidden' ); // headerInner.classList.remove( 'wpex-invisible' ); } if ( slideDown ) { headerReplace.classList.add( '-wpex-translate-y-50' ); } document.querySelectorAll( targets ).forEach( ( element ) => { element.setAttribute( 'aria-expanded', 'false' ); const elementLi = element.closest( 'li' ); if ( elementLi ) { elementLi.classList.remove( 'active' ); } } ); if ( toggleBtn && onCloseFocus ) { toggleBtn.focus(); } isOpen = false; }; const onDocClick = ( event ) => { const 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(); } }; const onKeyDown = ( event ) => { if ( ! isOpen ) { return; } const key = event.key; if ( 'Escape' === key ) { onCloseFocus = true; hide(); } else if ( event.target.closest( '#searchform-header-replace-close' ) && ( 'Space' === key || 'Enter' === key ) ) { onCloseFocus = true; } }; // Add events. document.addEventListener( 'click', onDocClick ); headerReplace.addEventListener( 'keydown', onKeyDown ); }, /** * Automatically add padding to row to offset header. */ headerOverlayOffset: function() { const header = document.querySelector( '#site-header' ); if ( ! header || ! header.classList.contains( 'overlay-header' ) || header.classList.contains( 'header-fixed-height' ) ) { return; } const setHeight = () => { const headerHeight = header.getBoundingClientRect().height; document.querySelectorAll( '.overlay-header-offset-div' ).forEach( ( el ) => { el.style.height = headerHeight + 'px'; } ); }; // Add events. setHeight(); window.addEventListener( 'resize', setHeight ); }, /** * Header 5 - Inline Logo. * * @todo move to it's own file. */ inlineHeaderLogo: function() { const self = this; const header = document.querySelector( '#site-header' ); if ( ! header || ! header.classList.contains( 'header-five' ) ) { return; } const logo = document.querySelector( '#site-header.header-five #site-header-inner > .header-five-logo' ); const nav = document.querySelector( '#site-header.header-five .navbar-style-five' ); let menuLogoLi = null; if ( ! logo || ! nav ) { return; } const getLogoBeforeEl = () => { const navLiElements = document.querySelectorAll( '.navbar-style-five .main-navigation-ul > li' ); let navLiElementsVisible = []; for ( let i=0; i < navLiElements.length; i++ ) { if ( self.isVisible( navLiElements[i] ) ) { navLiElementsVisible.push( navLiElements[i] ); } } const navLiCount = navLiElementsVisible.length; const insertLogoAfterIndex = Math.round( navLiCount / 2 ) - parseInt( l10n.headerFiveSplitOffset ); return navLiElementsVisible[insertLogoAfterIndex]; }; // Insert Logo into Menu. const insertLogo = () => { const 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' ); } }; const onResize = () => { const inlineLogo = document.querySelector( '.menu-item-logo .header-five-logo' ); if ( self.viewportWidth() <= l10n.mobileMenuBreakpoint ) { if ( inlineLogo ) { const 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. } }; // Add Events. insertLogo(); window.addEventListener( 'resize', onResize ); }, /** * Handles the skip to content link. */ skipToContent: function() { const self = this; const onDocClick = ( event ) => { if ( ! event.target.classList.contains( 'skip-to-content' ) ) { return; } const target = document.querySelector( event.target.getAttribute( 'href' ) ); if ( target ) { target.setAttribute( 'tabIndex', '-1' ); self.scrollTo( target ); } event.preventDefault(); event.stopPropagation(); }; document.addEventListener( 'click', onDocClick ); }, /** * Back to top link. */ backTopLink: function() { const self = this; const onDocClick = ( event ) => { let target = event.target; if ( ! target.closest( 'a#site-scroll-top, a.wpex-scroll-top, .wpex-scroll-top a' ) ) { return; } const mainLink = target.closest( '#site-scroll-top' ); if ( mainLink ) { target = mainLink; } if ( 0 !== window.scrollY ) { const topSpan = document.querySelector( '[data-ls_id="#site_top"]' ); if ( topSpan ) { topSpan.focus( { preventScroll: true, focusVisible: false } ); } const speed = parseInt( target.dataset.scrollSpeed || parseInt( l10n.localScrollSpeed ) ); const 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: self.getScrollToBehavior() } ); } } event.preventDefault(); event.stopPropagation(); }; // Add events. document.addEventListener( 'click', onDocClick ); }, /** * Back to top link. */ backTopButton: function() { const self = this; const backTopButton = document.querySelector( '#site-scroll-top' ); if ( ! backTopButton ) { return; } const offset = parseInt( backTopButton.dataset.scrollOffset || 100 ); if ( 0 === offset ) { return; } const showHide = ( 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' ); } }; // Add events. window.addEventListener( 'scroll', showHide, { passive: true } ); }, /** * Go back button. */ goBackButton: function() { document.addEventListener( 'click', ( event ) => { if ( event.target.closest( '.wpex-go-back' ) ) { event.preventDefault(); history.back(); } } ); }, /** * Smooth Comment Scroll. */ smoothCommentScroll: function() { const self = this; const onDocClick = ( event ) => { if ( ! event.target.closest( '.comments-link' ) ) { return; } const target = document.querySelector( '#comments' ); if ( ! target ) { return; } self.scrollTo( target, -20 ); event.preventDefault(); event.stopPropagation(); }; // Add events. document.addEventListener( 'click', onDocClick ); }, /** * Dialogs. */ dialogs: function() { const self = this; const dialogOnClick = ( event ) => { const target = event.target; if ( ! target.closest( '.wpex-modal__inner' ) ) { const dialog = target.closest( 'dialog' ); if ( dialog ) { closeDialog( dialog ); } } }; const openDialog = ( modalId ) => { if ( ! modalId ) { return; } const dialog = document.querySelector( `#${modalId.replace('#', '')}` ); if ( ! dialog ) { return; } if ( dialog.classList.contains( 'wpex-modal' ) ) { dialog.showModal(); const inner = dialog.querySelector( '.wpex-modal__inner' ); if ( inner ) { dialog.addEventListener( 'click', dialogOnClick ); } document.body.classList.add( 'modal-is-open' ); if ( ! document.body.classList.contains( 'vc_editor' ) ) { self.htmlNoScroll( 'add' ); } } else { dialog.show(); } }; const closeDialog = ( dialog ) => { if ( dialog.classList.contains( 'wpex-modal' ) ) { dialog.removeEventListener( 'click', dialogOnClick ); } dialog.close(); }; const onDocClick = ( event ) => { const openButton = event.target.closest( '.wpex-open-modal,.wpex-open-dialog' ); const closeButton = event.target.closest( '.wpex-close-modal' ); if ( ! openButton && ! closeButton ) { return; } event.preventDefault(); if ( openButton ) { const dialog = openButton.getAttribute( 'aria-controls' ); if ( dialog ) { return openDialog( dialog ); } } if ( closeButton ) { const dialog = event.target.closest( 'dialog' ); if ( dialog ) { return closeDialog( dialog ); } } }; const onModalClose = ( event ) => { document.body.classList.remove( 'modal-is-open' ); self.htmlNoScroll( 'remove' ); }; const addOnCloseEvents = () => { document.querySelectorAll( '.wpex-modal' ).forEach( dialog => { dialog.addEventListener( 'close', onModalClose ); } ); }; // Register event listeners. addOnCloseEvents(); // note: dialog close event does not bubble. document.addEventListener( 'click', onDocClick ); window.addEventListener( 'totaltheme_loadmore_posts', addOnCloseEvents ); window.addEventListener( 'vcexAfterAjaxSuccess', addOnCloseEvents ); }, /** * Toggle Elements. */ toggleElements: function() { const self = this; // const hasToggleElements = document.querySelector( '.wpex-toggle-element' ); const getTargetElement = ( trigger ) => { const target = trigger.getAttribute( 'aria-controls' ); if ( target ) { return document.querySelector( `#${target.replace('#', '')}` ); } }; const enableTrigger = ( trigger ) => { trigger.setAttribute( 'aria-expanded', 'true' ); if ( trigger.classList.contains( 'vcex-button' ) || trigger.classList.contains( 'theme-button' ) ) { trigger.classList.add( 'active' ); } const closeText = trigger.getAttribute( 'data-close-text' ); const openText = trigger.getAttribute( 'data-open-text' ); const textEl = trigger.querySelector( '.theme-button-text' ) || trigger; if ( openText && closeText && textEl ) { textEl.textContent = closeText; } }; const disableTrigger = ( trigger ) => { trigger.setAttribute( 'aria-expanded', 'false' ); if ( trigger.classList.contains( 'vcex-button' ) || trigger.classList.contains( 'theme-button' ) ) { trigger.classList.remove( 'active' ); } const closeText = trigger.getAttribute( 'data-close-text' ); const openText = trigger.getAttribute( 'data-open-text' ); const textEl = trigger.querySelector( '.theme-button-text' ) || trigger; if ( openText && closeText && textEl ) { textEl.textContent = openText; } }; const showToggleEl = ( toggleEl, container ) => { container = container || document; const toggleElId = toggleEl.getAttribute( 'id' ); if ( ! toggleElId ) { return; } toggleEl.classList.add( 'wpex-toggle-element--visible' ); const elParent = toggleEl.parentNode; // Fix issues with toggle elements inside toggle elements (@see vcex_icon_box). if ( elParent.classList.contains( 'wpex-toggle-element' ) ) { elParent.classList.add( 'wpex-toggle-element--visible' ); elParent.setAttribute( 'tabIndex', '-1' ); elParent.focus( { preventScroll: true, focusVisible: false } ); } else { toggleEl.setAttribute( 'tabIndex', '-1' ); toggleEl.focus( { preventScroll: true, focusVisible: false } ); } container.querySelectorAll( `.wpex-toggle-element-trigger[aria-controls="${toggleElId}"]` ).forEach( ( trigger ) => { enableTrigger( trigger ); } ); window.dispatchEvent( new Event( 'resize' ) ); toggleEl.dispatchEvent( new CustomEvent( 'wpex_toggle_element_open' ) ); }; const closeToggleEl = ( toggleEl, container ) => { container = container || document; const toggleElId = toggleEl.getAttribute( 'id' ); if ( ! toggleElId ) { return; } toggleEl.classList.remove( 'wpex-toggle-element--visible' ); toggleEl.dispatchEvent( new CustomEvent( 'wpex_toggle_element_close' ) ); container.querySelectorAll( `.wpex-toggle-element-trigger[aria-controls="${toggleElId}"]` ).forEach( ( trigger ) => { disableTrigger( trigger ); } ); }; /* const onLoad = () => { const hash = location.hash; if ( ! hash || 'undefined' === hash || '#' === hash || ! self.isSelectorValid( hash ) ) { return; } const element = document.querySelector( hash ); if ( ! element || ! element.classList.contains( 'wpex-toggle-element' ) ) { return; } showToggleEl( element, null ); window.setTimeout( function() { self.scrollTo( element, -20 ); }, parseInt( l10n.scrollToHashTimeout ) ); }; */ const onDocClick = ( event ) => { const button = event.target.closest( 'a.wpex-toggle-element-trigger,button.wpex-toggle-element-trigger' ); if ( ! button ) { return; } const toggleEl = getTargetElement( button ); if ( ! toggleEl || ! toggleEl.classList.contains( 'wpex-toggle-element' ) ) { return; } event.preventDefault(); const toggleElClass = toggleEl.classList; const triggerParent = button.closest( '.vc_section' ) || button.closest( '.vc_row' ); let isToggleElementContained = false; if ( triggerParent && triggerParent.contains( toggleEl ) ) { isToggleElementContained = true; } const targetSection = isToggleElementContained ? triggerParent : document; targetSection.querySelectorAll( '.wpex-toggle-element--visible' ).forEach( ( element ) => { if ( ! element.isSameNode( toggleEl ) ) { closeToggleEl( element, targetSection ); } } ); if ( toggleElClass.contains( 'wpex-toggle-element--visible' ) ) { if ( ! toggleElClass.contains( 'wpex-toggle-element--persist' ) ) { closeToggleEl( toggleEl, targetSection ); } } else { showToggleEl( toggleEl, targetSection ); if ( 'true' === button.getAttribute( 'data-wpex-toggle-localscroll' ) && ! button.classList.contains( 'local-scroll-link' ) ) { self.scrollTo( toggleEl ); } } }; // Focus on toggle when clicking escape inside a toggle element. const onKeyDown = ( event ) => { if ( 'Escape' !== event.key ) { return; } const toggleEl = event.target.closest( '.wpex-toggle-element--visible' ); if ( ! toggleEl ) { return; } const toggleElId = toggleEl.getAttribute( 'id' ); if ( ! toggleElId ) { return; } let container = toggleEl.closest( '.vc_row' ) || toggleEl.closest( '.vc_section' ); let trigger = container.querySelector( `.wpex-toggle-element-trigger[aria-controls="${toggleElId}"]` ); if ( ! trigger ) { trigger = document.querySelector( `.wpex-toggle-element-trigger[aria-controls="${toggleElId}"]` ); } if ( trigger ) { trigger.focus(); } }; // Register event listeners. document.addEventListener( 'click', onDocClick ); document.addEventListener( 'keydown', onKeyDown ); /*if ( hasToggleElements ) { window.addEventListener( 'load', onLoad ); }*/ }, /** * Local Scroll Offset. */ getLocalScrollOffset: function( instance ) { if ( l10n.localScrollOffset ) { return parseInt( l10n.localScrollOffset ); // custom user defined offset in pixels. } const self = this; let offset = 0; // Adds extra offset via filter. if ( l10n.localScrollExtraOffset ) { offset = parseInt( offset ) + parseInt( l10n.localScrollExtraOffset ); } // Fixed header. // !!!important!!! must target the #site-header to prevent issues with the Transparent header which // will return 0 for the sticky-wrapper height. const stickyHeader = document.querySelector( '#site-header-sticky-wrapper.wpex-can-sticky #site-header' ); 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. // @todo update elements to have the "wpex-ls-offset classname where possible". document.querySelectorAll( '.wpex-ls-offset,#wpadminbar,#top-bar-wrap-sticky-wrapper.wpex-can-sticky,#site-navigation-sticky-wrapper.wpex-can-sticky' ).forEach( ( element ) => { if ( self.isVisible( element ) ) { offset = parseInt( offset ) + element.getBoundingClientRect().height; } } ); /** * Remove 1 pixel to prevent cross browser rounding issues (firefox). * * @todo anyway to fix this? */ offset = offset ? offset - 1 : 0; return offset; }, /** * Scroll to function. * * @note: Important never change this function since it's used by 3rd party scripts. */ scrollTo: function( hash, addedOffset = 0, callback ) { const self = this; if ( ! hash ) { return; } let target = null; let isLsDataLink = false; let localSection = null; let offset = 0; const lsOffset = self.getLocalScrollOffset(); const lsSpeed = parseInt( l10n.localScrollSpeed ); const sections = document.querySelectorAll( '[data-ls_id]' ); const easing = self.getEasing(); // Use standard for loop since forEach doesn't support breaks. for ( let 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; } if ( ! target ) { return; // bail early since there isn't any target. } const run = () => { if ( target && target.nodeType ) { target.focus( { preventScroll: true, focusVisible: false } ); } if ( easing && 'function' === typeof jQuery ) { jQuery( 'html, body' ).stop( true, true ).animate( { scrollTop: offset }, lsSpeed, easing ); } else { window.scrollTo( { top: offset, behavior: self.getScrollToBehavior() } ); } }; // Change the target to parent tabs when linking to tab item and open the tab. if ( target.classList.contains( 'vc_tta-panel' ) ) { const tabs = target.closest( '.vc_tta-tabs' ); if ( tabs ) { if ( target.id ) { const tabLink = tabs.querySelector(`.vc_tta-tab:not(.vc_active) a[href="#${target.id}"]`); if ( tabLink ) { tabLink.click(); } } target = tabs; addedOffset = -20; } } // Set offset if 0. if ( ! offset ) { offset = self.offset( target ).top - lsOffset + addedOffset; } // 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 into the toggle.js file, so we listen to events there and then run the scrollTo class after the element is closed. * we simply need to add an extra check to localScrollLinks > onDocClick to exclude the links int his menu. */ const 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, button.mobile-menu-toggle, li.mobile-menu-toggle > a' ).forEach( ( 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 ) { let target; let 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 ) { return self.scrollTo( target, -20 ); } } // 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 ) { return self.scrollTo( target, -20 ); } } // Custom local scroll using #localscroll-{ID} for targeting elements on the page. if ( -1 !== hash.indexOf( 'localscroll-' ) ) { hash = hash.replace( 'localscroll-', '' ); } try { const element = document.querySelector( `[data-ls_id="${hash}"], ${hash}` ); if ( element && 'dialog' !== element.getAttribute( 'roles' ) && ! element.classList.contains( 'vcex-toggle__content' ) && ! element.classList.contains( 'wpex-toggle-element' ) && ! element.classList.contains( 'wpex-card-modal' ) ) { self.scrollTo( hash ); } } catch ( error ) { return false; } }, /** * Local scroll links array. */ localScrollSections: function() { const self = this; const links = document.querySelectorAll( l10n.localScrollTargets ); const array = []; // Loop through links. links.forEach( ( link ) => { const href = link.getAttribute( 'href' ); const hash = href ? `#${href.replace( /^.*?(#|$)/, '' )}` : null; if ( ! hash || '#' === hash ) { return; } if ( ! link.hasAttribute( 'data-ls_linkto' ) ) { link.setAttribute( 'data-ls_linkto', hash ); } let section = document.querySelector( `[data-ls_id="${hash}"]` ); if ( ! section && 'string' === typeof hash && self.isSelectorValid( hash ) ) { section = document.querySelector( hash ); } if ( section && -1 === array.indexOf( hash ) ) { array.push( hash ); } } ); self.config.localScrollSections = array; return self.config.localScrollSections; }, /** * Local Scroll link. */ localScrollLinks: function() { const self = this; const findLinks = () => { document.querySelectorAll( 'a[href^="#"]:not([href="#"]):not(.local-scroll-link):not([target]):not([aria-selected]):not([aria-pressed]):not([aria-expanded]):not([data-vc-tabs]):not(.skip-to-content):not(#site-scroll-top):not([data-vc-accordion]):not([role="button"]):not(.wpex-lightbox):not(.wpex-lightbox-group-item)' ).forEach( ( link ) => { if ( link.closest( '.local-scroll, .local-scroll-link, [aria-controls]' ) ) { return; } let href = link.getAttribute( 'href' ); try { if ( document.querySelector( `[data-ls_id="${href}"], ${href}` ) ) { link.classList.add( 'local-scroll-link' ); } } catch ( error ) { return false; } } ); }; const onDocClick = ( event ) => { const target = event.target; if ( target.closest( '.wpex-open-submenu' ) ) { return; } const link = target.closest( l10n.localScrollTargets ); if ( ! link ) { return; } let hash = link.dataset.ls_linkto || link.hash; // this.hash needed as fallback. let linkExists = false; if ( self.config.localScrollSections && -1 !== self.config.localScrollSections.indexOf( hash ) ) { linkExists = true; } else { linkExists = ( self.isSelectorValid( hash ) ) ? document.querySelector( `[data-ls_id="${hash}"], ${hash}` ) : false; } if ( ! linkExists ) { return; } if ( link.closest( '.sfHover' ) ) { link.parentNode.classList.remove( 'sfHover' ); } if ( link.classList.contains( 'wpex-toggle-element-trigger' ) && 'false' === link.getAttribute( 'aria-expanded' ) ) { return; } self.scrollTo( hash ); if ( link.closest( '.full-screen-overlay-nav-menu .local-scroll > a' ) ) { return; } event.preventDefault(); event.stopPropagation(); }; // Add events. if ( l10n.localScrollFindLinks ) { findLinks(); } document.addEventListener( 'click', onDocClick ); }, /** * Local Scroll Highlight on scroll. * * @todo can this be optimized somehow? Maybe using Observer API perhaps * by checking if the element is visible on the screen (at least 50%) and excluding the * self.getLocalScrollOffset() in the calculation as this is whitespace. */ localScrollHighlight: function() { const self = this; const localScrollSections = self.config.localScrollSections; if ( ! l10n.localScrollHighlight || ! localScrollSections.length ) { return; } // Highlight active items. const highlightSection = ( section ) => { const targetDiv = document.querySelector( `[data-ls_id="${section}"], ${section}` ); if ( ! targetDiv ) { return; } let highlightSection = false; const winTop = self.winScrollTop(); const lsOffset = self.getLocalScrollOffset(); const divPos = self.offset( targetDiv ).top - lsOffset - 1; const divHeight = targetDiv.offsetHeight; const 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' ); document.querySelectorAll( '.local-scroll.menu-item' ).forEach( ( element ) => { element.classList.remove( 'current-menu-item' ); } ); } else { targetDiv.classList.remove( 'wpex-ls-inview' ); } highlightLinks.forEach( ( element ) => { if ( highlightSection ) { element.classList.add( 'active' ); } else { element.classList.remove( 'active' ); } const li = element.closest( 'li' ); if ( li ) { if ( highlightSection ) { li.classList.add( 'current-menu-item' ); } else { li.classList.remove( 'current-menu-item' ); } } } ); }; const highlightSections = () => { for ( var i=0; i < localScrollSections.length; i++ ) { highlightSection( localScrollSections[i] ); } }; window.addEventListener( 'scroll', highlightSections, { passive: true } ); }, /** * 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( '.vc_row', '.equal-height-column', context ); wpexEqualHeights( '.vc_row', '.equal-height-content', context ); }, /** * Footer Reveal Display on Load. */ footerReveal: function() { const self = this; const footerReveal = document.querySelector( '#footer-reveal' ); const wrap = document.querySelector( '#wrap' ); const main = document.querySelector( '#main' ); if ( ! footerReveal || ! wrap || ! main ) { return; } const 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; } let heightCheck = 0; let revealHeight = footerReveal.offsetHeight; const windowHeight = window.innerHeight; if ( footerReveal.classList.contains( 'footer-reveal' ) ) { heightCheck = wrap.offsetHeight + self.getLocalScrollOffset(); } else { heightCheck = wrap.offsetHeight + self.getLocalScrollOffset() - 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' ); } } }; const 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, { passive: true } ); 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; } const main = document.querySelector( '#main' ); if ( ! main ) { return; } const run = () => { main.style.minHeight = ( main.offsetHeight + ( window.innerHeight - document.documentElement.offsetHeight ) ) + 'px'; }; // Add Events run(); window.addEventListener( 'resize', run ); }, /** * Custom Selects. */ customSelects: function( context ) { if ( ! l10n.customSelects ) { return; } const self = this; if ( ! context || ! context.childNodes ) { context = document; } context.querySelectorAll( l10n.customSelects ).forEach( ( element ) => { const parent = element.parentNode; if ( parent.classList.contains( 'wpex-select-wrap' ) || parent.classList.contains( 'wpex-multiselect-wrap' ) ) { return; } const elementId = element.id; let addIcon = false; if ( self.isVisible( element ) ) { // @todo is this really needed? // Wrap the select const div = document.createElement( 'div' ); if ( element.hasAttribute( 'multiple' ) ) { if ( elementId ) { div.className = `wpex-multiselect-wrap wpex-multiselect-wrap--${elementId} wpex-${elementId}`; } else { div.className = `wpex-multiselect-wrap`; } } else { if ( elementId ) { div.className = `wpex-select-wrap wpex-select-wrap--${elementId} wpex-${elementId}`; } else { div.className = `wpex-select-wrap`; } addIcon = true; } element = self.wrap( element, div ); // Add icon to wrapper if ( addIcon && l10n.selectArrowIcon ) { const arrow = document.createElement( 'span' ); arrow.className = 'wpex-select-arrow'; const icon = document.createDocumentFragment(); const 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() { let 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; } const parseItem = ( index ) => { let 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 ( let 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 ( let 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; } } }; for (let i = 0; i < itemsLength; i++) { parseItem( i ); } if ( cssObj ) { for ( let 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 ); } }, /** * Overlay Mobile Support. */ overlaysMobileSupport: function() { const self = this; const supportsTouch = (window.matchMedia("(any-pointer: coarse)").matches); if ( ! supportsTouch ) { return; } let dragging = false; // Remove overlays completely if mobile support is disabled. document.querySelectorAll( '.overlay-parent.overlay-hh' ).forEach( ( element ) => { if ( ! element.classList.contains( 'overlay-ms' ) ) { const overlay = element.querySelector( '.theme-overlay' ); if ( overlay ) { element.parentNode.removeChild( element ); } } } ); const hideAllOverlays = () => { document.querySelectorAll( '.wpex-touched' ).forEach( ( element ) => { element.classList.remove( 'wpex-touched' ); } ); }; const touchEnd = ( event ) => { const target = event.target; let overlay = target.closest( '.overlay-parent.overlay-ms.overlay-h,.vcex-image-swap,.wpex-card-touch-support' ); if ( ! overlay ) { return; } if ( dragging ) { hideAllOverlays(); return; } if ( overlay.classList.contains( 'wpex-touched' ) ) { overlay.classList.remove( 'wpex-touched' ); return; // overlay is open, allow clicking inside. } event.preventDefault(); hideAllOverlays(); overlay.classList.add( 'wpex-touched' ); }; const touchMove = ( event ) => { const target = event.target; if ( ! target.closest( '.wpex-touched' ) ) { hideAllOverlays(); } const overlay = target.closest( '.overlay-parent.overlay-ms.overlay-h,.vcex-image-swap,.wpex-card-touch-support' ); if ( overlay ) { dragging = true; } }; const touchStart = ( event ) => { const target = event.target; if ( ! target.closest( '.wpex-touched' ) ) { hideAllOverlays(); } const overlay = target.closest( '.overlay-parent.overlay-ms.overlay-h,.vcex-image-swap,.wpex-card-touch-support' ); if ( overlay ) { dragging = false; } }; // Add events. document.addEventListener( 'touchend', touchEnd, false ); // must have false event listeners. document.addEventListener( 'touchmove', touchMove, { passive: true } ); document.addEventListener( 'touchstart', touchStart, { passive: true } ); }, /** * Adds CSS variables to the body tag based on element heights. * * @todo enable this functionality. It's disabled because curently it causes severe lag * on the animated shrink header. * stickyElCssVar: function( el, varName ) { const setHeight = ( height ) => { document.body.style.setProperty(varName, `${height}px`); }; let prevHeight = 0; const resize_ob = new ResizeObserver( function( entries ) { let rect = entries[0].contentRect; let height = rect.height; if (typeof height === 'number' && height !== prevHeight) { prevHeight = height; setHeight( height ); } }); resize_ob.observe( el ); // @note the observer API will fire on init. }, */ /** * Sticky Topbar. */ stickyTopBar: function() { const self = this; let isSticky = false; let offset = 0; const stickyTopbar = document.querySelector( '#top-bar-wrap.wpex-top-bar-sticky' ); const brkPoint = l10n.stickyTopBarBreakPoint; if ( ! stickyTopbar ) { return; } // self.stickyElCssVar( stickyTopbar, '--wpex-topbar-height' ); // Add wrapper. const stickyWrap = document.createElement( 'div' ); stickyWrap.id = 'top-bar-wrap-sticky-wrapper'; stickyWrap.className = 'wpex-sticky-top-bar-holder not-sticky wpex-print-hidden'; self.wrap( stickyTopbar, stickyWrap ); // Get offset. const getOffset = () => { offset = 0; // Reset offset for resize. document.querySelectorAll( '.wpex-sticky-el-offset,#wpadminbar' ).forEach( ( element ) => { const elPosition = window.getComputedStyle( element ).position; if ( self.isBeforeElement( element, stickyWrap ) && self.isVisible( element ) && ( 'fixed' === elPosition || 'sticky' === elPosition ) ) { offset += element.getBoundingClientRect().height; } } ); return offset; }; // Stick the TopBar. const 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. const 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. const initSticky = () => { if ( ! l10n.hasStickyTopBarMobile && ( self.viewportWidth() < brkPoint ) ) { stickyWrap.classList.remove( 'wpex-can-sticky' ); destroySticky(); return; } const 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. const onScroll = () => { if ( ! stickyWrap || ! stickyWrap.classList.contains( 'wpex-can-sticky' ) ) { return; } const windowTop = self.winScrollTop(); // Set or destroy sticky based on offset. if ( ( 0 !== windowTop ) && ( windowTop >= ( self.offset( stickyWrap ).top - getOffset() ) ) ) { setSticky(); } else { destroySticky(); } }; // Add events. initSticky(); window.addEventListener( 'scroll', onScroll, { passive: true } ); window.addEventListener( 'resize', initSticky ); }, /** * Sticky Header Menu. */ stickyHeaderMenu: function() { const self = this; const stickyNav = document.querySelector( '#site-navigation-wrap.fixed-nav' ); if ( ! stickyNav ) { return; } // self.stickyElCssVar( stickyNav, '--wpex-header-menu-height' ); let isSticky = false; const header = document.querySelector( '#site-header' ); const stickyWrap = document.createElement( 'div' ); stickyWrap.id = 'site-navigation-sticky-wrapper'; stickyWrap.className = 'wpex-sticky-navigation-holder not-sticky'; self.wrap( stickyNav, stickyWrap ); const getOffset = () => { let offset = 0; document.querySelectorAll( '.wpex-sticky-el-offset,#wpadminbar,.wpex-top-bar-sticky' ).forEach( ( element ) => { const elPosition = window.getComputedStyle( element ).position; if ( self.isVisible( element ) && self.isBeforeElement( element, stickyWrap ) && ( 'fixed' === elPosition || 'sticky' === elPosition ) ) { offset += element.getBoundingClientRect().height; } } ); if ( l10n.addStickyHeaderOffset ) { offset += l10n.addStickyHeaderOffset; } return offset; }; // Add offsets. const stickyWrapTop = self.offset( stickyWrap ).top; const setStickyPos = stickyWrapTop - getOffset(); const 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; }; 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; }; const initResizeSetSticky = () => { if ( self.viewportWidth() <= l10n.stickyNavbarBreakPoint ) { destroySticky(); stickyWrap.classList.remove( 'wpex-can-sticky' ); return; } const 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(); } } }; const onScroll = () => { if ( ! stickyWrap.classList.contains( 'wpex-can-sticky' ) ) { return; } const windowTop = self.winScrollTop(); // Sticky menu. if ( 0 !== windowTop && windowTop >= setStickyPos ) { setSticky(); } else { destroySticky(); } }; const onOrientationChange = () => { destroySticky(); initResizeSetSticky(); }; // Add events. initResizeSetSticky(); window.addEventListener( 'scroll', onScroll, { passive: true } ); window.addEventListener( 'resize', initResizeSetSticky ); window.addEventListener( 'orientationchange', onOrientationChange ); }, /** * 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; } // self.stickyElCssVar( header, '--wpex-header-height' ); 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 wpex-print-hidden'; self.wrap( header, stickyWrap ); const getOffset = () => { let offset = 0; document.querySelectorAll( '.wpex-sticky-el-offset,.wpex-top-bar-sticky,#wpadminbar' ).forEach( ( element ) => { const elPosition = window.getComputedStyle( element ).position; if ( self.isVisible( element ) && self.isBeforeElement( element, stickyWrap ) && ( 'fixed' === elPosition || 'sticky' === elPosition ) ) { offset += element.getBoundingClientRect().height; } } ); if ( l10n.addStickyHeaderOffset ) { 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. const pastBreakPoint = () => { switch ( getBreakpointComparisonOperator() ) { case '<': return ( self.viewportWidth() < brkPoint ); case '>': return ( self.viewportWidth() > brkPoint ); } }; let pastHeaderBottomCheck = 0; const overlayHeader = document.querySelector( '#site-header.overlay-header' ); if ( overlayHeader && 'absolute' === window.getComputedStyle( overlayHeader ).position ) { pastHeaderBottomCheck = self.offset( header ).top + header.getBoundingClientRect().height; } else { pastHeaderBottomCheck = self.offset( stickyWrap ).top + stickyWrap.getBoundingClientRect().height; } // Check if we are past the header. 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(); } }; // Add Events. initResizeSetSticky(); window.addEventListener( 'scroll', onScroll, { passive: true } ); window.addEventListener( 'resize', initResizeSetSticky ); window.addEventListener( 'orientationchange', () => { destroySticky(); initResizeSetSticky(); } ); }, /** * Custom Sticky elements. */ stickyElements: function() { const bodyClass = document.body.classList; if ( bodyClass.contains( 'compose-mode' ) || bodyClass.contains( 'wp-admin' ) ) { return; } const stickyElements = document.querySelectorAll( '.wpex-js-sticky' ); if ( ! stickyElements ) { return; } const self = this; const addInnerWrap = ( el ) => { const wrapper = document.createElement( 'div' ); let wrapClassname = 'wpex-js-sticky-wrap'; const lsOffset = el.dataset.wpexStickyLsOffset || true; if ( 'true' == lsOffset || '1' == lsOffset ) { wrapClassname += ' wpex-ls-offset'; } wrapper.className = wrapClassname; if ( el.nextSibling ) { el.parentNode.insertBefore( wrapper, el.nextSibling ); } else { el.parentNode.appendChild( wrapper ); } wrapper.appendChild( el ); return wrapper; }; const addHolder = ( el ) => { const wrapper = document.createElement( 'div' ); wrapper.className = 'wpex-js-sticky-holder'; if ( el.nextSibling ) { el.parentNode.insertBefore( wrapper, el.nextSibling ); } else { el.parentNode.appendChild( wrapper ); } wrapper.appendChild( el ); return wrapper; }; stickyElements.forEach( ( stickyEl ) => { let isSticky = false; let stickyEndPoint = stickyEl.dataset.wpexStickyEndpoint || null; const stickyBreakpoint = parseInt( stickyEl.dataset.wpexStickyBreakpoint ); if ( stickyEndPoint && isNaN( stickyEndPoint ) ) { stickyEndPoint = document.querySelector( stickyEndPoint ); } const stickyWrapper = addInnerWrap( stickyEl ); const stickyHolder = addHolder( stickyWrapper ); const getStickyOffset = () => { let offset = stickyEl.dataset.wpexStickyOffset ? parseInt( stickyEl.dataset.wpexStickyOffset ) : 0; document.querySelectorAll( '#top-bar-wrap-sticky-wrapper.wpex-can-sticky #top-bar-wrap,#site-header-sticky-wrapper.wpex-can-sticky #site-header,#site-navigation-sticky-wrapper.wpex-can-sticky #site-navigation-wrap,#wpadminbar,.wpex-sticky-el-offset,.wpex-js-sticky-wrap' ).forEach( ( item ) => { const itemPosition = window.getComputedStyle( item ).position; if ( ! stickyEl.isEqualNode( item ) && self.isVisible( item ) && self.isBeforeElement( item, stickyEl ) && ( 'fixed' === itemPosition || 'sticky' === itemPosition ) ) { offset += self.elHeight( item ); } } ); return offset; }; const stickyCheck = () => { if ( stickyBreakpoint && self.viewportWidth() <= stickyBreakpoint ) { return destroySticky(); } const windowTop = self.winScrollTop(); const stickyOffset = getStickyOffset(); const stickyWrapTop = self.offset( stickyHolder ).top; const setStickyPos = stickyWrapTop - stickyOffset; if ( windowTop > setStickyPos && 0 !== windowTop ) { setSticky( stickyOffset ); if ( stickyEndPoint && self.isVisible( stickyEndPoint ) ) { if ( windowTop > ( self.offset( stickyEndPoint ).top - stickyOffset - self.elHeight( stickyEl ) ) ) { stickyWrapper.style.position = 'relative'; } else { stickyWrapper.style.position = 'fixed'; } } } else { destroySticky(); } }; const setWidth = () => { const wrapWidth = stickyHolder.getBoundingClientRect().width; if ( wrapWidth ) { stickyWrapper.style.setProperty( 'width', wrapWidth + 'px' ); } }; const setSticky = ( offset ) => { if ( ! self.isVisible( stickyEl ) ) { destroySticky(); return; } if ( isSticky ) { stickyWrapper.style.top = getStickyOffset() + 'px'; // fix for items that may have changed sized like shrink header. return; } stickyHolder.style.height = self.elHeight( stickyEl ) + 'px'; stickyWrapper.classList.add( 'wpex-z-99' ); stickyWrapper.style.position = 'fixed'; stickyWrapper.style.top = offset + 'px'; stickyEl.classList.add( 'wpex-js-sticky--stuck' ); setWidth(); isSticky = true; }; // Unsticks element. const destroySticky = () => { if ( ! isSticky ) { return; } stickyHolder.style.height = ''; stickyWrapper.classList.remove( 'wpex-z-99' ); stickyWrapper.style.position = ''; stickyWrapper.style.top = ''; stickyWrapper.style.width = ''; stickyEl.classList.remove( 'wpex-js-sticky--stuck' ); isSticky = false; }; const onResize = () => { stickyCheck(); if ( ! isSticky ) { return; } if ( ! self.isVisible( stickyEl ) ) { destroySticky(); } stickyHolder.style.height = self.elHeight( stickyEl ) + 'px'; stickyWrapper.style.top = getStickyOffset() + 'px'; setWidth( stickyEl ); }; const onFlip = () => { destroySticky(); stickyCheck(); }; // Run functions and add event listeners. stickyCheck(); window.addEventListener( 'scroll', stickyCheck, { passive: true } ); window.addEventListener( 'resize', onResize ); window.addEventListener( 'orientationchange', onFlip ); } ); }, /** * Accessability fixes/enhancements. */ accessibility: function() { // Add tabindex -1 to nav-no-click links. document.querySelectorAll( '#site-navigation li.nav-no-click:not(.menu-item-has-children) > a' ).forEach( ( 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( ( 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( ( element ) => { element.setAttribute( 'tabIndex', 0 ); element.addEventListener( 'keydown', ( event ) => { if ( 'Enter' === event.key ) { event.target.click(); } } ); } ); }, /* Helpers. ------------------------------------------------------------------------------ */ /** * Is the DOM ready? */ domReady: function( fn ) { if ( typeof fn !== 'function' || 'undefined' === typeof document ) { return; } const 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() { const 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; } }, /** * Get easing value. */ getEasing: function( customEasing ) { if ( ! l10n.localScrollEasing || 'function' !== typeof jQuery || 'undefined' === typeof jQuery.easing || 'function' !== typeof jQuery.easing.jswing ) { return; } let easing = customEasing || l10n.localScrollEasing; if ( ! jQuery.easing.hasOwnProperty( easing ) ) { easing = 'swing'; } return easing; }, /** * Viewport width. */ viewportWidth: function() { let a = 'inner'; let e = window; 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 ) { const queryCheck = ( s ) => { document.createDocumentFragment().querySelector( s ); }; try { queryCheck( selector ); } catch( error ) { return false; } return true; }, /** * SlideUp element. */ slideUp: function( target, duration, callback ) { if ( ! target ) { return; } const display = window.getComputedStyle( target ).display; // Element already closed. if ( 'none' === display ) { return; } // Allow for CSS defined transition duration. const 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( () => { 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; } let display = window.getComputedStyle( target ).display; // Already open. if ( 'block' === display ) { return; } // Allow for CSS defined transition duration. const 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. const 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( () => { 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 ) { const self = this; const focusElements = element.querySelectorAll( 'button, [href], input, select, textarea, a,[tabindex]:not([tabindex="-1"])' ); if ( ! focusElements.length ) { return; } const focus = []; for ( let i=0; i < focusElements.length; i++ ) { if ( self.isVisible( focusElements[i] ) ){ focus.push( focusElements[i] ); } } if ( ! focus.length ) { return; } const firstFocus = focus[0]; const lastFocus = focus[focus.length - 1]; // Add initial focus. if ( initialFocus ) { initialFocus.focus(); } else { firstFocus.focus(); } // Redirect last tab to first input. lastFocus.addEventListener( 'keydown', ( event ) => { if ( 'Tab' === event.key && ! event.shiftKey ) { event.preventDefault(); firstFocus.focus(); } } ); // Redirect first shift+tab to last input. firstFocus.addEventListener( 'keydown', ( event ) => { if ( 'Tab' === event.key && 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 ) { const rect = element.getBoundingClientRect(); return { top: rect.top + this.winScrollTop(), left: rect.left + this.winScrollTop(), bottom: rect.bottom + this.winScrollTop(), }; }, /** * Get element height. */ elHeight: function( element ) { return element.getBoundingClientRect().height; }, /** * 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() { let 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 ) { return this.winScrollTop() >= element.offsetTop + element.offsetHeight - window.innerHeight; }, /** * Remove class prefix. */ removeClassPrefix: function( elements, regex, prefix ) { elements.forEach( ( element ) => { const classes = element.classList; for ( let j=0; j < classes.length; j++ ) { if ( regex.test( classes[j] ) ) { const newclass = classes[j].replace( prefix, '' ); element.classList.replace( classes[j], newclass ); } } } ); }, /** * Creates accordion menu. */ menuAccordion: function( menu ) { const self = this; if ( ! menu ) { return; } let isAnimating = false; const 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( ( menuItem ) => { const link = menuItem.querySelector( 'a' ); if ( ! link ) { return; } const wrap = link.closest( 'span' ); const 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); } if ( l10n.mobileMenuOpenSubmenuIconActive ) { const temp = document.createElement( 'div' ); temp.innerHTML = l10n.mobileMenuOpenSubmenuIconActive; toggleBtn.appendChild(temp.firstChild); } } else { const toggleBtnIcon = document.createElement( 'span' ); toggleBtnIcon.className = 'ticon ticon-angle-down'; toggleBtnIcon.setAttribute( 'aria-hidden', 'true' ); toggleBtn.appendChild( toggleBtnIcon ); } if ( wrap ) { wrap.appendChild( toggleBtn ); } else { link.appendChild( toggleBtn ); } } ); const closeSubmenu = ( submenu ) => { const li = submenu.closest( 'li.active' ); li.classList.remove( 'active' ); const link = li.querySelector( 'a' ); const 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; const toggleIcon = toggle.querySelector( '.wpex-open-submenu__icon' ); if ( toggleIcon ) { const toggleIconOpen = toggle.querySelector( '.wpex-open-submenu__icon--open' ); if ( toggleIconOpen ) { toggleIcon.classList.remove( 'wpex-hidden' ); toggleIconOpen.classList.add( 'wpex-hidden' ); } else { toggleIcon.classList.remove( 'wpex-rotate-180' ); } } self.slideUp( submenu, null, function() { isAnimating = false; } ); }; const onDocClick = ( event ) => { const button = event.target.closest( '.wpex-open-submenu' ); if ( ! button || ! menu.contains( button ) ) { return; } const li = button.closest( 'li' ); if ( ! li ) { return; } const ul = li.querySelector( 'ul' ); if ( ! ul ) { return; // no dropdowns. } const 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( ( 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() ) ); const toggleIcon = button.querySelector( '.wpex-open-submenu__icon' ); if ( toggleIcon ) { const toggleIconOpen = button.querySelector( '.wpex-open-submenu__icon--open' ); if ( toggleIconOpen ) { toggleIcon.classList.add( 'wpex-hidden' ); toggleIconOpen.classList.remove( 'wpex-hidden' ); } else { toggleIcon.classList.add( 'wpex-rotate-180' ); } } // Close all open submenus that arent parents of this submenu. menu.querySelectorAll( 'li.active > ul' ).forEach( ( submenu ) => { if ( ! submenu.contains( ul ) ) { closeSubmenu( submenu ); } } ); // Open self. isAnimating = true; self.slideDown( ul, null, function() { isAnimating = false; } ); li.classList.add( 'active' ); } }; // Add events - @todo instead of adding event to document we should add it to menu? document.addEventListener( 'click', onDocClick ); }, /** * Set hamburger icon state. */ setHamburgerIconState: function( el ) { if ( ! el ) { const toggleBtn = document.querySelector( 'a.mobile-menu-toggle, button.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' ); } }, /** * Returns scrollbar width. */ getScrollBarWidth: function() { return window.innerWidth - document.body.clientWidth; }, /** * Returns scrollbar width. */ getElTransitionDuration: function( el ) { let duration = window.getComputedStyle( el ).transitionDuration; if ( ! duration || '0s' !== duration ) { duration = parseFloat( duration ) * ( duration.indexOf( 'ms' ) >- 1 ? 1 : 1000 ); } return parseFloat( duration ) || 0; }, /** * Set HTML no scroll. */ htmlNoScroll: function( action, duration ) { const html = document.querySelector( 'html' ); switch ( action ) { case 'add': const scrollbarWidth = this.getScrollBarWidth(); html.style.setProperty( '--html-noscroll-padding', `${scrollbarWidth}px` ); html.classList.add( 'html-noscroll' ); break; case 'remove': html.classList.remove( 'html-noscroll' ); html.style.setProperty( '--html-noscroll-padding', '' ); break; } }, /** * Debounce function. */ debounce: function(callback, wait = 10) { let timeoutId = null; return (...args) => { window.clearTimeout(timeoutId); timeoutId = window.setTimeout(() => { callback.apply(null, args); }, wait); }; }, /** * Returns scrollTo behavior. */ getScrollToBehavior: function() { return l10n.scrollToBehavior || 'smooth'; }, }; // Start things up wpex.init(); } ) ( wpex_theme_params );
修改文件时间
将文件时间修改为当前时间的前一年
删除文件