/**
* Images Gallery - Display all loaded images with optimization analysis
*/
async function init() {
const params = new URLSearchParams(window.location.search);
const testId = params.get('id');
if (!testId) {
document.getElementById('imagesGrid').innerHTML =
'
Error: No test ID provided
';
return;
}
try {
const response = await fetch(`/reports/${testId}.har.json`);
if (!response.ok) throw new Error('HAR data not found');
const harData = await response.json();
renderImageGallery(harData);
} catch (error) {
document.getElementById('imagesGrid').innerHTML =
`Error loading images: ${error.message}
`;
}
}
function renderImageGallery(harData) {
// Filter for image requests
const images = harData.entries.filter(entry =>
entry.resourceType === 'Image' ||
entry.mimeType?.startsWith('image/') ||
entry.url.match(/\.(jpg|jpeg|png|gif|webp|svg|ico|bmp)($|\?)/i)
);
if (images.length === 0) {
document.getElementById('imagesGrid').innerHTML =
'No images found in this page load.
';
return;
}
// Calculate summary stats
const totalSize = images.reduce((sum, img) => sum + img.size.transferSize, 0);
const unoptimized = images.filter(img => analyzeOptimization(img).level === 'error').length;
const avgSize = totalSize / images.length;
// Render summary
document.getElementById('summaryStats').innerHTML = `
${images.length}
Total Images
${formatBytes(totalSize)}
Total Size
${formatBytes(avgSize)}
Average Size
${unoptimized}
Needs Optimization
`;
// Render image cards
let html = '';
images.forEach(image => {
const optimization = analyzeOptimization(image);
const filename = extractFilename(image.url);
const format = getImageFormat(image);
html += `
🖼️
${filename}
Format
${format}
Size
${formatBytes(image.size.transferSize)}
Compression
${(image.size.compressionRatio * 100).toFixed(0)}%
Load Time
${image.timing.total.toFixed(0)}ms
${optimization.message}
`;
});
document.getElementById('imagesGrid').innerHTML = html;
}
function analyzeOptimization(image) {
const size = image.size.transferSize;
const format = getImageFormat(image);
// Check if modern format (WebP, AVIF)
if (format === 'WebP' || format === 'AVIF') {
return { level: 'good', message: '✓ Modern format' };
}
// Check size thresholds
if (size > 500 * 1024) { // > 500KB
return { level: 'error', message: '⚠️ Very large - optimize!' };
}
if (size > 200 * 1024) { // > 200KB
return { level: 'warning', message: '⚠️ Could be smaller' };
}
// Check if SVG (good for icons/logos)
if (format === 'SVG') {
return { level: 'good', message: '✓ Vector (scalable)' };
}
return { level: 'good', message: '✓ Optimized' };
}
function getImageFormat(image) {
const url = image.url.toLowerCase();
const mime = image.mimeType?.toLowerCase();
if (mime?.includes('webp') || url.includes('.webp')) return 'WebP';
if (mime?.includes('avif') || url.includes('.avif')) return 'AVIF';
if (mime?.includes('svg') || url.includes('.svg')) return 'SVG';
if (mime?.includes('png') || url.includes('.png')) return 'PNG';
if (mime?.includes('gif') || url.includes('.gif')) return 'GIF';
if (mime?.includes('jpeg') || mime?.includes('jpg') || url.match(/\.jpe?g/)) return 'JPEG';
if (url.includes('.ico')) return 'ICO';
if (url.includes('.bmp')) return 'BMP';
return 'Unknown';
}
function extractFilename(url) {
try {
const urlObj = new URL(url);
const pathname = urlObj.pathname;
const filename = pathname.split('/').pop() || pathname;
return filename.length > 40 ? filename.substring(0, 37) + '...' : filename;
} catch {
return url.substring(0, 40) + '...';
}
}
function formatBytes(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(2)) + ' ' + sizes[i];
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', init);