document.addEventListener('DOMContentLoaded', () => { setupNavigationHighlight(); setupNavigationClicks(); setupCopyright(); setupProjectCards(); setupAppsCarousel(); setupGameCards(); }); function setupNavigationHighlight() { const sections = document.querySelectorAll('.scroll_section'); const navOptions = document.querySelectorAll('.nav_option'); const header = document.querySelector('header'); const headerHeight = header ? header.offsetHeight : 0; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const sectionId = entry.target.id; window.history.replaceState(null, null, `#${sectionId}`); navOptions.forEach(link => { link.classList.toggle('active', link.getAttribute('href') === `#${sectionId}`); }); } }); }, { threshold: 0.7, rootMargin: `-${headerHeight}px 0px -40% 0px` }); sections.forEach(section => observer.observe(section)); } function setupNavigationClicks() { const header = document.querySelector('header'); const headerHeight = header ? header.offsetHeight : 0; document.querySelectorAll('.nav_option').forEach(link => { link.addEventListener('click', (event) => { event.preventDefault(); const targetId = link.getAttribute('href'); const targetSection = document.querySelector(targetId); if (targetSection) { const targetPosition = targetSection.getBoundingClientRect().top + window.pageYOffset - headerHeight; window.scrollTo({ top: targetPosition, behavior: 'smooth' }); } }); }); } function setupCopyright() { document.getElementById('copyright').textContent = `© ${new Date().getFullYear()} - douwco.be`; } function setupProjectCards() { const projectCards = document.querySelectorAll('.project-card-new'); projectCards.forEach(card => { const shortDesc = card.querySelector('.project-card-short-desc'); const fullContent = card.querySelector('.project-card-content'); const toggleIcon = card.querySelector('.toggle-icon'); if (!shortDesc || !fullContent || !toggleIcon) return; const toggleCard = (event) => { if (event.target.tagName === 'A') { return; } const isExpanded = card.classList.contains('expanded'); if (isExpanded) { card.classList.remove('expanded'); toggleIcon.textContent = '+'; } else { card.classList.add('expanded'); toggleIcon.textContent = '−'; } event.stopPropagation(); }; card.addEventListener('click', toggleCard); toggleIcon.addEventListener('click', toggleCard); }); } function setupAppsCarousel() { const carousel = document.getElementById('apps-carousel'); const carouselContainer = carousel ? carousel.closest('.carousel-container-full') : null; const items = document.querySelectorAll('#apps-carousel .carousel-item-full'); const prevBtn = carouselContainer ? carouselContainer.querySelector('.carousel-nav-full.prev') : null; const nextBtn = carouselContainer ? carouselContainer.querySelector('.carousel-nav-full.next') : null; const screenshotImg = document.getElementById('apps-screenshot-img'); const description = document.getElementById('apps-description'); const title = document.getElementById('apps-title'); const goToAppBtn = document.getElementById('apps-link'); if (!carousel || items.length === 0) return; const services = [ { name: 'Gitea', screenshot: '/assets/img/gitea_screenshot.png', description: 'Self-hosted Git service with a simple and lightweight interface.', url: 'https://gitea.douwco.be' }, { name: 'Nextcloud', screenshot: '/assets/img/nextcloud_screenshot.png', description: 'Private file hosting and collaboration platform with powerful features.', url: 'https://nextcloud.douwco.be' }, { name: 'Penpot', screenshot: '/assets/img/penpot_screenshot.png', description: 'Open-source design and prototyping platform for teams.', url: 'https://penpot.douwco.be' }, { name: 'Beszel', screenshot: '/assets/img/beszel_screenshot.png', description: 'Custom service management and monitoring dashboard.', url: 'https://beszel.douwco.be' }, { name: 'Wattif', screenshot: '/assets/img/wattif_screenshot.png', description: 'Energy monitoring and analysis tool for smart homes.', url: 'https://wattif.douwco.be' } ]; let currentIndex = 0; let isAnimating = false; function updateCarousel() { if (isAnimating) return; isAnimating = true; items.forEach((item, index) => { const isSelected = index === currentIndex; item.setAttribute('data-selected', isSelected.toString()); }); const service = services[currentIndex]; if (description) description.classList.remove('active'); if (screenshotImg) screenshotImg.classList.remove('active'); if (title) title.classList.remove('active'); if (goToAppBtn) goToAppBtn.classList.remove('active'); setTimeout(() => { if (screenshotImg) { screenshotImg.src = service.screenshot; } if (title) { title.textContent = service.name; } if (description) { description.innerHTML = `
${service.description}
`; } if (goToAppBtn) { goToAppBtn.href = service.url; } setTimeout(() => { if (description) description.classList.add('active'); if (screenshotImg) screenshotImg.classList.add('active'); if (title) title.classList.add('active'); if (goToAppBtn) goToAppBtn.classList.add('active'); isAnimating = false; }, 50); }, 300); } function rotateCarousel(direction) { if (isAnimating) return; currentIndex = (currentIndex + direction + items.length) % items.length; updateCarousel(); } if (prevBtn) { prevBtn.addEventListener('click', () => rotateCarousel(-1)); } if (nextBtn) { nextBtn.addEventListener('click', () => rotateCarousel(1)); } items.forEach((item, index) => { item.addEventListener('click', () => { if (isAnimating) return; currentIndex = index; updateCarousel(); }); }); let touchStartX = 0; let touchEndX = 0; if (carousel) { carousel.addEventListener('touchstart', (e) => { touchStartX = e.changedTouches[0].screenX; }, { passive: true }); carousel.addEventListener('touchend', (e) => { touchEndX = e.changedTouches[0].screenX; handleSwipe(); }, { passive: true }); } function handleSwipe() { if (isAnimating) return; const swipeThreshold = 50; const diff = touchStartX - touchEndX; if (Math.abs(diff) > swipeThreshold) { if (diff > 0) { rotateCarousel(1); } else { rotateCarousel(-1); } } } if (carousel) { carousel.addEventListener('wheel', (e) => { if (isAnimating) return; e.preventDefault(); if (e.deltaY > 0) { rotateCarousel(1); } else { rotateCarousel(-1); } }, { passive: false }); } updateCarousel(); } function setupGameCards() { const gameCards = document.querySelectorAll('.game-card'); gameCards.forEach(card => { card.addEventListener('click', (event) => { const isExpanded = card.classList.contains('expanded'); if (isExpanded) { card.classList.remove('expanded'); } else { gameCards.forEach(c => c.classList.remove('expanded')); card.classList.add('expanded'); } event.stopPropagation(); }); const playButton = card.querySelector('.play-button'); if (playButton) { playButton.addEventListener('click', (event) => { event.stopPropagation(); }); } }); }