mirror of https://github.com/kortix-ai/suna.git
271 lines
9.9 KiB
JavaScript
271 lines
9.9 KiB
JavaScript
// Wait for the DOM to be fully loaded
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Dark mode toggle functionality
|
|
const darkModeToggle = document.getElementById('darkModeToggle');
|
|
|
|
// Check for saved theme preference or use device preference
|
|
const savedTheme = localStorage.getItem('theme');
|
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
|
|
// Set initial theme
|
|
if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
|
|
document.documentElement.setAttribute('data-theme', 'dark');
|
|
}
|
|
|
|
// Toggle dark mode
|
|
darkModeToggle.addEventListener('click', function() {
|
|
const currentTheme = document.documentElement.getAttribute('data-theme');
|
|
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
|
|
|
document.documentElement.setAttribute('data-theme', newTheme);
|
|
localStorage.setItem('theme', newTheme);
|
|
});
|
|
// Mobile menu toggle
|
|
const mobileMenuBtn = document.querySelector('.mobile-menu-btn');
|
|
const navLinks = document.querySelector('.nav-links');
|
|
|
|
if (mobileMenuBtn) {
|
|
mobileMenuBtn.addEventListener('click', function() {
|
|
// Create mobile menu if it doesn't exist
|
|
if (!document.querySelector('.mobile-nav')) {
|
|
const mobileNav = document.createElement('div');
|
|
mobileNav.className = 'mobile-nav';
|
|
|
|
// Clone the navigation links
|
|
const navLinksClone = navLinks.cloneNode(true);
|
|
|
|
// Get all links
|
|
const links = navLinksClone.querySelectorAll('a');
|
|
|
|
// Convert to individual links for mobile
|
|
links.forEach(link => {
|
|
mobileNav.appendChild(link);
|
|
});
|
|
|
|
// Add to the DOM
|
|
document.querySelector('header').appendChild(mobileNav);
|
|
}
|
|
|
|
// Toggle the mobile menu
|
|
const mobileNav = document.querySelector('.mobile-nav');
|
|
mobileNav.classList.toggle('active');
|
|
|
|
// Animate the hamburger icon
|
|
const spans = this.querySelectorAll('span');
|
|
if (mobileNav.classList.contains('active')) {
|
|
spans[0].style.transform = 'rotate(45deg) translate(5px, 5px)';
|
|
spans[1].style.opacity = '0';
|
|
spans[2].style.transform = 'rotate(-45deg) translate(7px, -6px)';
|
|
} else {
|
|
spans[0].style.transform = 'none';
|
|
spans[1].style.opacity = '1';
|
|
spans[2].style.transform = 'none';
|
|
}
|
|
});
|
|
}
|
|
|
|
// Smooth scrolling for anchor links
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
|
|
const targetId = this.getAttribute('href');
|
|
if (targetId === '#') return;
|
|
|
|
const targetElement = document.querySelector(targetId);
|
|
if (targetElement) {
|
|
// Close mobile menu if open
|
|
const mobileNav = document.querySelector('.mobile-nav');
|
|
if (mobileNav && mobileNav.classList.contains('active')) {
|
|
mobileMenuBtn.click();
|
|
}
|
|
|
|
// Scroll to the target element
|
|
window.scrollTo({
|
|
top: targetElement.offsetTop - 80,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
// Form validation
|
|
const contactForm = document.getElementById('contactForm');
|
|
|
|
if (contactForm) {
|
|
contactForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
// Get form values
|
|
const name = document.getElementById('name').value.trim();
|
|
const email = document.getElementById('email').value.trim();
|
|
const message = document.getElementById('message').value.trim();
|
|
|
|
// Simple validation
|
|
if (name === '') {
|
|
showError('name', 'Please enter your name');
|
|
return;
|
|
}
|
|
|
|
if (email === '') {
|
|
showError('email', 'Please enter your email');
|
|
return;
|
|
}
|
|
|
|
if (!isValidEmail(email)) {
|
|
showError('email', 'Please enter a valid email');
|
|
return;
|
|
}
|
|
|
|
if (message === '') {
|
|
showError('message', 'Please enter your message');
|
|
return;
|
|
}
|
|
|
|
// If all validations pass, show success message
|
|
contactForm.innerHTML = `
|
|
<div style="text-align: center; padding: 40px 0;">
|
|
<i class="fas fa-check-circle" style="font-size: 48px; color: #10b981; margin-bottom: 20px;"></i>
|
|
<h3>Thank You!</h3>
|
|
<p>Your message has been sent successfully. We'll get back to you soon.</p>
|
|
</div>
|
|
`;
|
|
});
|
|
}
|
|
|
|
// Helper function to show error messages
|
|
function showError(fieldId, message) {
|
|
const field = document.getElementById(fieldId);
|
|
|
|
// Remove any existing error message
|
|
const existingError = field.parentElement.querySelector('.error-message');
|
|
if (existingError) {
|
|
existingError.remove();
|
|
}
|
|
|
|
// Create and add error message
|
|
const errorDiv = document.createElement('div');
|
|
errorDiv.className = 'error-message';
|
|
errorDiv.textContent = message;
|
|
errorDiv.style.color = '#ef4444';
|
|
errorDiv.style.fontSize = '0.875rem';
|
|
errorDiv.style.marginTop = '5px';
|
|
|
|
field.parentElement.appendChild(errorDiv);
|
|
|
|
// Highlight the field
|
|
field.style.borderColor = '#ef4444';
|
|
|
|
// Focus on the field
|
|
field.focus();
|
|
|
|
// Remove error when user starts typing
|
|
field.addEventListener('input', function() {
|
|
const error = this.parentElement.querySelector('.error-message');
|
|
if (error) {
|
|
error.remove();
|
|
}
|
|
this.style.borderColor = '';
|
|
});
|
|
}
|
|
|
|
// Helper function to validate email
|
|
function isValidEmail(email) {
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
return emailRegex.test(email);
|
|
}
|
|
|
|
// Add animation on scroll
|
|
const animateElements = document.querySelectorAll('.feature-card, .about-image, .about-text');
|
|
|
|
// Check if IntersectionObserver is supported
|
|
if ('IntersectionObserver' in window) {
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('animated');
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, { threshold: 0.1 });
|
|
|
|
animateElements.forEach(element => {
|
|
element.style.opacity = '0';
|
|
element.style.transform = 'translateY(20px)';
|
|
element.style.transition = 'opacity 0.5s ease, transform 0.5s ease';
|
|
observer.observe(element);
|
|
});
|
|
}
|
|
|
|
// Add animated class to elements
|
|
document.addEventListener('scroll', function() {
|
|
document.querySelectorAll('.animated').forEach(element => {
|
|
element.style.opacity = '1';
|
|
element.style.transform = 'translateY(0)';
|
|
});
|
|
});
|
|
|
|
// Testimonial slider functionality
|
|
const testimonialTrack = document.querySelector('.testimonial-track');
|
|
const testimonialCards = document.querySelectorAll('.testimonial-card');
|
|
const dots = document.querySelectorAll('.dot');
|
|
const prevBtn = document.querySelector('.prev-btn');
|
|
const nextBtn = document.querySelector('.next-btn');
|
|
|
|
if (testimonialTrack && testimonialCards.length > 0) {
|
|
let currentIndex = 0;
|
|
const cardCount = testimonialCards.length;
|
|
|
|
// Function to update the slider position
|
|
function updateSlider() {
|
|
testimonialTrack.style.transform = `translateX(-${currentIndex * 100}%)`;
|
|
|
|
// Update active dot
|
|
dots.forEach((dot, index) => {
|
|
dot.classList.toggle('active', index === currentIndex);
|
|
});
|
|
}
|
|
|
|
// Event listeners for navigation buttons
|
|
if (prevBtn) {
|
|
prevBtn.addEventListener('click', function() {
|
|
currentIndex = (currentIndex - 1 + cardCount) % cardCount;
|
|
updateSlider();
|
|
});
|
|
}
|
|
|
|
if (nextBtn) {
|
|
nextBtn.addEventListener('click', function() {
|
|
currentIndex = (currentIndex + 1) % cardCount;
|
|
updateSlider();
|
|
});
|
|
}
|
|
|
|
// Event listeners for dots
|
|
dots.forEach((dot, index) => {
|
|
dot.addEventListener('click', function() {
|
|
currentIndex = index;
|
|
updateSlider();
|
|
});
|
|
});
|
|
|
|
// Auto-advance the slider every 5 seconds
|
|
let sliderInterval = setInterval(function() {
|
|
currentIndex = (currentIndex + 1) % cardCount;
|
|
updateSlider();
|
|
}, 5000);
|
|
|
|
// Pause auto-advance when hovering over the slider
|
|
testimonialTrack.addEventListener('mouseenter', function() {
|
|
clearInterval(sliderInterval);
|
|
});
|
|
|
|
// Resume auto-advance when mouse leaves the slider
|
|
testimonialTrack.addEventListener('mouseleave', function() {
|
|
sliderInterval = setInterval(function() {
|
|
currentIndex = (currentIndex + 1) % cardCount;
|
|
updateSlider();
|
|
}, 5000);
|
|
});
|
|
}
|
|
}); |