// Mobile Navigation Toggle function initMobileMenu() { const navToggle = document.getElementById('nav-toggle'); const navMenu = document.getElementById('nav-menu'); if (navToggle && navMenu) { navToggle.addEventListener('click', () => { navMenu.classList.toggle('active'); navToggle.classList.toggle('active'); }); // Close mobile menu when clicking on a link const navLinks = navMenu.querySelectorAll('.nav-link'); navLinks.forEach(link => { link.addEventListener('click', () => { navMenu.classList.remove('active'); navToggle.classList.remove('active'); }); }); // Close mobile menu when clicking outside document.addEventListener('click', (e) => { if (!navToggle.contains(e.target as Node) && !navMenu.contains(e.target as Node)) { navMenu.classList.remove('active'); navToggle.classList.remove('active'); } }); } } // Contact Modal Functionality function initContactModal() { const modal = document.getElementById('contact-modal'); const modalClose = document.getElementById('modal-close'); const contactButtons = document.querySelectorAll('a[href="#contacto"]'); // Open modal when clicking contact buttons contactButtons.forEach(button => { button.addEventListener('click', (e) => { e.preventDefault(); if (modal) { modal.classList.add('active'); document.body.style.overflow = 'hidden'; } }); }); // Close modal when clicking close button if (modalClose && modal) { modalClose.addEventListener('click', () => { modal.classList.remove('active'); document.body.style.overflow = 'auto'; }); } // Close modal when clicking outside if (modal) { modal.addEventListener('click', (e) => { if (e.target === modal) { modal.classList.remove('active'); document.body.style.overflow = 'auto'; } }); } // Close modal with Escape key document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modal && modal.classList.contains('active')) { modal.classList.remove('active'); document.body.style.overflow = 'auto'; } }); } // Form Handling function initFormHandling() { const forms = document.querySelectorAll('form'); forms.forEach(form => { form.addEventListener('submit', async (e) => { e.preventDefault(); const formData = new FormData(form); const data = Object.fromEntries(formData.entries()); // Show loading state const submitButton = form.querySelector('button[type="submit"]') as HTMLButtonElement; const originalText = submitButton.textContent; submitButton.textContent = 'Enviando...'; submitButton.disabled = true; try { // Here you would normally send the data to your server console.log('Form data:', data); // Simulate API call await new Promise(resolve => setTimeout(resolve, 1000)); // Show success message showNotification('¡Mensaje enviado correctamente! Nos pondremos en contacto contigo pronto.', 'success'); // Reset form form.reset(); // Close modal if it's the modal form const modal = document.getElementById('contact-modal'); if (modal && modal.classList.contains('active')) { modal.classList.remove('active'); document.body.style.overflow = 'auto'; } } catch (error) { console.error('Error sending form:', error); showNotification('Hubo un error al enviar el mensaje. Por favor, intenta de nuevo.', 'error'); } finally { // Restore button state submitButton.textContent = originalText; submitButton.disabled = false; } }); }); } // Notification System function showNotification(message: string, type: 'success' | 'error' = 'success') { // Remove existing notifications const existingNotifications = document.querySelectorAll('.notification'); existingNotifications.forEach(notification => notification.remove()); const notification = document.createElement('div'); notification.className = `notification notification-${type}`; notification.innerHTML = `
${message}
`; // Add notification styles const style = document.createElement('style'); style.textContent = ` .notification { position: fixed; top: 20px; right: 20px; background: white; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); z-index: 3000; max-width: 400px; animation: slideInRight 0.3s ease-out; } .notification-success { border-left: 4px solid #4caf50; } .notification-error { border-left: 4px solid #f44336; } .notification-content { padding: 16px 20px; display: flex; align-items: center; justify-content: space-between; } .notification-message { color: #333; font-size: 14px; line-height: 1.4; } .notification-close { background: none; border: none; font-size: 20px; color: #999; cursor: pointer; margin-left: 15px; } .notification-close:hover { color: #333; } @keyframes slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOutRight { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } `; if (!document.querySelector('style[data-notification-styles]')) { style.setAttribute('data-notification-styles', 'true'); document.head.appendChild(style); } document.body.appendChild(notification); // Close notification const closeButton = notification.querySelector('.notification-close'); closeButton?.addEventListener('click', () => { notification.style.animation = 'slideOutRight 0.3s ease-out forwards'; setTimeout(() => notification.remove(), 300); }); // Auto close after 5 seconds setTimeout(() => { if (notification.parentNode) { notification.style.animation = 'slideOutRight 0.3s ease-out forwards'; setTimeout(() => notification.remove(), 300); } }, 5000); } // Smooth Scrolling for Anchor Links function initSmoothScrolling() { const anchors = document.querySelectorAll('a[href^="#"]'); anchors.forEach(anchor => { anchor.addEventListener('click', (e) => { e.preventDefault(); const targetId = anchor.getAttribute('href')?.substring(1); const targetElement = targetId ? document.getElementById(targetId) : null; if (targetElement) { const headerHeight = 80; // Account for fixed header const targetPosition = targetElement.offsetTop - headerHeight; window.scrollTo({ top: targetPosition, behavior: 'smooth' }); } }); }); } // Scroll Effects function initScrollEffects() { const header = document.querySelector('.header') as HTMLElement; window.addEventListener('scroll', () => { const scrollTop = window.pageYOffset || document.documentElement.scrollTop; // Header background on scroll if (scrollTop > 50) { header?.classList.add('scrolled'); } else { header?.classList.remove('scrolled'); } }); } // Intersection Observer for Animations function initAnimationsOnScroll() { 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'); } }); }, observerOptions); // Observe elements for animation const animateElements = document.querySelectorAll('.services-content, .software-content, .ai-content, .datacenter-content, .cyber-content, .compliance-content, .news-item'); animateElements.forEach(el => observer.observe(el)); } // Lazy Loading for Images function initLazyLoading() { const images = document.querySelectorAll('img[src]'); const imageObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target as HTMLImageElement; img.style.opacity = '1'; imageObserver.unobserve(img); } }); }); images.forEach(imgElement => { const img = imgElement as HTMLImageElement; img.style.opacity = '0'; img.style.transition = 'opacity 0.3s ease'; imageObserver.observe(img); // Show image when loaded img.addEventListener('load', () => { img.style.opacity = '1'; }); }); } // Initialize all functionality when DOM is loaded document.addEventListener('DOMContentLoaded', () => { initMobileMenu(); initContactModal(); initFormHandling(); initSmoothScrolling(); initScrollEffects(); initAnimationsOnScroll(); initLazyLoading(); }); // Handle window resize window.addEventListener('resize', () => { const navMenu = document.getElementById('nav-menu'); const navToggle = document.getElementById('nav-toggle'); // Close mobile menu on resize to desktop if (window.innerWidth > 768) { navMenu?.classList.remove('active'); navToggle?.classList.remove('active'); } }); // Add additional styles for scroll effects and animations const additionalStyles = document.createElement('style'); additionalStyles.textContent = ` .header.scrolled { background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); } .animate { animation: fadeInUp 1s ease-out; } @media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } `; document.head.appendChild(additionalStyles); export {};