// Main JavaScript file for Finger Web Flask App document.addEventListener('DOMContentLoaded', function() { // Initialize all functionality when DOM is loaded initializeFormValidation(); initializeNavigation(); initializeAnimations(); initializeTooltips(); }); // Form validation and enhancement function initializeFormValidation() { const forms = document.querySelectorAll('form'); forms.forEach(form => { form.addEventListener('submit', function(event) { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); } else { // Add loading state to submit button const submitBtn = form.querySelector('button[type="submit"]'); if (submitBtn) { submitBtn.classList.add('loading'); submitBtn.disabled = true; } } form.classList.add('was-validated'); }); // Real-time validation feedback const inputs = form.querySelectorAll('input, textarea'); inputs.forEach(input => { input.addEventListener('blur', function() { if (this.checkValidity()) { this.classList.remove('is-invalid'); this.classList.add('is-valid'); } else { this.classList.remove('is-valid'); this.classList.add('is-invalid'); } }); }); }); } // Navigation enhancements function initializeNavigation() { // Highlight current page in navigation const currentPath = window.location.pathname; const navLinks = document.querySelectorAll('.navbar-nav .nav-link'); navLinks.forEach(link => { if (link.getAttribute('href') === currentPath) { link.classList.add('active'); } }); // Smooth scrolling for anchor links const anchorLinks = document.querySelectorAll('a[href^="#"]'); anchorLinks.forEach(link => { link.addEventListener('click', function(e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); } // Animation and visual enhancements function initializeAnimations() { // Fade in cards on scroll const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver(function(entries) { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; } }); }, observerOptions); // Observe all cards const cards = document.querySelectorAll('.card'); cards.forEach(card => { card.style.opacity = '0'; card.style.transform = 'translateY(20px)'; card.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; observer.observe(card); }); } // Initialize Bootstrap tooltips function initializeTooltips() { // Enable tooltips if Bootstrap is available if (typeof bootstrap !== 'undefined') { const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.map(function(tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); } } // Utility functions function showNotification(message, type = 'info') { // Create a notification element const notification = document.createElement('div'); notification.className = `alert alert-${type} alert-dismissible fade show position-fixed`; notification.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;'; notification.innerHTML = ` ${message} `; document.body.appendChild(notification); // Auto-remove after 5 seconds setTimeout(() => { if (notification.parentNode) { notification.remove(); } }, 5000); } // API interaction helpers async function fetchAPI(endpoint) { try { const response = await fetch(endpoint); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error('API fetch error:', error); showNotification('Failed to fetch data from API', 'danger'); return null; } } // Form submission with AJAX (optional enhancement) function submitFormAjax(form, successCallback) { const formData = new FormData(form); fetch(form.action, { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { if (data.success) { showNotification(data.message, 'success'); if (successCallback) successCallback(data); } else { showNotification(data.message || 'An error occurred', 'danger'); } }) .catch(error => { console.error('Form submission error:', error); showNotification('Failed to submit form', 'danger'); }) .finally(() => { // Remove loading state const submitBtn = form.querySelector('button[type="submit"]'); if (submitBtn) { submitBtn.classList.remove('loading'); submitBtn.disabled = false; } }); } // Keyboard shortcuts document.addEventListener('keydown', function(e) { // Alt + H for Home if (e.altKey && e.key === 'h') { e.preventDefault(); window.location.href = '/'; } // Alt + A for About if (e.altKey && e.key === 'a') { e.preventDefault(); window.location.href = '/about'; } // Alt + F for Finger if (e.altKey && e.key === 'f') { e.preventDefault(); window.location.href = '/finger'; } }); // Console welcome message console.log('%c🚀 Finger Web Flask App', 'color: #007bff; font-size: 16px; font-weight: bold;'); console.log('%cWelcome to the developer console!', 'color: #6c757d;'); console.log('%cKeyboard shortcuts: Alt+H (Home), Alt+A (About), Alt+F (Finger)', 'color: #6c757d;');