Spaces:
Running
Running
| // Theme toggle functionality | |
| const themeToggle = document.getElementById('themeToggle'); | |
| const html = document.documentElement; | |
| // Add null check for themeToggle | |
| if (themeToggle) { | |
| const icon = themeToggle.querySelector('i'); | |
| // Check for saved theme preference or default to dark | |
| const currentTheme = localStorage.getItem('theme') || 'dark'; | |
| html.classList.toggle('dark', currentTheme === 'dark'); | |
| updateThemeIcon(currentTheme); | |
| function updateThemeIcon(theme) { | |
| if (theme === 'dark') { | |
| icon.setAttribute('data-feather', 'sun'); | |
| } else { | |
| icon.setAttribute('data-feather', 'moon'); | |
| } | |
| feather.replace(); | |
| } | |
| themeToggle.addEventListener('click', () => { | |
| const newTheme = html.classList.contains('dark') ? 'light' : 'dark'; | |
| html.classList.toggle('dark'); | |
| localStorage.setItem('theme', newTheme); | |
| updateThemeIcon(newTheme); | |
| }); | |
| } | |
| // Initialize on page load | |
| document.addEventListener('DOMContentLoaded', () => { | |
| feather.replace(); | |
| // Add loading animation | |
| document.body.classList.add('animate-fade-in'); | |
| // Initialize tooltips or other interactive elements | |
| console.log('Khmer23 Luxury Real Estate Platform Initialized'); | |
| // Ensure navbar component is loaded | |
| const navbar = document.querySelector('custom-navbar'); | |
| const backupNav = document.getElementById('backup-nav'); | |
| if (navbar) { | |
| console.log('Navbar component found and loaded'); | |
| // Hide backup nav if component loads | |
| if (backupNav) { | |
| backupNav.classList.add('hidden'); | |
| } | |
| } else { | |
| console.error('Navbar component not found! Using backup navigation'); | |
| // Show backup nav if component fails | |
| if (backupNav) { | |
| backupNav.classList.remove('hidden'); | |
| } | |
| // Adjust body padding for backup nav | |
| document.body.style.paddingTop = '80px'; | |
| } | |
| }); | |
| // Live activity feed | |
| const activities = [ | |
| { type: 'sale', location: 'Kep', price: '$2.4M', status: 'closed' }, | |
| { type: 'rental', location: 'Phnom Penh', price: '$8,500/mo', status: 'signed' }, | |
| { type: 'sale', location: 'Sihanoukville', price: '$3.1M', status: 'pending' }, | |
| { type: 'rental', location: 'Kampot', price: '$6,200/mo', status: 'closed' }, | |
| { type: 'sale', location: 'Phnom Penh', price: '$5.2M', status: 'closed' }, | |
| { type: 'rental', location: 'Kep', price: '$12,000/mo', status: 'signed' } | |
| ]; | |
| function addActivityItem() { | |
| const feed = document.getElementById('activityFeed'); | |
| if (!feed) return; | |
| const activity = activities[Math.floor(Math.random() * activities.length)]; | |
| const item = document.createElement('div'); | |
| item.className = 'activity-item flex items-center justify-between py-2 border-b border-slate-800 last:border-0'; | |
| const icon = activity.type === 'sale' ? '🏠' : '🔑'; | |
| const color = activity.status === 'closed' ? 'text-emerald-400' : activity.status === 'signed' ? 'text-amber-400' : 'text-slate-500'; | |
| item.innerHTML = ` | |
| <div class="flex items-center gap-2"> | |
| <span class="text-lg">${icon}</span> | |
| <div> | |
| <div class="${color} font-light capitalize">${activity.type} • ${activity.location}</div> | |
| <div class="text-slate-500">${activity.price}</div> | |
| </div> | |
| </div> | |
| <div class="text-xs text-slate-600">${formatTime(Date.now())}</div> | |
| `; | |
| feed.insertBefore(item, feed.firstChild); | |
| // Keep only last 4 activities | |
| while (feed.children.length > 4) { | |
| feed.removeChild(feed.lastChild); | |
| } | |
| } | |
| function formatTime(timestamp) { | |
| const now = new Date(); | |
| const time = new Date(timestamp); | |
| const diff = Math.floor((now - time) / 1000); | |
| if (diff < 60) return 'Just now'; | |
| if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; | |
| if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; | |
| return `${Math.floor(diff / 86400)}d ago`; | |
| } | |
| // Initialize activity feed | |
| if (document.getElementById('activityFeed')) { | |
| // Add initial activities | |
| for (let i = 0; i < 3; i++) { | |
| addActivityItem(); | |
| } | |
| // Add new activity every 10 seconds | |
| setInterval(addActivityItem, 10000); | |
| } | |
| // Contact form handling | |
| const contactForm = document.getElementById('contactForm'); | |
| if (contactForm) { | |
| contactForm.addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| // Show success message | |
| const formContainer = contactForm.parentElement; | |
| formContainer.innerHTML = ` | |
| <div class="text-center py-12"> | |
| <div class="w-20 h-20 mx-auto mb-6 bg-emerald-500/20 rounded-full flex items-center justify-center"> | |
| <i data-feather="check-circle" class="w-10 h-10 text-emerald-400"></i> | |
| </div> | |
| <h3 class="text-2xl font-light mb-4">Request Received</h3> | |
| <p class="text-slate-400 mb-8"> | |
| Your inquiry has been submitted for review. Our membership committee will contact qualified candidates within 72 hours. | |
| </p> | |
| <div class="bg-slate-800 rounded-2xl p-6 text-left"> | |
| <h4 class="text-lg font-light mb-3 text-emerald-400">Next Steps:</h4> | |
| <ul class="space-y-2 text-slate-300 text-sm font-light"> | |
| <li>• Preliminary qualification review (24-48 hours)</li> | |
| <li>• Background verification (additional 24 hours)</li> | |
| <li>• Personal consultation by invitation only</li> | |
| <li>• Access to exclusive property portfolio</li> | |
| </ul> | |
| </div> | |
| <a href="index.html" class="inline-block mt-8 px-8 py-3 bg-slate-800 hover:bg-slate-700 text-alabaster rounded-full transition-all duration-300"> | |
| Return to Home | |
| </a> | |
| </div> | |
| `; | |
| feather.replace(); | |
| }); | |
| } | |
| // Smooth scroll for anchor links | |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { | |
| anchor.addEventListener('click', function (e) { | |
| e.preventDefault(); | |
| const target = document.querySelector(this.getAttribute('href')); | |
| if (target) { | |
| target.scrollIntoView({ | |
| behavior: 'smooth', | |
| block: 'start' | |
| }); | |
| } | |
| }); | |
| }); | |
| // Intersection Observer for fade-in animations | |
| const observerOptions = { | |
| threshold: 0.1, | |
| rootMargin: '0px 0px -50px 0px' | |
| }; | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.classList.add('animate-fade-in'); | |
| observer.unobserve(entry.target); | |
| } | |
| }); | |
| }, observerOptions); | |
| // Observe elements for animation | |
| document.querySelectorAll('section').forEach(section => { | |
| observer.observe(section); | |
| }); | |
| // Property filter functionality | |
| const filterSelects = document.querySelectorAll('select'); | |
| filterSelects.forEach(select => { | |
| select.addEventListener('change', () => { | |
| // Add filter animation | |
| const cards = document.querySelectorAll('.group.cursor-pointer'); | |
| cards.forEach((card, index) => { | |
| setTimeout(() => { | |
| card.classList.add('animate-fade-in'); | |
| }, index * 100); | |
| }); | |
| }); | |
| }); | |
| // Load more functionality | |
| const loadMoreBtn = document.querySelector('button:has([text="Load More"])'); | |
| if (loadMoreBtn) { | |
| loadMoreBtn.addEventListener('click', () => { | |
| loadMoreBtn.innerHTML = '<span class="flex items-center gap-2"><svg class="animate-spin h-4 w-4" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>Loading...</span>'; | |
| setTimeout(() => { | |
| loadMoreBtn.innerHTML = 'No More Properties'; | |
| loadMoreBtn.disabled = true; | |
| loadMoreBtn.classList.add('opacity-50', 'cursor-not-allowed'); | |
| }, 1500); | |
| }); | |
| } | |
| // Parallax effect for hero images | |
| window.addEventListener('scroll', () => { | |
| const scrolled = window.pageYOffset; | |
| const parallaxElements = document.querySelectorAll('.parallax'); | |
| parallaxElements.forEach(element => { | |
| const speed = element.dataset.speed || 0.5; | |
| element.style.transform = `translateY(${scrolled * speed}px)`; | |
| }); | |
| }); | |
| // Initialize on page load | |
| document.addEventListener('DOMContentLoaded', () => { | |
| feather.replace(); | |
| // Add loading animation | |
| document.body.classList.add('animate-fade-in'); | |
| // Initialize tooltips or other interactive elements | |
| console.log('Khmer23 Luxury Real Estate Platform Initialized'); | |
| }); | |
| // Performance optimization - lazy loading 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.remove('lazy'); | |
| imageObserver.unobserve(img); | |
| } | |
| }); | |
| }); | |
| document.querySelectorAll('img[data-src]').forEach(img => { | |
| imageObserver.observe(img); | |
| }); | |
| } |