first commit
This commit is contained in:
@@ -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;');
|
||||
Reference in New Issue
Block a user