No trials generated. Click the 'Generate Trial List' button to create a list.
";
return;
}
const table = document.createElement('table');
table.className = "psf-dash-table";
// Header
table.innerHTML = `
Trial #
Stimulus Value (${configStimulusType.value})
Target Response
Notes
Action
`;
const tbody = table.querySelector('tbody');
// Rows
trialData.forEach((trial) => {
const tr = document.createElement('tr');
tr.dataset.id = trial.id;
tr.innerHTML = `
${trial.id}
×
`;
tbody.appendChild(tr);
});
dashboardList.appendChild(table);
// Attach input listeners for live updates to the master state array
setupDashboardListeners();
}
/**
* Attaches listeners to the newly created table elements
*/
function setupDashboardListeners() {
const table = dashboardList.querySelector('.psf-dash-table');
if (!table) return;
// Listen for changes in inputs (value, target, notes)
table.querySelectorAll('input').forEach(input => {
input.addEventListener('change', handleDashboardInput);
input.addEventListener('keyup', handleDashboardInput);
});
// Listen for removal clicks
table.querySelectorAll('button[data-action="remove"]').forEach(button => {
button.addEventListener('click', handleDashboardRemove);
});
}
/**
* Handles live updates to the master `trialData` array
*/
function handleDashboardInput(e) {
const row = e.target.closest("tr");
if (!row) return;
const trialId = parseInt(row.dataset.id);
const trial = trialData.find(t => t.id === trialId);
if (!trial) return;
// Update the specific field
if (e.target.classList.contains('psf-dash-value')) {
trial.value = parseFloat(e.target.value);
} else if (e.target.classList.contains('psf-dash-target')) {
trial.target = e.target.value;
} else if (e.target.classList.contains('psf-dash-notes')) {
trial.notes = e.target.value;
}
// Optional: console.log(trialData); // Check state update
}
/**
* Handles removing a trial directly from the dashboard
*/
function handleDashboardRemove(e) {
const row = e.target.closest("tr");
if (!row) return;
const trialId = parseInt(row.dataset.id);
// Remove from master array
trialData = trialData.filter(t => t.id !== trialId);
// Re-index the remaining trials (important for psychophysics)
trialData.forEach((t, index) => {
t.id = index + 1;
});
renderDashboard(); // Re-render to update trial numbers
}
/**
* Generates a PDF report from the dashboard data
*/
function downloadPDF() {
if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF.autoTable === 'undefined') {
alert("Error: PDF libraries could not be loaded. Please try again.");
return;
}
if (trialData.length === 0) {
alert("Checklist is empty. Please generate a trial list first.");
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF("p", "pt", "a4");
const margin = 40;
let yPos = margin;
const tableHead = [
["Trial #", configStimulusType.value, "Target Response", "Notes"]
];
const tableBody = [];
// Get data from the *current* master state (which reflects dashboard edits)
trialData.forEach((trial) => {
tableBody.push([
trial.id,
trial.value,
trial.target,
trial.notes
]);
});
doc.setFontSize(18);
doc.text(`Stimulus File: ${configTitle.value}`, margin, yPos);
yPos += 30;
const tableOptions = {
startY: yPos,
head: tableHead,
body: tableBody,
theme: 'striped',
headStyles: {
fillColor: [0, 115, 230], // Blue
textColor: [255, 255, 255],
},
columnStyles: {
0: { cellWidth: 50 },
1: { cellWidth: 100 },
2: { cellWidth: 120 },
3: { cellWidth: 'auto' },
},
styles: {
fontSize: 9,
cellPadding: 4,
overflow: 'linebreak'
},
margin: { left: margin, right: margin }
};
doc.autoTable(tableOptions);
yPos = doc.autoTable.previous.finalY + 25; // Update yPos
doc.save("Psychophysics_Stimulus_File.pdf");
}
/**
* Helper to escape HTML
*/
function escapeHTML(str) {
if (!str) return "";
return str
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// --- 4. INITIALIZATION & EVENT LISTENERS ---
// Tab Listeners
tabButtons.forEach((btn) => {
btn.addEventListener("click", () => showTab(btn.dataset.target));
});
navButtons.forEach((btn) => {
btn.addEventListener("click", () => showTab(btn.dataset.target));
});
// Config Tab Listeners
if (generateBtn) {
generateBtn.addEventListener("click", handleGenerate);
}
// Dashboard Tab Listeners
if (pdfBtn) {
pdfBtn.addEventListener("click", downloadPDF);
}
// Initial State
showTab("psf-tab-dashboard");
});