Created
November 3, 2023 13:01
-
-
Save georgwiese/af06566dd187533b16cd90b590492bfd to your computer and use it in GitHub Desktop.
Mostly ChatGPT-written CSV viewer that works well for Powdr-exported execution traces
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>CSV Viewer</title> | |
<style> | |
.headerCell { | |
position: sticky; | |
top: 0; | |
background-color: white; | |
cursor: pointer; | |
} | |
.highlight { | |
background-color: lightgrey; | |
} | |
.monospace { | |
font-family: 'Courier New', monospace; | |
} | |
</style> | |
</head> | |
<body> | |
<input type="file" id="fileInput" /> | |
<table id="dataTable" border="1"></table> | |
<script> | |
document.getElementById('fileInput').addEventListener('change', function (e) { | |
let file = e.target.files[0]; | |
if (file) { | |
let reader = new FileReader(); | |
reader.readAsText(file, 'UTF-8'); | |
reader.onload = function (evt) { | |
parseCSV(evt.target.result); | |
} | |
} | |
}); | |
function parseCSV(data) { | |
let rows = data.split('\n'); | |
let table = document.getElementById('dataTable'); | |
table.innerHTML = ''; // Clear the table | |
let headers = rows[0].split(','); | |
let sortedHeaders = headers.slice().sort((a, b) => { | |
let aPrefix = a.split('.').slice(0, -1).join('.'); | |
let bPrefix = b.split('.').slice(0, -1).join('.'); | |
return aPrefix.localeCompare(bPrefix); | |
}); | |
let headerRow = table.insertRow(); | |
sortedHeaders.forEach((header) => { | |
let cell = headerRow.insertCell(); | |
let parts = header.split('.'); | |
cell.innerHTML = parts.map((part, index) => `<span data-namespace="${parts.slice(0, index + 1).join('.')}">${part}</span>`).join('<br/>'); | |
cell.className = 'headerCell monospace'; | |
let spans = cell.querySelectorAll("span"); | |
spans.forEach(span => { | |
span.addEventListener('click', function () { | |
toggleNamespaceVisibility(span.dataset.namespace); | |
}); | |
}); | |
}); | |
for (let i = 1; i < rows.length; i++) { | |
let tableRow = table.insertRow(); | |
// Split the row into cells and store it. | |
let rowData = rows[i].split(','); | |
// Create an array of indices [0, 1, 2, ..., n] and sort it according to the headers' order. | |
let sortedIndices = rowData | |
.map((_, index) => index) // Create an array of indices | |
.sort((a, b) => { | |
// Use the indices to get the headers and then sort | |
let aHeader = headers[a]; | |
let bHeader = headers[b]; | |
return sortedHeaders.indexOf(aHeader) - sortedHeaders.indexOf(bHeader); | |
}); | |
// Use the sorted indices to append cells in the correct order | |
sortedIndices.forEach(cellIndex => { | |
let cellData = rowData[cellIndex]; | |
let cell = tableRow.insertCell(); | |
cell.innerHTML = cellData; | |
cell.className = 'monospace'; | |
if (cellData !== '0x0') { | |
cell.classList.add('highlight'); | |
} | |
}); | |
} | |
} | |
function toggleNamespaceVisibility(namespace) { | |
console.log(`toggleNamespaceVisibility: ${namespace}`); | |
let table = document.getElementById('dataTable'); | |
let isAnyColumnHidden = false; | |
let num_columns = 0; | |
// 1. Determine if any column in the target namespace is currently visible. | |
Array.from(table.rows[0].cells).forEach((cell, cellIndex) => { | |
let span = cell.querySelector(`span[data-namespace="${namespace}"]`); | |
if (span) { | |
num_columns++; | |
isAnyColumnHidden = isAnyColumnHidden || cell.style.display === 'none'; | |
} | |
}); | |
console.log(`isAnyColumnHidden: ${isAnyColumnHidden}`); | |
console.log(`num_columns: ${num_columns}`); | |
// Decide our display rule based on the visibility status | |
let shouldDisplay = isAnyColumnHidden ? '' : 'none'; | |
let isFirstColumnInNamespace = true; | |
Array.from(table.rows).forEach((row, rowIndex) => { | |
Array.from(row.cells).forEach((cell, cellIndex) => { | |
let headerCell = table.rows[0].cells[cellIndex]; | |
let span = headerCell.querySelector(`span[data-namespace="${namespace}"]`); | |
if (span) { | |
// For the header row | |
if (rowIndex === 0) { | |
cell.style.display = shouldDisplay; | |
// If we are about to hide this column and it's the first in the namespace | |
if (!isAnyColumnHidden && isFirstColumnInNamespace) { | |
isFirstColumnInNamespace = false; | |
cell.style.display = ''; | |
} | |
} | |
// For data rows | |
else if (num_columns > 1) { | |
cell.style.display = shouldDisplay; | |
// If we are about to hide the columns and this is the first one, show '...' | |
if (!isAnyColumnHidden && isFirstColumnInNamespace) { | |
cell.setAttribute('data-original-header', cell.innerHTML); | |
cell.innerHTML = '...'; | |
isFirstColumnInNamespace = false; | |
cell.style.display = ''; | |
} else if (isAnyColumnHidden && cell.getAttribute('data-original-header')) { | |
// If expanding columns, restore the original header if there's one saved | |
cell.innerHTML = cell.getAttribute('data-original-header'); | |
cell.removeAttribute('data-original-header'); | |
} | |
} | |
} | |
}); | |
isFirstColumnInNamespace = true; // Reset for the next row | |
}); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment