- Impressum rewritten with real Austrian business data (ECG instead of German TMG references); UID, Gewerbeberechtigung and Firmenbuchnummer marked as pending until available - Real contact email/phone/address across footer and Impressum - Meta description, Open Graph tags and favicon on all pages - Gallery images now offer both 800w and 1920w srcset candidates - Lightbox traps focus while open and restores it on close - Header/footer extracted into partials/, pages now built from templates/ via `npm run build` (scripts/build.js) to remove duplication across the 4 pages
214 lines
7 KiB
JavaScript
214 lines
7 KiB
JavaScript
/**
|
||
* Tischlerei Staffenberger - Main JavaScript
|
||
* Handles navigation active state, gallery filtering, and lightbox on Bilder page.
|
||
*/
|
||
|
||
(function () {
|
||
'use strict';
|
||
|
||
/**
|
||
* Set active state on the current page's nav link based on URL pathname.
|
||
*/
|
||
function setActiveNav() {
|
||
var navLinks = document.querySelectorAll('.main-nav a');
|
||
var path = window.location.pathname;
|
||
var page = path.split('/').pop() || 'index.html';
|
||
|
||
navLinks.forEach(function (link) {
|
||
var href = link.getAttribute('href');
|
||
if (!href) return;
|
||
var linkPage = href.split('/').pop() || 'index.html';
|
||
if (linkPage === page || (page === '' && linkPage === 'index.html')) {
|
||
link.classList.add('active');
|
||
} else {
|
||
link.classList.remove('active');
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Gallery filter: show/hide items by data-category.
|
||
*/
|
||
function initGalleryFilter() {
|
||
var filters = document.querySelectorAll('.gallery-filters button');
|
||
var items = document.querySelectorAll('.gallery-item');
|
||
|
||
if (!filters.length || !items.length) return;
|
||
|
||
filters.forEach(function (btn) {
|
||
btn.addEventListener('click', function () {
|
||
var category = this.getAttribute('data-filter');
|
||
|
||
// Update active button
|
||
filters.forEach(function (b) {
|
||
b.classList.toggle('active', b === btn);
|
||
});
|
||
|
||
// Show/hide items
|
||
items.forEach(function (item) {
|
||
var itemCategory = item.getAttribute('data-category');
|
||
var show = category === 'all' || itemCategory === category;
|
||
item.style.display = show ? '' : 'none';
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Lightbox on Bilder page: open on gallery image click, navigate with swipe / arrow
|
||
* keys / prev+next buttons, close via X, backdrop or ESC.
|
||
* Respects the active gallery filter – only visible items are part of the sequence.
|
||
*/
|
||
function initLightbox() {
|
||
var lightbox = document.getElementById('lightbox');
|
||
var lightboxImg = document.getElementById('lightbox-img');
|
||
if (!lightbox || !lightboxImg) return;
|
||
|
||
var closeBtn = lightbox.querySelector('.lightbox-close');
|
||
var backdrop = lightbox.querySelector('.lightbox-backdrop');
|
||
var prevBtn = lightbox.querySelector('.lightbox-prev');
|
||
var nextBtn = lightbox.querySelector('.lightbox-next');
|
||
|
||
var currentIndex = 0;
|
||
var sequence = []; // visible gallery links at the time the lightbox was opened
|
||
var lastFocused = null; // element to restore focus to on close
|
||
|
||
function getFocusableEls() {
|
||
return [prevBtn, nextBtn, closeBtn].filter(function (el) {
|
||
return el && !el.hidden;
|
||
});
|
||
}
|
||
|
||
function onFocusTrap(e) {
|
||
if (e.key !== 'Tab') return;
|
||
var focusable = getFocusableEls();
|
||
if (!focusable.length) return;
|
||
var first = focusable[0];
|
||
var last = focusable[focusable.length - 1];
|
||
if (e.shiftKey && document.activeElement === first) {
|
||
e.preventDefault();
|
||
last.focus();
|
||
} else if (!e.shiftKey && document.activeElement === last) {
|
||
e.preventDefault();
|
||
first.focus();
|
||
}
|
||
}
|
||
|
||
function getVisibleLinks() {
|
||
return Array.from(document.querySelectorAll('.gallery-item'))
|
||
.filter(function (item) { return item.style.display !== 'none'; })
|
||
.map(function (item) { return item.querySelector('.gallery-image-link'); })
|
||
.filter(Boolean);
|
||
}
|
||
|
||
function showImage(index) {
|
||
var link = sequence[index];
|
||
if (!link) return;
|
||
currentIndex = index;
|
||
lightboxImg.src = link.getAttribute('data-lightbox-src') || link.getAttribute('href');
|
||
lightboxImg.alt = link.getAttribute('data-lightbox-alt') || '';
|
||
var multiple = sequence.length > 1;
|
||
if (prevBtn) prevBtn.hidden = !multiple;
|
||
if (nextBtn) nextBtn.hidden = !multiple;
|
||
}
|
||
|
||
function navigate(dir) {
|
||
showImage((currentIndex + dir + sequence.length) % sequence.length);
|
||
}
|
||
|
||
function openLightbox(index, trigger) {
|
||
sequence = getVisibleLinks();
|
||
lastFocused = trigger || document.activeElement;
|
||
showImage(index);
|
||
lightbox.classList.add('is-open');
|
||
document.addEventListener('keydown', onKeydown);
|
||
var focusable = getFocusableEls();
|
||
if (focusable.length) focusable[0].focus();
|
||
}
|
||
|
||
function closeLightbox() {
|
||
lightbox.classList.remove('is-open');
|
||
document.removeEventListener('keydown', onKeydown);
|
||
if (lastFocused && typeof lastFocused.focus === 'function') lastFocused.focus();
|
||
lastFocused = null;
|
||
}
|
||
|
||
function onKeydown(e) {
|
||
if (e.key === 'Escape') closeLightbox();
|
||
if (e.key === 'ArrowRight') navigate(1);
|
||
if (e.key === 'ArrowLeft') navigate(-1);
|
||
onFocusTrap(e);
|
||
}
|
||
|
||
// Touch swipe
|
||
var touchStartX = 0;
|
||
lightbox.addEventListener('touchstart', function (e) {
|
||
touchStartX = e.changedTouches[0].clientX;
|
||
}, { passive: true });
|
||
lightbox.addEventListener('touchend', function (e) {
|
||
var dx = e.changedTouches[0].clientX - touchStartX;
|
||
if (Math.abs(dx) > 50) navigate(dx < 0 ? 1 : -1);
|
||
}, { passive: true });
|
||
|
||
// Open on gallery click
|
||
document.querySelectorAll('.gallery-image-link').forEach(function (link) {
|
||
link.addEventListener('click', function (e) {
|
||
e.preventDefault();
|
||
var visibles = getVisibleLinks();
|
||
var idx = visibles.indexOf(link);
|
||
openLightbox(idx >= 0 ? idx : 0, link);
|
||
});
|
||
});
|
||
|
||
if (prevBtn) prevBtn.addEventListener('click', function () { navigate(-1); });
|
||
if (nextBtn) nextBtn.addEventListener('click', function () { navigate(1); });
|
||
if (closeBtn) closeBtn.addEventListener('click', closeLightbox);
|
||
if (backdrop) backdrop.addEventListener('click', closeLightbox);
|
||
}
|
||
|
||
/**
|
||
* "Request" buttons on shop page – open mailto with product name in subject.
|
||
*/
|
||
function initShopRequests() {
|
||
var requestBtns = document.querySelectorAll('.product-card .btn[data-product]');
|
||
requestBtns.forEach(function (btn) {
|
||
btn.addEventListener('click', function (e) {
|
||
var product = this.getAttribute('data-product');
|
||
if (product) {
|
||
// Open mail with subject line (no real checkout) – replace email with your address
|
||
var mailto = 'mailto:info@tischlerei-staffenberger.at?subject=Anfrage%20Produkt%3A%20' + encodeURIComponent(product);
|
||
window.location.href = mailto;
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Shop empty state: show message when no products are present.
|
||
*/
|
||
function initShopEmptyState() {
|
||
var emptyMsg = document.getElementById('shop-empty-message');
|
||
var grid = document.getElementById('shop-grid');
|
||
if (!emptyMsg || !grid) return;
|
||
|
||
var hasCards = grid.querySelectorAll('.product-card').length > 0;
|
||
emptyMsg.hidden = hasCards;
|
||
}
|
||
|
||
/**
|
||
* Run on DOM ready.
|
||
*/
|
||
function init() {
|
||
setActiveNav();
|
||
initGalleryFilter();
|
||
initLightbox();
|
||
initShopRequests();
|
||
initShopEmptyState();
|
||
}
|
||
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', init);
|
||
} else {
|
||
init();
|
||
}
|
||
})();
|