/* global window, document */ (function () { 'use strict'; var Luxtools = window.Luxtools || (window.Luxtools = {}); // ============================================================ // Lightbox Module // ============================================================ Luxtools.Lightbox = (function () { var lb = null; var img = null; var cap = null; var items = []; var index = 0; // Zoom/pan state var scale = 1; var panX = 0; var panY = 0; var minScale = 1; var maxScale = 5; var isPanning = false; var panStartX = 0; var panStartY = 0; // History state var pushedHistory = false; var closingFromPopstate = false; function ensureElement() { if (lb) return; lb = document.createElement('div'); lb.className = 'luxtools-lightbox'; lb.setAttribute('role', 'dialog'); lb.setAttribute('aria-modal', 'true'); lb.setAttribute('aria-hidden', 'true'); lb.innerHTML = '
' + '
' + '' + '' + '' + '
' + '' + '
' + '
' + '
'; document.body.appendChild(lb); img = lb.querySelector('img.luxtools-lightbox-img'); cap = lb.querySelector('.luxtools-lightbox-caption'); lb.addEventListener('click', onClick); } function clampIndex(n) { if (n < 0) return items.length - 1; if (n >= items.length) return 0; return n; } function applyTransform() { if (scale <= 1 && panX === 0 && panY === 0) { img.style.transform = ''; } else { img.style.transform = 'scale(' + scale + ') translate(' + panX + 'px, ' + panY + 'px)'; } img.style.cursor = scale > 1 ? 'grab' : ''; } function resetZoom() { scale = 1; panX = 0; panY = 0; applyTransform(); } function render() { var it = items[index]; img.src = it.full; img.setAttribute('data-luxtools-index', String(index)); if (cap) cap.textContent = (it.name || '').trim(); resetZoom(); } function next() { index = clampIndex(index + 1); render(); } function prev() { index = clampIndex(index - 1); render(); } // Event handlers function onWheel(e) { e.preventDefault(); var delta = e.deltaY > 0 ? -0.15 : 0.15; scale = Math.max(minScale, Math.min(maxScale, scale + delta)); if (scale <= 1) { panX = 0; panY = 0; } applyTransform(); } function onDblClick(e) { e.preventDefault(); if (scale > 1) { scale = 1; panX = 0; panY = 0; } else { scale = 2.5; } applyTransform(); } function onMouseDown(e) { if (scale > 1 && e.button === 0) { isPanning = true; panStartX = e.clientX - panX * scale; panStartY = e.clientY - panY * scale; img.style.cursor = 'grabbing'; e.preventDefault(); } } function onMouseMove(e) { if (isPanning && scale > 1) { panX = (e.clientX - panStartX) / scale; panY = (e.clientY - panStartY) / scale; applyTransform(); img.style.cursor = 'grabbing'; } } function onMouseUp() { isPanning = false; img.style.cursor = scale > 1 ? 'grab' : ''; } function onKeyDown(e) { if (!lb || !lb.classList.contains('is-open')) return; var key = e.key || ''; if (key === 'Escape') { e.preventDefault(); close(); } else if (key === 'ArrowRight') { e.preventDefault(); next(); } else if (key === 'ArrowLeft') { e.preventDefault(); prev(); } } function onPopState() { if (!lb || !lb.classList.contains('is-open')) return; closingFromPopstate = true; try { close(); } finally { closingFromPopstate = false; } } function onClick(e) { var t = e.target; if (!t || !t.getAttribute) return; var action = t.getAttribute('data-luxtools-action') || ''; if (action === 'close') { e.preventDefault(); close(); return; } if (action === 'next') { e.preventDefault(); next(); return; } if (action === 'prev') { e.preventDefault(); prev(); return; } if (t.closest && t.closest('button.luxtools-lightbox-zone')) return; if (t.closest && t.closest('img.luxtools-lightbox-img')) return; e.preventDefault(); close(); } function attachListeners() { document.addEventListener('keydown', onKeyDown, true); window.addEventListener('popstate', onPopState, true); img.addEventListener('wheel', onWheel, { passive: false }); img.addEventListener('dblclick', onDblClick); img.addEventListener('mousedown', onMouseDown); document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); } function detachListeners() { document.removeEventListener('keydown', onKeyDown, true); window.removeEventListener('popstate', onPopState, true); img.removeEventListener('wheel', onWheel); img.removeEventListener('dblclick', onDblClick); img.removeEventListener('mousedown', onMouseDown); document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); } function open(galleryEl, startEl) { var links = galleryEl.querySelectorAll('a.luxtools-gallery-item[data-luxtools-full]'); items = []; links.forEach(function (a) { var full = a.getAttribute('data-luxtools-full') || a.getAttribute('href') || ''; var name = a.getAttribute('data-luxtools-name') || a.getAttribute('title') || ''; if (!full) return; items.push({ el: a, full: full, name: name }); }); if (!items.length) return; index = 0; for (var i = 0; i < items.length; i++) { if (items[i].el === startEl) { index = i; break; } } ensureElement(); pushedHistory = false; closingFromPopstate = false; lb.classList.add('is-open'); lb.setAttribute('aria-hidden', 'false'); try { document.documentElement.classList.add('luxtools-noscroll'); } catch (e) {} try { document.body.style.overflow = 'hidden'; } catch (e) {} attachListeners(); try { if (window.history && window.history.pushState) { window.history.pushState({ luxtoolsLightbox: 1 }, '', window.location.href); pushedHistory = true; } } catch (e) {} render(); } function close() { if (!lb) return; lb.classList.remove('is-open'); lb.setAttribute('aria-hidden', 'true'); try { document.documentElement.classList.remove('luxtools-noscroll'); } catch (e) {} try { document.body.style.overflow = ''; } catch (e) {} img.src = ''; resetZoom(); detachListeners(); if (pushedHistory && !closingFromPopstate) { try { if (window.history && window.history.state && window.history.state.luxtoolsLightbox === 1) { window.history.back(); } } catch (e) {} } items = []; } return { open: open, close: close }; })(); })();