console.log("Script.js started executing at:", new Date().toISOString()); // Initialize Particles.js particlesJS('particles-js', { particles: { number: { value: 80, density: { enable: true, value_area: 800 } }, color: { value: ['#1E90FF'] }, shape: { type: 'circle' }, opacity: { value: 0.5, random: true }, size: { value: 3, random: true }, line_linked: { enable: true, distance: 150, color: '#1E90FF', opacity: 0.4, width: 1 }, move: { enable: true, speed: 2, direction: 'none', random: true, straight: false, out_mode: 'out', bounce: false } }, interactivity: { detect_on: 'canvas', events: { onhover: { enable: true, mode: 'repulse' }, onclick: { enable: true, mode: 'push' }, resize: true }, modes: { repulse: { distance: 100, duration: 0.4 }, push: { particles_nb: 4 } } }, retina_detect: true }); // Glowing Cursor Trail const canvas = document.getElementById('cursor-trail'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; const trails = []; window.addEventListener('mousemove', (e) => { trails.push({ x: e.clientX, y: e.clientY, life: 1 }); }); function animateTrail() { ctx.clearRect(0, 0, canvas.width, canvas.height); trails.forEach((trail, index) => { ctx.beginPath(); ctx.arc(trail.x, trail.y, 5 * trail.life, 0, Math.PI * 2); ctx.fillStyle = `rgba(30, 144, 255, ${trail.life})`; ctx.fill(); trail.life -= 0.02; if (trail.life <= 0) trails.splice(index, 1); }); requestAnimationFrame(animateTrail); } animateTrail(); window.addEventListener('resize', () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }); // GSAP Animations gsap.registerPlugin(ScrollTrigger); // Animate the static text and buttons in hero section gsap.fromTo('.animate-slide-in', { y: 100, opacity: 0 }, { y: 0, opacity: 1, duration: 1.2, ease: 'power3.out', onComplete: () => { document.querySelector('.animate-slide-in').style.opacity = '1'; }} ); // Animate the image in the hero section gsap.fromTo('.animate-fade-in', { opacity: 0 }, { opacity: 1, duration: 1.5, ease: 'power3.out', onComplete: () => { document.querySelector('.animate-fade-in').style.opacity = '1'; }} ); // Animate stat cards on scroll gsap.from('.stat-card', { scrollTrigger: { trigger: '#about', start: 'top 80%' }, y: 50, opacity: 0, duration: 0.8, stagger: 0.2, ease: 'power3.out' }); // Navigation Bar const menuToggle = document.getElementById('menu-toggle'); const mobileMenu = document.getElementById('mobile-menu'); menuToggle.addEventListener('click', () => { mobileMenu.classList.toggle('hidden'); menuToggle.querySelector('i').classList.toggle('fa-bars'); menuToggle.querySelector('i').classList.toggle('fa-times'); }); // Smooth Scrolling document.querySelectorAll('.nav-link').forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); mobileMenu.classList.add('hidden'); menuToggle.querySelector('i').classList.add('fa-bars'); menuToggle.querySelector('i').classList.remove('fa-times'); document.querySelector(link.getAttribute('href')).scrollIntoView({ behavior: 'smooth' }); }); }); // Theme Toggle (Disabled since theme switching isn't implemented) const themeToggle = document.getElementById('theme-toggle'); themeToggle.addEventListener('click', () => { // Theme switching not implemented }); // Back to Top Button const backToTop = document.getElementById('back-to-top'); window.addEventListener('scroll', () => { backToTop.classList.toggle('hidden', window.scrollY < 500); }); backToTop.addEventListener('click', () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }); // Ripple Effect for Buttons document.querySelectorAll('.ripple').forEach(btn => { btn.addEventListener('click', (e) => { const rect = btn.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; const ripple = document.createElement('span'); ripple.style.left = `${x}px`; ripple.style.top = `${y}px`; ripple.classList.add('ripple-effect'); btn.appendChild(ripple); setTimeout(() => ripple.remove(), 600); }); }); // Fetch Data Helper const fetchData = async (url, elementId, renderCallback) => { try { console.log(`Fetching data from ${url}`); const response = await fetch(url); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); const data = await response.json(); console.log(`Fetched data from ${url}:`, data); const container = document.getElementById(elementId); if (!container) throw new Error(`Container with ID ${elementId} not found`); renderCallback(data, container); } catch (error) { console.error(`Error fetching ${url}:`, error); document.getElementById(elementId).innerHTML = '

