/** @odoo-module **/ /** * Aakriti Events – Premium Theme V2 JS * Handles: sticky header, mobile drawer, gallery carousel, scroll reveal, counter animation */ (function () { 'use strict'; /* ── Helpers ──────────────────────────────────────────── */ function qs(sel, ctx) { return (ctx || document).querySelector(sel); } function qsa(sel, ctx) { return (ctx || document).querySelectorAll(sel); } function onReady(fn) { if (document.readyState !== 'loading') { fn(); } else { document.addEventListener('DOMContentLoaded', fn); } } /* ── Sticky Header ────────────────────────────────────── */ function initHeader() { var header = qs('#v2-header'); if (!header) return; function updateHeader() { if (window.scrollY > 40) { header.classList.add('scrolled'); } else { header.classList.remove('scrolled'); } } window.addEventListener('scroll', updateHeader, { passive: true }); updateHeader(); } /* ── Mobile Drawer ────────────────────────────────────── */ function initMobileDrawer() { var toggle = qs('#v2-mobile-toggle'); var drawer = qs('#v2-mobile-drawer'); var overlay = qs('#v2-drawer-overlay'); var closeBtn = qs('#v2-drawer-close'); if (!toggle || !drawer) return; function openDrawer() { drawer.classList.add('open'); if (overlay) { overlay.classList.add('active'); } document.body.style.overflow = 'hidden'; } function closeDrawer() { drawer.classList.remove('open'); if (overlay) { overlay.classList.remove('active'); } document.body.style.overflow = ''; } toggle.addEventListener('click', openDrawer); if (closeBtn) { closeBtn.addEventListener('click', closeDrawer); } if (overlay) { overlay.addEventListener('click', closeDrawer); } qsa('#v2-mobile-drawer a').forEach(function (a) { a.addEventListener('click', closeDrawer); }); window.addEventListener('resize', function () { if (window.innerWidth > 991) { closeDrawer(); } }); } /* ── Gallery Carousel ─────────────────────────────────── */ function initGallery() { var track = qs('#v2-gallery-track'); var prevBtn = qs('#v2-gallery-prev'); var nextBtn = qs('#v2-gallery-next'); if (!track) return; var current = 0; var itemWidth = 0; var visibleItems = 4; function updateLayout() { var ww = window.innerWidth; if (ww > 991) { visibleItems = 4; } else if (ww > 576) { visibleItems = 3; } else { visibleItems = 2; } var containerWidth = track.parentElement.offsetWidth; var gap = 12; // White gap between slanted items in pixels var items = qsa('.v2-gallery-item-slant', track); // Calculate width 'w' so that 'visibleItems' fit exactly in containerWidth var w = (containerWidth - (visibleItems - 1) * gap) / (0.75 * visibleItems + 0.25); var mr = (-0.25 * w) + gap; items.forEach(function (item, idx) { item.style.width = w + 'px'; if (idx === items.length - 1) { item.style.marginRight = '0px'; } else { item.style.marginRight = mr + 'px'; } }); itemWidth = (w * 0.75) + gap; // The effective step width to slide current = clamp(current, 0, getMax()); goTo(current, false); updateButtons(); } function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); } function getMax() { var itemsCount = track.children.length; return Math.max(0, itemsCount - visibleItems); } function updateButtons() { if (prevBtn) { if (current <= 0) { prevBtn.style.opacity = '0.5'; prevBtn.style.pointerEvents = 'none'; } else { prevBtn.style.opacity = '1'; prevBtn.style.pointerEvents = 'auto'; } } if (nextBtn) { if (current >= getMax()) { nextBtn.style.opacity = '0.5'; nextBtn.style.pointerEvents = 'none'; } else { nextBtn.style.opacity = '1'; nextBtn.style.pointerEvents = 'auto'; } } } function goTo(index, animate) { if (animate === undefined) animate = true; current = clamp(index, 0, getMax()); track.style.transition = animate ? 'transform 0.4s ease' : 'none'; track.style.transform = 'translateX(-' + (current * itemWidth) + 'px)'; updateButtons(); } if (prevBtn) { prevBtn.addEventListener('click', function () { goTo(current - 1); resetAutoSlide(); }); } if (nextBtn) { nextBtn.addEventListener('click', function () { goTo(current + 1); resetAutoSlide(); }); } // Touch/swipe support var startX = 0; track.addEventListener('touchstart', function (e) { startX = e.touches[0].clientX; }, { passive: true }); track.addEventListener('touchend', function (e) { var diff = startX - e.changedTouches[0].clientX; if (Math.abs(diff) > 50) { goTo(current + (diff > 0 ? 1 : -1)); resetAutoSlide(); } }, { passive: true }); // Auto slide var autoSlideInterval; function startAutoSlide() { autoSlideInterval = setInterval(function () { if (current >= getMax()) { goTo(0); } else { goTo(current + 1); } }, 3000); } function resetAutoSlide() { clearInterval(autoSlideInterval); startAutoSlide(); } window.addEventListener('resize', updateLayout); updateLayout(); startAutoSlide(); } /* ── Scroll Reveal ────────────────────────────────────── */ function initReveal() { var els = qsa('.v2-reveal'); if (!els.length) return; if ('IntersectionObserver' in window) { var io = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting) { entry.target.classList.add('visible'); io.unobserve(entry.target); } }); }, { threshold: 0.12 }); els.forEach(function (el) { io.observe(el); }); } else { els.forEach(function (el) { el.classList.add('visible'); }); } } /* ── Counter Animation ────────────────────────────────── */ function animateCounter(el, target, suffix) { var start = 0; var duration = 1800; var startTime = null; function step(ts) { if (!startTime) startTime = ts; var progress = Math.min((ts - startTime) / duration, 1); var eased = 1 - Math.pow(1 - progress, 3); // ease out cubic var val = Math.round(eased * target); el.textContent = val + suffix; if (progress < 1) { requestAnimationFrame(step); } } requestAnimationFrame(step); } function initCounters() { var counters = qsa('[data-v2-counter]'); if (!counters.length) return; var observed = false; if ('IntersectionObserver' in window) { var io = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting && !observed) { observed = true; counters.forEach(function (el) { var target = parseInt(el.getAttribute('data-v2-counter'), 10); var suffix = el.getAttribute('data-v2-suffix') || ''; animateCounter(el, target, suffix); }); io.disconnect(); } }); }, { threshold: 0.3 }); var statsSection = qs('#v2-stats'); if (statsSection) { io.observe(statsSection); } } else { counters.forEach(function (el) { el.textContent = el.getAttribute('data-v2-counter') + (el.getAttribute('data-v2-suffix') || ''); }); } } /* ── Active nav link ──────────────────────────────────── */ function initActiveNav() { var path = window.location.pathname; qsa('.v2-nav a, .v2-drawer-nav a').forEach(function (a) { var href = a.getAttribute('href') || ''; if (href === path || (href !== '/' && path.startsWith(href))) { a.classList.add('active'); } }); } /* ── Service Tabs (Themes) ────────────────────────────── */ function initServiceTabs() { var pillBtns = qsa('.svc-pill-btn'); var themePanels = qsa('.svc-fw-theme-panel'); if (!pillBtns.length || !themePanels.length) return; pillBtns.forEach(function (btn) { btn.addEventListener('click', function () { var targetId = btn.getAttribute('data-target'); if (!targetId) return; // Remove active class from all buttons and panels pillBtns.forEach(function (b) { b.classList.remove('active'); }); themePanels.forEach(function (p) { p.classList.remove('active'); }); // Add active class to clicked button and target panel btn.classList.add('active'); var targetPanel = qs('#' + targetId); if (targetPanel) { targetPanel.classList.add('active'); } }); }); } /* ── Bootstrap ────────────────────────────────────────── */ function initPortfolio() { const grid = document.getElementById('v2-portfolio-grid'); const tabs = document.querySelectorAll('.v2-portfolio-tab'); if (!grid || !tabs.length) return; // Dynamic portfolio data const portfolioData = [ { category: 'weddings', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' }, { category: 'corporate', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' }, { category: 'birthday', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' }, { category: 'cultural', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' }, { category: 'private', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' }, { category: 'weddings', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' } ]; // Function to render items function renderItems(filter) { grid.innerHTML = ''; // Clear current portfolioData.forEach(item => { if (filter === 'all' || filter === item.category) { const itemHTML = `
${item.title}

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

`; // Parse and append to trigger animation const temp = document.createElement('div'); temp.innerHTML = itemHTML.trim(); const element = temp.firstChild; grid.appendChild(element); // Trigger animation after append setTimeout(() => { element.style.opacity = '1'; element.style.transform = 'scale(1)'; }, 50); } }); } // Initial render renderItems('all'); // Tab click handlers tabs.forEach(tab => { tab.addEventListener('click', function () { tabs.forEach(t => t.classList.remove('active')); this.classList.add('active'); const filter = this.getAttribute('data-filter'); // Fade out existing items const currentItems = grid.querySelectorAll('.v2-portfolio-item'); currentItems.forEach(item => { item.style.opacity = '0'; item.style.transform = 'scale(0.9)'; }); // Wait for fade out, then render new filtered list setTimeout(() => { renderItems(filter); }, 400); // Wait for transition }); }); } onReady(function () { initHeader(); initMobileDrawer(); initGallery(); initReveal(); initCounters(); initActiveNav(); initServiceTabs(); initPortfolio(); }); })();