<div class="pride-gallery-intro">
<p class="pride-gallery-kicker">Galleria fotografica</p>
<h2 class="pride-gallery-main-title">Foto Pride</h2>
<p class="pride-gallery-description">
Clicca su una foto per vedere la galleria completa del/la fotografo/a.
</p>
</div>
<nav id="pride-photographer-nav" class="pride-photographer-nav" aria-label="Elenco fotografi"></nav>
<div id="pride-gallery" class="pride-gallery-wrapper"></div>
<style>
.pride-gallery-intro {
max-width: 1180px;
margin: 0 auto 22px;
padding: 8px 0 0;
}
.pride-gallery-kicker {
margin: 0 0 6px;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.08em;
opacity: 0.65;
}
.pride-gallery-main-title {
margin: 0 0 8px;
font-size: clamp(30px, 4vw, 56px);
line-height: 0.95;
font-weight: 800;
letter-spacing: -0.055em;
}
.pride-gallery-description {
margin: 0;
max-width: 640px;
font-size: 16px;
line-height: 1.5;
opacity: 0.78;
}
.pride-photographer-nav {
max-width: 1180px;
margin: 24px auto 32px;
padding: 18px 22px;
background: #f7f7f7;
border: 1px solid #e2e2e2;
border-radius: 12px;
box-sizing: border-box;
}
.pride-photographer-nav::before {
content: "Fotografi";
display: block;
font-size: 18px;
font-weight: 700;
margin-bottom: 12px;
}
.pride-photographer-nav-list {
margin: 0;
padding-left: 20px;
columns: 3;
column-gap: 36px;
}
.pride-photographer-nav-list li {
break-inside: avoid;
margin: 0 0 7px;
padding: 0;
line-height: 1.35;
}
.pride-photographer-nav-list a {
color: inherit;
text-decoration: underline;
text-underline-offset: 3px;
font-size: 14px;
}
.pride-photographer-nav-list a:hover {
opacity: 0.7;
}
.pride-gallery-wrapper {
max-width: 1180px;
margin: 0 auto;
padding: 4px 0 40px;
}
.pride-photographer-section {
margin: 30px 0 42px;
scroll-margin-top: 90px;
}
.pride-photographer-title {
font-size: clamp(18px, 1.8vw, 26px);
line-height: 1.1;
margin: 0 0 14px;
font-weight: 750;
letter-spacing: -0.025em;
}
.pride-gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(105px, 1fr));
gap: 14px;
}
.pride-gallery-item {
display: block;
overflow: hidden;
border-radius: 12px;
background: #f2f2f2;
aspect-ratio: 1 / 1;
text-decoration: none;
position: relative;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
}
.pride-gallery-item img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
transform: scale(1.01);
transition: transform 0.2s ease, filter 0.2s ease;
}
.pride-gallery-item:hover img {
transform: scale(1.06);
filter: brightness(1.04);
}
.pride-gallery-placeholder {
width: 100%;
height: 100%;
min-height: 105px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 8px;
box-sizing: border-box;
color: #555;
font-size: 12px;
background: #eee;
}
.pride-gallery-error {
padding: 18px;
border-radius: 12px;
background: #f5f5f5;
color: #444;
font-size: 15px;
}
@media (min-width: 900px) {
.pride-gallery-grid {
grid-template-columns: repeat(auto-fill, minmax(115px, 1fr));
gap: 16px;
}
}
@media (max-width: 900px) {
.pride-photographer-nav-list {
columns: 2;
}
}
@media (max-width: 600px) {
.pride-gallery-intro {
padding: 4px 0 0;
margin-bottom: 18px;
}
.pride-gallery-main-title {
font-size: 34px;
}
.pride-gallery-description {
font-size: 15px;
}
.pride-photographer-nav {
margin: 20px auto 28px;
padding: 16px 18px;
}
.pride-photographer-nav-list {
columns: 1;
}
.pride-photographer-nav-list a {
font-size: 14px;
}
.pride-gallery-wrapper {
padding: 4px 0 30px;
}
.pride-photographer-section {
margin: 26px 0 36px;
scroll-margin-top: 70px;
}
.pride-photographer-title {
font-size: 18px;
margin-bottom: 12px;
}
.pride-gallery-grid {
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
.pride-gallery-item {
border-radius: 10px;
}
}
</style>
<script>
(function () {
const JSON_URL = "/images/pride/foto_pride.txt?v=1";
const PHOTOS_PER_PHOTOGRAPHER = 6;
const RANDOMIZE_PHOTOGRAPHER_ORDER = false;
const gallery = document.getElementById("pride-gallery");
const photographerNav = document.getElementById("pride-photographer-nav");
let allPhotos = [];
let lightboxPhotos = [];
let lightboxIndex = 0;
let overlay = null;
let overlayImg = null;
let overlayTitle = null;
let overlayCounter = null;
let overlayDriveLink = null;
function cleanText(value) {
return String(value || "").trim();
}
function getPhotographerName(photo) {
const folderPath = cleanText(photo.folderPath);
if (folderPath) {
const parts = folderPath
.split("/")
.map(function (part) {
return part.trim();
})
.filter(Boolean);
if (parts.length >= 2) {
return parts[1];
}
}
return cleanText(photo.folder) || "Fotografo";
}
function getFolderGalleryKey(photo) {
return cleanText(photo.folderPath) || cleanText(photo.folderId) || getPhotographerName(photo);
}
function getFolderDisplayName(photo) {
const folderPath = cleanText(photo.folderPath);
if (folderPath) {
const parts = folderPath
.split("/")
.map(function (part) {
return part.trim();
})
.filter(Boolean);
if (parts.length) {
return parts[parts.length - 1];
}
}
return cleanText(photo.folder) || getPhotographerName(photo);
}
function getPhotoThumb(photo) {
return photo.thumbnailUrl || photo.imageUrl || "";
}
function getPhotoFull(photo) {
return photo.imageUrl || photo.viewUrl || "";
}
function normalizePhotos(data) {
const photos = Array.isArray(data) ? data : (data && data.photos ? data.photos : []);
return photos.filter(function (photo) {
return photo &&
getPhotoThumb(photo) &&
getPhotoFull(photo) &&
getPhotographerName(photo);
});
}
function shuffleArray(array) {
const copy = array.slice();
for (let i = copy.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const temp = copy[i];
copy[i] = copy[j];
copy[j] = temp;
}
return copy;
}
function groupByPhotographer(photos) {
const groups = {};
photos.forEach(function (photo) {
const photographer = getPhotographerName(photo);
if (!groups[photographer]) {
groups[photographer] = [];
}
groups[photographer].push(photo);
});
return groups;
}
function getPhotosFromSameFolder(photo) {
const key = getFolderGalleryKey(photo);
return allPhotos.filter(function (item) {
return getFolderGalleryKey(item) === key;
});
}
function createPlaceholder() {
const placeholder = document.createElement("div");
placeholder.className = "pride-gallery-placeholder";
placeholder.textContent = "Anteprima non disponibile";
return placeholder;
}
function createPhotoItem(photo) {
const link = document.createElement("a");
link.className = "pride-gallery-item";
link.href = getPhotoFull(photo);
const img = document.createElement("img");
img.src = getPhotoThumb(photo);
img.alt = photo.name || "Foto Pride";
img.loading = "lazy";
img.onerror = function () {
link.innerHTML = "";
link.href = photo.viewUrl || getPhotoFull(photo);
link.target = "_blank";
link.rel = "noopener";
link.appendChild(createPlaceholder());
};
link.addEventListener("click", function (event) {
event.preventDefault();
openLightbox(photo);
});
link.appendChild(img);
return link;
}
function createPhotographerNav(photographerNames, sectionIds) {
if (!photographerNav) {
return;
}
photographerNav.innerHTML = "";
const list = document.createElement("ul");
list.className = "pride-photographer-nav-list";
photographerNames.forEach(function (photographer, index) {
const item = document.createElement("li");
const link = document.createElement("a");
link.href = "#" + sectionIds[index];
link.textContent = photographer;
link.addEventListener("click", function (event) {
event.preventDefault();
const target = document.getElementById(sectionIds[index]);
if (target) {
target.scrollIntoView({
behavior: "smooth",
block: "start"
});
}
});
item.appendChild(link);
list.appendChild(item);
});
photographerNav.appendChild(list);
}
function renderGallery(photos) {
gallery.innerHTML = "";
const groups = groupByPhotographer(photos);
let photographerNames = Object.keys(groups).sort(function (a, b) {
return a.localeCompare(b, "it", { sensitivity: "base" });
});
if (RANDOMIZE_PHOTOGRAPHER_ORDER) {
photographerNames = shuffleArray(photographerNames);
}
const sectionIds = photographerNames.map(function (photographer, index) {
return "fotografo-pride-" + index;
});
createPhotographerNav(photographerNames, sectionIds);
const fragment = document.createDocumentFragment();
photographerNames.forEach(function (photographer, index) {
const allPhotosForPhotographer = groups[photographer];
const selectedPhotos = shuffleArray(allPhotosForPhotographer).slice(0, PHOTOS_PER_PHOTOGRAPHER);
const section = document.createElement("section");
section.className = "pride-photographer-section";
section.id = sectionIds[index];
const title = document.createElement("h2");
title.className = "pride-photographer-title";
title.textContent = photographer;
const grid = document.createElement("div");
grid.className = "pride-gallery-grid";
selectedPhotos.forEach(function (photo) {
grid.appendChild(createPhotoItem(photo));
});
section.appendChild(title);
section.appendChild(grid);
fragment.appendChild(section);
});
gallery.appendChild(fragment);
}
function createLightboxIfNeeded() {
if (overlay) {
return;
}
overlay = document.createElement("div");
overlay.id = "pride-lightbox-generated";
overlay.setAttribute("aria-hidden", "true");
overlay.style.cssText = [
"position: fixed",
"top: 0",
"right: 0",
"bottom: 0",
"left: 0",
"z-index: 2147483647",
"display: none",
"align-items: center",
"justify-content: center",
"background: rgba(0, 0, 0, 0.94)",
"padding: 24px",
"box-sizing: border-box"
].join(";");
const closeBtn = document.createElement("button");
closeBtn.type = "button";
closeBtn.innerHTML = "×";
closeBtn.setAttribute("aria-label", "Chiudi");
closeBtn.style.cssText = [
"position: fixed",
"top: 16px",
"right: 18px",
"z-index: 2147483647",
"width: 46px",
"height: 46px",
"border: 0",
"border-radius: 999px",
"background: rgba(255, 255, 255, 0.18)",
"color: #fff",
"font-size: 34px",
"line-height: 1",
"cursor: pointer"
].join(";");
const prevBtn = document.createElement("button");
prevBtn.type = "button";
prevBtn.innerHTML = "‹";
prevBtn.setAttribute("aria-label", "Foto precedente");
prevBtn.style.cssText = [
"position: fixed",
"top: 50%",
"left: 18px",
"transform: translateY(-50%)",
"z-index: 2147483647",
"width: 50px",
"height: 66px",
"border: 0",
"border-radius: 999px",
"background: rgba(255, 255, 255, 0.18)",
"color: #fff",
"font-size: 56px",
"line-height: 1",
"cursor: pointer"
].join(";");
const nextBtn = document.createElement("button");
nextBtn.type = "button";
nextBtn.innerHTML = "›";
nextBtn.setAttribute("aria-label", "Foto successiva");
nextBtn.style.cssText = [
"position: fixed",
"top: 50%",
"right: 18px",
"transform: translateY(-50%)",
"z-index: 2147483647",
"width: 50px",
"height: 66px",
"border: 0",
"border-radius: 999px",
"background: rgba(255, 255, 255, 0.18)",
"color: #fff",
"font-size: 56px",
"line-height: 1",
"cursor: pointer"
].join(";");
const content = document.createElement("div");
content.style.cssText = [
"width: min(1100px, 100%)",
"max-height: 92vh",
"display: flex",
"flex-direction: column",
"align-items: center",
"justify-content: center"
].join(";");
overlayImg = document.createElement("img");
overlayImg.alt = "";
overlayImg.style.cssText = [
"max-width: 100%",
"max-height: 78vh",
"object-fit: contain",
"border-radius: 10px",
"background: #111"
].join(";");
const meta = document.createElement("div");
meta.style.cssText = [
"width: min(900px, 100%)",
"margin-top: 12px",
"color: #fff",
"text-align: center",
"font-size: 14px"
].join(";");
overlayTitle = document.createElement("div");
overlayTitle.style.cssText = [
"font-weight: 600",
"margin-bottom: 4px"
].join(";");
overlayCounter = document.createElement("div");
overlayCounter.style.cssText = [
"opacity: 0.75",
"margin-bottom: 6px"
].join(";");
overlayDriveLink = document.createElement("a");
overlayDriveLink.textContent = "Apri su Drive";
overlayDriveLink.target = "_blank";
overlayDriveLink.rel = "noopener";
overlayDriveLink.style.cssText = [
"color: #fff",
"opacity: 0.85",
"text-decoration: underline",
"text-underline-offset: 3px"
].join(";");
meta.appendChild(overlayTitle);
meta.appendChild(overlayCounter);
meta.appendChild(overlayDriveLink);
content.appendChild(overlayImg);
content.appendChild(meta);
overlay.appendChild(closeBtn);
overlay.appendChild(prevBtn);
overlay.appendChild(content);
overlay.appendChild(nextBtn);
document.body.appendChild(overlay);
closeBtn.addEventListener("click", closeLightbox);
prevBtn.addEventListener("click", showPrevPhoto);
nextBtn.addEventListener("click", showNextPhoto);
overlay.addEventListener("click", function (event) {
if (event.target === overlay) {
closeLightbox();
}
});
}
function openLightbox(photo) {
createLightboxIfNeeded();
lightboxPhotos = getPhotosFromSameFolder(photo);
if (!lightboxPhotos.length) {
lightboxPhotos = [photo];
}
lightboxIndex = lightboxPhotos.findIndex(function (item) {
return item.id === photo.id;
});
if (lightboxIndex < 0) {
lightboxIndex = 0;
}
updateLightbox();
overlay.style.display = "flex";
overlay.setAttribute("aria-hidden", "false");
document.documentElement.style.overflow = "hidden";
document.body.style.overflow = "hidden";
}
function closeLightbox() {
if (!overlay) {
return;
}
overlay.style.display = "none";
overlay.setAttribute("aria-hidden", "true");
document.documentElement.style.overflow = "";
document.body.style.overflow = "";
if (overlayImg) {
overlayImg.src = "";
}
}
function updateLightbox() {
const photo = lightboxPhotos[lightboxIndex];
if (!photo) {
return;
}
overlayImg.src = getPhotoFull(photo);
overlayImg.alt = photo.name || "Foto Pride";
overlayTitle.textContent = getFolderDisplayName(photo);
overlayCounter.textContent = (lightboxIndex + 1) + " / " + lightboxPhotos.length;
overlayDriveLink.href = photo.viewUrl || getPhotoFull(photo);
}
function showPrevPhoto() {
if (!lightboxPhotos.length) {
return;
}
lightboxIndex = (lightboxIndex - 1 + lightboxPhotos.length) % lightboxPhotos.length;
updateLightbox();
}
function showNextPhoto() {
if (!lightboxPhotos.length) {
return;
}
lightboxIndex = (lightboxIndex + 1) % lightboxPhotos.length;
updateLightbox();
}
function showError(message) {
if (!gallery) {
return;
}
gallery.innerHTML = "";
const errorBox = document.createElement("div");
errorBox.className = "pride-gallery-error";
errorBox.textContent = message;
gallery.appendChild(errorBox);
}
async function loadTextJsonFile() {
const response = await fetch(JSON_URL, {
cache: "force-cache"
});
if (!response.ok) {
throw new Error("Impossibile caricare il file. HTTP " + response.status);
}
const text = await response.text();
const jsonText = text
.replace(/^\uFEFF/, "")
.replace(/^PRIDE_PHOTOS_TXT_V1\s*/, "")
.trim();
return JSON.parse(jsonText);
}
async function loadGallery() {
try {
if (!gallery) {
return;
}
const data = await loadTextJsonFile();
allPhotos = normalizePhotos(data);
if (!allPhotos.length) {
throw new Error("Nessuna foto valida trovata nel file.");
}
renderGallery(allPhotos);
} catch (error) {
console.error("Errore galleria Pride:", error);
showError("Errore nel caricamento della galleria.");
}
}
document.addEventListener("keydown", function (event) {
if (!overlay || overlay.style.display !== "flex") {
return;
}
if (event.key === "Escape") {
closeLightbox();
}
if (event.key === "ArrowLeft") {
showPrevPhoto();
}
if (event.key === "ArrowRight") {
showNextPhoto();
}
});
loadGallery();
})();
</script>