Error loading data. Please try again later.

'; } }; // Render Skills with Circular Progress fetchData('/api/skills', 'skills-grid', (data, container) => { console.log('Rendering skills data:', data); container.innerHTML = ''; data.forEach(category => { const card = document.createElement('div'); card.className = 'skill-card rounded-xl hover:scale-105 transition-transform'; card.style.opacity = '1'; card.style.display = 'block'; card.innerHTML = `

${category.title}

${category.skills.map(skill => `
${skill.name}
${skill.proficiency}%
`).join('')}
`; container.appendChild(card); console.log(`Rendered skill card for ${category.title}`); card.querySelectorAll('.progress-fill').forEach(circle => { const proficiency = circle.parentElement.parentElement.querySelector('.progress-percentage').textContent.replace('%', ''); const circumference = 2 * Math.PI * 18; const offset = circumference * (proficiency / 100); gsap.to(circle, { scrollTrigger: { trigger: card, start: 'left 80%', end: 'right 20%', scrub: true }, strokeDasharray: `${offset} 113`, duration: 1.5, ease: 'power3.out' }); }); }); }); // Render Achievements fetchData('/api/achievements', 'achievements-grid', (data, container) => { data.forEach(achievement => { const card = document.createElement('div'); card.className = 'achievement-card glassmorphic p-6 rounded-xl hover:scale-105 transition-transform'; card.setAttribute('data-tilt', ''); card.setAttribute('data-tilt-max', '10'); card.innerHTML = `

${achievement.title}

${achievement.organization}

${achievement.description}

`; container.appendChild(card); VanillaTilt.init(card); gsap.from(card, { scrollTrigger: { trigger: card, start: 'top 80%' }, y: 50, opacity: 0, duration: 1, ease: 'power3.out' }); }); }); // Render Hobbies with Review Form for "Educating Others" fetchData('/api/hobbies', 'hobbies-grid', (data, container) => { data.forEach(hobby => { const card = document.createElement('div'); card.className = 'hobby-card glassmorphic p-6 rounded-xl hover:scale-105 transition-transform'; card.setAttribute('data-tilt', ''); card.setAttribute('data-tilt-max', '10'); card.innerHTML = `

${hobby.title}

${hobby.description}

`; if (hobby.title === "Educating Others") { card.innerHTML += `

Share Your Feedback

Testimonials

`; } container.appendChild(card); VanillaTilt.init(card); gsap.from(card, { scrollTrigger: { trigger: card, start: 'top 80%' }, y: 50, opacity: 0, duration: 1, ease: 'power3.out' }); if (hobby.title === "Educating Others") { const stars = card.querySelectorAll('.star'); const ratingInput = card.querySelector('#review-rating'); stars.forEach(star => { star.addEventListener('click', () => { const rating = star.getAttribute('data-value'); ratingInput.value = rating; stars.forEach(s => { const value = s.getAttribute('data-value'); if (value <= rating) { s.innerHTML = ''; } else { s.innerHTML = ''; } }); }); }); card.querySelector('#submit-review').addEventListener('click', () => { const name = card.querySelector('#review-name').value.trim(); const rating = parseInt(card.querySelector('#review-rating').value); const description = card.querySelector('#review-description').value.trim(); if (!name || rating === 0 || !description) { alert('Please fill out all fields and select a rating.'); return; } fetch('/api/reviews', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, rating, description }) }) .then(response => response.json()) .then(data => { if (data.message) { alert('Thank you for your feedback!'); card.querySelector('#review-name').value = ''; card.querySelector('#review-description').value = ''; card.querySelector('#review-rating').value = '0'; stars.forEach(star => star.innerHTML = ''); fetchReviews(card.querySelector('#reviews-container')); } else { alert('Error submitting feedback: ' + (data.error || 'Unknown error')); } }) .catch(error => { console.error('Error submitting review:', error); alert('Error submitting feedback. Please try again later.'); }); }); function fetchReviews(reviewsContainer) { fetch('/api/reviews') .then(response => response.json()) .then(reviews => { reviewsContainer.innerHTML = ''; if (reviews.length === 0) { reviewsContainer.innerHTML = '

No testimonials yet. Be the first to share your experience!

'; return; } reviews.forEach(review => { const reviewDiv = document.createElement('div'); reviewDiv.className = 'glassmorphic p-4 rounded-xl flex justify-between items-start'; reviewDiv.innerHTML = `
${review.name}
${''.repeat(review.rating)} ${''.repeat(5 - review.rating)}

