/** * CSV Viewer functionality * Automatically processes and displays CSV data when pasted * Using Papa Parse for robust CSV handling */ document.addEventListener('DOMContentLoaded', function() { // DOM Elements const csvInput = document.getElementById('csvInput'); const delimiterSelect = document.getElementById('delimiter'); const hasHeaderCheckbox = document.getElementById('hasHeader'); const outputDiv = document.getElementById('output'); // Variables to store data let csvData = []; let headers = []; let currentSortColumn = null; let sortDirection = 1; // 1 for ascending, -1 for descending // Add input event listener with debounce to process CSV when pasted let debounceTimer; csvInput.addEventListener('input', function() { clearTimeout(debounceTimer); debounceTimer = setTimeout(function() { if (csvInput.value.trim() !== '') { processCSV(); } }, 300); // 300ms debounce delay }); // Add paste event listener to format CSV data on paste csvInput.addEventListener('paste', function(e) { // Let the paste happen naturally, then process after a brief delay setTimeout(function() { const text = csvInput.value; if (text && text.length > 0) { // Auto-detect delimiter autoDetectDelimiter(text); // Process immediately after paste processCSV(); } }, 50); // Slightly longer delay to ensure paste completes }); // Add change listeners to delimiter and header options to reprocess data delimiterSelect.addEventListener('change', function() { if (csvInput.value.trim() !== '') { processCSV(); } }); hasHeaderCheckbox.addEventListener('change', function() { if (csvInput.value.trim() !== '') { processCSV(); } }); /** * Auto-detect the delimiter in pasted CSV data */ function autoDetectDelimiter(text) { // Count occurrences of common delimiters const firstFewLines = text.split('\n').slice(0, 5).join('\n'); const counts = { ',': (firstFewLines.match(/,/g) || []).length, ';': (firstFewLines.match(/;/g) || []).length, '\t': (firstFewLines.match(/\t/g) || []).length, '|': (firstFewLines.match(/\|/g) || []).length }; // Find the most common delimiter let maxCount = 0; let detectedDelimiter = ','; // default for (const [delimiter, count] of Object.entries(counts)) { if (count > maxCount) { maxCount = count; detectedDelimiter = delimiter; } } // Set the delimiter dropdown if (maxCount > 0) { delimiterSelect.value = detectedDelimiter === '\t' ? '\\t' : detectedDelimiter; } } /** * Process the CSV data based on selected options */ function processCSV() { const csvText = csvInput.value.trim(); if (!csvText) { outputDiv.innerHTML = '
Paste CSV data above to view it as a table.
'; return; } try { // Show processing message outputDiv.innerHTML = 'Processing data...
'; // Parse CSV using Papa Parse const delimiter = delimiterSelect.value; const hasHeader = hasHeaderCheckbox.checked; // Enhanced parsing options Papa.parse(csvText, { delimiter: delimiter, header: hasHeader, skipEmptyLines: 'greedy', // Skip truly empty lines dynamicTyping: true, // Automatically convert numeric values trimHeaders: true, // Trim whitespace from headers complete: function(results) { if (results.errors.length > 0) { showError('Error parsing CSV: ' + results.errors[0].message); return; } if (results.data.length === 0 || (results.data.length === 1 && Object.keys(results.data[0]).length === 0)) { showError('No valid data found. Please check your CSV format and delimiter.'); return; } csvData = results.data; // Handle headers if (hasHeader) { if (results.meta.fields && results.meta.fields.length > 0) { headers = results.meta.fields.map(h => h.trim()); } else { // Fallback if no headers detected headers = Object.keys(results.data[0] || {}).map((_, i) => `Column${i + 1}`); } } else { headers = Object.keys(results.data[0] || {}).map((_, i) => `Column${i + 1}`); } // Preview the data previewData(); }, error: function(error) { showError('Error processing CSV: ' + error.message); } }); } catch (error) { showError('Error processing CSV: ' + error.message); } } /** * Preview the CSV data in a table with sortable columns */ function previewData() { if (csvData.length === 0) { showError('No data to preview.'); return; } // Limit preview to first 500 rows const previewData = csvData.slice(0, 500); // Generate table HTML let tableHtml = 'Showing ${previewData.length} of ${csvData.length} rows
`; tableHtml += '| ${header} ${isSorted ? (sortDirection > 0 ? '↑' : '↓') : ''} | `; }); tableHtml += '
|---|
| ${formattedValue} | `; }); tableHtml += '