← back to Jill Website

public/js/main.js

389 lines

// Mobile Navigation Toggle
const hamburger = document.querySelector('.hamburger');
const navMenu = document.querySelector('.nav-menu');

hamburger.addEventListener('click', () => {
    hamburger.classList.toggle('active');
    navMenu.classList.toggle('active');
});

// Mobile dropdown toggle
document.querySelectorAll('.nav-dropdown > .nav-link').forEach(dropdownLink => {
    dropdownLink.addEventListener('click', (e) => {
        if (window.innerWidth <= 768) {
            e.preventDefault();
            const dropdown = dropdownLink.parentElement;
            dropdown.classList.toggle('active');
        }
    });
});

// Close mobile menu when clicking a link
document.querySelectorAll('.nav-link').forEach(link => {
    link.addEventListener('click', () => {
        if (!link.parentElement.classList.contains('nav-dropdown')) {
            hamburger.classList.remove('active');
            navMenu.classList.remove('active');
        }
    });
});

// Close dropdown when clicking dropdown item
document.querySelectorAll('.dropdown-menu a').forEach(link => {
    link.addEventListener('click', () => {
        hamburger.classList.remove('active');
        navMenu.classList.remove('active');
        document.querySelectorAll('.nav-dropdown').forEach(d => d.classList.remove('active'));
    });
});

// Smooth Scrolling
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
    anchor.addEventListener('click', function(e) {
        e.preventDefault();
        const target = document.querySelector(this.getAttribute('href'));
        if (target) {
            const offset = 70; // navbar height
            const targetPosition = target.offsetTop - offset;
            window.scrollTo({
                top: targetPosition,
                behavior: 'smooth'
            });
        }
    });
});

// Navbar scroll effect
let lastScroll = 0;
const navbar = document.querySelector('.navbar');

window.addEventListener('scroll', () => {
    const currentScroll = window.pageYOffset;

    if (currentScroll <= 0) {
        navbar.style.boxShadow = '0 5px 20px rgba(0,0,0,0.1)';
        return;
    }

    if (currentScroll > lastScroll && currentScroll > 100) {
        // Scrolling down
        navbar.style.transform = 'translateY(-100%)';
    } else {
        // Scrolling up
        navbar.style.transform = 'translateY(0)';
        navbar.style.boxShadow = '0 5px 30px rgba(0,0,0,0.15)';
    }

    lastScroll = currentScroll;
});

// Google Maps Integration - Nosara Location
function initMap() {
    // Coordinates for Nosara, Costa Rica
    const nosaraLocation = { lat: 9.9759, lng: -85.6532 };

    const map = new google.maps.Map(document.getElementById('nosara-map'), {
        zoom: 12,
        center: nosaraLocation,
        styles: [
            {
                featureType: 'water',
                elementType: 'geometry',
                stylers: [{ color: '#667eea' }]
            },
            {
                featureType: 'landscape',
                elementType: 'geometry',
                stylers: [{ color: '#f5f5f5' }]
            }
        ]
    });

    // Add marker for property location
    const propertyMarker = new google.maps.Marker({
        position: nosaraLocation,
        map: map,
        title: 'Nosara Beachfront Rentals',
        icon: {
            url: 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(`
                <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="#2c5f8d">
                    <path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>
                </svg>
            `),
            scaledSize: new google.maps.Size(40, 40)
        }
    });

    // Info window for property
    const infoWindow = new google.maps.InfoWindow({
        content: `
            <div style="padding: 10px; font-family: 'Inter', sans-serif;">
                <h3 style="margin: 0 0 5px 0; color: #2c5f8d;">Nosara Beachfront Rentals</h3>
                <p style="margin: 0; color: #666;">Luxury villas at the mouth of Rio Nosara</p>
            </div>
        `
    });

    propertyMarker.addListener('click', () => {
        infoWindow.open(map, propertyMarker);
    });

    // Add nearby points of interest
    const pois = [
        { lat: 9.9641, lng: -85.6595, title: 'Playa Guiones', type: 'beach' },
        { lat: 9.9823, lng: -85.6482, title: 'La Luna Restaurant', type: 'restaurant' },
        { lat: 9.9785, lng: -85.6445, title: 'Hotel Lagarta Lodge', type: 'hotel' },
        { lat: 10.0156, lng: -85.6921, title: 'Playa Ostional', type: 'beach' }
    ];

    const markerColors = {
        restaurant: '#e74c3c',
        hotel: '#3498db',
        beach: '#f39c12',
        activity: '#2ecc71'
    };

    pois.forEach(poi => {
        const marker = new google.maps.Marker({
            position: { lat: poi.lat, lng: poi.lng },
            map: map,
            title: poi.title,
            icon: {
                path: google.maps.SymbolPath.CIRCLE,
                scale: 8,
                fillColor: markerColors[poi.type],
                fillOpacity: 0.8,
                strokeWeight: 2,
                strokeColor: '#ffffff'
            }
        });

        const poiInfoWindow = new google.maps.InfoWindow({
            content: `
                <div style="padding: 5px; font-family: 'Inter', sans-serif;">
                    <strong style="color: ${markerColors[poi.type]};">${poi.title}</strong>
                </div>
            `
        });

        marker.addListener('click', () => {
            poiInfoWindow.open(map, marker);
        });
    });
}

// Initialize map when Google Maps API is loaded
if (typeof google !== 'undefined' && google.maps) {
    google.maps.event.addDomListener(window, 'load', initMap);
}

