281 lines
8.2 KiB
JavaScript
281 lines
8.2 KiB
JavaScript
/**
|
|
* EzSepa Bank Payment Landing Page - Main JavaScript
|
|
* Handles mobile navigation, smooth scrolling, and analytics
|
|
*/
|
|
|
|
import { initHeroScene2D } from './hero-scene-2d.js';
|
|
|
|
// Mobile menu toggle
|
|
const initMobileMenu = () => {
|
|
const menuButton = document.getElementById('mobile-menu-button');
|
|
const mobileMenu = document.getElementById('mobile-menu');
|
|
const menuOpenIcon = menuButton?.querySelector('.menu-open');
|
|
const menuCloseIcon = menuButton?.querySelector('.menu-close');
|
|
|
|
if (!menuButton || !mobileMenu) return;
|
|
|
|
menuButton.addEventListener('click', () => {
|
|
const isExpanded = menuButton.getAttribute('aria-expanded') === 'true';
|
|
|
|
// Toggle menu visibility
|
|
mobileMenu.classList.toggle('hidden');
|
|
|
|
// Toggle icons
|
|
menuOpenIcon?.classList.toggle('hidden');
|
|
menuCloseIcon?.classList.toggle('hidden');
|
|
|
|
// Update aria-expanded
|
|
menuButton.setAttribute('aria-expanded', !isExpanded);
|
|
});
|
|
|
|
// Close mobile menu when clicking on a link
|
|
const mobileMenuLinks = mobileMenu.querySelectorAll('a');
|
|
mobileMenuLinks.forEach(link => {
|
|
link.addEventListener('click', () => {
|
|
mobileMenu.classList.add('hidden');
|
|
menuOpenIcon?.classList.remove('hidden');
|
|
menuCloseIcon?.classList.add('hidden');
|
|
menuButton.setAttribute('aria-expanded', 'false');
|
|
});
|
|
});
|
|
|
|
// Close mobile menu when clicking outside
|
|
document.addEventListener('click', (event) => {
|
|
const isClickInsideMenu = mobileMenu.contains(event.target);
|
|
const isClickOnButton = menuButton.contains(event.target);
|
|
|
|
if (!isClickInsideMenu && !isClickOnButton && !mobileMenu.classList.contains('hidden')) {
|
|
mobileMenu.classList.add('hidden');
|
|
menuOpenIcon?.classList.remove('hidden');
|
|
menuCloseIcon?.classList.add('hidden');
|
|
menuButton.setAttribute('aria-expanded', 'false');
|
|
}
|
|
});
|
|
};
|
|
|
|
// Smooth scroll for anchor links
|
|
const initSmoothScroll = () => {
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', function (e) {
|
|
const href = this.getAttribute('href');
|
|
|
|
// Skip if href is just "#"
|
|
if (href === '#') return;
|
|
|
|
const targetId = href.substring(1);
|
|
const targetElement = document.getElementById(targetId);
|
|
|
|
if (targetElement) {
|
|
e.preventDefault();
|
|
|
|
// Calculate offset for fixed header
|
|
const headerOffset = 80;
|
|
const elementPosition = targetElement.getBoundingClientRect().top;
|
|
const offsetPosition = elementPosition + window.pageYOffset - headerOffset;
|
|
|
|
window.scrollTo({
|
|
top: offsetPosition,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
// Add shadow to nav on scroll
|
|
const initNavShadow = () => {
|
|
const nav = document.querySelector('nav');
|
|
if (!nav) return;
|
|
|
|
const handleScroll = () => {
|
|
if (window.scrollY > 10) {
|
|
nav.classList.add('shadow-md');
|
|
} else {
|
|
nav.classList.remove('shadow-md');
|
|
}
|
|
};
|
|
|
|
window.addEventListener('scroll', handleScroll);
|
|
handleScroll(); // Initial check
|
|
};
|
|
|
|
// Animate elements on scroll (Intersection Observer)
|
|
const initScrollAnimations = () => {
|
|
const observerOptions = {
|
|
threshold: 0.1,
|
|
rootMargin: '0px 0px -100px 0px'
|
|
};
|
|
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('animate-fade-in-up');
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, observerOptions);
|
|
|
|
// Observe feature cards, pricing cards, etc.
|
|
const animatedElements = document.querySelectorAll('.card, .grid > div');
|
|
animatedElements.forEach(el => observer.observe(el));
|
|
};
|
|
|
|
// Analytics stub - replace with your actual analytics implementation
|
|
const initAnalytics = () => {
|
|
// Placeholder for analytics tracking
|
|
window._ezsepa = window._ezsepa || {
|
|
track: (eventName, properties = {}) => {
|
|
console.log('📊 Analytics Event:', eventName, properties);
|
|
|
|
// Replace this with your actual analytics provider
|
|
// Examples:
|
|
// - Google Analytics: gtag('event', eventName, properties);
|
|
// - Mixpanel: mixpanel.track(eventName, properties);
|
|
// - Segment: analytics.track(eventName, properties);
|
|
// - Plausible: plausible(eventName, { props: properties });
|
|
}
|
|
};
|
|
|
|
// Track page view
|
|
window._ezsepa.track('Page View', {
|
|
page: window.location.pathname,
|
|
referrer: document.referrer,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
// Track CTA clicks
|
|
const ctaButtons = document.querySelectorAll('a[href*="{INSTALL_LINK}"]');
|
|
ctaButtons.forEach(button => {
|
|
button.addEventListener('click', () => {
|
|
window._ezsepa.track('CTA Clicked', {
|
|
location: button.closest('section')?.id || 'unknown',
|
|
text: button.textContent.trim(),
|
|
url: button.href
|
|
});
|
|
});
|
|
});
|
|
|
|
// Track demo link clicks
|
|
const demoLinks = document.querySelectorAll('a[href*="{DEMO_LINK}"]');
|
|
demoLinks.forEach(link => {
|
|
link.addEventListener('click', () => {
|
|
window._ezsepa.track('Demo Link Clicked', {
|
|
location: link.closest('section')?.id || 'unknown',
|
|
text: link.textContent.trim()
|
|
});
|
|
});
|
|
});
|
|
|
|
// Track section visibility
|
|
const sections = document.querySelectorAll('section[id]');
|
|
const sectionObserver = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
window._ezsepa.track('Section Viewed', {
|
|
section: entry.target.id
|
|
});
|
|
}
|
|
});
|
|
}, { threshold: 0.5 });
|
|
|
|
sections.forEach(section => sectionObserver.observe(section));
|
|
};
|
|
|
|
// Performance monitoring
|
|
const initPerformanceMonitoring = () => {
|
|
if ('PerformanceObserver' in window) {
|
|
// Largest Contentful Paint (LCP)
|
|
const lcpObserver = new PerformanceObserver((list) => {
|
|
const entries = list.getEntries();
|
|
const lastEntry = entries[entries.length - 1];
|
|
console.log('⚡ LCP:', lastEntry.renderTime || lastEntry.loadTime);
|
|
});
|
|
lcpObserver.observe({ entryTypes: ['largest-contentful-paint'] });
|
|
|
|
// First Input Delay (FID)
|
|
const fidObserver = new PerformanceObserver((list) => {
|
|
list.getEntries().forEach((entry) => {
|
|
console.log('⚡ FID:', entry.processingStart - entry.startTime);
|
|
});
|
|
});
|
|
fidObserver.observe({ entryTypes: ['first-input'] });
|
|
|
|
// Cumulative Layout Shift (CLS)
|
|
let clsValue = 0;
|
|
const clsObserver = new PerformanceObserver((list) => {
|
|
list.getEntries().forEach((entry) => {
|
|
if (!entry.hadRecentInput) {
|
|
clsValue += entry.value;
|
|
console.log('⚡ CLS:', clsValue);
|
|
}
|
|
});
|
|
});
|
|
clsObserver.observe({ entryTypes: ['layout-shift'] });
|
|
}
|
|
};
|
|
|
|
// Handle external links (open in new tab with security)
|
|
const initExternalLinks = () => {
|
|
const externalLinks = document.querySelectorAll('a[href^="http"]');
|
|
externalLinks.forEach(link => {
|
|
const url = new URL(link.href);
|
|
if (url.hostname !== window.location.hostname) {
|
|
link.setAttribute('target', '_blank');
|
|
link.setAttribute('rel', 'noopener noreferrer');
|
|
}
|
|
});
|
|
};
|
|
|
|
// Keyboard navigation improvements
|
|
const initKeyboardNav = () => {
|
|
// Add visible focus indicators for keyboard users
|
|
let isUsingMouse = false;
|
|
|
|
document.addEventListener('mousedown', () => {
|
|
isUsingMouse = true;
|
|
});
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Tab') {
|
|
isUsingMouse = false;
|
|
}
|
|
});
|
|
|
|
document.addEventListener('focusin', () => {
|
|
if (!isUsingMouse) {
|
|
document.body.classList.add('using-keyboard');
|
|
}
|
|
});
|
|
|
|
document.addEventListener('focusout', () => {
|
|
document.body.classList.remove('using-keyboard');
|
|
});
|
|
};
|
|
|
|
// Initialize all features when DOM is ready
|
|
const init = () => {
|
|
initMobileMenu();
|
|
initSmoothScroll();
|
|
initNavShadow();
|
|
initScrollAnimations();
|
|
initAnalytics();
|
|
initPerformanceMonitoring();
|
|
initExternalLinks();
|
|
initKeyboardNav();
|
|
|
|
// Initialize 2D hero scene
|
|
initHeroScene2D();
|
|
|
|
console.log('✅ EzSepa landing page initialized');
|
|
};
|
|
|
|
// Run initialization
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|
|
|
|
// Export for potential use in other scripts
|
|
export { initMobileMenu, initSmoothScroll, initAnalytics };
|