flen-crypto's picture
| Expert(s) | Possible Keywords | Question | Plan |
d24bca5 verified
// FitForge Pro - Security-First JavaScript
// Compliance: UK GDPR, OWASP ASVS, SLSA, White-Hat Standards
document.addEventListener('DOMContentLoaded', function() {
// Initialize all security-first components
initSecurityHeaders();
initSmoothScrolling();
initMobileMenu();
initFormHandling();
initScrollAnimations();
initClassSchedule();
initImageLazyLoading();
initParallaxEffect();
initPrivacyControls();
initRateLimiting();
console.log('FitForge Pro Security-First initialized 🔒🔥');
});
// Initialize security headers and CSP compliance
function initSecurityHeaders() {
// Ensure all external scripts are from trusted sources
const externalScripts = document.querySelectorAll('script[src]');
externalScripts.forEach(script => {
const src = script.getAttribute('src');
// Validate external script sources
if (src && !src.startsWith('/') && !src.includes('cdn.tailwindcss.com') &&
!src.includes('cdn.jsdelivr.net') &&
!src.includes('unpkg.com') &&
!src.includes('huggingface.co')) {
console.warn('Untrusted script source detected:', src);
}
});
// Add security badge
addSecurityBadge();
}
// Add security compliance badge
function addSecurityBadge() {
const badge = document.createElement('div');
badge.className = 'fixed bottom-4 right-4 z-50 bg-green-600 text-white px-3 py-2 rounded-lg text-sm font-semibold hidden print:hidden';
badge.innerHTML = `
<div class="flex items-center gap-2">
<i data-feather="shield" class="w-4 h-4"></i>
<span>Security-First Compliant</span>
`;
document.body.appendChild(badge);
// Show on hover for transparency
badge.addEventListener('mouseenter', () => {
badge.classList.remove('hidden');
});
badge.addEventListener('mouseleave', () => {
badge.classList.add('hidden');
});
}
// Smooth scrolling for anchor links
function initSmoothScrolling() {
const links = document.querySelectorAll('a[href^="#"]');
links.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
const targetSection = document.querySelector(targetId);
if (targetSection) {
targetSection.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
// Update active nav link
updateActiveNavLink(targetId);
}
});
});
}
// Update active navigation link
function updateActiveNavLink(targetId) {
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === targetId) {
link.classList.add('active');
}
});
}
// Mobile menu functionality
function initMobileMenu() {
const mobileMenuToggle = document.querySelector('.mobile-menu-toggle');
const mobileMenu = document.querySelector('.mobile-menu');
const mobileMenuClose = document.querySelector('.mobile-menu-close');
if (mobileMenuToggle && mobileMenu) {
mobileMenuToggle.addEventListener('click', function() {
mobileMenu.classList.add('active');
document.body.style.overflow = 'hidden';
});
mobileMenuClose.addEventListener('click', function() {
mobileMenu.classList.remove('active');
document.body.style.overflow = '';
});
// Close menu when clicking on links
const mobileLinks = mobileMenu.querySelectorAll('a');
mobileLinks.forEach(link => {
link.addEventListener('click', function() {
mobileMenu.classList.remove('active');
document.body.style.overflow = '';
});
});
}
}
// Form handling with security controls
function initFormHandling() {
const forms = document.querySelectorAll('form');
forms.forEach(form => {
// Add form validation attributes
form.setAttribute('novalidate', 'true'); // Use custom validation
form.setAttribute('autocomplete', 'on');
form.addEventListener('submit', function(e) {
e.preventDefault();
// Validate all inputs before submission
const inputs = form.querySelectorAll('input[required], select[required]');
let isValid = true;
inputs.forEach(input => {
if (!validateInput(input)) {
isValid = false;
}
});
if (!isValid) {
showNotification('Please fill in all required fields correctly.', 'error');
return;
}
const formData = new FormData(form);
const formType = form.id || 'general';
// Show loading state
const submitBtn = form.querySelector('button[type="submit"]');
const originalBtnText = submitBtn.textContent;
submitBtn.textContent = 'Processing...';
submitBtn.disabled = true;
// Rate limit check
if (!checkRateLimit('form-submission')) {
showNotification('Please wait before submitting again.', 'warning');
submitBtn.textContent = originalBtnText;
submitBtn.disabled = false;
return;
}
// Simulate secure form submission
setTimeout(() => {
showNotification('Success! We\'ll contact you soon.', 'success');
form.reset();
submitBtn.textContent = originalBtnText;
submitBtn.disabled = false;
// Track conversion with privacy controls
trackConversion(formType);
}, 2000);
});
// Real-time validation with security focus
const inputs = form.querySelectorAll('input, select, textarea');
inputs.forEach(input => {
input.addEventListener('blur', function() {
validateInput(this);
});
input.addEventListener('input', function() {
clearValidationError(this);
});
});
});
}
// Input validation with OWASP ASVS compliance
function validateInput(input) {
const value = input.value.trim();
const type = input.type;
const name = input.name || input.getAttribute('aria-label') || 'field';
let isValid = true;
let errorMessage = '';
// Required field validation
if (input.hasAttribute('required') && !value) {
isValid = false;
errorMessage = 'This field is required';
}
// Email validation with strict regex
if ((type === 'email' || name.toLowerCase().includes('email')) && value) {
const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])+$/;
if (!emailRegex.test(value)) {
isValid = false;
errorMessage = 'Please enter a valid email address';
}
}
// Phone validation with international support
if ((type === 'tel' || name.toLowerCase().includes('phone')) && value) {
const phoneRegex = /^[\+]?[1-9][\d]{0,15}$/;
if (!phoneRegex.test(value.replace(/\s/g, ''))) {
isValid = false;
errorMessage = 'Please enter a valid phone number';
}
}
// Input sanitization for security
if (value) {
// Prevent potential XSS in form inputs
const sanitizedValue = value.replace(/</g, '&lt;').replace(/>/g, '&gt;');
input.value = sanitizedValue;
}
// Update UI with accessibility
if (isValid) {
input.classList.remove('input-error');
input.classList.add('input-success');
input.setAttribute('aria-invalid', 'false');
} else {
input.classList.remove('input-success');
input.classList.add('input-error');
input.setAttribute('aria-invalid', 'true');
input.setAttribute('aria-describedby', `${input.id}-error`);
showFieldError(input, errorMessage);
}
return isValid;
}
// Show field error
function showFieldError(input, message) {
removeExistingError(input);
const errorDiv = document.createElement('div');
errorDiv.className = 'field-error text-red-400 text-sm mt-1';
errorDiv.textContent = message;
input.parentNode.appendChild(errorDiv);
}
// Clear validation error
function clearValidationError(input) {
input.classList.remove('input-error', 'input-success');
removeExistingError(input);
}
// Remove existing error message
function removeExistingError(input) {
const existingError = input.parentNode.querySelector('.field-error');
if (existingError) {
existingError.remove();
}
}
// Scroll animations
function initScrollAnimations() {
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-fade-in-up');
}
});
}, observerOptions);
// Observe elements
const animateElements = document.querySelectorAll('.class-card, .trainer-card, .pricing-card, .transformation-card, .facility-card');
animateElements.forEach(el => {
observer.observe(el);
});
}
// Class schedule data and interactions
function initClassSchedule() {
const classCards = document.querySelectorAll('.class-card');
classCards.forEach(card => {
card.addEventListener('click', function() {
const className = this.querySelector('h3').textContent;
showClassDetails(className);
});
});
}
// Show class details modal
function showClassDetails(className) {
const modal = document.createElement('div');
modal.className = 'fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 p-4';
modal.innerHTML = `
<div class="bg-gray-800 rounded-lg p-8 max-w-md w-full">
<h3 class="text-2xl font-bold text-orange-400 mb-4">${className}</h3>
<p class="text-gray-300 mb-6">Ready to join this class? Sign up for your free trial to get started!</p>
<div class="flex gap-4">
<button class="flex-1 bg-orange-500 hover:bg-orange-600 py-2 px-4 rounded-lg font-semibold transition-all" onclick="closeModal(this)">
Sign Up Now
</button>
<button class="flex-1 border border-gray-600 hover:bg-gray-700 py-2 px-4 rounded-lg font-semibold transition-all" onclick="closeModal(this)">
Maybe Later
</button>
</div>
</div>
`;
document.body.appendChild(modal);
document.body.style.overflow = 'hidden';
// Close on backdrop click
modal.addEventListener('click', function(e) {
if (e.target === modal) {
closeModal(modal);
}
});
}
// Close modal
function closeModal(element) {
const modal = element.closest('.fixed');
if (modal) {
modal.remove();
document.body.style.overflow = '';
}
}
// Lazy loading for images
function initImageLazyLoading() {
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
observer.unobserve(img);
}
});
});
images.forEach(img => imageObserver.observe(img));
}
// Parallax effect
function initParallaxEffect() {
const parallaxElements = document.querySelectorAll('.parallax-bg');
window.addEventListener('scroll', () => {
const scrolled = window.pageYOffset;
parallaxElements.forEach(element => {
const rate = scrolled * -0.5;
element.style.transform = `translateY(${rate}px)`;
});
});
}
// Notification system
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 p-4 rounded-lg z-50 max-w-sm animate-fade-in-up`;
const bgColor = {
success: 'bg-green-600',
error: 'bg-red-600',
warning: 'bg-yellow-600',
info: 'bg-blue-600'
}[type];
notification.classList.add(bgColor);
notification.innerHTML = `
<div class="flex items-center justify-between">
<span>${message}</span>
<button class="ml-4 text-white hover:text-gray-200" onclick="this.parentElement.parentElement.remove()">
<i data-feather="x" class="w-5 h-5"></i>
</button>
</div>
`;
document.body.appendChild(notification);
// Auto-remove after 5 seconds
setTimeout(() => {
if (notification.parentElement) {
notification.remove();
}
}, 5000);
// Re-render feather icons
if (typeof feather !== 'undefined') {
feather.replace();
}
}
// Track conversions with privacy-by-design
function trackConversion(formType) {
// Anonymized tracking - no PII
const timestamp = Date.now();
const eventId = `conversion_${formType}_${timestamp}`;
console.log(`Secure conversion tracked: ${formType}`, eventId);
// UK GDPR compliant - only track with consent
// In production, implement proper consent management
if (typeof gtag !== 'undefined') {
gtag('event', 'conversion', {
'send_to': 'AW-XXXXXX/XXXXX',
'anonymize_ip': true,
'allow_ad_personalization_signals': false
});
}
}
// Utility functions
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
function throttle(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// Dynamic year in footer
function updateFooterYear() {
const yearElements = document.querySelectorAll('.current-year');
yearElements.forEach(el => {
el.textContent = new Date().getFullYear();
});
}
updateFooterYear();
// Performance monitoring with security telemetry
window.addEventListener('load', function() {
const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart;
console.log(`Secure page loaded in ${loadTime}ms`);
// Track Core Web Vitals with privacy
if ('webVitals' in window) {
webVitals.getCLS(console.log);
webVitals.getFID(console.log);
webVitals.getFCP(console.log);
webVitals.getLCP(console.log);
webVitals.getFID(console.log);
webVitals.getTTFB(console.log);
}
});
// Service Worker registration for PWA features
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
console.log('ServiceWorker registration successful');
}, function(err) {
console.log('ServiceWorker registration failed: ', err);
});
});
}
// Dark mode detection
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark-mode');
}
// Online/Offline detection
window.addEventListener('online', function() {
showNotification('You\'re back online!', 'success');
});
window.addEventListener('offline', function() {
showNotification('You\'re offline. Some features may not work.', 'warning');
});
// Privacy controls for UK GDPR compliance
function initPrivacyControls() {
// Ensure no PII in logs
const originalConsoleLog = console.log;
console.log = function(...args) {
// Sanitize any potential PII from console logs
const sanitizedArgs = args.map(arg => {
if (typeof arg === 'string') {
// Remove potential email patterns from logs
return arg.replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, '[EMAIL_REDACTED]');
}
return arg;
});
originalConsoleLog.apply(console, sanitizedArgs);
};
}
// Rate limiting for abuse prevention
function initRateLimiting() {
window.rateLimitStore = window.rateLimitStore || new Map();
// Clean old entries every minute
setInterval(() => {
const now = Date.now();
window.rateLimitStore.forEach((timestamp, key) => {
if (now - timestamp > 60000) { // 1 minute
window.rateLimitStore.delete(key);
}
}, 60000);
}
// Check rate limit for actions
function checkRateLimit(action) {
const key = `${action}_${getUserIdentifier()}`;
const now = Date.now();
if (window.rateLimitStore.has(key)) {
const lastAttempt = window.rateLimitStore.get(key);
if (now - lastAttempt < 5000) { // 5 second cooldown
return false;
}
window.rateLimitStore.set(key, now);
return true;
}
function getUserIdentifier() {
// Use session-based identifier, not personal data
return 'session_' + (sessionStorage.getItem('sessionId') || 'default');
return true;
}
// Export security-first functions for use in components
window.FitForgeApp = {
showNotification,
trackConversion,
validateInput,
closeModal,
debounce,
throttle,
checkRateLimit
};