first commit

This commit is contained in:
pmb
2025-06-26 12:44:28 -07:00
commit b672b249a6
15 changed files with 1476 additions and 0 deletions
+205
View File
@@ -0,0 +1,205 @@
// 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;');