(function() { 'use strict'; // Export functionality // Escape a single value for RFC-4180 CSV function csvCell(value) { const str = String(value == null ? '' : value); if (str.includes(',') || str.includes('"') || str.includes('\n')) { return '"' + str.replace(/"/g, '""') + '"'; } return str; } // Convert an array of row arrays to a CSV string function rowsToCSV(rows) { return rows.map(row => row.map(csvCell).join(',')).join('\n'); } // Export selected files to CSV function exportCSV() { if (window.app.selectedFiles.size === 0) { alert('No files selected for export.'); return; } const headers = ['Tracking Number', 'Title', 'Revision', 'Status', 'Extension', 'Size', 'Size (bytes)', 'Path', 'Modified']; const rows = [headers]; // Add data rows for selected files only window.app.files.forEach(file => { if (!window.app.selectedFiles.has(file.id)) return; rows.push([ file.trackingNumber || '', file.title || '', file.revision || '', file.status || '', file.extension || '', formatFileSize(file.size), file.size != null ? file.size : '', file.path, file.modified ? new Date(file.modified).toLocaleString() : '—' ]); }); downloadFile(rowsToCSV(rows), 'archive-export.csv', 'text/csv'); } // Download selected files as ZIP async function downloadSelected() { if (window.app.selectedFiles.size === 0) { alert('No files selected for download.'); return; } // JSZip is vendored (concat'd by build.sh), so window.JSZip is // already defined. Defensive check in case a future refactor // reorders things. if (typeof JSZip === 'undefined') { alert('JSZip library not bundled — rebuild archive with shared/vendor/jszip.min.js'); return; } const zip = new JSZip(); const selectedFiles = []; // Get selected file objects window.app.files.forEach(file => { if (window.app.selectedFiles.has(file.id)) { selectedFiles.push(file); } }); // Show progress showProgress('Preparing ZIP file...', 0, selectedFiles.length); try { // Add files to ZIP for (let i = 0; i < selectedFiles.length; i++) { const file = selectedFiles[i]; showProgress(`Adding ${file.name}...`, i + 1, selectedFiles.length); try { let arrayBuffer; if (file.handle) { // Local mode: read via File System Access API const fileData = await file.handle.getFile(); arrayBuffer = await fileData.arrayBuffer(); } else if (file.url) { // HTTP mode: fetch from server const resp = await fetch(file.url); if (!resp.ok) throw new Error('HTTP ' + resp.status); arrayBuffer = await resp.arrayBuffer(); } else { throw new Error('No file handle or URL available'); } // Create folder structure in ZIP const relativePath = file.path.substring(file.path.indexOf('/') + 1); // Remove root directory zip.file(relativePath, arrayBuffer); } catch (err) { console.error(`Error adding file ${file.name}:`, err); } } showProgress('Generating ZIP...', selectedFiles.length, selectedFiles.length); // Generate ZIP const blob = await zip.generateAsync({ type: 'blob', compression: 'DEFLATE', compressionOptions: { level: 6 } }); // Download const timestamp = new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19); downloadBlob(blob, `archive-${timestamp}.zip`); hideProgress(); } catch (err) { hideProgress(); console.error('Error creating ZIP:', err); alert('Error creating ZIP file: ' + err.message); } } // Show progress indicator function showProgress(message, current, total) { let progressDiv = document.getElementById('progressIndicator'); if (!progressDiv) { progressDiv = document.createElement('div'); progressDiv.id = 'progressIndicator'; progressDiv.className = 'progress-indicator'; document.body.appendChild(progressDiv); } const percentage = Math.round((current / total) * 100); progressDiv.innerHTML = '
' + 'Generated: ${new Date().toLocaleString()}
Total Files: ${window.app.filteredFiles.length}
| Tracking Number | Title | Revisions |
|---|---|---|
| ${window.app.modules.app.escapeHtml(group.trackingNumber)} | ${window.app.modules.app.escapeHtml(group.title)} |
${group.sortedRevisions.map(rev => `
${window.app.modules.app.escapeHtml(rev.revision)}
(${window.app.modules.app.escapeHtml(rev.status)})
${rev.files.map(f => f.extension.toUpperCase()).join(', ')}
`).join('')}
|