// Street View Integration
function initStreetView() {
    const streetViewDiv = document.getElementById('street-view');

    if (!streetViewDiv || typeof google === 'undefined') {
        return;
    }

    // Coordinates for a scenic spot near Nosara beach
    const panorama = new google.maps.StreetViewPanorama(
        streetViewDiv,
        {
            position: { lat: 9.9759, lng: -85.6532 },
            pov: { heading: 270, pitch: 0 },
            zoom: 1,
            addressControl: false,
            fullscreenControl: true,
            motionTracking: true,
            motionTrackingControl: true
        }
    );
}

// Toggle Street View
const toggleStreetViewBtn = document.getElementById('toggle-street-view');
let streetViewActive = false;

if (toggleStreetViewBtn) {
    toggleStreetViewBtn.addEventListener('click', () => {
        streetViewActive = !streetViewActive;

        if (streetViewActive && typeof google !== 'undefined') {
            initStreetView();
            toggleStreetViewBtn.innerHTML = '<i class="fas fa-times"></i> Close Tour';
        } else {
            const streetViewDiv = document.getElementById('street-view');
            streetViewDiv.innerHTML = '';
            toggleStreetViewBtn.innerHTML = '<i class="fas fa-street-view"></i> 360° Virtual Tour';
        }
    });
}

// Scroll Animations
const observerOptions = {
    threshold: 0.1,
    rootMargin: '0px 0px -50px 0px'
};

const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            entry.target.style.opacity = '1';
            entry.target.style.transform = 'translateY(0)';
        }
    });
}, observerOptions);

// Observe elements for animation
document.addEventListener('DOMContentLoaded', () => {
    const animateElements = document.querySelectorAll('.villa-card, .season-card, .rec-item, .heritage-item, .timeline-item');

    animateElements.forEach(el => {
        el.style.opacity = '0';
        el.style.transform = 'translateY(30px)';
        el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
        observer.observe(el);
    });
});

// Stats Counter Animation
function animateCounter(element, target, duration = 2000) {
    const start = 0;
    const increment = target / (duration / 16);
    let current = start;

    const timer = setInterval(() => {
        current += increment;
        if (current >= target) {
            element.textContent = target;
            clearInterval(timer);
        } else {
            element.textContent = Math.floor(current);
        }
    }, 16);
}

// Trigger counter animation when stats section is visible
const statsSection = document.querySelector('.stats');
let statsAnimated = false;

const statsObserver = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting && !statsAnimated) {
            const statNumbers = document.querySelectorAll('.stat-item h3');
            statNumbers.forEach(stat => {
                const target = parseInt(stat.textContent);
                if (!isNaN(target)) {
                    animateCounter(stat, target);
                }
            });
            statsAnimated = true;
        }
    });
}, { threshold: 0.5 });

if (statsSection) {
    statsObserver.observe(statsSection);
}

// Form Validation (Formspree handles submission)
const contactForm = document.querySelector('.contact-form');

if (contactForm) {
    contactForm.addEventListener('submit', (e) => {
        // Get form values
        const formData = new FormData(contactForm);
        const data = Object.fromEntries(formData);

        // Date validation
        if (data.checkin && data.checkout) {
            const checkin = new Date(data.checkin);
            const checkout = new Date(data.checkout);
            const today = new Date();
            today.setHours(0, 0, 0, 0);

            if (checkin < today) {
                alert('Check-in date must be today or later');
                e.preventDefault();
                return;
            }

            if (checkout <= checkin) {
                alert('Check-out date must be after check-in date');
                e.preventDefault();
                return;
            }
        }

        // Form will submit to Formspree
    });
}

// Parallax Effect for Hero Section
window.addEventListener('scroll', () => {
    const scrolled = window.pageYOffset;
    const heroText = document.querySelector('.hero-text');

    if (heroText && scrolled < window.innerHeight) {
        heroText.style.transform = `translateY(${scrolled * 0.5}px)`;
        heroText.style.opacity = 1 - (scrolled / 500);
    }
});

// Lazy Loading for Images
if ('IntersectionObserver' in window) {
    const imageObserver = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                const img = entry.target;
                img.src = img.dataset.src;
                img.classList.add('loaded');
                observer.unobserve(img);
            }
        });
    });

    document.querySelectorAll('img[data-src]').forEach(img => {
        imageObserver.observe(img);
    });
}

// Current year for footer
const currentYearElement = document.querySelector('.footer-bottom p');
if (currentYearElement) {
    const currentYear = new Date().getFullYear();
    currentYearElement.textContent = `© ${currentYear} Nosara Beachfront Rentals. All rights reserved.`;
}

// Add active class to navigation based on scroll position
window.addEventListener('scroll', () => {
    let current = '';
    const sections = document.querySelectorAll('section[id]');

    sections.forEach(section => {
        const sectionTop = section.offsetTop;
        const sectionHeight = section.clientHeight;
        if (pageYOffset >= (sectionTop - 100)) {
            current = section.getAttribute('id');
        }
    });

    document.querySelectorAll('.nav-link').forEach(link => {
        link.classList.remove('active');
        if (link.getAttribute('href') === `#${current}`) {
            link.classList.add('active');
        }
    });
});

// Disable right-click on images (optional, for image protection)
// Uncomment if needed:
/*
document.querySelectorAll('img').forEach(img => {
    img.addEventListener('contextmenu', (e) => {
        e.preventDefault();
    });
});
*/

console.log('🌴 Nosara Beachfront Rentals - Website Loaded Successfully');