| Linux server27.hostingraja.org 2.6.32-954.3.5.lve1.4.93.el6.x86_64 #1 SMP Wed Oct 4 17:04:29 UTC 2023 x86_64 Path : /home/udaipurk/www/websites/rohitnagda.com/js/ |
| Current File : /home/udaipurk/www/websites/rohitnagda.com/js/scripts.js |
/* ────────────────────────────────────────────────────────
NAVBAR: scroll + active section
──────────────────────────────────────────────────────── */
const mainNav = document.getElementById('mainNav');
const navLinks = document.querySelectorAll('[data-nav]');
const sections = document.querySelectorAll('section[id]');
window.addEventListener('scroll', () => {
mainNav.classList.toggle('scrolled', window.scrollY > 50);
let current = '';
sections.forEach(s => {
if (window.scrollY >= s.offsetTop - 120) current = s.id;
});
navLinks.forEach(a => {
a.classList.toggle('active', a.getAttribute('href') === '#' + current);
});
}, { passive: true });
/* ────────────────────────────────────────────────────────
MOBILE NAV
──────────────────────────────────────────────────────── */
const navToggle = document.getElementById('navToggle');
const mobileNav = document.getElementById('mobileNav');
const navOverlay = document.getElementById('navOverlay');
const mobileNavClose = document.getElementById('mobileNavClose');
function openNav() { mobileNav.classList.add('open'); navOverlay.classList.add('open'); document.body.style.overflow='hidden'; }
function closeNav() { mobileNav.classList.remove('open'); navOverlay.classList.remove('open'); document.body.style.overflow=''; }
navToggle.addEventListener('click', openNav);
mobileNavClose.addEventListener('click', closeNav);
navOverlay.addEventListener('click', closeNav);
document.querySelectorAll('.mobile-link').forEach(a => a.addEventListener('click', closeNav));
/* ────────────────────────────────────────────────────────
REVEAL ON SCROLL
──────────────────────────────────────────────────────── */
const revealObs = new IntersectionObserver((entries) => {
entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('visible'); revealObs.unobserve(e.target); } });
}, { threshold: 0.1 });
document.querySelectorAll('.reveal').forEach(el => revealObs.observe(el));
/* ────────────────────────────────────────────────────────
TIMELINE REVEAL
──────────────────────────────────────────────────────── */
const tlObs = new IntersectionObserver((entries) => {
entries.forEach((e, i) => {
if (e.isIntersecting) {
setTimeout(() => e.target.classList.add('visible'), i * 100);
tlObs.unobserve(e.target);
}
});
}, { threshold: 0.15 });
document.querySelectorAll('.tl-item').forEach(el => tlObs.observe(el));
/* ────────────────────────────────────────────────────────
SKILL BARS ANIMATE
──────────────────────────────────────────────────────── */
const skillObs = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
const pct = e.target.dataset.pct;
e.target.querySelector('.skill-fill').style.width = pct + '%';
skillObs.unobserve(e.target);
}
});
}, { threshold: 0.3 });
document.querySelectorAll('.skill-row').forEach(el => skillObs.observe(el));
/* ────────────────────────────────────────────────────────
LANGUAGE RINGS ANIMATE
──────────────────────────────────────────────────────── */
const CIRCUMFERENCE = 2 * Math.PI * 45; // r=45
const ringObs = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
const pct = parseInt(e.target.dataset.pct) / 100;
const fill = e.target.querySelector('.fill');
fill.style.strokeDasharray = CIRCUMFERENCE;
fill.style.strokeDashoffset = CIRCUMFERENCE * (1 - pct);
ringObs.unobserve(e.target);
}
});
}, { threshold: 0.3 });
document.querySelectorAll('.lang-ring').forEach(el => {
const fill = el.querySelector('.fill');
fill.style.strokeDasharray = CIRCUMFERENCE;
fill.style.strokeDashoffset = CIRCUMFERENCE;
ringObs.observe(el);
});
/* ────────────────────────────────────────────────────────
TESTIMONIAL SLIDER
──────────────────────────────────────────────────────── */
let tIdx = 0;
const tCards = document.querySelectorAll('.testimonial-card');
const tDots = document.querySelectorAll('.t-dot');
function showTestimonial(idx) {
tCards.forEach((c, i) => {
c.style.display = i === idx ? 'block' : 'none';
c.classList.toggle('active', i === idx);
});
tDots.forEach((d, i) => d.classList.toggle('active', i === idx));
tIdx = idx;
}
showTestimonial(0);
tDots.forEach(d => d.addEventListener('click', () => showTestimonial(parseInt(d.dataset.idx))));
setInterval(() => showTestimonial((tIdx + 1) % tCards.length), 5000);
/* ────────────────────────────────────────────────────────
PORTFOLIO FILTER
──────────────────────────────────────────────────────── */
const filterBtns = document.querySelectorAll('.filter-btn');
const bentoItems = document.querySelectorAll('.bento-item');
filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
filterBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const filter = btn.dataset.filter;
bentoItems.forEach(item => {
const match = filter === 'all' || item.dataset.filter === filter;
item.classList.toggle('hidden', !match);
});
});
});
/* ────────────────────────────────────────────────────────
LIGHTBOX
──────────────────────────────────────────────────────── */
const lightbox = document.getElementById('lightbox');
const lbImg = document.getElementById('lightbox-img');
const lbClose = document.getElementById('lightbox-close');
const lbExpand = document.getElementById('lightbox-expand');
const lbPrev = document.getElementById('lb-prev');
const lbNext = document.getElementById('lb-next');
let lbImages = [];
let lbCurrent = 0;
function openLightbox(src, imgs, idx) {
lbImages = imgs;
lbCurrent = idx;
lbImg.src = src;
lightbox.classList.add('open');
document.body.style.overflow = 'hidden';
}
function closeLightbox() {
lightbox.classList.remove('open');
document.body.style.overflow = '';
}
lbClose.addEventListener('click', closeLightbox);
lightbox.addEventListener('click', e => { if (e.target === lightbox) closeLightbox(); });
lbExpand.addEventListener('click', (e) => {
e.stopPropagation();
window.open(lbImg.src, '_blank');
});
lbPrev.addEventListener('click', (e) => {
e.stopPropagation();
lbCurrent = (lbCurrent - 1 + lbImages.length) % lbImages.length;
lbImg.src = lbImages[lbCurrent];
});
lbNext.addEventListener('click', (e) => {
e.stopPropagation();
lbCurrent = (lbCurrent + 1) % lbImages.length;
lbImg.src = lbImages[lbCurrent];
});
document.addEventListener('keydown', e => {
if (!lightbox.classList.contains('open')) return;
if (e.key === 'Escape') closeLightbox();
if (e.key === 'ArrowLeft') lbPrev.click();
if (e.key === 'ArrowRight') lbNext.click();
});
/* ────────────────────────────────────────────────────────
BENTO ITEM CLICK HANDLING
──────────────────────────────────────────────────────── */
bentoItems.forEach(item => {
item.addEventListener('click', () => {
if (item.classList.contains('hidden')) return;
const isCase = item.dataset.case === 'true';
if (isCase) {
const href = item.dataset.href;
window.open(href, '_blank');
return;
}
const src = item.dataset.src;
if (!src) return;
// collect all visible non-case items for lightbox navigation
const visibleImgs = Array.from(bentoItems)
.filter(i => !i.classList.contains('hidden') && i.dataset.src && i.dataset.case !== 'true')
.map(i => i.dataset.src);
const idx = visibleImgs.indexOf(src);
openLightbox(src, visibleImgs, idx !== -1 ? idx : 0);
});
});
/* ────────────────────────────────────────────────────────
PREVENT IMAGE DRAG
──────────────────────────────────────────────────────── */
document.querySelectorAll('img').forEach(img => {
img.addEventListener('dragstart', e => e.preventDefault());
});