// 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 {};