/** * Metadata Popup functionality for Gallery * * Handles showing metadata in small popups overlaid on plot thumbnails */ class MetadataPopup { constructor() { this.activePopup = null; this.init(); } init() { // Close popup when clicking outside document.addEventListener('click', (e) => { if (!e.target.closest('.metadata-btn') && !e.target.closest('.metadata-popup')) { this.hidePopup(); } }); // Close popup on Escape key document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { this.hidePopup(); } }); } showPopup(button, plotName, metadata) { // Hide any existing popup this.hidePopup(); // Create popup element const popup = document.createElement('div'); popup.className = 'metadata-popup'; popup.innerHTML = this.formatMetadata(plotName, metadata); // Position popup relative to button const rect = button.getBoundingClientRect(); popup.style.position = 'fixed'; popup.style.left = rect.left + 'px'; popup.style.top = (rect.bottom + 5) + 'px'; popup.style.zIndex = '1000'; // Add to DOM document.body.appendChild(popup); this.activePopup = popup; // Adjust position if popup goes off screen setTimeout(() => { const popupRect = popup.getBoundingClientRect(); // Adjust horizontal position if (popupRect.right > window.innerWidth) { popup.style.left = (rect.right - popupRect.width) + 'px'; } // Adjust vertical position if (popupRect.bottom > window.innerHeight) { popup.style.top = (rect.top - popupRect.height - 5) + 'px'; } }, 0); // Animate in requestAnimationFrame(() => { popup.classList.add('show'); }); } hidePopup() { if (this.activePopup) { this.activePopup.classList.remove('show'); setTimeout(() => { if (this.activePopup && this.activePopup.parentNode) { this.activePopup.parentNode.removeChild(this.activePopup); } this.activePopup = null; }, 200); } } formatMetadata(plotName, metadata) { if (!metadata || Object.keys(metadata).length === 0) { return `
`; } let html = ` '; return html; } formatMetadataField(key, value) { const displayKey = key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); let formattedValue; if (value === null || value === undefined) { formattedValue = 'null'; } else if (typeof value === 'object') { if (Array.isArray(value)) { if (value.length <= 3) { formattedValue = value.map(item => ``).join(' '); } else { formattedValue = `${value.slice(0, 3).map(item => ``).join(' ')} `; } } else { // Show object as compact JSON for small objects, or just key count for large ones const keys = Object.keys(value); if (keys.length <= 3) { formattedValue = '' + JSON.stringify(value) + '';
} else {
formattedValue = `Object with ${keys.length} properties`;
}
}
} else {
// Truncate long strings
const str = String(value);
formattedValue = str.length > 50 ? str.substring(0, 47) + '...' : str;
}
return `
`;
}
}
// Global instance
window.metadataPopup = new MetadataPopup();
// Global function for template usage
window.showMetadataPopup = function(button, plotName, metadata) {
window.metadataPopup.showPopup(button, plotName, metadata);
};