// ─────────────────────────────────────────────────
// PREVIEW IMAGES
// Legt auf der Webflow-Seite ein Element mit data-lightbox-demo-assets an,
// darin direkt 5 Elemente — deren src wird automatisch übernommen.
// Beispiel:
//
...
//
// ─────────────────────────────────────────────────
(function() {
const assetEl = document.querySelector('[data-lightbox-demo-assets]');
if (!assetEl) return;
const srcs = Array.from(assetEl.querySelectorAll('img')).map(i => i.src).filter(Boolean);
if (!srcs.length) return;
// Main image: erstes Bild
const mainImg = document.getElementById('main-img');
if (mainImg && srcs[0]) mainImg.src = srcs[0];
// Thumbnails
const thumbImgs = document.querySelectorAll('#lightbox-preview .w-lightbox-thumbnail-image');
thumbImgs.forEach((img, i) => { if (srcs[i]) img.src = srcs[i]; });
})();
// ─────────────────────────────────────────────────
// ── State ──
const state = {
color: '#000000',
opacity: 90,
blur: 0,
thumbWidth: 10,
thumbHeight: 10,
thumbRadius: 0,
thumbMargin: 1,
thumbPaddingTB: 2,
frameRadius: 0,
fullSize: false,
objectFit: 'cover',
arrowPreset: 'Default',
arrowPosition: 0,
arrowSize: 1.5,
arrowOpacity: 50,
arrowMirror: true,
arrowSvgRight: '',
arrowSvgLeft: '',
arrowColor: '#ffffff',
closePreset: 'Default',
closeSide: 'right',
closeOffsetX: 0,
closeOffsetY: 0,
closeSize: 1,
closeOpacity: 80,
closeColor: '#ffffff',
closeSvg: '',
stripHide: false,
stripAlign: 'center',
stripBgEnable: false,
stripBgType: 'solid',
stripGradFrom: '#000000',
stripGradFromOpacity: 50,
stripGradTo: 'transparent',
stripGradDir: 'to top',
stripBgColor: '#000000',
stripBgOpacity: 100,
activeOpacity: 30,
activeBorderWidth: 0,
activeBorderColor: '#ffffff',
};
let activeTab = 'backdrop';
const backdrop = document.getElementById('preview-backdrop');
// ── Controls wiring ──
function wire(id, stateKey, displayId) {
const slider = document.getElementById(id);
const disp = document.getElementById(displayId);
slider.addEventListener('input', () => {
const v = parseFloat(slider.value);
state[stateKey] = v;
if (disp) disp.value = v;
applyStyles();
});
if (disp) {
disp.addEventListener('change', () => {
const raw = parseFloat(disp.value);
if (isNaN(raw)) { disp.value = state[stateKey]; return; }
state[stateKey] = raw;
const mn = parseFloat(slider.min), mx = parseFloat(slider.max);
slider.value = Math.min(mx, Math.max(mn, raw));
applyStyles();
});
}
}
wire('ctrl-opacity', 'opacity', 'val-opacity');
wire('ctrl-blur', 'blur', 'val-blur');
// ── Color picker ──
const colorPicker = document.getElementById('ctrl-color');
const hexInput = document.getElementById('ctrl-hex');
function setColor(hex) {
hex = hex.replace(/[^#0-9a-fA-F]/g, '');
if (!hex.startsWith('#')) hex = '#' + hex;
if (hex.length === 7) {
state.color = hex.toLowerCase();
colorPicker.value = hex;
hexInput.value = hex.toLowerCase();
applyStyles();
}
}
colorPicker.addEventListener('input', e => setColor(e.target.value));
hexInput.addEventListener('input', e => setColor(e.target.value));
hexInput.addEventListener('keydown', e => { if (e.key === 'Enter') setColor(hexInput.value); });
// ── Thumbnail click — preload + instant swap ──
const mainImg = document.getElementById('main-img');
// Alle Thumbnail-Bilder vorladen damit der Wechsel aus dem Cache kommt
function preloadThumbImages() {
document.querySelectorAll('#lightbox-preview .w-lightbox-thumbnail-image').forEach(img => {
if (img.src && !img.src.startsWith('data:')) {
const pre = new Image();
pre.src = img.src;
}
});
}
// Nach kurzem Delay preloaden (damit data-lightbox-demo-assets zuerst gesetzt wird)
setTimeout(preloadThumbImages, 300);
document.querySelectorAll('.w-lightbox-item').forEach(item => {
item.addEventListener('click', () => {
document.querySelectorAll('.w-lightbox-item').forEach(i => i.classList.remove('w-lightbox-active'));
item.classList.add('w-lightbox-active');
const thumbImg = item.querySelector('.w-lightbox-thumbnail-image');
if (!thumbImg) return;
// Bild ist im Cache → kein Fade nötig, direkt wechseln
mainImg.src = thumbImg.src;
});
});
// ── Apply styles to preview ──
function applyStyles() {
const r = parseInt(state.color.slice(1,3), 16);
const g = parseInt(state.color.slice(3,5), 16);
const b = parseInt(state.color.slice(5,7), 16);
const a = (state.opacity / 100).toFixed(2);
const filters = [];
if (state.blur > 0) filters.push('blur(' + state.blur + 'px)');
backdrop.style.backgroundColor = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
backdrop.style.backdropFilter = filters.length ? filters.join(' ') : '';
backdrop.style.webkitBackdropFilter = backdrop.style.backdropFilter;
backdrop.style.transition = 'all, opacity ' + state.duration + 'ms ' + state.easing + ', transform ' + state.duration + 'ms ' + state.easing;
}
// ── Thumb styles ──
const thumbDynamicStyle = document.createElement('style');
document.head.appendChild(thumbDynamicStyle);
function applyThumbStyles() {
const viewH = 96 - state.thumbHeight - 2;
if (state.fullSize) {
thumbDynamicStyle.textContent =
'#lightbox-preview .w-lightbox-content { height: 100vh; margin-top: 0; }' +
'#lightbox-preview .w-lightbox-group,' +
'#lightbox-preview .w-lightbox-group .w-lightbox-view,' +
'#lightbox-preview .w-lightbox-group .w-lightbox-view:before { height: 100vh; }' +
'#lightbox-preview .w-lightbox-view:before { display: none; }' +
'#lightbox-preview .w-lightbox-frame { display: block; width: 100%; height: 100vh; vertical-align: top; }' +
'#lightbox-preview .w-lightbox-figure { width: 100%; height: 100%; margin: 0; }' +
'#lightbox-preview .w-lightbox-image:not(.w-lightbox-thumbnail-image) { display: block; width: 100%; height: 100%; max-height: none; max-width: none; float: none; object-fit: ' + state.objectFit + '; }';
} else {
thumbDynamicStyle.textContent =
'@media (min-width: 768px) {' +
' #lightbox-preview .w-lightbox-group,' +
' #lightbox-preview .w-lightbox-group .w-lightbox-view,' +
' #lightbox-preview .w-lightbox-group .w-lightbox-view:before' +
' { height: ' + viewH + 'vh; }' +
' #lightbox-preview .w-lightbox-group .w-lightbox-image' +
' { max-height: ' + viewH + 'vh; }' +
'}';
}
document.querySelectorAll('.w-lightbox-thumbnail').forEach(function(el) {
el.style.height = state.thumbHeight + 'vh';
el.style.borderRadius = state.thumbRadius > 0 ? state.thumbRadius + 'rem' : '';
});
const strip = document.querySelector('#lightbox-preview .w-lightbox-strip');
if (strip) {
strip.style.fontSize = '0';
strip.style.display = state.stripHide ? 'none' : '';
strip.style.textAlign = state.stripAlign;
if (state.stripBgEnable) {
if (state.stripBgType === 'gradient') {
const fromHex = state.stripGradFrom;
const fR = parseInt(fromHex.slice(1,3),16), fG = parseInt(fromHex.slice(3,5),16), fB = parseInt(fromHex.slice(5,7),16);
const fromRgba = 'rgba(' + fR + ',' + fG + ',' + fB + ',' + (state.stripGradFromOpacity/100).toFixed(2) + ')';
strip.style.background = 'linear-gradient(' + state.stripGradDir + ', ' + fromRgba + ', ' + state.stripGradTo + ')';
strip.style.backgroundColor = '';
} else if (state.stripBgOpacity > 0) {
const r = parseInt(state.stripBgColor.slice(1,3),16);
const g = parseInt(state.stripBgColor.slice(3,5),16);
const b = parseInt(state.stripBgColor.slice(5,7),16);
strip.style.background = '';
strip.style.backgroundColor = 'rgba(' + r + ',' + g + ',' + b + ',' + (state.stripBgOpacity/100).toFixed(2) + ')';
}
} else {
strip.style.background = '';
strip.style.backgroundColor = '';
}
}
document.querySelectorAll('.w-lightbox-item').forEach(function(el) {
el.style.width = state.thumbWidth + 'vh';
el.style.marginLeft = state.thumbMargin > 0 ? state.thumbMargin + 'vh' : '';
el.style.marginRight = state.thumbMargin > 0 ? state.thumbMargin + 'vh' : '';
el.style.paddingLeft = '0';
el.style.paddingRight = '0';
el.style.paddingTop = state.thumbPaddingTB + 'vh';
el.style.paddingBottom = state.thumbPaddingTB + 'vh';
});
// Active thumb
const activeStyle = document.getElementById('active-thumb-style') || (() => {
const s = document.createElement('style'); s.id = 'active-thumb-style';
document.head.appendChild(s); return s;
})();
let activeRules = '#lightbox-preview .w-lightbox-active { opacity: ' + (state.activeOpacity / 100).toFixed(2) + '; }';
if (state.activeBorderWidth > 0) {
activeRules += ' #lightbox-preview .w-lightbox-active .w-lightbox-thumbnail { border: ' + state.activeBorderWidth + 'rem solid ' + state.activeBorderColor + '; box-sizing: border-box; }';
} else {
activeRules += ' #lightbox-preview .w-lightbox-active .w-lightbox-thumbnail { border: none; }';
}
activeStyle.textContent = activeRules;
}
// ── Frame styles ──
const frameDynamicStyle = document.createElement('style');
document.head.appendChild(frameDynamicStyle);
function applyFrameStyles() {
const frame = document.querySelector('#lightbox-preview .w-lightbox-frame');
const img = document.querySelector('#lightbox-preview .w-lightbox-img');
if (!frame) return;
frame.style.borderRadius = state.frameRadius > 0 ? state.frameRadius + 'rem' : '';
frame.style.overflow = (state.frameRadius > 0 || state.fullSize) ? 'hidden' : '';
// fullSize layout is handled via thumbDynamicStyle in applyThumbStyles
applyThumbStyles();
updateModified();
}
const arrowPresets = [
{ name: "Default", right: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii00IDAgMjQgNDAiIHdpZHRoPSIyNCIgaGVpZ2h0PSI0MCI+PGcgdHJhbnNmb3JtPSJyb3RhdGUoNDUpIj48cGF0aCBkPSJtMC0waDI4djI4aC01di0yM2gtMjN6IiBvcGFjaXR5PSIuNCIvPjxwYXRoIGQ9Im0xIDFoMjZ2MjZoLTN2LTIzaC0yM3oiIGZpbGw9IiNmZmYiLz48L2c+PC9zdmc+", left: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii00IDAgMjQgNDAiIHdpZHRoPSIyNCIgaGVpZ2h0PSI0MCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjQsMCkgc2NhbGUoLTEsMSkiPjxnIHRyYW5zZm9ybT0icm90YXRlKDQ1KSI+PHBhdGggZD0ibTAtMGgyOHYyOGgtNXYtMjNoLTIzeiIgb3BhY2l0eT0iLjQiLz48cGF0aCBkPSJtMSAxaDI2djI2aC0zdi0yM2gtMjN6IiBmaWxsPSIjZmZmIi8+PC9nPjwvZz48L3N2Zz4=", svg: ' ', svgLeft: ' ' },
{ name: "Chevron", right: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCA0MCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjQwIj48cG9seWxpbmUgcG9pbnRzPSI1LDQgMTksMjAgNSwzNiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjIuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PC9zdmc+", left: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCA0MCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjQwIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNCwwKSBzY2FsZSgtMSwxKSI+PHBvbHlsaW5lIHBvaW50cz0iNSw0IDE5LDIwIDUsMzYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIyLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvZz48L3N2Zz4=", svg: ' ' },
{ name: "Arrow", right: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCA0MCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjQwIj48bGluZSB4MT0iMiIgeTE9IjIwIiB4Mj0iMjEiIHkyPSIyMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjIuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBvbHlsaW5lIHBvaW50cz0iMTMsMTEgMjEsMjAgMTMsMjkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIyLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg==", left: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCA0MCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjQwIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNCwwKSBzY2FsZSgtMSwxKSI+PGxpbmUgeDE9IjIiIHkxPSIyMCIgeDI9IjIxIiB5Mj0iMjAiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIyLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxwb2x5bGluZSBwb2ludHM9IjEzLDExIDIxLDIwIDEzLDI5IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMi41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L2c+PC9zdmc+", svg: ' ' },
{ name: "Triangle", right: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCA0MCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjQwIj48cG9seWdvbiBwb2ludHM9IjYsOCAyMCwyMCA2LDMyIiBmaWxsPSIjZmZmIiBvcGFjaXR5PSIwLjkiLz48L3N2Zz4=", left: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCA0MCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjQwIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNCwwKSBzY2FsZSgtMSwxKSI+PHBvbHlnb24gcG9pbnRzPSI2LDggMjAsMjAgNiwzMiIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iMC45Ii8+PC9nPjwvc3ZnPg==", svg: ' ' },
{ name: "Thin", right: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCA0MCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjQwIj48cG9seWxpbmUgcG9pbnRzPSI3LDYgMTcsMjAgNywzNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PC9zdmc+", left: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCA0MCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjQwIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNCwwKSBzY2FsZSgtMSwxKSI+PHBvbHlsaW5lIHBvaW50cz0iNyw2IDE3LDIwIDcsMzQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvZz48L3N2Zz4=", svg: ' ' },
{ name: "Double", right: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyOCA0MCIgd2lkdGg9IjI4IiBoZWlnaHQ9IjQwIj48cG9seWxpbmUgcG9pbnRzPSI0LDYgMTQsMjAgNCwzNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwb2x5bGluZSBwb2ludHM9IjEzLDYgMjMsMjAgMTMsMzQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4=", left: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyOCA0MCIgd2lkdGg9IjI4IiBoZWlnaHQ9IjQwIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyOCwwKSBzY2FsZSgtMSwxKSI+PHBvbHlsaW5lIHBvaW50cz0iNCw2IDE0LDIwIDQsMzQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cG9seWxpbmUgcG9pbnRzPSIxMyw2IDIzLDIwIDEzLDM0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PC9nPjwvc3ZnPg==", svg: ' ' },
];
// ── Arrow styles ──
function svgToDataUri(svg) {
svg = svg.trim();
if (!svg.startsWith(']*>)/, '$1');
mirrored = mirrored.replace(' ', '');
return svgToDataUri(mirrored);
}
function colorizeArrowSvg(svg) {
const c = state.arrowColor;
return svg
.replace(/fill="[^"none][^"]*"/gi, 'fill="' + c + '"')
.replace(/stroke="[^"none][^"]*"/gi, 'stroke="' + c + '"')
.replace(/fill='[^'none][^']*'/gi, "fill='" + c + "'")
.replace(/stroke='[^'none][^']*'/gi, "stroke='" + c + "'")
.replace(/fill="currentColor"/gi, 'fill="' + c + '"')
.replace(/stroke="currentColor"/gi, 'stroke="' + c + '"');
}
function applyArrowStyles() {
const leftEl = document.querySelector('#lightbox-preview .w-lightbox-left');
const rightEl = document.querySelector('#lightbox-preview .w-lightbox-right');
if (!leftEl || !rightEl) return;
let uriRight, uriLeft;
if (state.arrowSvgRight.trim()) {
const colored = colorizeArrowSvg(state.arrowSvgRight);
uriRight = svgToDataUri(colored);
if (state.arrowMirror) {
uriLeft = mirrorSvgUri(colored);
} else {
const coloredLeft = state.arrowSvgLeft.trim() ? colorizeArrowSvg(state.arrowSvgLeft) : colored;
uriLeft = svgToDataUri(coloredLeft);
}
} else {
const preset = arrowPresets.find(p => p.name === state.arrowPreset) || arrowPresets[0];
if (preset.name === 'Default' && state.arrowColor === '#ffffff') {
// Restore Webflow originals — remove overrides
rightEl.style.backgroundImage = '';
leftEl.style.backgroundImage = '';
} else {
const coloredRight = colorizeArrowSvg(preset.svg);
uriRight = svgToDataUri(coloredRight);
uriLeft = preset.svgLeft
? svgToDataUri(colorizeArrowSvg(preset.svgLeft))
: mirrorSvgUri(coloredRight);
}
}
if (uriRight) rightEl.style.backgroundImage = 'url("' + uriRight + '")';
if (uriLeft) leftEl.style.backgroundImage = 'url("' + uriLeft + '")';
const sizePx = state.arrowSize + 'rem';
rightEl.style.backgroundSize = sizePx;
leftEl.style.backgroundSize = sizePx;
// Expand hit area when icon exceeds default 1.5rem — only on left/right, never close
const extra = state.arrowSize - 1.5;
const arrowWidth = extra > 0 ? 'calc(4em + ' + extra.toFixed(4) + 'rem)' : '';
rightEl.style.width = arrowWidth;
leftEl.style.width = arrowWidth;
rightEl.style.right = state.arrowPosition !== 0 ? state.arrowPosition + 'rem' : '';
leftEl.style.left = state.arrowPosition !== 0 ? state.arrowPosition + 'rem' : '';
// Opacity via dynamic style to override media query
const opacityStyle = document.getElementById('arrow-opacity-style') || (() => {
const s = document.createElement('style');
s.id = 'arrow-opacity-style';
document.head.appendChild(s);
return s;
})();
const op = (state.arrowOpacity / 100).toFixed(2);
opacityStyle.textContent =
'#lightbox-preview .w-lightbox-left, #lightbox-preview .w-lightbox-right { opacity: ' + op + '; }' +
'#lightbox-preview .w-lightbox-control:hover { opacity: 1; }';
updateModified();
}
// Preset tile clicks
document.querySelectorAll('.arrow-preset-tile').forEach(tile => {
tile.addEventListener('click', () => {
document.querySelectorAll('.arrow-preset-tile').forEach(t => t.classList.remove('active'));
tile.classList.add('active');
state.arrowPreset = tile.dataset.preset;
// Clear custom SVG
state.arrowSvgRight = '';
state.arrowSvgLeft = '';
document.getElementById('ctrl-arrow-svg-right').value = '';
document.getElementById('ctrl-arrow-svg-left').value = '';
applyArrowStyles();
});
});
// Set first preset active
const firstTile = document.querySelector('.arrow-preset-tile');
if (firstTile) firstTile.classList.add('active');
// Arrow size + opacity wires
function wireArrow(id, stateKey, displayId) {
const slider = document.getElementById(id);
const disp = document.getElementById(displayId);
if (!slider) return;
slider.addEventListener('input', () => {
state[stateKey] = parseFloat(slider.value);
if (disp) disp.value = slider.value;
applyArrowStyles();
});
if (disp) {
disp.addEventListener('change', () => {
const raw = parseFloat(disp.value);
if (isNaN(raw)) { disp.value = state[stateKey]; return; }
state[stateKey] = raw;
const mn = parseFloat(slider.min), mx = parseFloat(slider.max);
slider.value = Math.min(mx, Math.max(mn, raw));
applyArrowStyles();
});
}
}
wireArrow('ctrl-arrow-position', 'arrowPosition', 'val-arrow-position');
wireArrow('ctrl-arrow-size', 'arrowSize', 'val-arrow-size');
wireArrow('ctrl-arrow-opacity', 'arrowOpacity', 'val-arrow-opacity');
// Arrow color picker
const arrowColorPicker = document.getElementById('ctrl-arrow-color');
const arrowHexInput = document.getElementById('ctrl-arrow-hex');
function setArrowColor(hex) {
hex = hex.replace(/[^#0-9a-fA-F]/g, '');
if (!hex.startsWith('#')) hex = '#' + hex;
if (hex.length === 7) {
state.arrowColor = hex.toLowerCase();
arrowColorPicker.value = hex;
arrowHexInput.value = hex.toLowerCase();
applyArrowStyles();
}
}
arrowColorPicker.addEventListener('input', e => setArrowColor(e.target.value));
arrowHexInput.addEventListener('input', e => setArrowColor(e.target.value));
arrowHexInput.addEventListener('keydown', e => { if (e.key === 'Enter') setArrowColor(arrowHexInput.value); });
// Mirror toggle
document.getElementById('ctrl-arrow-mirror').addEventListener('change', function() {
state.arrowMirror = this.checked;
document.getElementById('wrap-arrow-left').style.display = this.checked ? 'none' : '';
applyArrowStyles();
});
// Hide left input initially (mirror on by default)
document.getElementById('wrap-arrow-left').style.display = 'none';
// Custom SVG inputs
function handleSvgInput(id, errId, isRight) {
const ta = document.getElementById(id);
const err = document.getElementById(errId);
ta.addEventListener('input', () => {
const val = ta.value.trim();
if (!val) {
err.textContent = '';
if (isRight) state.arrowSvgRight = '';
else state.arrowSvgLeft = '';
applyArrowStyles();
return;
}
if (!val.startsWith(' t.classList.remove('active'));
state.arrowPreset = '';
applyArrowStyles();
});
}
handleSvgInput('ctrl-arrow-svg-right', 'err-arrow-right', true);
handleSvgInput('ctrl-arrow-svg-left', 'err-arrow-left', false);
const closePresets = [
{ name: "Default", uri: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii00IDAgMTggMTciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxNyI+PGcgdHJhbnNmb3JtPSJyb3RhdGUoNDUpIj48cGF0aCBkPSJtMCAwaDd2LTdoNXY3aDd2NWgtN3Y3aC01di03aC03eiIgb3BhY2l0eT0iLjQiLz48cGF0aCBkPSJtMSAxaDd2LTdoM3Y3aDd2M2gtN3Y3aC0zdi03aC03eiIgZmlsbD0iI2ZmZiIvPjwvZz48L3N2Zz4=", svg: ' ' },
{ name: "Cross", uri: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxOCAxOCIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE4Ij48bGluZSB4MT0iMiIgeTE9IjIiIHgyPSIxNiIgeTI9IjE2IiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PGxpbmUgeDE9IjE2IiB5MT0iMiIgeDI9IjIiIHkyPSIxNiIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjwvc3ZnPg==", svg: ' ' },
{ name: "Thin", uri: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxOCAxOCIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE4Ij48bGluZSB4MT0iMiIgeTE9IjIiIHgyPSIxNiIgeTI9IjE2IiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PGxpbmUgeDE9IjE2IiB5MT0iMiIgeDI9IjIiIHkyPSIxNiIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjwvc3ZnPg==", svg: ' ' },
{ name: "Circle", uri: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxOCAxOCIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE4Ij48Y2lyY2xlIGN4PSI5IiBjeT0iOSIgcj0iOCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxsaW5lIHgxPSI1LjUiIHkxPSI1LjUiIHgyPSIxMi41IiB5Mj0iMTIuNSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PGxpbmUgeDE9IjEyLjUiIHkxPSI1LjUiIHgyPSI1LjUiIHkyPSIxMi41IiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48L3N2Zz4=", svg: ' ' },
{ name: "Square", uri: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxOCAxOCIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE4Ij48cmVjdCB4PSIxIiB5PSIxIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHJ4PSIyIiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS41Ii8+PGxpbmUgeDE9IjUuNSIgeTE9IjUuNSIgeDI9IjEyLjUiIHkyPSIxMi41IiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48bGluZSB4MT0iMTIuNSIgeTE9IjUuNSIgeDI9IjUuNSIgeTI9IjEyLjUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjwvc3ZnPg==", svg: ' ' },
{ name: "Plus", uri: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxOCAxOCIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE4Ij48bGluZSB4MT0iMiIgeTE9IjIiIHgyPSIxNiIgeTI9IjE2IiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMi41IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48bGluZSB4MT0iMTYiIHkxPSIyIiB4Mj0iMiIgeTI9IjE2IiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMi41IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48L3N2Zz4=", svg: ' ' },
];
function colorizeCloseSvg(svg) {
const c = state.closeColor;
return svg
.replace(/fill="[^"none][^"]*"/gi, 'fill="' + c + '"')
.replace(/stroke="[^"none][^"]*"/gi, 'stroke="' + c + '"')
.replace(/fill='[^'none][^']*'/gi, "fill='" + c + "'")
.replace(/stroke='[^'none][^']*'/gi, "stroke='" + c + "'")
.replace(/fill="currentColor"/gi, 'fill="' + c + '"')
.replace(/stroke="currentColor"/gi, 'stroke="' + c + '"');
}
function applyCloseStyles() {
const el = document.querySelector('#lightbox-preview .w-lightbox-close');
if (!el) return;
// SVG / preset
if (state.closeSvg.trim()) {
const uri = svgToDataUri(colorizeCloseSvg(state.closeSvg));
if (uri) el.style.backgroundImage = 'url("' + uri + '")';
} else {
const preset = closePresets.find(p => p.name === state.closePreset) || closePresets[0];
if (preset.name === 'Default' && state.closeColor === '#ffffff') {
el.style.backgroundImage = '';
} else {
const uri = svgToDataUri(colorizeCloseSvg(preset.svg));
if (uri) el.style.backgroundImage = 'url("' + uri + '")';
}
}
// Size
el.style.backgroundSize = state.closeSize + 'rem';
el.style.height = (state.closeSize * 2.3).toFixed(4) + 'rem';
// Opacity via dynamic style
const opStyle = document.getElementById('close-opacity-style') || (() => {
const s = document.createElement('style'); s.id = 'close-opacity-style';
document.head.appendChild(s); return s;
})();
opStyle.textContent = '#lightbox-preview .w-lightbox-close { opacity: ' + (state.closeOpacity/100).toFixed(2) + '; }';
// Position — reset both sides first, then set
el.style.right = ''; el.style.left = '';
if (state.closeSide === 'right') {
el.style.right = state.closeOffsetX !== 0 ? state.closeOffsetX + 'rem' : '';
el.style.left = 'auto';
} else {
el.style.left = state.closeOffsetX !== 0 ? state.closeOffsetX + 'rem' : '0';
el.style.right = 'auto';
}
el.style.top = state.closeOffsetY !== 0 ? state.closeOffsetY + 'rem' : '';
updateModified();
}
// Close preset tiles
document.querySelectorAll('.close-preset-tile').forEach(tile => {
tile.addEventListener('click', () => {
document.querySelectorAll('.close-preset-tile').forEach(t => t.classList.remove('active'));
tile.classList.add('active');
state.closePreset = tile.dataset.closePreset;
state.closeSvg = '';
document.getElementById('ctrl-close-svg').value = '';
applyCloseStyles();
});
});
const firstCloseTile = document.querySelector('.close-preset-tile');
if (firstCloseTile) firstCloseTile.classList.add('active');
// Position quick-select buttons
document.querySelectorAll('.close-pos-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.close-pos-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
state.closeSide = btn.dataset.pos === 'top-left' ? 'left' : 'right';
applyCloseStyles();
});
});
// Set top-right active by default
const defaultPosBtn = document.querySelector('.close-pos-btn[data-pos="top-right"]');
if (defaultPosBtn) defaultPosBtn.classList.add('active');
// Wire sliders
function wireClose(id, stateKey, displayId) {
const slider = document.getElementById(id);
const disp = document.getElementById(displayId);
if (!slider) return;
slider.addEventListener('input', () => {
state[stateKey] = parseFloat(slider.value);
if (disp) disp.value = slider.value;
applyCloseStyles();
});
if (disp) {
disp.addEventListener('change', () => {
const raw = parseFloat(disp.value);
if (isNaN(raw)) { disp.value = state[stateKey]; return; }
state[stateKey] = raw;
slider.value = Math.min(parseFloat(slider.max), Math.max(parseFloat(slider.min), raw));
applyCloseStyles();
});
}
}
wireClose('ctrl-close-x', 'closeOffsetX', 'val-close-x');
wireClose('ctrl-close-y', 'closeOffsetY', 'val-close-y');
wireClose('ctrl-close-size', 'closeSize', 'val-close-size');
wireClose('ctrl-close-opacity', 'closeOpacity', 'val-close-opacity');
// Active border color picker
const activeBorderColorPicker = document.getElementById('ctrl-active-border-color');
const activeBorderHexInput = document.getElementById('ctrl-active-border-hex');
function setActiveBorderColor(hex) {
hex = hex.replace(/[^#0-9a-fA-F]/g, '');
if (!hex.startsWith('#')) hex = '#' + hex;
if (hex.length === 7) {
state.activeBorderColor = hex.toLowerCase();
activeBorderColorPicker.value = hex;
activeBorderHexInput.value = hex.toLowerCase();
applyThumbStyles();
}
}
activeBorderColorPicker.addEventListener('input', e => setActiveBorderColor(e.target.value));
activeBorderHexInput.addEventListener('input', e => setActiveBorderColor(e.target.value));
// Close color picker
const closeColorPicker = document.getElementById('ctrl-close-color');
const closeHexInput = document.getElementById('ctrl-close-hex');
function setCloseColor(hex) {
hex = hex.replace(/[^#0-9a-fA-F]/g, '');
if (!hex.startsWith('#')) hex = '#' + hex;
if (hex.length === 7) {
state.closeColor = hex.toLowerCase();
closeColorPicker.value = hex;
closeHexInput.value = hex.toLowerCase();
applyCloseStyles();
}
}
closeColorPicker.addEventListener('input', e => setCloseColor(e.target.value));
closeHexInput.addEventListener('input', e => setCloseColor(e.target.value));
// Custom SVG
document.getElementById('ctrl-close-svg').addEventListener('input', function() {
const val = this.value.trim();
const err = document.getElementById('err-close-svg');
if (!val) { err.textContent = ''; state.closeSvg = ''; applyCloseStyles(); return; }
if (!val.startsWith(' t.classList.remove('active'));
state.closePreset = '';
applyCloseStyles();
});
// ── Tab switching ──
const backdropPanel = document.querySelector('.sidebar-scroll > .section:first-child, #panel-backdrop');
const allSections = document.querySelectorAll('.sidebar-scroll > .section, .sidebar-scroll > #panel-thumbs');
document.querySelectorAll('.sidebar-tab:not(.disabled)').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.sidebar-tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
activeTab = tab.dataset.tab;
// Show/hide panels
const scroll = document.querySelector('.sidebar-scroll');
// Hide all direct section children and named panels
scroll.querySelectorAll(':scope > .section, :scope > div[id^="panel-"]').forEach(el => el.style.display = 'none');
if (activeTab === 'backdrop') {
scroll.querySelectorAll(':scope > .section').forEach(el => el.style.display = '');
} else if (activeTab === 'close') {
document.getElementById('panel-close').style.display = '';
} else if (activeTab === 'arrows') {
document.getElementById('panel-arrows').style.display = '';
} else if (activeTab === 'thumbs') {
document.getElementById('panel-thumbs').style.display = '';
} else if (activeTab === 'frame') {
document.getElementById('panel-frame').style.display = '';
}
});
});
// ── Thumb controls ──
function wireThumb(id, stateKey, displayId) {
const slider = document.getElementById(id);
const disp = document.getElementById(displayId);
const mn = parseFloat(slider.min), mx = parseFloat(slider.max);
slider.addEventListener('input', () => {
const v = parseFloat(slider.value);
state[stateKey] = v;
disp.value = v;
applyThumbStyles();
});
disp.addEventListener('change', () => {
const raw = parseFloat(disp.value);
if (isNaN(raw)) { disp.value = state[stateKey]; return; }
state[stateKey] = raw;
slider.value = Math.min(mx, Math.max(mn, raw));
applyThumbStyles();
});
}
// Strip alignment
document.getElementById('ctrl-strip-align').addEventListener('change', function() {
state.stripAlign = this.value;
applyThumbStyles();
});
// Strip hide toggle
document.getElementById('ctrl-strip-hide').addEventListener('change', function() {
state.stripHide = this.checked;
applyThumbStyles();
});
// Strip bg color
const stripBgPicker = document.getElementById('ctrl-strip-bg-color');
const stripBgHex = document.getElementById('ctrl-strip-bg-hex');
function setStripBgColor(hex) {
hex = hex.replace(/[^#0-9a-fA-F]/g, '');
if (!hex.startsWith('#')) hex = '#' + hex;
if (hex.length === 7) {
state.stripBgColor = hex.toLowerCase();
stripBgPicker.value = hex;
stripBgHex.value = hex.toLowerCase();
applyThumbStyles();
}
}
stripBgPicker.addEventListener('input', e => setStripBgColor(e.target.value));
stripBgHex.addEventListener('input', e => setStripBgColor(e.target.value));
wireThumb('ctrl-strip-bg-opacity', 'stripBgOpacity', 'val-strip-bg-opacity');
document.getElementById('ctrl-strip-bg-enable').addEventListener('change', function() {
state.stripBgEnable = this.checked;
document.getElementById('strip-bg-controls').style.display = this.checked ? '' : 'none';
applyThumbStyles();
});
// Strip BG type toggle
document.querySelectorAll('.strip-bg-type-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.strip-bg-type-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
state.stripBgType = btn.dataset.type;
document.getElementById('strip-solid-controls').style.display = state.stripBgType === 'solid' ? '' : 'none';
document.getElementById('strip-gradient-controls').style.display = state.stripBgType === 'gradient' ? '' : 'none';
applyThumbStyles();
});
});
// Gradient pickers
function wireGradientPickerReset(pickerId, hexId, val) {
const picker = document.getElementById(pickerId);
const hex = document.getElementById(hexId);
if (hex) hex.value = val;
if (picker && val.startsWith('#') && val.length === 7) picker.value = val;
}
function wireGradientPicker(pickerId, hexId, stateKey) {
const picker = document.getElementById(pickerId);
const hex = document.getElementById(hexId);
function apply(val) {
state[stateKey] = val;
if (picker && val.startsWith('#') && val.length === 7) {
picker.value = val;
picker.style.visibility = '';
picker.style.width = '';
}
if (hex) hex.value = val;
applyThumbStyles();
}
if (picker) picker.addEventListener('input', e => apply(e.target.value));
if (hex) hex.addEventListener('input', e => apply(e.target.value));
}
wireGradientPicker('ctrl-strip-grad-from', 'ctrl-strip-grad-from-hex', 'stripGradFrom');
wireGradientPicker('ctrl-strip-grad-to', 'ctrl-strip-grad-to-hex', 'stripGradTo');
// From opacity
(function() {
const slider = document.getElementById('ctrl-strip-grad-from-opacity');
const disp = document.getElementById('val-strip-grad-from-opacity');
slider.addEventListener('input', () => { state.stripGradFromOpacity = parseFloat(slider.value); disp.value = slider.value; applyThumbStyles(); });
disp.addEventListener('change', () => { const v = parseFloat(disp.value); if (!isNaN(v)) { state.stripGradFromOpacity = v; slider.value = Math.min(100,Math.max(0,v)); applyThumbStyles(); } });
})();
document.getElementById('ctrl-strip-grad-dir').addEventListener('change', function() {
state.stripGradDir = this.value;
applyThumbStyles();
});
wireThumb('ctrl-thumb-width', 'thumbWidth', 'val-thumb-width');
wireThumb('ctrl-thumb-height', 'thumbHeight', 'val-thumb-height');
wireThumb('ctrl-thumb-radius', 'thumbRadius', 'val-thumb-radius');
wireThumb('ctrl-thumb-margin', 'thumbMargin', 'val-thumb-margin');
wireThumb('ctrl-thumb-paddingtb', 'thumbPaddingTB', 'val-thumb-paddingtb');
wireThumb('ctrl-active-opacity', 'activeOpacity', 'val-active-opacity');
wireThumb('ctrl-active-border-width', 'activeBorderWidth', 'val-active-border-width');
// ── Frame wires ──
function wireFrame(id, stateKey, displayId) {
const slider = document.getElementById(id);
const disp = document.getElementById(displayId);
if (!slider) return;
slider.addEventListener('input', () => {
state[stateKey] = parseFloat(slider.value);
if (disp) disp.value = slider.value;
applyFrameStyles();
});
if (disp) {
disp.addEventListener('change', () => {
const raw = parseFloat(disp.value);
if (isNaN(raw)) { disp.value = state[stateKey]; return; }
state[stateKey] = raw;
const mn = parseFloat(slider.min), mx = parseFloat(slider.max);
slider.value = Math.min(mx, Math.max(mn, raw));
applyFrameStyles();
});
}
}
wireFrame('ctrl-frame-radius', 'frameRadius', 'val-frame-radius');
document.getElementById('ctrl-full-size').addEventListener('change', function() {
state.fullSize = this.checked;
document.getElementById('full-size-fit-wrap').style.display = this.checked ? '' : 'none';
applyFrameStyles();
});
document.getElementById('ctrl-object-fit').addEventListener('change', function() {
state.objectFit = this.value;
applyThumbStyles();
});
// ── Export ──
function buildCSS() {
const sections = [];
// ── Arrows ──
const arrowPreset = arrowPresets.find(p => p.name === state.arrowPreset);
const hasCustomRight = state.arrowSvgRight.trim().length > 0;
let uriRight, uriLeft;
if (hasCustomRight) {
const cr = colorizeArrowSvg(state.arrowSvgRight);
uriRight = svgToDataUri(cr);
uriLeft = (!state.arrowMirror && state.arrowSvgLeft.trim())
? svgToDataUri(colorizeArrowSvg(state.arrowSvgLeft))
: mirrorSvgUri(cr);
} else if (arrowPreset) {
const cr = colorizeArrowSvg(arrowPreset.svg);
uriRight = svgToDataUri(cr);
uriLeft = arrowPreset.svgLeft
? svgToDataUri(colorizeArrowSvg(arrowPreset.svgLeft))
: mirrorSvgUri(cr);
}
{
let arrows = '/* Arrows */\n';
let hasArrow = false;
if (state.arrowSize !== 1.5) {
arrows += '.w-lightbox-control { background-size: ' + state.arrowSize + 'rem; }\n';
hasArrow = true;
}
const extra = state.arrowSize - 1.5;
if (extra > 0) {
const w = 'calc(4em + ' + extra.toFixed(4) + 'rem)';
arrows += '.w-lightbox-left, .w-lightbox-right { width: ' + w + '; }\n';
hasArrow = true;
}
if (state.arrowPosition !== 0) {
arrows += '.w-lightbox-right { right: ' + state.arrowPosition + 'rem; }\n';
arrows += '.w-lightbox-left { left: ' + state.arrowPosition + 'rem; }\n';
hasArrow = true;
}
if (uriRight && !(arrowPreset && arrowPreset.name === 'Default' && state.arrowColor === '#ffffff')) {
arrows += '.w-lightbox-right { background-image: url("' + uriRight + '"); }\n';
arrows += '.w-lightbox-left { background-image: url("' + uriLeft + '"); }\n';
hasArrow = true;
}
const op = (state.arrowOpacity / 100).toFixed(2);
if (state.arrowOpacity !== 50) {
arrows += '@media (min-width: 768px) {\n';
arrows += ' .w-lightbox-left, .w-lightbox-right { opacity: ' + op + '; }\n';
arrows += ' .w-lightbox-inactive, .w-lightbox-inactive:hover { opacity: 0; }\n}\n';
hasArrow = true;
}
if (hasArrow) sections.push(arrows.trim());
}
// ── Close ──
{
const preset = closePresets.find(p => p.name === state.closePreset);
const hasCustom = state.closeSvg.trim().length > 0;
let closeRules = '';
if (hasCustom) {
const uri = svgToDataUri(colorizeCloseSvg(state.closeSvg));
if (uri) closeRules += '.w-lightbox-close { background-image: url("' + uri + '"); }\n';
} else if (preset && !(preset.name === 'Default' && state.closeColor === '#ffffff')) {
const uri = svgToDataUri(colorizeCloseSvg(preset.svg));
if (uri) closeRules += '.w-lightbox-close { background-image: url("' + uri + '"); }\n';
}
if (state.closeSize !== 1) closeRules += '.w-lightbox-close { background-size: ' + state.closeSize + 'rem; height: ' + (state.closeSize * 2.3).toFixed(4) + 'rem; }\n';
if (state.closeOpacity !== 80) closeRules += '@media (min-width: 768px) { .w-lightbox-close { opacity: ' + (state.closeOpacity/100).toFixed(2) + '; } }\n';
if (state.closeSide === 'left') {
closeRules += '.w-lightbox-close { left: ' + (state.closeOffsetX !== 0 ? state.closeOffsetX + 'rem' : '0') + '; right: auto; }\n';
} else if (state.closeOffsetX !== 0) {
closeRules += '.w-lightbox-close { right: ' + state.closeOffsetX + 'rem; }\n';
}
if (state.closeOffsetY !== 0) closeRules += '.w-lightbox-close { top: ' + state.closeOffsetY + 'rem; }\n';
if (closeRules) sections.push('/* Close button */\n' + closeRules.trim());
}
// ── Backdrop ──
{
const r = parseInt(state.color.slice(1,3), 16);
const g = parseInt(state.color.slice(3,5), 16);
const b = parseInt(state.color.slice(5,7), 16);
const a = (state.opacity / 100).toFixed(2);
let backdrop = '/* Backdrop */\n.w-lightbox-backdrop {\n';
backdrop += ' background: rgba(' + r + ', ' + g + ', ' + b + ', ' + a + ');\n';
if (state.blur > 0) {
backdrop += ' backdrop-filter: blur(' + state.blur + 'px);\n';
backdrop += ' -webkit-backdrop-filter: blur(' + state.blur + 'px);\n';
}
backdrop += '}';
sections.push(backdrop);
}
// ── Thumbnails + Strip (all together before push) ──
{
const viewH = 96 - state.thumbHeight - 2;
const thumbRem = parseFloat(state.thumbRadius.toFixed(4));
let thumbs = '/* Thumbnails */\n';
thumbs += '/* viewHeight = 96 - ' + state.thumbHeight + 'vh(thumb) - 2vh(padding) = ' + viewH + 'vh */\n';
thumbs += '@media (min-width: 768px) {\n';
thumbs += ' .w-lightbox-group,\n';
thumbs += ' .w-lightbox-group .w-lightbox-view,\n';
thumbs += ' .w-lightbox-group .w-lightbox-view:before { height: ' + viewH + 'vh; }\n';
thumbs += ' .w-lightbox-group .w-lightbox-image { max-height: ' + viewH + 'vh; }\n';
thumbs += '}\n';
// Thumb item/thumbnail sizing
thumbs += '.w-lightbox-strip { font-size: 0; }\n';
let itemRules = ' width: ' + state.thumbWidth + 'vh;\n padding-left: 0;\n padding-right: 0;\n';
if (state.thumbMargin !== 1) {
if (state.thumbMargin > 0) {
itemRules += ' margin-left: ' + state.thumbMargin + 'vh;\n';
itemRules += ' margin-right: ' + state.thumbMargin + 'vh;\n';
}
}
itemRules += ' padding-top: ' + state.thumbPaddingTB + 'vh;\n';
itemRules += ' padding-bottom: ' + state.thumbPaddingTB + 'vh;\n';
thumbs += '.w-lightbox-item {\n' + itemRules + '}\n';
thumbs += '.w-lightbox-thumbnail {\n';
thumbs += ' height: ' + state.thumbHeight + 'vh;\n';
if (state.thumbRadius > 0) thumbs += ' border-radius: ' + thumbRem + 'rem;\n';
thumbs += '}\n';
thumbs += '.w-lightbox-thumbnail-image { object-fit: cover; width: 100%; height: 100%; }';
if (state.activeOpacity !== 30) thumbs += '\n.w-lightbox-active { opacity: ' + (state.activeOpacity/100).toFixed(2) + '; }';
if (state.activeBorderWidth > 0) thumbs += '\n.w-lightbox-active .w-lightbox-thumbnail { border: ' + state.activeBorderWidth + 'rem solid ' + state.activeBorderColor + '; box-sizing: border-box; }';
// Strip alignment
if (state.stripAlign && state.stripAlign !== 'center') {
thumbs += '\n.w-lightbox-strip { text-align: ' + state.stripAlign + '; }';
}
// Strip hide / background
if (state.stripHide) {
thumbs += '\n.w-lightbox-strip { display: none; }';
} else if (state.stripBgEnable) {
if (state.stripBgType === 'gradient') {
const fromHex = state.stripGradFrom;
const fR = parseInt(fromHex.slice(1,3),16), fG = parseInt(fromHex.slice(3,5),16), fB = parseInt(fromHex.slice(5,7),16);
const fromRgba = 'rgba(' + fR + ',' + fG + ',' + fB + ',' + (state.stripGradFromOpacity/100).toFixed(2) + ')';
thumbs += '\n.w-lightbox-strip { background: linear-gradient(' + state.stripGradDir + ', ' + fromRgba + ', ' + state.stripGradTo + '); }';
} else if (state.stripBgOpacity > 0) {
const sr = parseInt(state.stripBgColor.slice(1,3),16);
const sg = parseInt(state.stripBgColor.slice(3,5),16);
const sb = parseInt(state.stripBgColor.slice(5,7),16);
thumbs += '\n.w-lightbox-strip { background-color: rgba(' + sr + ',' + sg + ',' + sb + ',' + (state.stripBgOpacity/100).toFixed(2) + '); }';
}
}
sections.push(thumbs);
}
// ── Full size ──
if (state.fullSize) {
let fs = '/* Full size */\n';
fs += '.w-lightbox-content { height: 100vh; margin-top: 0; }\n';
fs += '.w-lightbox-group,\n.w-lightbox-group .w-lightbox-view,\n.w-lightbox-group .w-lightbox-view:before { height: 100vh; }\n';
fs += '.w-lightbox-view:before { display: none; }\n';
fs += '.w-lightbox-frame { display: block; width: 100%; height: 100vh; vertical-align: top; }\n';
fs += '.w-lightbox-figure { width: 100%; height: 100%; margin: 0; }\n';
fs += '.w-lightbox-image:not(.w-lightbox-thumbnail-image) { display: block; width: 100%; height: 100%; max-height: none; max-width: none; float: none; object-fit: ' + state.objectFit + '; }';
sections.push(fs);
}
// ── Frame ──
if (state.frameRadius > 0) {
let frame = '/* Frame */\n.w-lightbox-frame {\n';
frame += ' border-radius: ' + state.frameRadius + 'rem;\n';
frame += ' overflow: hidden;\n}';
sections.push(frame);
}
sections.unshift('/* Built with Lightbox Customizer by formburg.com */');
return sections.join('\n\n');
}
function syntaxHighlight(css) {
return css
.replace(/(\/\*[^*]*\*\/)/g, '')
.replace(/(\.[\w-]+\s*\{)/g, '$1 ')
.replace(/(\})/g, '$1 ')
.replace(/([\w-]+)(\s*:)/g, '$1 $2')
.replace(/:\s*([^;{}\n]+)(;)/g, ': $1 ;');
}
function openExport() {
const css = buildCSS();
const wrapped = '';
// Display: escape < > so browser renders them as text
const display = wrapped.replace(//g, '>');
document.getElementById('export-code').innerHTML = syntaxHighlight(display);
document.getElementById('modal').classList.add('open');
document.getElementById('copy-btn').innerHTML = ' Copy code';
document.getElementById('copy-btn').classList.remove('copied');
}
function closeExport() {
document.getElementById('modal').classList.remove('open');
}
function handleModalClick(e) {
if (e.target === document.getElementById('modal')) closeExport();
}
function copyCode() {
const css = '';
const btn = document.getElementById('copy-btn');
function onSuccess() {
btn.innerHTML = '✓ Copied to clipboard!';
btn.classList.add('copied');
setTimeout(() => {
btn.innerHTML = ' Copy code';
btn.classList.remove('copied');
}, 2500);
}
// Try modern clipboard API first
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(css).then(onSuccess).catch(() => fallbackCopy(css, onSuccess));
} else {
fallbackCopy(css, onSuccess);
}
}
function fallbackCopy(text, onSuccess) {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.cssText = 'position:fixed;top:-9999px;left:-9999px;opacity:0;';
document.body.appendChild(ta);
ta.focus();
ta.select();
try {
document.execCommand('copy');
onSuccess();
} catch(e) {
alert('Copy failed — please select and copy the code manually.');
}
document.body.removeChild(ta);
}
// ── Modified indicators + label reset ──
function updateModified() {
document.querySelectorAll('.prop-label[data-key]').forEach(lbl => {
const key = lbl.dataset.key;
const def = lbl.dataset.default;
const cur = state[key];
const changed = def.startsWith('#')
? cur.toLowerCase() !== def.toLowerCase()
: isNaN(parseFloat(def))
? String(cur) !== String(def)
: Math.abs(parseFloat(cur) - parseFloat(def)) > 0.001;
lbl.classList.toggle('modified', changed);
});
}
document.querySelectorAll('.prop-label[data-key]').forEach(lbl => {
lbl.title = 'Click to reset to default';
lbl.addEventListener('click', () => {
const key = lbl.dataset.key;
const def = lbl.dataset.default;
const prop = lbl.closest('.prop');
if (def.startsWith('#')) {
state[key] = def;
if (key === 'arrowColor') setArrowColor(def);
else if (key === 'closeColor') setCloseColor(def);
else if (key === 'activeBorderColor') setActiveBorderColor(def);
else if (key === 'stripBgColor') setStripBgColor(def);
} else if (isNaN(parseFloat(def))) {
// String/select value (including color strings like 'transparent')
state[key] = def;
const sel = prop ? prop.querySelector('select') : null;
if (sel) sel.value = def;
// Color fields that are strings
if (key === 'color') { setColor(def); }
else if (key === 'stripGradFrom') { wireGradientPickerReset('ctrl-strip-grad-from', 'ctrl-strip-grad-from-hex', def); applyThumbStyles(); }
else if (key === 'stripGradTo') { wireGradientPickerReset('ctrl-strip-grad-to', 'ctrl-strip-grad-to-hex', def); applyThumbStyles(); }
else if (key === 'stripGradDir') { applyThumbStyles(); }
else if (['objectFit'].includes(key)) applyFrameStyles();
else if (['stripAlign'].includes(key)) applyThumbStyles();
else applyStyles();
} else {
state[key] = parseFloat(def);
const slider = prop ? prop.querySelector('input[type="range"]') : null;
const numInput = prop ? prop.querySelector('input[type="number"]') : null;
if (slider) slider.value = def;
if (numInput) numInput.value = def;
const thumbKeys = ['thumbWidth','thumbHeight','thumbRadius','thumbMargin','thumbPaddingTB','stripBgOpacity','activeOpacity','activeBorderWidth','stripGradFromOpacity'];
const closeKeys = ['closeOffsetX','closeOffsetY','closeSize','closeOpacity'];
const arrowKeys = ['arrowPosition','arrowSize','arrowOpacity'];
const frameKeys = ['frameRadius'];
if (thumbKeys.includes(key)) {
if (key === 'stripGradFromOpacity') {
const sl = document.getElementById('ctrl-strip-grad-from-opacity');
const dv = document.getElementById('val-strip-grad-from-opacity');
if (sl) sl.value = def;
if (dv) dv.value = def;
}
applyThumbStyles();
}
else if (closeKeys.includes(key)) applyCloseStyles();
else if (arrowKeys.includes(key)) applyArrowStyles();
else if (frameKeys.includes(key)) applyFrameStyles();
else applyStyles();
}
updateModified();
});
});
// Patch applyStyles + applyThumbStyles to call updateModified
const _origApplyStyles = applyStyles;
applyStyles = function() { _origApplyStyles(); updateModified(); };
const _origApplyThumb = applyThumbStyles;
applyThumbStyles = function() { _origApplyThumb(); updateModified(); };
const _origApplyFrame = applyFrameStyles;
applyFrameStyles = function() { _origApplyFrame(); updateModified(); };
// ── Init ──
applyStyles();