File manager - Edit - /home/webapp69.cm.in.th/u69319090022/porfolio/69319090022/assets/js/main.js
Back
/* --- Application Logic for Portfolio --- */ document.addEventListener('DOMContentLoaded', () => { // Initialize Lucide Icons if (typeof lucide !== 'undefined') { lucide.createIcons(); } // --- Theme Toggle System --- const themeToggleBtns = document.querySelectorAll('.theme-toggle'); const sunIcons = document.querySelectorAll('.sun-icon'); const moonIcons = document.querySelectorAll('.moon-icon'); // Check initial theme state and apply classes const currentTheme = localStorage.getItem('theme') || 'dark'; // Default to dark for a wow effect applyTheme(currentTheme); themeToggleBtns.forEach(btn => { btn.addEventListener('click', () => { const activeTheme = document.documentElement.classList.contains('dark') ? 'light' : 'dark'; applyTheme(activeTheme); }); }); function applyTheme(theme) { if (theme === 'dark') { document.documentElement.classList.add('dark'); localStorage.setItem('theme', 'dark'); // Show sun icon, hide moon icon sunIcons.forEach(icon => icon.classList.remove('hidden')); moonIcons.forEach(icon => icon.classList.add('hidden')); } else { document.documentElement.classList.remove('dark'); localStorage.setItem('theme', 'light'); // Show moon icon, hide sun icon sunIcons.forEach(icon => icon.classList.add('hidden')); moonIcons.forEach(icon => icon.classList.remove('hidden')); } } // --- Hamburger Mobile Menu Toggle --- const menuBtn = document.getElementById('menu-btn'); const mobileMenu = document.getElementById('mobile-menu'); const mobileLinks = document.querySelectorAll('.mobile-link'); const menuIconOpen = document.getElementById('menu-icon-open'); const menuIconClose = document.getElementById('menu-icon-close'); if (menuBtn && mobileMenu) { menuBtn.addEventListener('click', () => { const isExpanded = menuBtn.getAttribute('aria-expanded') === 'true'; menuBtn.setAttribute('aria-expanded', !isExpanded); mobileMenu.classList.toggle('hidden'); mobileMenu.classList.toggle('flex'); // Toggle hamburger and close icons menuIconOpen.classList.toggle('hidden'); menuIconClose.classList.toggle('hidden'); }); // Close menu when a link is clicked mobileLinks.forEach(link => { link.addEventListener('click', () => { mobileMenu.classList.add('hidden'); mobileMenu.classList.remove('flex'); menuIconOpen.classList.remove('hidden'); menuIconClose.classList.add('hidden'); menuBtn.setAttribute('aria-expanded', 'false'); }); }); } // --- Scroll Spy & Active Nav Item Highlight --- const sections = document.querySelectorAll('section[id]'); const navLinks = document.querySelectorAll('.nav-link'); const navMobileLinks = document.querySelectorAll('.mobile-link'); window.addEventListener('scroll', () => { let scrollY = window.pageYOffset; sections.forEach(current => { const sectionHeight = current.offsetHeight; const sectionTop = current.offsetTop - 120; // offset for nav height const sectionId = current.getAttribute('id'); if (scrollY > sectionTop && scrollY <= sectionTop + sectionHeight) { // Desktop Links navLinks.forEach(link => { link.classList.remove('text-primary-600', 'dark:text-primary-400', 'font-semibold'); if (link.getAttribute('href') === `#${sectionId}`) { link.classList.add('text-primary-600', 'dark:text-primary-400', 'font-semibold'); } }); // Mobile Links navMobileLinks.forEach(link => { link.classList.remove('text-primary-600', 'dark:text-primary-400', 'bg-primary-50/50', 'dark:bg-primary-950/20'); if (link.getAttribute('href') === `#${sectionId}`) { link.classList.add('text-primary-600', 'dark:text-primary-400', 'bg-primary-50/50', 'dark:bg-primary-950/20'); } }); } }); }); // --- Intersection Observer for Scroll Animations --- const revealElements = document.querySelectorAll('.reveal'); const revealObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('active'); // Unobserve after animating once observer.unobserve(entry.target); } }); }, { threshold: 0.1, // Trigger when 10% of element is in view rootMargin: '0px 0px -50px 0px' // Trigger slightly before element is centered }); revealElements.forEach(element => { revealObserver.observe(element); }); // --- Contact Form Submission Handler --- const contactForm = document.getElementById('contact-form'); if (contactForm) { contactForm.addEventListener('submit', (e) => { e.preventDefault(); // Get fields const nameInput = document.getElementById('name'); const emailInput = document.getElementById('email'); const subjectInput = document.getElementById('subject'); const messageInput = document.getElementById('message'); let isValid = true; // Reset errors resetFormErrors(); // Validate inputs if (!nameInput.value.trim()) { showError(nameInput, 'กรุณากรอกชื่อของคุณ'); isValid = false; } if (!emailInput.value.trim()) { showError(emailInput, 'กรุณากรอกอีเมล'); isValid = false; } else if (!validateEmail(emailInput.value.trim())) { showError(emailInput, 'กรุณากรอกอีเมลที่ถูกต้อง'); isValid = false; } if (!subjectInput.value.trim()) { showError(subjectInput, 'กรุณากรอกหัวข้อข้อความ'); isValid = false; } if (!messageInput.value.trim()) { showError(messageInput, 'กรุณากรอกข้อความของคุณ'); isValid = false; } if (isValid) { // Simulate sending mail const submitBtn = contactForm.querySelector('button[type="submit"]'); const originalText = submitBtn.innerHTML; submitBtn.disabled = true; submitBtn.innerHTML = ` <svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> </svg> กำลังส่งข้อความ... `; setTimeout(() => { // Success Feedback showToast('ส่งข้อความสำเร็จแล้ว! ฉันจะติดต่อกลับโดยเร็วที่สุด', 'success'); contactForm.reset(); submitBtn.disabled = false; submitBtn.innerHTML = originalText; }, 1500); } else { showToast('กรุณากรอกข้อมูลให้ครบถ้วนและถูกต้อง', 'error'); } }); } function validateEmail(email) { const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-family0-9]+\.)+[a-zA-Z]{2,}))$/; return re.test(String(email).toLowerCase()); } function showError(inputElement, errorMessage) { inputElement.classList.add('border-red-500', 'focus:ring-red-500', 'focus:border-red-500'); const parent = inputElement.parentElement; const errorDisplay = document.createElement('p'); errorDisplay.className = 'text-red-500 text-xs mt-1 form-error-msg'; errorDisplay.innerText = errorMessage; parent.appendChild(errorDisplay); } function resetFormErrors() { const errorInputs = contactForm.querySelectorAll('.border-red-500'); errorInputs.forEach(input => { input.classList.remove('border-red-500', 'focus:ring-red-500', 'focus:border-red-500'); }); const errorMsgs = contactForm.querySelectorAll('.form-error-msg'); errorMsgs.forEach(msg => { msg.remove(); }); } // --- Custom Toast System --- function showToast(message, type = 'success') { // Check if container exists, else create it let toastContainer = document.getElementById('toast-container'); if (!toastContainer) { toastContainer = document.createElement('div'); toastContainer.id = 'toast-container'; toastContainer.className = 'fixed bottom-5 right-5 z-50 flex flex-col gap-3 max-w-sm w-full px-4'; document.body.appendChild(toastContainer); } const toast = document.createElement('div'); toast.className = `theme-transition glass-panel flex items-center p-4 rounded-xl border shadow-lg transform translate-y-10 opacity-0 transition-all duration-300 ease-out`; const iconColor = type === 'success' ? 'text-green-500' : 'text-red-500'; const borderColor = type === 'success' ? 'border-green-500/20' : 'border-red-500/20'; toast.classList.add(borderColor); const iconName = type === 'success' ? 'check-circle' : 'alert-circle'; toast.innerHTML = ` <div class="flex-shrink-0 mr-3 ${iconColor}"> <i data-lucide="${iconName}" class="w-6 h-6"></i> </div> <div class="flex-1 text-sm font-medium text-slate-800 dark:text-slate-200"> ${message} </div> <button class="ml-4 text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 focus:outline-none" onclick="this.parentElement.remove()"> <i data-lucide="x" class="w-4 h-4"></i> </button> `; toastContainer.appendChild(toast); // Re-trigger lucide icons inside the toast if (typeof lucide !== 'undefined') { lucide.createIcons({ attrs: { class: ['lucide'] }, nameAttr: 'data-lucide' }); } // Animate In setTimeout(() => { toast.classList.remove('translate-y-10', 'opacity-0'); }, 10); // Auto-remove after 4 seconds setTimeout(() => { toast.classList.add('translate-y-10', 'opacity-0'); setTimeout(() => { toast.remove(); }, 300); }, 4000); } });
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.27 |
proxy
|
phpinfo
|
Settings