206 lines
6.4 KiB
JavaScript
206 lines
6.4 KiB
JavaScript
// 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}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
`;
|
|
|
|
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;');
|