main.js

← Back to explorer
website/ static/ js/ main.js
Raw
JavaScript 180 lines · UTF-8
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
document.querySelectorAll('a[href="#top"]').forEach((link) => {
    link.addEventListener("click", (event) => {
        event.preventDefault();
        window.scrollTo({ top: 0, behavior: "smooth" });
    });
});

const themeToggle = document.getElementById("theme-toggle");
const themeThumb = themeToggle.querySelector(".theme-toggle-thumb");
const root = document.documentElement;

function setThemeIcon(theme) {
    themeThumb.innerHTML = theme === "dark" ? "☀" : "☽";
}

setThemeIcon(root.getAttribute("data-theme") === "dark" ? "dark" : "light");

themeToggle.addEventListener("click", () => {
    const isDark = root.getAttribute("data-theme") === "dark";
    if (isDark) {
        root.removeAttribute("data-theme");
        localStorage.setItem("theme", "light");
        setThemeIcon("light");
    } else {
        root.setAttribute("data-theme", "dark");
        localStorage.setItem("theme", "dark");
        setThemeIcon("dark");
    }
});

const lightbox = document.getElementById("lightbox");
const lightboxImg = lightbox.querySelector(".lightbox-img");
const lightboxClose = lightbox.querySelector(".lightbox-close");

function openLightbox(img) {
    lightboxImg.src = img.src;
    lightboxImg.alt = img.alt;
    lightbox.hidden = false;
    document.body.style.overflow = "hidden";
}

function closeLightbox() {
    lightbox.hidden = true;
    lightboxImg.src = "";
    document.body.style.overflow = "";
}

document.querySelectorAll(".fullscreen-btn").forEach((btn) => {
    btn.addEventListener("click", () => {
        const img = btn.previousElementSibling;
        openLightbox(img);
    });
});

lightboxClose.addEventListener("click", closeLightbox);

lightbox.addEventListener("click", (event) => {
    if (event.target === lightbox) {
        closeLightbox();
    }
});

document.addEventListener("keydown", (event) => {
    if (event.key === "Escape" && !lightbox.hidden) {
        closeLightbox();
    }
});

function letterboxGap(slide) {
    const img = slide.querySelector("img");
    if (!img || !img.naturalWidth || !img.naturalHeight) return 0;
    const cw = slide.clientWidth;
    const ch = slide.clientHeight;
    const scale = Math.min(cw / img.naturalWidth, ch / img.naturalHeight);
    const renderedHeight = img.naturalHeight * scale;
    return Math.max(0, (ch - renderedHeight) / 2);
}

function pinOverlayToImage(slide) {
    const gap = letterboxGap(slide);
    const btn = slide.querySelector(".fullscreen-btn");
    if (btn) btn.style.bottom = gap + 10 + "px";
    return gap;
}

function initCarousel(carousel) {
    const track = carousel.querySelector(".carousel-track");
    const slides = Array.from(track.children);
    const prevBtn = carousel.querySelector(".carousel-arrow--prev");
    const nextBtn = carousel.querySelector(".carousel-arrow--next");
    const dotsWrap = carousel.querySelector(".carousel-dots");

    if (slides.length <= 1) {
        prevBtn.hidden = true;
        nextBtn.hidden = true;
        dotsWrap.hidden = true;
        return;
    }

    const dots = slides.map((_, i) => {
        const dot = document.createElement("button");
        dot.type = "button";
        dot.className = "carousel-dot" + (i === 0 ? " active" : "");
        dot.setAttribute("aria-label", "Go to image " + (i + 1));
        dot.addEventListener("click", () => scrollToSlide(i));
        dotsWrap.appendChild(dot);
        return dot;
    });

    slides.forEach((slide) => {
        const img = slide.querySelector("img");
        if (!img) return;
        if (img.complete && img.naturalWidth) {
            pinOverlayToImage(slide);
        } else {
            img.addEventListener("load", () => {
                pinOverlayToImage(slide);
                updateUI();
            });
        }
    });

    function currentIndex() {
        return Math.round(track.scrollLeft / track.clientWidth);
    }

    function scrollToSlide(i) {
        track.scrollTo({ left: slides[i].offsetLeft, behavior: "smooth" });
    }

    function updateUI() {
        const index = currentIndex();
        dots.forEach((dot, i) => dot.classList.toggle("active", i === index));
        prevBtn.style.visibility = index === 0 ? "hidden" : "visible";
        nextBtn.style.visibility = index === slides.length - 1 ? "hidden" : "visible";
        dotsWrap.style.bottom = pinOverlayToImage(slides[index]) + 14 + "px";
    }

    prevBtn.addEventListener("click", () => scrollToSlide(Math.max(0, currentIndex() - 1)));
    nextBtn.addEventListener("click", () => scrollToSlide(Math.min(slides.length - 1, currentIndex() + 1)));
    track.addEventListener("scroll", () => window.requestAnimationFrame(updateUI));
    window.addEventListener("resize", () => {
        slides.forEach(pinOverlayToImage);
        updateUI();
    });

    updateUI();
}

document.querySelectorAll("[data-carousel]").forEach(initCarousel);

async function initPriceTags() {
    const tags = document.querySelectorAll(".price-tag");
    if (!tags.length) return;

    let usdPerInr = null;
    try {
        const res = await fetch("https://open.er-api.com/v6/latest/INR");
        const data = await res.json();
        usdPerInr = data.rates.USD;
    } catch (err) {
        usdPerInr = null;
    }

    tags.forEach((tag) => {
        const tooltip = tag.querySelector(".price-tooltip");
        const inr = parseFloat(tag.dataset.inr);
        if (usdPerInr) {
            const usd = (inr * usdPerInr).toLocaleString("en-US", {
                style: "currency",
                currency: "USD",
            });
            tooltip.textContent = "≈ " + usd;
        } else {
            tooltip.textContent = "Exchange rate unavailable";
        }
    });
}

initPriceTags();