"use strict"; /*Namespace ------------------------------------------------------- */ var blogmarks = blogmarks || {}; /* Handle Accessiblity for Menu Items **-----------------------------------------------------*/ blogmarks.traverseMenu = { init: function () { let topNavigation = document.querySelector(".topbar-component-navigation"); let primaryNavigation = document.getElementById("site-navigation"); // For top menu navigation if (topNavigation) { this.traverse(topNavigation); } // For primary menu navigation if (primaryNavigation) { this.traverse(primaryNavigation); } }, traverse: function (navigation) { let menu = navigation.getElementsByTagName("ul")[0]; if ("undefined" !== typeof menu) { if (!menu.classList.contains("nav-menu")) { menu.classList.add("nav-menu"); } // Get all the link elements within the menu. let links = menu.getElementsByTagName("a"); // Get all the link elements with children within the menu. let linksWithChildren = menu.querySelectorAll( ".menu-item-has-children > a, .page_item_has_children > a" ); // Toggle focus each time a menu link is focused or blurred. for (let link of links) { link.addEventListener("focus", this.toggleFocus, true); link.addEventListener("blur", this.toggleFocus, true); } // Toggle focus each time a menu link with children receive a touch event. for (let link of linksWithChildren) { link.addEventListener("touchstart", this.toggleFocus, false); } } }, toggleFocus: function (event) { if (event.type === "focus" || event.type === "blur") { let self = this; // Move up through the ancestors of the current link until we hit .nav-menu. while (!self.classList.contains("nav-menu")) { // On li elements toggle the class .focus. if ("li" === self.tagName.toLowerCase()) { self.classList.toggle("focus"); } self = self.parentNode; } } if (event.type === "touchstart") { let menuItem = this.parentNode; event.preventDefault(); for (let link of menuItem.parentNode.children) { if (menuItem !== link) { link.classList.remove("focus"); } } menuItem.classList.toggle("focus"); } }, }; /* Handle Focus for Dialog Accessiblity **-----------------------------------------------------*/ blogmarks.handleFocus = { init: function () { this.keepFocusInModal(); }, keepFocusInModal: function () { let modal = document.querySelectorAll(".site-canvas-modal"); document.addEventListener("keydown", function (event) { // Check for if tab key is pressed let KEYCODE_TAB = 9; let isTabPressed = event.key === "Tab" || event.keyCode === KEYCODE_TAB; if (!isTabPressed) { return; } if (modal) { modal.forEach(function (element) { let focusableEls = element.querySelectorAll( 'a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="search"]:not([disabled]), input[type="submit"]:not([disabled]), input[type="radio"]:not([disabled]), input[type="checkbox"]:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])' ); let firstFocusableEl = focusableEls[0]; let lastFocusableEl = focusableEls[focusableEls.length - 1]; // if shift key pressed for shift + tab combination if (event.shiftKey) { if (document.activeElement === firstFocusableEl) { lastFocusableEl.focus(); // add focus for the last focusable element event.preventDefault(); } } else { // if tab key is pressed if (document.activeElement === lastFocusableEl) { // if focused has reached to last focusable element then focus first focusable element after pressing tab firstFocusableEl.focus(); // add focus for the first focusable element event.preventDefault(); } } }); } }); }, }; /* Preloader **-----------------------------------------------------*/ blogmarks.fadeOutPreloader = { init: function () { let preloader = document.querySelector("#blogmarks-preloader-wrapper"); if (preloader) { preloader.classList.add("fadeOut"); setTimeout(function () { preloader.style.display = "none"; }, 1000); } }, }; /* Scroll to top **-----------------------------------------------------*/ blogmarks.scrollToTop = { init: function () { let rootElement = document.documentElement; let _this = this; // Scroll to top on click let scrollToTopBtn = document.querySelectorAll( ".scroll-to-top" ); if (scrollToTopBtn) { scrollToTopBtn.forEach(function (item) { _this.goToTop(item, rootElement); }); } // Display Floating Button let floatingScrollTopBtn = document.querySelectorAll( ".floating-scroll-to-top" ); if (floatingScrollTopBtn) { floatingScrollTopBtn.forEach(function (item) { _this.scrollToTopPosition(item, rootElement); }); } }, goToTop: function (scrollToTopBtn, rootElement) { scrollToTopBtn.addEventListener("click", function (elem) { elem.preventDefault(); rootElement.scrollTo({ top: 0, behavior: "smooth", }); }); }, scrollToTopPosition: function (scrollToTopBtn, rootElement) { window.addEventListener("scroll", function (event) { let scrollTotal = rootElement.scrollHeight - rootElement.clientHeight; // Show on certain window height if (rootElement.scrollTop / scrollTotal > 0.4) { scrollToTopBtn.classList.add("visible"); } else { scrollToTopBtn.classList.remove("visible"); } }); }, }; /* Sticky Menu **-----------------------------------------------------*/ blogmarks.stickyMenu = { init: function () { const stickyElement = document.querySelector( ".site-header-navbar" ); if (stickyElement) { if (stickyElement.className.includes("affix-navbar")) { let stickyPoint = stickyElement.offsetTop + stickyElement.clientHeight + 100; window.addEventListener("scroll", function (event) { let currentScroll = window.scrollY; if ( currentScroll <= stickyElement.offsetTop || currentScroll === 0 ) { stickyElement.classList.remove("affix-navbar-active"); stickyElement.classList.remove( "affix-navbar-translate-up" ); return; } if (currentScroll > stickyElement.offsetTop) { stickyElement.classList.add("affix-navbar-translate-up"); stickyElement.classList.remove("affix-navbar-active"); } if (currentScroll > stickyPoint) { stickyElement.classList.remove( "affix-navbar-translate-up" ); stickyElement.classList.add("affix-navbar-active"); } }); } } }, }; /* Sub Menu Toggles **-----------------------------------------------------*/ blogmarks.subMenuToggle = { init: function () { const toggleItems = document.querySelectorAll(".sub-menu-toggle"); if (toggleItems) { toggleItems.forEach(function (item) { item.addEventListener("click", function (e) { e.preventDefault(); this.classList.toggle("active"); this.setAttribute( "aria-selected", `${!(this.getAttribute("aria-selected") === "true")}` ); let currentClass = this.getAttribute("data-toggle-target"); if (currentClass) { document .querySelector(currentClass) .classList.toggle("active"); } }); }); } }, }; /* Canvas Modal **-----------------------------------------------------*/ blogmarks.CanvasModal = { init: function () { if (document.querySelector(".toggle-canvas-modal")) { // Handle canvas modal when opened this.onOpen(); // Handle canvas modal when closed this.onClose(); // When open, close if visitor clicks on the wrapping element of the modal. this.outsideModal(); // Close on escape key press. this.closeOnEscape(); } }, onOpen: function () { document .querySelectorAll(".toggle-canvas-modal") .forEach(function (element) { element.addEventListener("click", function (event) { event.preventDefault(); document.body.classList.add("canvas-modal-is-open"); document.body.classList.add( this.getAttribute("data-body-class") ); element.classList.add("active"); element.setAttribute("aria-expanded", true); let focusElement = this.getAttribute("data-focus"); if (focusElement) { setTimeout(function () { document.querySelector(focusElement).focus(); }, 500); } }); }); }, onClose: function () { document.querySelectorAll(".close-canvas-modal").forEach( function (element) { element.addEventListener( "click", function (event) { event.preventDefault(); this.hideModal(); }.bind(this) ); }.bind(this) ); }, outsideModal: function () { document.addEventListener( "click", function (event) { if (document.body.classList.contains("canvas-modal-is-open")) { let overlayDiv = document.querySelector("#page.site"); if (event.target == overlayDiv) { this.hideModal(); } } }.bind(this) ); }, closeOnEscape: function () { document.addEventListener( "keydown", function (event) { if (event.key === "Escape") { event.preventDefault(); this.hideModal(); } }.bind(this) ); }, hideModal: function () { document.body.classList.remove("canvas-modal-is-open"); let activeItem = document.querySelector(".toggle-canvas-modal.active"); if (activeItem) { document.body.classList.remove( activeItem.getAttribute("data-body-class") ); let focusElement = activeItem.getAttribute("data-focus"); if (focusElement) { document.querySelector(focusElement).blur(); } activeItem.setAttribute("aria-expanded", false); activeItem.focus(); activeItem.classList.remove("active"); } }, }; /* Search Toggle **-----------------------------------------------------*/ blogmarks.SearchBlock = { init: function () { let searchCanvasBtn = document.querySelector(".site-button-search"); let closeSearchCanvas = document.querySelector(".site-search-close"); let overlayDiv = document.querySelector("#page.site"); if (searchCanvasBtn) { let focusElement = document.querySelector( ".site-search-model .search-field" ); // Handle cover modals when they're opened this.onOpen(searchCanvasBtn, focusElement); // Handle cover modals when they're closed this.onClose(searchCanvasBtn, closeSearchCanvas, focusElement); // When open, close if visitor clicks on the outside the modal. this.outsideModal(searchCanvasBtn, overlayDiv, focusElement); // Close on escape key press. this.closeOnEscape(searchCanvasBtn, focusElement); } }, onOpen: function (searchCanvasBtn, focusElement) { searchCanvasBtn.addEventListener("click", function (event) { event.preventDefault(); document.body.classList.add("blogmarks-search-canvas-open"); searchCanvasBtn.setAttribute("aria-expanded", true); // Add focus after a timeout to take effect on hidden element to make the "all" transition work if (focusElement) { setTimeout(function () { focusElement.focus(); }, 500); } }); }, onClose: function (searchCanvasBtn, closeSearchCanvas, focusElement) { closeSearchCanvas.addEventListener("click", function (event) { event.preventDefault(); document.body.classList.remove("blogmarks-search-canvas-open"); searchCanvasBtn.setAttribute("aria-expanded", false); if (focusElement) { focusElement.blur(); searchCanvasBtn.focus(); } }); }, outsideModal: function (searchCanvasBtn, overlayDiv, focusElement) { document.addEventListener("click", function (event) { if (document.body.classList.contains("blogmarks-search-canvas-open")) { if (event.target == overlayDiv) { document.body.classList.remove("blogmarks-search-canvas-open"); searchCanvasBtn.setAttribute("aria-expanded", false); focusElement.blur(); searchCanvasBtn.focus(); } } }); }, closeOnEscape: function (searchCanvasBtn, focusElement) { document.addEventListener("keydown", function (event) { if (document.body.classList.contains("blogmarks-search-canvas-open")) { if (event.key === "Escape") { event.preventDefault(); document.body.classList.remove("blogmarks-search-canvas-open"); searchCanvasBtn.setAttribute("aria-expanded", false); focusElement.blur(); searchCanvasBtn.focus(); } } }); }, }; /* Background Image **-----------------------------------------------------*/ blogmarks.setBackgroundImage = { init: function () { let bgImageContainer = document.querySelectorAll(".blogmarks-bg-image"); if (bgImageContainer) { bgImageContainer.forEach(function (item) { let image = item.querySelector("img"); if (image) { let imageSrc = image.getAttribute("src"); if (imageSrc) { item.style.backgroundImage = "url(" + imageSrc + ")"; image.style.display = "none"; } } }); } }, }; /* Progress Bar **-----------------------------------------------------*/ blogmarks.progressBar = { init: function () { let progressBarDiv = document.getElementById("blogmarks-progress-bar"); if (progressBarDiv) { let body = document.body; let rootElement = document.documentElement; window.addEventListener("scroll", function (event) { let winScroll = body.scrollTop || rootElement.scrollTop; let height = rootElement.scrollHeight - rootElement.clientHeight; let scrolled = (winScroll / height) * 100; progressBarDiv.style.width = scrolled + "%"; }); } }, }; /* Custom Cursor **-----------------------------------------------------*/ let cursorObj; blogmarks.customCursor = { init: function () { cursorObj = this; this.customCursor(); }, isVariableDefined: function (el) { return typeof !!el && el != "undefined" && el != null; }, select: function (selectors) { return document.querySelector(selectors); }, selectAll: function (selectors) { return document.querySelectorAll(selectors); }, customCursor: function () { let c = cursorObj.select(".site-cursor-dot"); if (cursorObj.isVariableDefined(c)) { let cursor = { delay: 8, _x: 0, _y: 0, endX: window.innerWidth / 2, endY: window.innerHeight / 2, cursorVisible: true, cursorEnlarged: false, $dot: cursorObj.select(".site-cursor-dot"), $outline: cursorObj.select(".site-cursor-outline"), init: function () { // Set up element sizes this.dotSize = this.$dot.offsetWidth; this.outlineSize = this.$outline.offsetWidth; this.setupEventListeners(); this.animateDotOutline(); }, updateCursor: function (e) { let self = this; // Show the cursor self.cursorVisible = true; self.toggleCursorVisibility(); // Position the dot self.endX = e.clientX; self.endY = e.clientY; self.$dot.style.top = self.endY + "px"; self.$dot.style.left = self.endX + "px"; }, setupEventListeners: function () { let self = this; // Reposition cursor on window load window.addEventListener("load", (event) => { self.cursorEnlarged = false; self.toggleCursorSize(); }); // Anchor hovering cursorObj.selectAll("a, button").forEach(function (el) { el.addEventListener("mouseover", function () { self.cursorEnlarged = true; self.toggleCursorSize(); }); el.addEventListener("mouseout", function () { self.cursorEnlarged = false; self.toggleCursorSize(); }); }); // Click events document.addEventListener("mousedown", function () { self.cursorEnlarged = true; self.toggleCursorSize(); }); document.addEventListener("mouseup", function () { self.cursorEnlarged = false; self.toggleCursorSize(); }); document.addEventListener("mousemove", function (e) { // Show the cursor self.cursorVisible = true; self.toggleCursorVisibility(); // Position the dot self.endX = e.clientX; self.endY = e.clientY; self.$dot.style.top = self.endY + "px"; self.$dot.style.left = self.endX + "px"; }); // Hide/show cursor document.addEventListener("mouseenter", function (e) { self.cursorVisible = true; self.toggleCursorVisibility(); self.$dot.style.opacity = 1; self.$outline.style.opacity = 1; }); document.addEventListener("mouseleave", function (e) { self.cursorVisible = true; self.toggleCursorVisibility(); self.$dot.style.opacity = 0; self.$outline.style.opacity = 0; }); }, animateDotOutline: function () { let self = this; self._x += (self.endX - self._x) / self.delay; self._y += (self.endY - self._y) / self.delay; self.$outline.style.top = self._y + "px"; self.$outline.style.left = self._x + "px"; requestAnimationFrame(this.animateDotOutline.bind(self)); }, toggleCursorSize: function () { let self = this; if (self.cursorEnlarged) { self.$dot.style.transform = "translate(-50%, -50%) scale(0.75)"; self.$outline.style.transform = "translate(-50%, -50%) scale(1.6)"; } else { self.$dot.style.transform = "translate(-50%, -50%) scale(1)"; self.$outline.style.transform = "translate(-50%, -50%) scale(1)"; } }, toggleCursorVisibility: function () { let self = this; if (self.cursorVisible) { self.$dot.style.opacity = 1; self.$outline.style.opacity = 1; } else { self.$dot.style.opacity = 0; self.$outline.style.opacity = 0; } }, }; cursor.init(); } }, }; /* Slider **-----------------------------------------------------*/ blogmarks.slider = { init: function () { this.tickerSlider(); this.bannerSlider(); this.widgetSlider(); }, tickerSlider: function () { let sliderWrapper = document.querySelector( ".site-ticker-init" ); if (sliderWrapper) { let sliderOptions; let sliderData = sliderWrapper.getAttribute("data-slider") || {}; if (sliderData) { sliderOptions = JSON.parse(sliderData); } let swiper = new Swiper(sliderWrapper, sliderOptions); } }, bannerSlider: function () { let sliderWrapper = document.querySelector(".site-banner-init"); if (sliderWrapper) { let bannerDefaultOptions = { loop: true, }; let swiperThumbnail, bannerDataOptions, thumbnailDataOptions; let thumbnailWrapper = document.querySelector( ".blogmarks-banner-thumb-container" ); if (thumbnailWrapper) { let thumbnailData = thumbnailWrapper.getAttribute("data-banner-thumb") || {}; if (thumbnailData) { thumbnailDataOptions = JSON.parse(thumbnailData); // Init Thumbnail. swiperThumbnail = new Swiper( thumbnailWrapper, thumbnailDataOptions ); // Link Thumb to Main Slider. bannerDefaultOptions.loop = false; bannerDefaultOptions.thumbs = { swiper: swiperThumbnail, }; bannerDefaultOptions.on = { slideChange: function () { let activeIndex = this.activeIndex + 1; let activeSlide = document.querySelector( `.blogmarks-banner-thumb-container .swiper-slide:nth-child(${activeIndex})` ); let nextSlide = document.querySelector( `.blogmarks-banner-thumb-container .swiper-slide:nth-child(${ activeIndex + 1 })` ); let prevSlide = document.querySelector( `.blogmarks-banner-thumb-container .swiper-slide:nth-child(${ activeIndex - 1 })` ); if ( nextSlide && !nextSlide.classList.contains( "swiper-slide-visible" ) ) { this.thumbs.swiper.slideNext(); } else if ( prevSlide && !prevSlide.classList.contains( "swiper-slide-visible" ) ) { this.thumbs.swiper.slidePrev(); } }, }; } } // Setup Banner. let bannerData = sliderWrapper.getAttribute("data-banner") || {}; if (bannerData) { bannerDataOptions = JSON.parse(bannerData); } let sliderOptions = { ...bannerDefaultOptions, ...bannerDataOptions, }; let swiper = new Swiper(sliderWrapper, sliderOptions); } }, widgetSlider: function () { let sliderWrapper = document.querySelectorAll( ".site-swiper-wrapper .swiper" ); if (sliderWrapper) { sliderWrapper.forEach(function (item) { let parentWrapper = item.parentNode; let navNext = parentWrapper.querySelector( ".swiper-button-next" ); let navPrev = parentWrapper.querySelector( ".swiper-button-prev" ); let paginate = parentWrapper.querySelector(".swiper-pagination"); let defaultOptions = { slidesPerView: 1, lazyloading: true, navigation: { nextEl: navNext, prevEl: navPrev, }, pagination: { el: paginate, clickable: true, }, }; let data = item.getAttribute("data-slider") || {}; if (data) { var dataOptions = JSON.parse(data); } let sliderOptions = { ...defaultOptions, ...dataOptions, }; let swiper = new Swiper(item, sliderOptions); let containerWidth = item.clientWidth; if (containerWidth < 500) { swiper.params.slidesPerView = 1; swiper.update(); } }); } }, }; /* Tabs **-----------------------------------------------------*/ blogmarks.tabs = { init: function () { let tabLinks = document.querySelectorAll("[data-toggle='wpintf-tab']"); if (tabLinks) { tabLinks.forEach(function (tabLink) { tabLink.addEventListener("click", function (e) { e.preventDefault(); let tabHeadings = [...tabLink.parentNode.children]; let tabContents = [ ...tabLink.parentNode.nextElementSibling.children, ]; tabHeadings.forEach((tabLink) => { tabLink.classList.remove("active"); tabLink.setAttribute("aria-selected", "false"); }); tabContents.forEach((tabContent) => { tabContent.classList.remove("active"); }); let selectedTabId = tabLink.getAttribute("aria-controls"); let selectedContentTab = document.getElementById(selectedTabId); tabLink.classList.add("active"); tabLink.setAttribute("aria-selected", "true"); selectedContentTab.classList.add("active"); }); }); } }, }; /* Ajax Load Posts **-----------------------------------------------------*/ blogmarks.loadPosts = { canBeLoaded: true, currentPage: 0, nextPage: 0, maxPage: 0, template: "", loadButton: "", loader: "", postsListsWrapper: "", loadType: "", init: function () { let loadButtonWrapper = document.querySelector( ".blogmarks-load-posts-btn-wrapper" ); if (loadButtonWrapper) { let self = this; self.currentPage = parseInt( loadButtonWrapper.getAttribute("data-page") ); self.nextPage = self.currentPage + 1; self.maxPage = parseInt( loadButtonWrapper.getAttribute("data-max-pages") ); self.template = loadButtonWrapper .closest("#primary") .getAttribute("data-template"); self.loadButton = document.querySelector(".blogmarks-ajax-load-btn"); self.loader = document.querySelector(".blogmarks-ajax-loader"); self.postsListsWrapper = document.querySelector( ".site-archive-style" ); self.loadType = loadButtonWrapper.getAttribute("data-load-type"); if (!self.loadType) { self.loadType = "button_click_load"; } if ("button_click_load" == self.loadType) { self.loadButton.addEventListener("click", function (event) { event.preventDefault(); if (self.canBeLoaded) { self.fetchThePosts(); } }); } if ("infinite_scroll_load" == self.loadType) { loadButtonWrapper.style.opacity = 0.7; window.addEventListener("scroll", function (event) { let btnPosition = loadButtonWrapper.getBoundingClientRect().top; let isBtnVisible = btnPosition - window.innerHeight <= 400; if ( self.nextPage <= self.maxPage && isBtnVisible && self.canBeLoaded ) { self.fetchThePosts(); } }); } } }, fetchThePosts: function () { let self = this; self.canBeLoaded = false; self.loadButton.classList.add("loading-posts"); self.loader.classList.add("active"); let data = { action: "blogmarks_load_posts", load_post_nonce: BlogmarksVars.load_post_nonce, query_vars: BlogmarksVars.query_vars, page: self.nextPage, template: self.template, }; fetch(BlogmarksVars.ajaxurl, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams(data), }) .then((response) => response.json()) .then((response) => { if (response.success) { let contentJoin = response.data.content.join(""); // self.postsListsWrapper.innerHTML += contentJoin; let newElements = blogmarks.createElementsFromString(contentJoin); newElements.forEach((element) => { element.classList.add("animatefadeIn"); self.postsListsWrapper.appendChild(element); }); self.currentPage = self.nextPage; self.nextPage++; self.canBeLoaded = true; if (self.nextPage <= self.maxPage) { self.loadButton.classList.remove("loading-posts"); self.loader.classList.remove("active"); } else { document.querySelector( ".blogmarks-load-posts-btn-wrapper" ).style.display = "none"; } document.body.dispatchEvent(new Event("posts-loaded")); } else { self.loadButton.classList.remove("loading-posts"); self.loader.classList.remove("active"); } }) .catch((error) => { console.error("Error during fetch:", error); self.loadButton.classList.remove("loading-posts"); self.loader.classList.remove("active"); }); }, }; /* Create Elements from String *--------------------------------------------------*/ blogmarks.createElementsFromString = function (htmlString) { const parser = new DOMParser(); const doc = parser.parseFromString(htmlString, "text/html"); return Array.from(doc.body.children); }; /* Load functions at proper events *--------------------------------------------------*/ /** * Is the DOM ready? * * This implementation is coming from https://gomakethings.com/a-native-javascript-equivalent-of-jquerys-ready-method/ * * @param {Function} fn Callback function to run. */ function blogmarksDomReady(fn) { if (typeof fn !== "function") { return; } if ( document.readyState === "interactive" || document.readyState === "complete" ) { return fn(); } document.addEventListener("DOMContentLoaded", fn, false); } blogmarksDomReady(function () { blogmarks.stickyMenu.init(); blogmarks.subMenuToggle.init(); blogmarks.traverseMenu.init(); blogmarks.handleFocus.init(); blogmarks.CanvasModal.init(); blogmarks.SearchBlock.init(); blogmarks.scrollToTop.init(); blogmarks.setBackgroundImage.init(); blogmarks.progressBar.init(); blogmarks.customCursor.init(); blogmarks.slider.init(); blogmarks.tabs.init(); blogmarks.loadPosts.init(); }); window.addEventListener("load", function (event) { blogmarks.fadeOutPreloader.init(); });