feat: added main.min.js
This commit is contained in:
		
							parent
							
								
									4af0fc7cfe
								
							
						
					
					
						commit
						06d19988c7
					
				
					 1 changed files with 175 additions and 0 deletions
				
			
		
							
								
								
									
										175
									
								
								static/js/main.min.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								static/js/main.min.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,175 @@ | ||||||
|  | (() => { | ||||||
|  |     'use strict'; | ||||||
|  |    | ||||||
|  |     const navbar = document.querySelector('.navbar'); | ||||||
|  |     const featureCards = document.querySelectorAll('.feature-card'); | ||||||
|  |     const newsletterCards = document.querySelectorAll('.newsletter-card'); | ||||||
|  |     const progressBar = document.querySelector('.reading-progress'); | ||||||
|  |     const emailInput = document.getElementById('email-input'); | ||||||
|  |     const notifyBtn = document.getElementById('notify-button'); | ||||||
|  |     const emailRE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | ||||||
|  |    | ||||||
|  |     document.addEventListener( | ||||||
|  |       'click', | ||||||
|  |       (e) => { | ||||||
|  |         const a = e.target.closest('a[href^="#"]'); | ||||||
|  |         if (!a) return; | ||||||
|  |    | ||||||
|  |         const href = a.getAttribute('href'); | ||||||
|  |         if (!href || href === '#') return; | ||||||
|  |    | ||||||
|  |         const target = document.querySelector(href); | ||||||
|  |         if (!target) return; | ||||||
|  |    | ||||||
|  |         e.preventDefault(); | ||||||
|  |         target.scrollIntoView({ behavior: 'smooth', block: 'start' }); | ||||||
|  |       }, | ||||||
|  |       { passive: false } | ||||||
|  |     ); | ||||||
|  |    | ||||||
|  |     if ('IntersectionObserver' in window) { | ||||||
|  |       const observer = new IntersectionObserver( | ||||||
|  |         (entries, obs) => { | ||||||
|  |           for (const entry of entries) { | ||||||
|  |             if (entry.isIntersecting) { | ||||||
|  |               entry.target.classList.add('is-visible'); | ||||||
|  |               obs.unobserve(entry.target); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         { threshold: 0.1, rootMargin: '0px 0px -50px 0px' } | ||||||
|  |       ); | ||||||
|  |    | ||||||
|  |       featureCards.forEach((card) => { | ||||||
|  |         card.classList.add('will-animate'); | ||||||
|  |         observer.observe(card); | ||||||
|  |       }); | ||||||
|  |     } else { | ||||||
|  |       featureCards.forEach((card) => card.classList.add('is-visible')); | ||||||
|  |     } | ||||||
|  |    | ||||||
|  |     window.addEventListener('load', () => { | ||||||
|  |       document | ||||||
|  |         .querySelectorAll('.newsletter-header, .newsletter-content') | ||||||
|  |         .forEach((el, i) => { | ||||||
|  |           el.style.transitionDelay = `${i * 0.2}s`; | ||||||
|  |           el.classList.add('is-visible'); | ||||||
|  |         }); | ||||||
|  |    | ||||||
|  |       newsletterCards.forEach((card, i) => { | ||||||
|  |         card.style.transitionDelay = `${i * 0.1}s`; | ||||||
|  |         card.classList.add('is-visible'); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |    | ||||||
|  |     let lastY = 0; | ||||||
|  |     let ticking = false; | ||||||
|  |    | ||||||
|  |     function onScroll() { | ||||||
|  |       lastY = window.scrollY || window.pageYOffset; | ||||||
|  |       if (!ticking) { | ||||||
|  |         requestAnimationFrame(updateOnScroll); | ||||||
|  |         ticking = true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |    | ||||||
|  |     function updateOnScroll() { | ||||||
|  |       if (navbar) { | ||||||
|  |         navbar.classList.toggle('navbar--scrolled', lastY > 50); | ||||||
|  |         navbar.classList.toggle('navbar--deeper', lastY > 100); | ||||||
|  |       } | ||||||
|  |    | ||||||
|  |       if (progressBar) { | ||||||
|  |         const max = document.body.scrollHeight - window.innerHeight; | ||||||
|  |         const progress = max > 0 ? Math.min(Math.max(lastY / max, 0), 1) : 0; | ||||||
|  |         progressBar.style.width = `${progress * 100}%`; | ||||||
|  |       } | ||||||
|  |    | ||||||
|  |       ticking = false; | ||||||
|  |     } | ||||||
|  |    | ||||||
|  |     window.addEventListener('scroll', onScroll, { passive: true }); | ||||||
|  |     updateOnScroll(); // initial state
 | ||||||
|  |    | ||||||
|  |     if (notifyBtn && emailInput) { | ||||||
|  |       let inFlight = false; | ||||||
|  |       const controller = new AbortController(); | ||||||
|  |    | ||||||
|  |       notifyBtn.addEventListener('click', async () => { | ||||||
|  |         const email = emailInput.value.trim(); | ||||||
|  |         if (!emailRE.test(email)) { | ||||||
|  |           alert('Please enter a valid email address.'); | ||||||
|  |           emailInput.focus(); | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |         if (inFlight) return; | ||||||
|  |    | ||||||
|  |         inFlight = true; | ||||||
|  |         notifyBtn.disabled = true; | ||||||
|  |    | ||||||
|  |         try { | ||||||
|  |           const res = await fetch('/subscribe', { | ||||||
|  |             method: 'POST', | ||||||
|  |             headers: { 'Content-Type': 'application/json' }, | ||||||
|  |             body: JSON.stringify({ email }), | ||||||
|  |             signal: controller.signal, | ||||||
|  |           }); | ||||||
|  |    | ||||||
|  |           let message = 'Thank you for subscribing!'; | ||||||
|  |           if (res.ok) { | ||||||
|  |             const data = await res.json().catch(() => ({})); | ||||||
|  |             message = data.message || message; | ||||||
|  |           } else { | ||||||
|  |             message = "Thanks! We'll notify you when we launch."; | ||||||
|  |           } | ||||||
|  |    | ||||||
|  |           alert(message); | ||||||
|  |           emailInput.value = ''; | ||||||
|  |         } catch (err) { | ||||||
|  |           console.error('Subscribe error:', err); | ||||||
|  |           alert("Thanks! We'll notify you when we launch."); | ||||||
|  |           emailInput.value = ''; | ||||||
|  |         } finally { | ||||||
|  |           notifyBtn.disabled = false; | ||||||
|  |           inFlight = false; | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |    | ||||||
|  |       window.addEventListener('beforeunload', () => controller.abort(), { | ||||||
|  |         passive: true, | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |    | ||||||
|  |     window.shareNewsletter = async function shareNewsletter() { | ||||||
|  |       try { | ||||||
|  |         if (navigator.share) { | ||||||
|  |           await navigator.share({ | ||||||
|  |             title: document.title, | ||||||
|  |             text: 'Check out this newsletter from RideAware', | ||||||
|  |             url: location.href, | ||||||
|  |           }); | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |       } catch (err) { | ||||||
|  |         console.warn('navigator.share error/cancel:', err); | ||||||
|  |       } | ||||||
|  |    | ||||||
|  |       if (navigator.clipboard && window.isSecureContext) { | ||||||
|  |         try { | ||||||
|  |           await navigator.clipboard.writeText(location.href); | ||||||
|  |           alert('Newsletter URL copied to clipboard!'); | ||||||
|  |           return; | ||||||
|  |         } catch { | ||||||
|  |           /* fall through */ | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |    | ||||||
|  |       const tmp = document.createElement('input'); | ||||||
|  |       tmp.value = location.href; | ||||||
|  |       document.body.appendChild(tmp); | ||||||
|  |       tmp.select(); | ||||||
|  |       document.execCommand('copy'); | ||||||
|  |       document.body.removeChild(tmp); | ||||||
|  |       alert('Newsletter URL copied to clipboard!'); | ||||||
|  |     }; | ||||||
|  |   })();   | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Cipher
						Cipher