${review.description}

`; reviewsContainer.appendChild(reviewDiv); gsap.from(reviewDiv, { opacity: 0, y: 20, duration: 0.8, ease: 'power3.out' }); reviewDiv.querySelector('.delete-review').addEventListener('click', () => { const confirmDelete = confirm(`Are you sure you want to delete the review by ${review.name}?`); if (confirmDelete) { const password = prompt('Please enter the admin password to delete this review:'); if (!password) { alert('Password is required to delete a review.'); return; } fetch(`/api/reviews/delete/${review.id}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password }) }) .then(response => response.json()) .then(data => { if (data.message) { alert(data.message); fetchReviews(reviewsContainer); } else { alert('Error deleting review: ' + (data.error || 'Unknown error')); } }) .catch(error => { console.error('Error deleting review:', error); alert('Error deleting review. Please try again later.'); }); } }); }); }) .catch(error => { console.error('Error fetching reviews:', error); reviewsContainer.innerHTML = '

Error loading testimonials. Please try again later.

'; }); } fetchReviews(card.querySelector('#reviews-container')); } }); }); // Render Projects with Modal const modal = document.getElementById('project-modal'); const modalClose = document.getElementById('modal-close'); fetchData('/api/projects', 'projects-grid', (data, container) => { data.forEach(project => { const card = document.createElement('div'); card.className = 'project-card glassmorphic rounded-xl overflow-hidden cursor-pointer hover:scale-105 transition-transform'; card.setAttribute('data-tilt', ''); card.setAttribute('data-tilt-max', '10'); card.innerHTML = `
${project.title}

${project.title}

${project.description.substring(0, 100)}...

${project.technologies.map(tech => `${tech}`).join('')}
`; card.addEventListener('click', () => { const mediaContainer = document.getElementById('modal-media'); mediaContainer.innerHTML = project.video ? `
` : `${project.title}`; document.getElementById('modal-title').textContent = project.title; document.getElementById('modal-description').textContent = project.description; document.getElementById('modal-role').textContent = project.role; const contributionsContainer = document.getElementById('modal-contributions'); contributionsContainer.innerHTML = project.contributions.map(contribution => `
  • ${contribution}
  • `).join(''); const techContainer = document.getElementById('modal-tech'); techContainer.innerHTML = project.technologies.map(tech => `${tech}`).join(''); document.getElementById('modal-github').href = project.githubLink; document.getElementById('modal-live').href = project.liveLink; modal.classList.remove('hidden'); gsap.from(modal.querySelector('div'), { y: 100, opacity: 0, duration: 0.8, ease: 'power3.out' }); }); container.appendChild(card); VanillaTilt.init(card); gsap.from(card, { scrollTrigger: { trigger: card, start: 'top 80%' }, y: 50, opacity: 0, duration: 1, ease: 'power3.out' }); }); }); modalClose.addEventListener('click', () => { modal.classList.add('hidden'); }); modal.addEventListener('click', (e) => { if (e.target === modal) modal.classList.add('hidden'); }); // Render Education fetchData('/api/education', 'education-grid', (data, container) => { data.forEach(edu => { const card = document.createElement('div'); card.className = 'education-card glassmorphic p-6 rounded-xl hover:scale-105 transition-transform'; card.setAttribute('data-tilt', ''); card.setAttribute('data-tilt-max', '10'); card.innerHTML = `

    ${edu.degree}

    ${edu.period}

    ${edu.institution}

    ${edu.description}

    Key Achievements:
    `; container.appendChild(card); VanillaTilt.init(card); gsap.from(card, { scrollTrigger: { trigger: card, start: 'top 80%' }, y: 50, opacity: 0, duration: 1, ease: 'power3.out' }); }); }); // Render Certifications fetchData('/api/certifications', 'certifications-grid', (data, container) => { data.forEach(cert => { const card = document.createElement('div'); card.className = 'certification-card glassmorphic p-6 rounded-xl hover:scale-105 transition-transform'; card.setAttribute('data-tilt', ''); card.setAttribute('data-tilt-max', '10'); card.innerHTML = `

    ${cert.title}

    ${cert.year}

    ${cert.platform}

    ${cert.description}

    ${cert.title} badge Verify Certificate
    `; container.appendChild(card); VanillaTilt.init(card); gsap.from(card, { scrollTrigger: { trigger: card, start: 'top 80%' }, y: 50, opacity: 0, duration: 1, ease: 'power3.out' }); }); }); // Render Volunteer Experience fetchData('/api/volunteer', 'volunteer-grid', (data, container) => { data.forEach(vol => { const card = document.createElement('div'); card.className = 'volunteer-card glassmorphic p-6 rounded-xl hover:scale-105 transition-transform'; card.setAttribute('data-tilt', ''); card.setAttribute('data-tilt-max', '10'); card.innerHTML = `

    ${vol.role}

    ${vol.period}

    ${vol.organization}

    ${vol.description}

    Contributions:
    `; container.appendChild(card); VanillaTilt.init(card); gsap.from(card, { scrollTrigger: { trigger: card, start: 'top 80%' }, y: 50, opacity: 0, duration: 1, ease: 'power3.out' }); }); }); // Render Talks fetchData('/api/talks', 'talks-grid', (data, container) => { data.forEach(talk => { const card = document.createElement('div'); card.className = 'talk-card glassmorphic p-6 rounded-xl hover:scale-105 transition-transform'; card.setAttribute('data-tilt', ''); card.setAttribute('data-tilt-max', '10'); card.innerHTML = `

    ${talk.title}

    ${talk.date}

    ${talk.event}

    ${talk.description}

    ${talk.image ? `
    ${talk.title} thumbnail
    ` : ''}
    Watch Full Talk
    `; container.appendChild(card); VanillaTilt.init(card); gsap.from(card, { scrollTrigger: { trigger: card, start: 'top 80%' }, y: 50, opacity: 0, duration: 1, ease: 'power3.out' }); }); }); // Handle Contact Form Submission const contactSubmitButton = document.getElementById('contact-submit'); const emailInput = document.getElementById('email'); const messageInput = document.getElementById('message'); contactSubmitButton.addEventListener('click', async () => { const email = emailInput.value.trim(); const message = messageInput.value.trim(); // Validate inputs if (!email || !message) { alert('Please fill in both email and message fields.'); return; } if (!/\S+@\S+\.\S+/.test(email)) { alert('Please enter a valid email address.'); return; } try { const response = await fetch('/api/contact', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, message }) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || `HTTP error! status: ${response.status}`); } const data = await response.json(); alert(data.message); emailInput.value = ''; messageInput.value = ''; } catch (error) { console.error('Error submitting contact form:', error); alert('Error submitting form. If you\'re on Hugging Face Spaces, please use LinkedIn or GitHub to contact me.'); } });