dbd67f6039
- Fix template rendering by storing rendered HTML in variable before writing This resolves subdirectories not appearing in navigation despite being correctly detected and included in template variables - Add missing template variables (paths.work_dir) to gallery config - Add CGI refresh handler for web-based gallery regeneration - Improve PDF export functionality with better error handling - Add ESC key shortcut documentation for exiting selection mode - Enhanced export manager with proper dependency checking Fixes issue where new subdirectories were detected but not displayed in browser navigation due to incomplete template rendering.
152 lines
4.4 KiB
JavaScript
152 lines
4.4 KiB
JavaScript
/**
|
|
* Utility functions and helpers
|
|
*/
|
|
export class Utils {
|
|
/**
|
|
* Format file size in human readable format
|
|
*/
|
|
static formatFileSize(bytes) {
|
|
if (bytes === 0) return '0 B';
|
|
const k = 1024;
|
|
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
|
}
|
|
|
|
/**
|
|
* Handle thumbnail highlighting from URL parameters
|
|
*/
|
|
static handleThumbnailHighlight() {
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const highlightPlot = urlParams.get('highlight');
|
|
|
|
if (highlightPlot) {
|
|
// Find and highlight the thumbnail
|
|
const gridItems = document.querySelectorAll('.grid-item');
|
|
gridItems.forEach(item => {
|
|
const plotName = item.querySelector('.plot-name');
|
|
if (plotName && plotName.textContent.trim() === highlightPlot) {
|
|
item.classList.add('highlighted');
|
|
// Scroll to the highlighted item
|
|
setTimeout(() => {
|
|
item.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'center'
|
|
});
|
|
}, 100);
|
|
// Remove highlight after animation
|
|
setTimeout(() => {
|
|
item.classList.remove('highlighted');
|
|
}, 3000);
|
|
}
|
|
});
|
|
|
|
// Clean up URL
|
|
const newUrl = new URL(window.location);
|
|
newUrl.searchParams.delete('highlight');
|
|
window.history.replaceState({}, document.title, newUrl.toString());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate approximate total size of displayed files
|
|
*/
|
|
static async calculateApproximateSize() {
|
|
const images = document.querySelectorAll('.grid-item img');
|
|
let totalSize = 0;
|
|
let loadedCount = 0;
|
|
|
|
const sizeElement = document.getElementById('totalSize');
|
|
if (!sizeElement) return;
|
|
|
|
sizeElement.textContent = 'Loading...';
|
|
|
|
// Estimate size based on a sample of images
|
|
const sampleSize = Math.min(images.length, 5);
|
|
const sampleImages = Array.from(images).slice(0, sampleSize);
|
|
|
|
if (sampleImages.length === 0) {
|
|
sizeElement.textContent = '0 KB';
|
|
return;
|
|
}
|
|
|
|
// Calculate average size from sample
|
|
for (const img of sampleImages) {
|
|
try {
|
|
const response = await fetch(img.src, { method: 'HEAD' });
|
|
const size = parseInt(response.headers.get('content-length') || '0');
|
|
if (size > 0) {
|
|
totalSize += size;
|
|
loadedCount++;
|
|
}
|
|
} catch (e) {
|
|
// Fallback: estimate 100KB per image
|
|
totalSize += 102400;
|
|
loadedCount++;
|
|
}
|
|
}
|
|
|
|
if (loadedCount > 0) {
|
|
const averageSize = totalSize / loadedCount;
|
|
const estimatedTotal = averageSize * images.length;
|
|
sizeElement.textContent = Utils.formatFileSize(estimatedTotal);
|
|
} else {
|
|
sizeElement.textContent = 'Unknown';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Refresh gallery by calling CGI script
|
|
*/
|
|
static async refreshGallery() {
|
|
const refreshBtn = document.getElementById('refreshBtn');
|
|
if (!refreshBtn) return;
|
|
|
|
refreshBtn.disabled = true;
|
|
refreshBtn.textContent = '⏳';
|
|
|
|
// Use the CGI script path from config if available
|
|
let cgiPath = '/cgi-bin/refresh_gallery.py';
|
|
if (window.galleryConfig && window.galleryConfig.paths && window.galleryConfig.paths.cgi_script) {
|
|
cgiPath = window.galleryConfig.paths.cgi_script;
|
|
// Ensure it starts with a slash for fetch
|
|
if (!cgiPath.startsWith('/')) cgiPath = '/' + cgiPath;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(cgiPath, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
}
|
|
});
|
|
|
|
if (response.ok) {
|
|
refreshBtn.textContent = '✅';
|
|
setTimeout(() => {
|
|
window.location.reload();
|
|
}, 1000);
|
|
} else {
|
|
throw new Error('Refresh failed');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error refreshing gallery:', error);
|
|
refreshBtn.textContent = '❌';
|
|
setTimeout(() => {
|
|
refreshBtn.disabled = false;
|
|
refreshBtn.textContent = '🔄';
|
|
}, 3000);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Toggle keyboard shortcuts help display
|
|
*/
|
|
static toggleShortcutsHelp() {
|
|
const help = document.getElementById('shortcutsHelp');
|
|
if (help) {
|
|
help.style.display = help.style.display === 'block' ? 'none' : 'block';
|
|
}
|
|
}
|
|
}
|