ertuncozbay.dev/assets/js/main.js

170 lines
6.1 KiB
JavaScript

/* ==========================================================================
Ertunç Özbay — Workshop & Portfolio Script (Plain JS, No Dependencies)
Handles:
1. Accessible mobile navigation menu toggle (with Escape key support)
2. Luminous Aurora / Daylight Studio theme switcher (persisted in localStorage)
3. Static blog category filtering on the Blog page
4. One-click copy email button on Contact page
5. Image lightbox for hardware prototype photos and schematics
========================================================================== */
(function () {
"use strict";
// 1. Theme Switcher (Vibrant Aurora Default + Daylight Studio Mode)
var rootEl = document.documentElement;
var savedTheme = null;
try {
savedTheme = localStorage.getItem("eo-theme");
} catch (e) {}
if (savedTheme === "light" || savedTheme === "dark") {
rootEl.setAttribute("data-theme", savedTheme);
}
var navList = document.querySelector(".nav-list");
if (navList && !document.querySelector("[data-theme-toggle]")) {
var li = document.createElement("li");
var btn = document.createElement("button");
btn.type = "button";
btn.className = "theme-toggle";
btn.setAttribute("data-theme-toggle", "");
btn.setAttribute("aria-label", "Toggle color theme");
function updateThemeBtnLabel() {
var isLight = rootEl.getAttribute("data-theme") === "light";
btn.textContent = isLight ? "🌙 Aurora" : "☀️ Daylight";
}
updateThemeBtnLabel();
btn.addEventListener("click", function () {
var isLight = rootEl.getAttribute("data-theme") === "light";
var nextTheme = isLight ? "dark" : "light";
rootEl.setAttribute("data-theme", nextTheme);
try {
localStorage.setItem("eo-theme", nextTheme);
} catch (e) {}
updateThemeBtnLabel();
});
li.appendChild(btn);
navList.appendChild(li);
}
// 2. Mobile Navigation Toggle
var navToggle = document.querySelector("[data-nav-toggle]");
var siteNav = document.querySelector("[data-site-nav]");
if (navToggle && siteNav) {
navToggle.addEventListener("click", function () {
var isExpanded = navToggle.getAttribute("aria-expanded") === "true";
var nextState = !isExpanded;
navToggle.setAttribute("aria-expanded", String(nextState));
siteNav.classList.toggle("is-open", nextState);
var labelSpan = navToggle.querySelector("[data-nav-label]");
if (labelSpan) {
labelSpan.textContent = nextState ? "Close" : "Menu";
}
});
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && siteNav.classList.contains("is-open")) {
navToggle.setAttribute("aria-expanded", "false");
siteNav.classList.remove("is-open");
var labelSpan = navToggle.querySelector("[data-nav-label]");
if (labelSpan) {
labelSpan.textContent = "Menu";
}
navToggle.focus();
}
});
}
// 3. Blog Category Filter
var filterButtons = document.querySelectorAll("[data-blog-filter]");
var blogCards = document.querySelectorAll("[data-blog-category]");
var filterStatus = document.getElementById("blog-filter-status");
if (filterButtons.length > 0 && blogCards.length > 0) {
filterButtons.forEach(function (button) {
button.addEventListener("click", function () {
var selectedCategory = button.getAttribute("data-blog-filter");
filterButtons.forEach(function (btn) {
btn.setAttribute("aria-pressed", btn === button ? "true" : "false");
});
var visibleCount = 0;
blogCards.forEach(function (card) {
var categories = (card.getAttribute("data-blog-category") || "").split(",");
var match = selectedCategory === "all" || categories.indexOf(selectedCategory) !== -1;
card.hidden = !match;
if (match) {
visibleCount += 1;
}
});
if (filterStatus) {
filterStatus.textContent =
"Showing " + visibleCount + (visibleCount === 1 ? " entry" : " entries") +
(selectedCategory === "all" ? "." : " in selected category.");
}
});
});
}
// 4. Copy Email to Clipboard Buttons
var copyButtons = document.querySelectorAll("[data-copy-email]");
copyButtons.forEach(function (btn) {
btn.addEventListener("click", function () {
var email = btn.getAttribute("data-copy-email");
if (!email || !navigator.clipboard) return;
var originalText = btn.textContent;
navigator.clipboard.writeText(email).then(function () {
btn.textContent = "✓ Copied: " + email;
setTimeout(function () {
btn.textContent = originalText;
}, 2200);
});
});
});
// 5. Image Lightbox for Hardware Photos & Schematics
var zoomImages = document.querySelectorAll(".zoomable-img");
if (zoomImages.length > 0) {
var backdrop = document.createElement("div");
backdrop.className = "lightbox-backdrop";
backdrop.hidden = true;
backdrop.innerHTML =
'<button type="button" class="lightbox-close" aria-label="Close image preview">✕ Close (Esc)</button>' +
'<img class="lightbox-Backdrop-img" src="" alt="">' +
'<div class="lightbox-caption"></div>';
document.body.appendChild(backdrop);
var lbImg = backdrop.querySelector(".lightbox-Backdrop-img");
var lbCap = backdrop.querySelector(".lightbox-caption");
var lbClose = backdrop.querySelector(".lightbox-close");
function closeLightbox() {
backdrop.hidden = true;
lbImg.src = "";
}
zoomImages.forEach(function (img) {
img.addEventListener("click", function () {
lbImg.src = img.src;
lbImg.alt = img.alt || "";
lbCap.textContent = img.alt || "";
backdrop.hidden = false;
});
});
lbClose.addEventListener("click", closeLightbox);
backdrop.addEventListener("click", function (e) {
if (e.target === backdrop) closeLightbox();
});
document.addEventListener("keydown", function (e) {
if (e.key === "Escape" && !backdrop.hidden) closeLightbox();
});
}
})();