Files
matt ec72c5d62b
Some checks failed
build-website / build (push) Failing after 1m2s
Initial import of HarborSmith website
2025-09-18 22:20:01 +02:00

339 lines
10 KiB
JavaScript

// HarborSmith - Main JavaScript
// ========================
// Theme Management
const themeManager = {
init() {
this.themeToggle = document.getElementById('themeToggle');
this.themeDropdown = document.getElementById('themeDropdown');
this.themeOptions = document.querySelectorAll('.theme-option');
// Load saved theme or default to nautical
const savedTheme = localStorage.getItem('harborsmith-theme') || 'nautical';
this.setTheme(savedTheme);
// Event listeners
this.themeToggle?.addEventListener('click', (e) => {
e.stopPropagation();
this.themeDropdown.classList.toggle('active');
});
this.themeOptions.forEach(option => {
option.addEventListener('click', (e) => {
const theme = e.currentTarget.dataset.theme;
this.setTheme(theme);
this.themeDropdown.classList.remove('active');
});
});
// Close dropdown when clicking outside
document.addEventListener('click', () => {
this.themeDropdown?.classList.remove('active');
});
},
setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('harborsmith-theme', theme);
// Update active state
this.themeOptions.forEach(option => {
option.classList.toggle('active', option.dataset.theme === theme);
});
}
};
// Navigation
const navigation = {
init() {
this.navbar = document.getElementById('navbar');
this.navToggle = document.getElementById('navToggle');
this.navMenu = document.getElementById('navMenu');
this.navLinks = document.querySelectorAll('.nav-link');
// Mobile menu toggle
this.navToggle?.addEventListener('click', () => {
this.navToggle.classList.toggle('active');
this.navMenu.classList.toggle('active');
});
// Close mobile menu on link click
this.navLinks.forEach(link => {
link.addEventListener('click', () => {
this.navToggle?.classList.remove('active');
this.navMenu?.classList.remove('active');
});
});
// Scroll behavior
window.addEventListener('scroll', () => {
if (window.scrollY > 50) {
this.navbar?.classList.add('scrolled');
} else {
this.navbar?.classList.remove('scrolled');
}
});
// Active link highlighting
this.updateActiveLink();
},
updateActiveLink() {
const currentPath = window.location.pathname.split('/').pop() || 'index.html';
this.navLinks.forEach(link => {
const href = link.getAttribute('href');
if (href === currentPath) {
link.classList.add('active');
}
});
}
};
// Smooth Scrolling
const smoothScroll = {
init() {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', (e) => {
e.preventDefault();
const target = document.querySelector(anchor.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
}
};
// Testimonial Slider
const testimonialSlider = {
init() {
this.cards = document.querySelectorAll('.testimonial-card');
this.dots = document.querySelectorAll('.dot');
this.currentIndex = 0;
if (this.cards.length === 0) return;
// Auto-play
this.startAutoPlay();
// Dot navigation
this.dots.forEach((dot, index) => {
dot.addEventListener('click', () => {
this.goToSlide(index);
this.resetAutoPlay();
});
});
},
goToSlide(index) {
this.cards[this.currentIndex]?.classList.remove('active');
this.dots[this.currentIndex]?.classList.remove('active');
this.currentIndex = index;
this.cards[this.currentIndex]?.classList.add('active');
this.dots[this.currentIndex]?.classList.add('active');
},
nextSlide() {
const nextIndex = (this.currentIndex + 1) % this.cards.length;
this.goToSlide(nextIndex);
},
startAutoPlay() {
this.autoPlayInterval = setInterval(() => {
this.nextSlide();
}, 5000);
},
resetAutoPlay() {
clearInterval(this.autoPlayInterval);
this.startAutoPlay();
}
};
// Counter Animation
const counterAnimation = {
init() {
this.counters = document.querySelectorAll('.stat-number');
this.animated = false;
if (this.counters.length === 0) return;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !this.animated) {
this.animateCounters();
this.animated = true;
}
});
}, { threshold: 0.5 });
const statsSection = document.querySelector('.stats-section');
if (statsSection) {
observer.observe(statsSection);
}
},
animateCounters() {
this.counters.forEach(counter => {
const target = parseInt(counter.dataset.count);
const duration = 2000;
const increment = target / (duration / 16);
let current = 0;
const updateCounter = () => {
current += increment;
if (current < target) {
counter.textContent = Math.floor(current);
requestAnimationFrame(updateCounter);
} else {
counter.textContent = target;
}
};
updateCounter();
});
}
};
// Form Validation
const formValidation = {
init() {
const forms = document.querySelectorAll('form');
forms.forEach(form => {
form.addEventListener('submit', (e) => {
if (!this.validateForm(form)) {
e.preventDefault();
}
});
// Real-time validation
const inputs = form.querySelectorAll('input, textarea, select');
inputs.forEach(input => {
input.addEventListener('blur', () => {
this.validateField(input);
});
});
});
},
validateForm(form) {
const inputs = form.querySelectorAll('[required]');
let isValid = true;
inputs.forEach(input => {
if (!this.validateField(input)) {
isValid = false;
}
});
return isValid;
},
validateField(field) {
const value = field.value.trim();
const type = field.type;
let isValid = true;
// Remove previous error
field.classList.remove('error');
const errorMsg = field.parentElement.querySelector('.error-message');
if (errorMsg) {
errorMsg.remove();
}
// Required field
if (field.hasAttribute('required') && !value) {
this.showError(field, 'This field is required');
return false;
}
// Email validation
if (type === 'email' && value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
this.showError(field, 'Please enter a valid email address');
return false;
}
}
// Phone validation
if (type === 'tel' && value) {
const phoneRegex = /^[\d\s\-\+\(\)]+$/;
if (!phoneRegex.test(value)) {
this.showError(field, 'Please enter a valid phone number');
return false;
}
}
return isValid;
},
showError(field, message) {
field.classList.add('error');
const errorElement = document.createElement('div');
errorElement.className = 'error-message';
errorElement.textContent = message;
field.parentElement.appendChild(errorElement);
}
};
// Yacht Quick View Modal
const yachtModal = {
init() {
const viewButtons = document.querySelectorAll('.yacht-view-btn');
viewButtons.forEach(btn => {
btn.addEventListener('click', (e) => {
e.preventDefault();
this.openModal();
});
});
},
openModal() {
// For now, just alert - in production, would open a modal
alert('Quick view modal would open here with yacht details and 360° view');
}
};
// Parallax Effects
const parallaxEffects = {
init() {
this.elements = document.querySelectorAll('.parallax');
if (this.elements.length === 0) return;
window.addEventListener('scroll', () => {
this.updateParallax();
});
},
updateParallax() {
const scrolled = window.pageYOffset;
this.elements.forEach(element => {
const rate = element.dataset.rate || 0.5;
const yPos = -(scrolled * rate);
element.style.transform = `translateY(${yPos}px)`;
});
}
};
// Initialize everything when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
themeManager.init();
navigation.init();
smoothScroll.init();
testimonialSlider.init();
counterAnimation.init();
formValidation.init();
yachtModal.init();
parallaxEffects.init();
// Log successful initialization
console.log('HarborSmith website initialized successfully!');
});