Add pages in Step 3 to begin the audit.
';
return;
}
reportData.sample.forEach((page, pageIndex) => {
const pageAuditEl = document.createElement('div');
pageAuditEl.className = 'p-4 border border-slate-200 rounded-lg';
let criteriaHtml = wcagCriteria.map(sc => {
const finding = page.findings.find(f => f.sc === sc.id) || { result: 'na', notes: '' };
return `
${sc.id} ${sc.name}
Pass
Fail
N/A
`;
}).join('');
pageAuditEl.innerHTML = `
${page.url}
Criterion Result Notes
${criteriaHtml}
`;
dom.auditSection.appendChild(pageAuditEl);
});
};
const renderReport = () => {
const { scope, exploration, sample } = reportData;
let failedCriteria = {};
let totalPass = 0, totalFail = 0, totalNa = 0;
sample.forEach(page => {
page.findings.forEach(finding => {
if(finding.result === 'fail') {
totalFail++;
if(!failedCriteria[finding.sc]) {
failedCriteria[finding.sc] = [];
}
failedCriteria[finding.sc].push({url: page.url, notes: finding.notes});
} else if (finding.result === 'pass') {
totalPass++;
} else {
totalNa++;
}
});
});
const conformanceStatus = totalFail === 0 ? 'Passes' : 'Fails';
let reportHtml = `
${scope.title || 'Accessibility Evaluation Report'}
For: ${scope.url || 'N/A'}
Evaluation Details
Website: ${scope.url}
Evaluation Date: ${new Date().toLocaleDateString('en-US')}
Commissioner: ${scope.commissioner}
Conformance Target: ${scope.conformance}
Technologies Relied Upon: ${scope.tech.replace(/\n/g, ' ')}
Executive Summary
This report summarizes the accessibility conformance of the website ${scope.url} against the ${scope.conformance} . The overall result of this evaluation is: ${conformanceStatus} .
A total of ${sample.length} pages were tested, with ${totalFail} failed instances, ${totalPass} passed instances, and ${totalNa} not applicable instances found across ${wcagCriteria.length} criteria.
Detailed Findings
${Object.keys(failedCriteria).length > 0 ? '' : '
No failed criteria found.
'}
`;
for (const sc in failedCriteria) {
const criterion = wcagCriteria.find(c => c.id === sc);
reportHtml += `
${criterion.id} ${criterion.name} (Level ${criterion.level}) - FAILED
${failedCriteria[sc].map(f => `On page ${f.url} : ${f.notes || 'No notes provided.'} `).join('')}
`;
}
reportHtml += `
`;
dom.reportPreview.innerHTML = reportHtml;
};
// --- EVENT HANDLERS & LOGIC ---
const updateReportDataFromUI = () => {
reportData.scope.title = dom.evalTitle.value;
reportData.scope.commissioner = dom.evalCommissioner.value;
reportData.scope.url = dom.evalUrl.value;
reportData.scope.conformance = dom.conformanceLevel.value;
reportData.scope.tech = dom.techRelied.value;
reportData.exploration.commonPages = dom.commonPages.value;
reportData.exploration.keyTasks = dom.keyTasks.value;
};
dom.addPageBtn.addEventListener('click', () => {
const url = dom.newPageUrl.value.trim();
if (url && !reportData.sample.some(p => p.url === url)) {
const initialFindings = wcagCriteria.map(sc => ({ sc: sc.id, result: 'na', notes: '' }));
reportData.sample.push({ url, findings: initialFindings });
dom.newPageUrl.value = '';
renderPageSampleList();
renderAuditSection();
}
});
dom.pageSampleList.addEventListener('click', (e) => {
if (e.target.closest('.remove-page-btn')) {
const index = e.target.closest('.remove-page-btn').dataset.index;
reportData.sample.splice(index, 1);
renderPageSampleList();
renderAuditSection();
}
});
dom.auditSection.addEventListener('change', (e) => {
if (e.target.classList.contains('audit-result')) {
const { pageIndex, scId } = e.target.dataset;
const finding = reportData.sample[pageIndex].findings.find(f => f.sc === scId);
if (finding) {
finding.result = e.target.value;
}
}
});
dom.auditSection.addEventListener('input', (e) => {
if (e.target.classList.contains('audit-notes')) {
const { pageIndex, scId } = e.target.dataset;
const finding = reportData.sample[pageIndex].findings.find(f => f.sc === scId);
if (finding) {
finding.notes = e.target.value;
}
}
});
dom.downloadPdfBtn.addEventListener('click', async () => {
const { jsPDF } = window.jspdf;
const pdfExportArea = document.getElementById('pdf-export-area');
const originalBg = pdfExportArea.style.backgroundColor;
pdfExportArea.style.backgroundColor = 'white';
try {
const canvas = await html2canvas(pdfExportArea, { scale: 2, useCORS: true });
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'portrait', unit: 'px', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const canvasWidth = canvas.width;
const canvasHeight = canvas.height;
const ratio = canvasWidth / canvasHeight;
const newCanvasWidth = pdfWidth;
const newCanvasHeight = newCanvasWidth / ratio;
let heightLeft = newCanvasHeight;
let position = 0;
pdf.addImage(imgData, 'PNG', 0, position, newCanvasWidth, newCanvasHeight);
heightLeft -= pdfHeight;
while (heightLeft > 0) {
position = heightLeft - newCanvasHeight;
pdf.addPage();
pdf.addImage(imgData, 'PNG', 0, position, newCanvasWidth, newCanvasHeight);
heightLeft -= pdfHeight;
}
pdf.save('WCAG-EM-Report.pdf');
} catch (error) {
console.error("Error generating PDF:", error);
alert("Could not generate PDF. Please try again.");
} finally {
pdfExportArea.style.backgroundColor = originalBg;
}
});
// --- TAB NAVIGATION ---
window.changeTab = (button, tabId) => {
updateReportDataFromUI(); // Save data before switching tabs
currentTabIndex = Array.from(dom.tabButtons).indexOf(button);
dom.tabs.forEach(tab => tab.classList.remove('active'));
dom.tabButtons.forEach(btn => btn.classList.remove('active'));
document.getElementById(tabId).classList.add('active');
button.classList.add('active');
if (tabId === 'step5') {
renderReport();
}
updateNavButtons();
};
const updateNavButtons = () => {
dom.prevTabBtn.disabled = currentTabIndex === 0;
dom.nextTabBtn.disabled = currentTabIndex === dom.tabButtons.length - 1;
};
dom.nextTabBtn.addEventListener('click', () => {
if (currentTabIndex < dom.tabButtons.length - 1) {
dom.tabButtons[currentTabIndex + 1].click();
}
});
dom.prevTabBtn.addEventListener('click', () => {
if (currentTabIndex > 0) {
dom.tabButtons[currentTabIndex - 1].click();
}
});
// --- INITIALIZATION ---
renderPageSampleList();
renderAuditSection();
updateNavButtons();
lucide.createIcons();
});