Okay Wish I could say I know exactly. Will do better next time promise lol
This commit is contained in:
611
mkdocs/docs/assets/js/image-gallery.js
Normal file
611
mkdocs/docs/assets/js/image-gallery.js
Normal file
@@ -0,0 +1,611 @@
|
||||
/**
|
||||
* Image Gallery Hydration for MkDocs
|
||||
*
|
||||
* Automatically converts .photo-block, .photo-card-block, and .photo-album-block
|
||||
* placeholders into actual images/galleries when landing pages with photo blocks
|
||||
* are exported to MkDocs.
|
||||
*
|
||||
* Uses the same MEDIA_API_URL and PUBLIC_URL globals as video-player.js
|
||||
* (injected by hooks/env_config_hook.py).
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Configuration — same globals as video-player.js
|
||||
var MEDIA_API_URL = window.MEDIA_API_URL || 'http://localhost:4100';
|
||||
var PUBLIC_URL = window.PUBLIC_URL || 'http://localhost:3000';
|
||||
|
||||
/**
|
||||
* Escape HTML to prevent XSS
|
||||
*/
|
||||
function escapeHtml(unsafe) {
|
||||
return String(unsafe)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format view count (e.g. 1200 -> "1.2K views")
|
||||
*/
|
||||
function formatViewCount(count) {
|
||||
if (!count || count <= 0) return '0 views';
|
||||
if (count === 1) return '1 view';
|
||||
if (count < 1000) return count + ' views';
|
||||
if (count < 1000000) return (count / 1000).toFixed(1).replace(/\.0$/, '') + 'K views';
|
||||
return (count / 1000000).toFixed(1).replace(/\.0$/, '') + 'M views';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get gallery URL for a photo
|
||||
*/
|
||||
function getGalleryUrl(photoId) {
|
||||
return PUBLIC_URL + '/gallery?expanded=photo-' + photoId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error state on an element
|
||||
*/
|
||||
function showError(el, message) {
|
||||
el.innerHTML =
|
||||
'<div class="photo-error">' +
|
||||
'<svg fill="currentColor" viewBox="0 0 20 20">' +
|
||||
'<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />' +
|
||||
'</svg>' +
|
||||
'<p>' + escapeHtml(message) + '</p>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch photo metadata from Media API
|
||||
*/
|
||||
async function fetchPhotoMetadata(photoId) {
|
||||
try {
|
||||
var response = await fetch(MEDIA_API_URL + '/api/public/photos/' + photoId);
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch photo ' + photoId + ': ' + response.statusText);
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error fetching photo ' + photoId + ' metadata:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch album data from Media API
|
||||
*/
|
||||
async function fetchAlbumData(albumId) {
|
||||
try {
|
||||
var response = await fetch(MEDIA_API_URL + '/api/public/albums/' + albumId);
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch album ' + albumId + ': ' + response.statusText);
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error fetching album ' + albumId + ':', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Photo Block ─────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Render a single photo block.
|
||||
* Replaces placeholder with an <img> tag, optional caption, and gallery link.
|
||||
*/
|
||||
async function renderPhotoBlock(el) {
|
||||
var photoId = parseInt(el.dataset.photoId, 10);
|
||||
var size = el.dataset.size || 'large';
|
||||
var caption = el.dataset.caption || '';
|
||||
var linkToGallery = el.dataset.linkToGallery !== 'false';
|
||||
var alignment = el.dataset.alignment || 'center';
|
||||
|
||||
if (!photoId || isNaN(photoId)) {
|
||||
showError(el, 'Invalid photo ID');
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
el.innerHTML = '<div class="photo-loading">Loading photo...</div>';
|
||||
|
||||
// Fetch metadata for alt text and title
|
||||
var metadata = await fetchPhotoMetadata(photoId);
|
||||
if (!metadata) {
|
||||
showError(el, 'Photo not found (ID: ' + photoId + ')');
|
||||
return;
|
||||
}
|
||||
|
||||
var imgUrl = MEDIA_API_URL + '/api/public/photos/' + photoId + '/image?size=' + size;
|
||||
var altText = escapeHtml(metadata.title || 'Photo ' + photoId);
|
||||
|
||||
// Build image element
|
||||
var img = document.createElement('img');
|
||||
img.src = imgUrl;
|
||||
img.alt = altText;
|
||||
img.title = altText;
|
||||
img.style.width = '100%';
|
||||
img.style.height = 'auto';
|
||||
img.style.borderRadius = '8px';
|
||||
img.style.display = 'block';
|
||||
|
||||
// Clear and rebuild
|
||||
el.innerHTML = '';
|
||||
el.style.textAlign = alignment;
|
||||
|
||||
if (linkToGallery) {
|
||||
var link = document.createElement('a');
|
||||
link.href = getGalleryUrl(photoId);
|
||||
link.target = '_blank';
|
||||
link.appendChild(img);
|
||||
el.appendChild(link);
|
||||
} else {
|
||||
el.appendChild(img);
|
||||
}
|
||||
|
||||
// Add caption if provided (from data attribute or metadata)
|
||||
var captionText = caption || metadata.description || '';
|
||||
if (captionText) {
|
||||
var captionEl = document.createElement('div');
|
||||
captionEl.className = 'photo-caption';
|
||||
captionEl.textContent = captionText;
|
||||
el.appendChild(captionEl);
|
||||
}
|
||||
|
||||
console.log('Rendered photo block for photo ' + photoId);
|
||||
}
|
||||
|
||||
// ─── Photo Card ──────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Hydrate a photo card block:
|
||||
* 1. Fix thumbnail URL using MEDIA_API_URL
|
||||
* 2. Fetch live metadata (view count)
|
||||
* 3. Mark as hydrated
|
||||
*/
|
||||
async function hydratePhotoCard(cardEl) {
|
||||
var photoId = parseInt(cardEl.dataset.photoId, 10);
|
||||
if (!photoId || isNaN(photoId)) return;
|
||||
|
||||
// 1. Fix thumbnail URL
|
||||
var img = cardEl.querySelector('img');
|
||||
if (img) {
|
||||
var correctThumbUrl = MEDIA_API_URL + '/api/public/photos/' + photoId + '/thumbnail?v=' + Date.now();
|
||||
img.src = correctThumbUrl;
|
||||
img.onerror = function() {
|
||||
img.style.display = 'none';
|
||||
};
|
||||
}
|
||||
|
||||
// 2. Fetch live metadata to update views
|
||||
try {
|
||||
var metadata = await fetchPhotoMetadata(photoId);
|
||||
if (metadata) {
|
||||
// Update view count if rendered in card
|
||||
var viewsEl = cardEl.querySelector('[data-role="views"]');
|
||||
if (viewsEl && metadata.viewCount !== undefined) {
|
||||
viewsEl.textContent = formatViewCount(metadata.viewCount);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Non-critical
|
||||
}
|
||||
|
||||
// 3. Fix "View →" link to use correct gallery URL
|
||||
var link = cardEl.querySelector('a');
|
||||
if (link) {
|
||||
link.href = getGalleryUrl(photoId);
|
||||
link.target = '_blank';
|
||||
}
|
||||
|
||||
// 4. Replace "View →" text with gallery button
|
||||
var infoBar = cardEl.querySelector('a > div:last-child');
|
||||
if (infoBar) {
|
||||
var flexRow = infoBar.querySelector('div:last-child');
|
||||
if (flexRow) {
|
||||
var spans = flexRow.querySelectorAll('span');
|
||||
var viewSpan = null;
|
||||
for (var i = 0; i < spans.length; i++) {
|
||||
if (spans[i].textContent.indexOf('View') !== -1) {
|
||||
viewSpan = spans[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (viewSpan) {
|
||||
var galleryLink = document.createElement('a');
|
||||
galleryLink.href = getGalleryUrl(photoId);
|
||||
galleryLink.target = '_blank';
|
||||
galleryLink.innerHTML = '📷 Gallery';
|
||||
galleryLink.className = 'photo-card-gallery-btn';
|
||||
galleryLink.style.cssText = 'background:#43cea2;border:none;color:#fff;font-size:12px;font-weight:600;padding:5px 14px;border-radius:4px;cursor:pointer;transition:background 0.15s;text-decoration:none;display:inline-block;';
|
||||
galleryLink.title = 'Open in gallery';
|
||||
galleryLink.onmouseenter = function() { galleryLink.style.background = '#5dd8b0'; };
|
||||
galleryLink.onmouseleave = function() { galleryLink.style.background = '#43cea2'; };
|
||||
galleryLink.addEventListener('click', function(ev) {
|
||||
ev.stopPropagation();
|
||||
});
|
||||
viewSpan.replaceWith(galleryLink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark as hydrated
|
||||
cardEl.dataset.hydrated = 'true';
|
||||
console.log('Hydrated photo card for photo ' + photoId);
|
||||
}
|
||||
|
||||
// ─── Photo Album ─────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Render a photo album block.
|
||||
* Fetches album data and builds a responsive grid of thumbnails.
|
||||
*/
|
||||
async function renderAlbumBlock(el) {
|
||||
var albumId = parseInt(el.dataset.albumId, 10);
|
||||
var columns = parseInt(el.dataset.columns, 10) || 3;
|
||||
var maxPhotos = parseInt(el.dataset.maxPhotos, 10) || 12;
|
||||
var showTitle = el.dataset.showTitle !== 'false';
|
||||
|
||||
if (!albumId || isNaN(albumId)) {
|
||||
showError(el, 'Invalid album ID');
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
el.innerHTML = '<div class="photo-loading">Loading album...</div>';
|
||||
|
||||
// Fetch album data
|
||||
var album = await fetchAlbumData(albumId);
|
||||
if (!album) {
|
||||
showError(el, 'Album not found (ID: ' + albumId + ')');
|
||||
return;
|
||||
}
|
||||
|
||||
// Build album content
|
||||
el.innerHTML = '';
|
||||
|
||||
// Album title
|
||||
if (showTitle && album.title) {
|
||||
var titleEl = document.createElement('h3');
|
||||
titleEl.className = 'photo-album-title';
|
||||
titleEl.textContent = album.title;
|
||||
el.appendChild(titleEl);
|
||||
}
|
||||
|
||||
// Photo grid
|
||||
var photos = (album.photos || []).slice(0, maxPhotos);
|
||||
if (photos.length === 0) {
|
||||
el.innerHTML += '<div class="photo-loading">This album has no photos yet.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
var grid = document.createElement('div');
|
||||
grid.className = 'photo-album-grid cols-' + columns;
|
||||
|
||||
for (var i = 0; i < photos.length; i++) {
|
||||
var photo = photos[i];
|
||||
var cell = document.createElement('div');
|
||||
cell.className = 'photo-album-cell';
|
||||
|
||||
var cellLink = document.createElement('a');
|
||||
cellLink.href = getGalleryUrl(photo.id);
|
||||
cellLink.target = '_blank';
|
||||
cellLink.title = photo.title || 'Photo ' + photo.id;
|
||||
|
||||
var cellImg = document.createElement('img');
|
||||
cellImg.src = MEDIA_API_URL + '/api/public/photos/' + photo.id + '/thumbnail';
|
||||
cellImg.alt = photo.title || 'Photo ' + photo.id;
|
||||
cellImg.loading = 'lazy';
|
||||
|
||||
cellLink.appendChild(cellImg);
|
||||
cell.appendChild(cellLink);
|
||||
grid.appendChild(cell);
|
||||
}
|
||||
|
||||
el.appendChild(grid);
|
||||
|
||||
// "View full album" footer
|
||||
var footer = document.createElement('div');
|
||||
footer.className = 'photo-album-footer';
|
||||
var footerLink = document.createElement('a');
|
||||
footerLink.href = PUBLIC_URL + '/gallery?album=' + albumId;
|
||||
footerLink.target = '_blank';
|
||||
footerLink.textContent = 'View full album \u2192';
|
||||
footer.appendChild(footerLink);
|
||||
el.appendChild(footer);
|
||||
|
||||
console.log('Rendered album block for album ' + albumId + ' (' + photos.length + ' photos)');
|
||||
}
|
||||
|
||||
// ─── Photo Album Carousel ──────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Render a photo album carousel.
|
||||
* Fetches album data and builds a swipeable slideshow with navigation.
|
||||
*/
|
||||
async function renderCarouselBlock(el) {
|
||||
var albumId = parseInt(el.dataset.albumId, 10);
|
||||
var maxPhotos = parseInt(el.dataset.maxPhotos, 10) || 20;
|
||||
var showTitle = el.dataset.showTitle !== 'false';
|
||||
var autoPlayEnabled = el.dataset.autoPlay === 'true';
|
||||
|
||||
if (!albumId || isNaN(albumId)) {
|
||||
showError(el, 'Invalid album ID');
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
el.innerHTML = '<div class="photo-loading">Loading carousel...</div>';
|
||||
|
||||
// Fetch album data
|
||||
var album = await fetchAlbumData(albumId);
|
||||
if (!album) {
|
||||
showError(el, 'Album not found (ID: ' + albumId + ')');
|
||||
return;
|
||||
}
|
||||
|
||||
var photos = (album.photos || []).slice(0, maxPhotos);
|
||||
if (photos.length === 0) {
|
||||
el.innerHTML = '<div class="photo-loading">This album has no photos yet.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Build carousel
|
||||
el.innerHTML = '';
|
||||
el.classList.add('carousel-initialized');
|
||||
|
||||
// Album title
|
||||
if (showTitle && album.title) {
|
||||
var titleEl = document.createElement('h3');
|
||||
titleEl.className = 'photo-album-title';
|
||||
titleEl.textContent = album.title;
|
||||
el.appendChild(titleEl);
|
||||
}
|
||||
|
||||
// Viewport + track
|
||||
var viewport = document.createElement('div');
|
||||
viewport.className = 'carousel-viewport';
|
||||
|
||||
var track = document.createElement('div');
|
||||
track.className = 'carousel-track';
|
||||
|
||||
for (var i = 0; i < photos.length; i++) {
|
||||
var photo = photos[i];
|
||||
var slide = document.createElement('div');
|
||||
slide.className = 'carousel-slide';
|
||||
|
||||
var slideLink = document.createElement('a');
|
||||
slideLink.href = getGalleryUrl(photo.id);
|
||||
slideLink.target = '_blank';
|
||||
slideLink.title = photo.title || 'Photo ' + photo.id;
|
||||
|
||||
var slideImg = document.createElement('img');
|
||||
slideImg.src = MEDIA_API_URL + '/api/public/photos/' + photo.id + '/image?size=large';
|
||||
slideImg.alt = photo.title || 'Photo ' + photo.id;
|
||||
slideImg.loading = i === 0 ? 'eager' : 'lazy';
|
||||
|
||||
slideLink.appendChild(slideImg);
|
||||
slide.appendChild(slideLink);
|
||||
track.appendChild(slide);
|
||||
}
|
||||
|
||||
viewport.appendChild(track);
|
||||
el.appendChild(viewport);
|
||||
|
||||
// Navigation buttons (only if > 1 photo)
|
||||
if (photos.length > 1) {
|
||||
var prevBtn = document.createElement('button');
|
||||
prevBtn.className = 'carousel-btn carousel-btn-prev';
|
||||
prevBtn.setAttribute('aria-label', 'Previous photo');
|
||||
prevBtn.innerHTML = '❮';
|
||||
viewport.appendChild(prevBtn);
|
||||
|
||||
var nextBtn = document.createElement('button');
|
||||
nextBtn.className = 'carousel-btn carousel-btn-next';
|
||||
nextBtn.setAttribute('aria-label', 'Next photo');
|
||||
nextBtn.innerHTML = '❯';
|
||||
viewport.appendChild(nextBtn);
|
||||
|
||||
// Dot indicators
|
||||
var dotsWrap = document.createElement('div');
|
||||
dotsWrap.className = 'carousel-dots';
|
||||
for (var d = 0; d < photos.length; d++) {
|
||||
var dot = document.createElement('button');
|
||||
dot.className = 'carousel-dot' + (d === 0 ? ' active' : '');
|
||||
dot.setAttribute('aria-label', 'Go to photo ' + (d + 1));
|
||||
dot.dataset.index = String(d);
|
||||
dotsWrap.appendChild(dot);
|
||||
}
|
||||
el.appendChild(dotsWrap);
|
||||
|
||||
// Carousel state
|
||||
var currentIndex = 0;
|
||||
var totalSlides = photos.length;
|
||||
var autoPlayTimer = null;
|
||||
|
||||
function goTo(idx) {
|
||||
if (idx < 0) idx = totalSlides - 1;
|
||||
if (idx >= totalSlides) idx = 0;
|
||||
currentIndex = idx;
|
||||
track.style.transform = 'translateX(-' + (currentIndex * 100) + '%)';
|
||||
// Update dots
|
||||
var dots = dotsWrap.querySelectorAll('.carousel-dot');
|
||||
for (var j = 0; j < dots.length; j++) {
|
||||
dots[j].classList.toggle('active', j === currentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
prevBtn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
goTo(currentIndex - 1);
|
||||
resetAutoPlay();
|
||||
});
|
||||
|
||||
nextBtn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
goTo(currentIndex + 1);
|
||||
resetAutoPlay();
|
||||
});
|
||||
|
||||
dotsWrap.addEventListener('click', function(e) {
|
||||
var target = e.target;
|
||||
if (target.classList.contains('carousel-dot')) {
|
||||
goTo(parseInt(target.dataset.index, 10));
|
||||
resetAutoPlay();
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard navigation
|
||||
el.setAttribute('tabindex', '0');
|
||||
el.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'ArrowLeft') {
|
||||
e.preventDefault();
|
||||
goTo(currentIndex - 1);
|
||||
resetAutoPlay();
|
||||
} else if (e.key === 'ArrowRight') {
|
||||
e.preventDefault();
|
||||
goTo(currentIndex + 1);
|
||||
resetAutoPlay();
|
||||
}
|
||||
});
|
||||
|
||||
// Touch/swipe support
|
||||
var touchStartX = 0;
|
||||
var touchDeltaX = 0;
|
||||
viewport.addEventListener('touchstart', function(e) {
|
||||
touchStartX = e.touches[0].clientX;
|
||||
touchDeltaX = 0;
|
||||
}, { passive: true });
|
||||
viewport.addEventListener('touchmove', function(e) {
|
||||
touchDeltaX = e.touches[0].clientX - touchStartX;
|
||||
}, { passive: true });
|
||||
viewport.addEventListener('touchend', function(e) {
|
||||
if (Math.abs(touchDeltaX) > 50) {
|
||||
e.preventDefault(); // suppress follow-up click from swipe
|
||||
if (touchDeltaX < 0) goTo(currentIndex + 1);
|
||||
else goTo(currentIndex - 1);
|
||||
resetAutoPlay();
|
||||
}
|
||||
touchDeltaX = 0;
|
||||
});
|
||||
|
||||
// Auto-play
|
||||
function startAutoPlay() {
|
||||
if (!autoPlayEnabled) return;
|
||||
stopAutoPlay();
|
||||
autoPlayTimer = setInterval(function() {
|
||||
goTo(currentIndex + 1);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function stopAutoPlay() {
|
||||
if (autoPlayTimer) {
|
||||
clearInterval(autoPlayTimer);
|
||||
autoPlayTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function resetAutoPlay() {
|
||||
stopAutoPlay();
|
||||
startAutoPlay();
|
||||
}
|
||||
|
||||
// Pause auto-play on hover
|
||||
el.addEventListener('mouseenter', stopAutoPlay);
|
||||
el.addEventListener('mouseleave', startAutoPlay);
|
||||
|
||||
startAutoPlay();
|
||||
}
|
||||
|
||||
// "View full album" footer
|
||||
var footer = document.createElement('div');
|
||||
footer.className = 'photo-album-footer';
|
||||
var footerLink = document.createElement('a');
|
||||
footerLink.href = PUBLIC_URL + '/gallery?album=' + albumId;
|
||||
footerLink.target = '_blank';
|
||||
footerLink.textContent = 'View full album \u2192';
|
||||
footer.appendChild(footerLink);
|
||||
el.appendChild(footer);
|
||||
|
||||
console.log('Rendered carousel for album ' + albumId + ' (' + photos.length + ' photos)');
|
||||
}
|
||||
|
||||
// ─── Initialization ──────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Initialize all photo carousel blocks on the page
|
||||
*/
|
||||
function initPhotoCarousels() {
|
||||
var carousels = document.querySelectorAll('.photo-album-carousel');
|
||||
console.log('Found ' + carousels.length + ' photo carousel(s) to hydrate');
|
||||
carousels.forEach(function(carousel) {
|
||||
if (carousel.classList.contains('carousel-initialized')) return; // Already rendered
|
||||
renderCarouselBlock(carousel);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all photo blocks on the page
|
||||
*/
|
||||
function initPhotoBlocks() {
|
||||
var blocks = document.querySelectorAll('.photo-block');
|
||||
console.log('Found ' + blocks.length + ' photo block(s) to hydrate');
|
||||
blocks.forEach(function(block) {
|
||||
if (block.querySelector('img')) return; // Already rendered
|
||||
renderPhotoBlock(block);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all photo card blocks on the page
|
||||
*/
|
||||
function initPhotoCards() {
|
||||
var cards = document.querySelectorAll('.photo-card-block');
|
||||
console.log('Found ' + cards.length + ' photo card(s) to hydrate');
|
||||
cards.forEach(function(card) {
|
||||
if (card.dataset.hydrated === 'true') return;
|
||||
hydratePhotoCard(card);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all photo album blocks on the page
|
||||
*/
|
||||
function initPhotoAlbums() {
|
||||
var albums = document.querySelectorAll('.photo-album-block');
|
||||
console.log('Found ' + albums.length + ' photo album(s) to hydrate');
|
||||
albums.forEach(function(album) {
|
||||
if (album.querySelector('.photo-album-grid')) return; // Already rendered
|
||||
renderAlbumBlock(album);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all photo elements (blocks + cards + albums + carousels)
|
||||
*/
|
||||
function initAll() {
|
||||
initPhotoBlocks();
|
||||
initPhotoCards();
|
||||
initPhotoAlbums();
|
||||
initPhotoCarousels();
|
||||
}
|
||||
|
||||
// Initialize when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initAll);
|
||||
} else {
|
||||
initAll();
|
||||
}
|
||||
|
||||
// Re-initialize on MkDocs SPA navigation
|
||||
if (typeof window.document$ !== 'undefined') {
|
||||
window.document$.subscribe(function() {
|
||||
console.log('MkDocs navigation detected, re-initializing photo elements');
|
||||
setTimeout(initAll, 100);
|
||||
});
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user