Download script.js from flen-crypto/fitforge-pro-transform-your-limits: direct link, hf CLI and curl.
- Browser
- Download file 18.4 kB
-
https://huggingface.co/spaces/flen-crypto/fitforge-pro-transform-your-limits/resolve/main/script.js
- Command line
-
hf download hf://spaces/flen-crypto/fitforge-pro-transform-your-limits/script.js
-
curl -L -o script.js https://huggingface.co/spaces/flen-crypto/fitforge-pro-transform-your-limits/resolve/main/script.js
18.4 kB
| // 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, '<').replace(/>/g, '>'); | |
| 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 | |
| }; | |