// Portfolio Website JavaScript // Interactive functionality for navigation, theme toggle, and user experience (function() { 'use strict'; // DOM Elements const themeToggle = document.getElementById('themeToggle'); const navToggle = document.querySelector('.nav-toggle'); const navLinks = document.querySelector('.nav-links'); const header = document.querySelector('.site-header'); const resumeBtn = document.getElementById('resumeBtn'); // Theme Management class ThemeManager { constructor() { this.currentTheme = localStorage.getItem('theme') || 'light'; this.init(); } init() { this.applyTheme(this.currentTheme); this.updateThemeIcon(); if (themeToggle) { themeToggle.addEventListener('click', () => this.toggleTheme()); } } toggleTheme() { this.currentTheme = this.currentTheme === 'light' ? 'dark' : 'light'; this.applyTheme(this.currentTheme); this.updateThemeIcon(); localStorage.setItem('theme', this.currentTheme); } applyTheme(theme) { document.documentElement.setAttribute('data-theme', theme); } updateThemeIcon() { if (themeToggle) { themeToggle.innerHTML = this.currentTheme === 'light' ? '🌙' : '☀️'; themeToggle.setAttribute('aria-label', `Switch to ${this.currentTheme === 'light' ? 'dark' : 'light'} mode`); } } } // Navigation Management class NavigationManager { constructor() { this.isMenuOpen = false; this.init(); } init() { // Mobile menu toggle if (navToggle && navLinks) { navToggle.addEventListener('click', () => this.toggleMobileMenu()); } // Close mobile menu when clicking on links if (navLinks) { navLinks.addEventListener('click', (e) => { if (e.target.tagName === 'A') { this.closeMobileMenu(); } }); } // Close mobile menu when clicking outside document.addEventListener('click', (e) => { if (this.isMenuOpen && !e.target.closest('.nav')) { this.closeMobileMenu(); } }); // Smooth scrolling for anchor links this.initSmoothScrolling(); // Header scroll effect this.initHeaderScrollEffect(); } toggleMobileMenu() { this.isMenuOpen = !this.isMenuOpen; navLinks.classList.toggle('active', this.isMenuOpen); navToggle.setAttribute('aria-expanded', this.isMenuOpen.toString()); navToggle.innerHTML = this.isMenuOpen ? '✕' : '☰'; } closeMobileMenu() { this.isMenuOpen = false; navLinks.classList.remove('active'); navToggle.setAttribute('aria-expanded', 'false'); navToggle.innerHTML = '☰'; } initSmoothScrolling() { document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', (e) => { const href = anchor.getAttribute('href'); if (href === '#' || href === '#top') { e.preventDefault(); window.scrollTo({ top: 0, behavior: 'smooth' }); return; } const target = document.querySelector(href); if (target) { e.preventDefault(); const headerHeight = header ? header.offsetHeight : 0; const targetPosition = target.offsetTop - headerHeight - 20; window.scrollTo({ top: targetPosition, behavior: 'smooth' }); } }); }); } initHeaderScrollEffect() { if (!header) return; let lastScrollY = window.scrollY; let ticking = false; const updateHeader = () => { const scrollY = window.scrollY; if (scrollY > 100) { header.style.background = 'rgba(255, 255, 255, 0.95)'; header.style.backdropFilter = 'blur(10px)'; } else { header.style.background = ''; header.style.backdropFilter = ''; } lastScrollY = scrollY; ticking = false; }; const requestTick = () => { if (!ticking) { requestAnimationFrame(updateHeader); ticking = true; } }; window.addEventListener('scroll', requestTick, { passive: true }); } } // Resume Download Manager class ResumeManager { constructor() { this.init(); } init() { if (resumeBtn) { resumeBtn.addEventListener('click', (e) => this.handleResumeDownload(e)); } } handleResumeDownload(e) { e.preventDefault(); // Show loading state const originalText = resumeBtn.innerHTML; resumeBtn.innerHTML = '📄 Downloading...'; resumeBtn.classList.add('loading'); // Create download link const link = document.createElement('a'); link.href = 'Rakesh Resume.pdf'; link.download = 'Rakesh_Kumar_Resume.pdf'; link.style.display = 'none'; document.body.appendChild(link); // Trigger download try { link.click(); // Track download (if analytics is available) if (typeof gtag !== 'undefined') { gtag('event', 'download', { 'event_category': 'Resume', 'event_label': 'PDF Download' }); } // Show success message setTimeout(() => { resumeBtn.innerHTML = '✅ Downloaded!'; setTimeout(() => { resumeBtn.innerHTML = originalText; resumeBtn.classList.remove('loading'); }, 2000); }, 500); } catch (error) { console.error('Download failed:', error); resumeBtn.innerHTML = '❌ Download Failed'; setTimeout(() => { resumeBtn.innerHTML = originalText; resumeBtn.classList.remove('loading'); }, 2000); } finally { document.body.removeChild(link); } } } // Animation and Intersection Observer class AnimationManager { constructor() { this.init(); } init() { // Intersection Observer for fade-in animations if ('IntersectionObserver' in window) { this.initScrollAnimations(); } // Typing animation for hero subtitle this.initTypingAnimation(); } initScrollAnimations() { const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; } }); }, observerOptions); // Observe elements for animation document.querySelectorAll('.card, .timeline li, .case').forEach(el => { el.style.opacity = '0'; el.style.transform = 'translateY(30px)'; el.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; observer.observe(el); }); } initTypingAnimation() { const subtitle = document.querySelector('.hero-subtitle'); if (!subtitle) return; const text = subtitle.textContent; subtitle.textContent = ''; subtitle.style.borderRight = '2px solid var(--accent-primary)'; let i = 0; const typeWriter = () => { if (i < text.length) { subtitle.textContent += text.charAt(i); i++; setTimeout(typeWriter, 50); } else { // Remove cursor after typing is complete setTimeout(() => { subtitle.style.borderRight = 'none'; }, 1000); } }; // Start typing animation after a short delay setTimeout(typeWriter, 1000); } } // Utility Functions class Utils { static debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } static 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); } }; } static isElementInViewport(el) { const rect = el.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); } } // Performance Monitoring class PerformanceMonitor { constructor() { this.init(); } init() { // Log page load performance window.addEventListener('load', () => { if ('performance' in window) { const perfData = performance.getEntriesByType('navigation')[0]; console.log('Page Load Time:', perfData.loadEventEnd - perfData.fetchStart, 'ms'); } }); } } // Error Handling class ErrorHandler { constructor() { this.init(); } init() { window.addEventListener('error', (e) => { console.error('JavaScript Error:', e.error); // Could send to analytics or error reporting service }); window.addEventListener('unhandledrejection', (e) => { console.error('Unhandled Promise Rejection:', e.reason); // Could send to analytics or error reporting service }); } } // Initialize Application class App { constructor() { this.init(); } init() { // Wait for DOM to be ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => this.start()); } else { this.start(); } } start() { try { // Initialize all managers new ThemeManager(); new NavigationManager(); new ResumeManager(); new AnimationManager(); new PerformanceMonitor(); new ErrorHandler(); console.log('Portfolio website initialized successfully!'); } catch (error) { console.error('Failed to initialize portfolio:', error); } } } // Start the application new App(); })(); // Export for potential module use if (typeof module !== 'undefined' && module.exports) { module.exports = { App }; }