/** * OxiCloud File Viewer Module * Provides integrated viewers for images, PDFs and other file types */ class FileViewer { constructor() { this.viewerContainer = null; this.fileData = null; this.isOpen = false; // Create the viewer container on initialization this.createViewerContainer(); } /** * Create the viewer container DOM element */ createViewerContainer() { console.log('Creating file viewer container'); // Check if container already exists if (document.getElementById('file-viewer-container')) { console.log('Viewer container already exists'); this.viewerContainer = document.getElementById('file-viewer-container'); return; } // Create viewer container this.viewerContainer = document.createElement('div'); this.viewerContainer.id = 'file-viewer-container'; this.viewerContainer.className = 'file-viewer-container'; // Create viewer content const viewerContent = document.createElement('div'); viewerContent.className = 'file-viewer-content'; // Create header const header = document.createElement('div'); header.className = 'file-viewer-header'; const title = document.createElement('div'); title.className = 'file-viewer-title'; title.textContent = 'File Viewer'; header.appendChild(title); const closeBtn = document.createElement('button'); closeBtn.className = 'file-viewer-close'; closeBtn.innerHTML = ''; closeBtn.addEventListener('click', () => { console.log('Close button clicked'); this.close(); }); header.appendChild(closeBtn); // Create viewer area const viewerArea = document.createElement('div'); viewerArea.className = 'file-viewer-area'; // Create toolbar const toolbar = document.createElement('div'); toolbar.className = 'file-viewer-toolbar'; const downloadBtn = document.createElement('button'); downloadBtn.className = 'file-viewer-download'; downloadBtn.innerHTML = ''; downloadBtn.addEventListener('click', () => this.downloadFile()); toolbar.appendChild(downloadBtn); // Assemble the viewer viewerContent.appendChild(header); viewerContent.appendChild(viewerArea); viewerContent.appendChild(toolbar); this.viewerContainer.appendChild(viewerContent); // Add to the document if (document.body) { console.log('Adding viewer container to body'); document.body.appendChild(this.viewerContainer); } else { console.warn('Document body not ready, will add viewer later'); // Try to add it after document is ready setTimeout(() => { if (document.body) { console.log('Adding viewer container to body (delayed)'); document.body.appendChild(this.viewerContainer); } else { console.error('Document body still not available after delay'); } }, 500); } // Add event listeners for keyboard navigation document.addEventListener('keydown', (e) => { if (this.isOpen && e.key === 'Escape') { console.log('Escape key pressed, closing viewer'); this.close(); } }); console.log('Viewer container created'); } /** * Open the viewer with the specified file * @param {Object} fileData - File data with id, name, mime_type */ async open(fileData) { console.log('FileViewer: Opening file', fileData); this.fileData = fileData; this.isOpen = true; // Reset the viewer area const viewerArea = this.viewerContainer.querySelector('.file-viewer-area'); viewerArea.innerHTML = ''; // Set the title const title = this.viewerContainer.querySelector('.file-viewer-title'); title.textContent = fileData.name; // Show the container this.viewerContainer.classList.add('active'); // Determine content type and load appropriate viewer if (fileData.mime_type && fileData.mime_type.startsWith('image/')) { console.log('FileViewer: Loading image viewer'); this.loadImageViewer(fileData.id, viewerArea); } else if (fileData.mime_type && fileData.mime_type === 'application/pdf') { console.log('FileViewer: Loading PDF viewer'); this.loadPdfViewer(fileData.id, viewerArea); } else { console.log('FileViewer: Unsupported file type', fileData.mime_type); // For unsupported files, show download prompt this.showUnsupportedFileMessage(viewerArea); } } /** * Load the image viewer * @param {string} fileId - ID of the file to view * @param {HTMLElement} container - Container element to render into */ loadImageViewer(fileId, container) { // Create image element const img = document.createElement('img'); img.className = 'file-viewer-image'; img.src = `/api/files/${fileId}`; img.alt = this.fileData.name; // Create loader const loader = document.createElement('div'); loader.className = 'file-viewer-loader'; loader.innerHTML = ''; container.appendChild(loader); // When image loads, remove loader img.onload = () => { container.removeChild(loader); }; // Add image to container container.appendChild(img); // Add zoom controls to toolbar const toolbar = this.viewerContainer.querySelector('.file-viewer-toolbar'); const zoomInBtn = document.createElement('button'); zoomInBtn.className = 'file-viewer-zoom-in'; zoomInBtn.innerHTML = ''; zoomInBtn.addEventListener('click', () => this.zoomImage(1.2)); toolbar.appendChild(zoomInBtn); const zoomOutBtn = document.createElement('button'); zoomOutBtn.className = 'file-viewer-zoom-out'; zoomOutBtn.innerHTML = ''; zoomOutBtn.addEventListener('click', () => this.zoomImage(0.8)); toolbar.appendChild(zoomOutBtn); const resetZoomBtn = document.createElement('button'); resetZoomBtn.className = 'file-viewer-zoom-reset'; resetZoomBtn.innerHTML = ''; resetZoomBtn.addEventListener('click', () => this.resetZoom()); toolbar.appendChild(resetZoomBtn); } /** * Zoom the image * @param {number} factor - Zoom factor */ zoomImage(factor) { const img = this.viewerContainer.querySelector('.file-viewer-image'); if (!img) return; // Get current scale let scale = img.style.transform ? parseFloat(img.style.transform.replace('scale(', '').replace(')', '')) : 1; // Apply new scale scale *= factor; // Limit scale range scale = Math.max(0.5, Math.min(5, scale)); img.style.transform = `scale(${scale})`; } /** * Reset image zoom */ resetZoom() { const img = this.viewerContainer.querySelector('.file-viewer-image'); if (!img) return; img.style.transform = 'scale(1)'; } /** * Load the PDF viewer * @param {string} fileId - ID of the file to view * @param {HTMLElement} container - Container element to render into */ loadPdfViewer(fileId, container) { // Create iframe for PDF viewer const iframe = document.createElement('iframe'); iframe.className = 'file-viewer-pdf'; iframe.src = `/api/files/${fileId}`; iframe.title = this.fileData.name; // Create loader const loader = document.createElement('div'); loader.className = 'file-viewer-loader'; loader.innerHTML = ''; container.appendChild(loader); // When iframe loads, remove loader iframe.onload = () => { container.removeChild(loader); }; // Add iframe to container container.appendChild(iframe); } /** * Show message for unsupported file types * @param {HTMLElement} container - Container element to render into */ showUnsupportedFileMessage(container) { const message = document.createElement('div'); message.className = 'file-viewer-unsupported'; message.innerHTML = `
${window.i18n ? window.i18n.t('viewer.unsupported_file') : 'Este tipo de archivo no se puede previsualizar.'}
`; // Add download button click event message.querySelector('.download-btn').addEventListener('click', () => { this.downloadFile(); }); container.appendChild(message); } /** * Download the current file */ downloadFile() { if (!this.fileData) return; // Create a link and simulate click const link = document.createElement('a'); link.href = `/api/files/${this.fileData.id}`; link.download = this.fileData.name; link.target = '_blank'; document.body.appendChild(link); link.click(); document.body.removeChild(link); } /** * Close the viewer */ close() { this.isOpen = false; this.fileData = null; this.viewerContainer.classList.remove('active'); // Reset toolbar (remove zoom controls) const toolbar = this.viewerContainer.querySelector('.file-viewer-toolbar'); const downloadBtn = toolbar.querySelector('.file-viewer-download'); toolbar.innerHTML = ''; toolbar.appendChild(downloadBtn); } } // Create the file viewer immediately and make it accessible globally window.fileViewer = new FileViewer(); // Ensure the file viewer is available when the DOM is ready document.addEventListener('DOMContentLoaded', () => { console.log('FileViewer initialized:', window.fileViewer ? 'Yes' : 'No'); if (!window.fileViewer) { console.warn('Re-initializing fileViewer as it was not properly set'); window.fileViewer = new FileViewer(); } });