- api.php: override_check / unoverride_check endpoints write per-job .overrides.json; handleResult() injects overridden_checks on reload - js/results.js: score breakdown rows show "Mark as Passed" / "Undo" buttons; recalculateScore() adjusts penalty for dismissed issues and base score for manual overrides without mutating original data - index.html: score display gains hidden (Adjusted) badge and Recalculate Score button, revealed after first check - css/styles.css: btn-mark-passed, btn-unoverride, check-manual-pass, btn-recheck, score-adjusted-label styles - js/utils.js: escapeAttr() helper for safe inline onclick values Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
76 lines
2.9 KiB
JavaScript
76 lines
2.9 KiB
JavaScript
/* Utility functions — logging, progress, theme */
|
|
|
|
function addLog(message, type = 'info') {
|
|
const logContent = document.getElementById('logContent');
|
|
const entry = document.createElement('div');
|
|
entry.className = `log-entry ${type}`;
|
|
entry.setAttribute('role', type === 'error' ? 'alert' : 'status');
|
|
|
|
const timestamp = new Date().toLocaleTimeString();
|
|
entry.innerHTML = `<strong>${timestamp}</strong> ${message}`;
|
|
|
|
logContent.appendChild(entry);
|
|
logContent.scrollTop = logContent.scrollHeight;
|
|
}
|
|
|
|
function clearLog() {
|
|
const logContent = document.getElementById('logContent');
|
|
logContent.innerHTML = '<div class="log-entry" role="status">Initializing...</div>';
|
|
}
|
|
|
|
function updateProgress(percent, message) {
|
|
const fill = document.getElementById('progressFill');
|
|
const pct = document.getElementById('progressPercent');
|
|
const txt = document.getElementById('progressText');
|
|
|
|
fill.style.width = percent + '%';
|
|
fill.setAttribute('aria-valuenow', percent);
|
|
pct.textContent = percent + '%';
|
|
txt.textContent = message;
|
|
}
|
|
|
|
/* Dark mode toggle */
|
|
function toggleDarkMode() {
|
|
const root = document.documentElement;
|
|
const isDark = root.getAttribute('data-theme') === 'dark';
|
|
root.setAttribute('data-theme', isDark ? 'light' : 'dark');
|
|
localStorage.setItem('theme', isDark ? 'light' : 'dark');
|
|
const btn = document.getElementById('themeToggle');
|
|
if (btn) btn.textContent = isDark ? 'Dark' : 'Light';
|
|
}
|
|
|
|
function loadTheme() {
|
|
const saved = localStorage.getItem('theme');
|
|
if (saved === 'dark') {
|
|
document.documentElement.setAttribute('data-theme', 'dark');
|
|
const btn = document.getElementById('themeToggle');
|
|
if (btn) btn.textContent = 'Light';
|
|
}
|
|
}
|
|
|
|
/* Severity helpers */
|
|
function getSeverityColor(severity) {
|
|
const map = { CRITICAL: '#dc2626', ERROR: '#ef4444', WARNING: '#f59e0b', INFO: '#3b82f6', SUCCESS: '#10b981' };
|
|
return map[severity] || '#3b82f6';
|
|
}
|
|
|
|
function getSeverityIcon(severity) {
|
|
const map = { CRITICAL: '\u{1F6A8}', ERROR: '\u274C', WARNING: '\u26A0\uFE0F', INFO: '\u2139\uFE0F', SUCCESS: '\u2705' };
|
|
return map[severity] || '\u2022';
|
|
}
|
|
|
|
function escapeAttr(str) {
|
|
return String(str).replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/"/g, '"');
|
|
}
|
|
|
|
function getCategoryIcon(category) {
|
|
const icons = {
|
|
'Document Structure': '\u{1F3D7}\uFE0F', 'Metadata': '\u{1F4CB}', 'Language': '\u{1F310}',
|
|
'Text Accessibility': '\u{1F4DD}', 'Images': '\u{1F5BC}\uFE0F', 'Color Contrast': '\u{1F3A8}',
|
|
'Readability': '\u{1F4DA}', 'Link Text': '\u{1F517}', 'Forms': '\u{1F4C4}',
|
|
'Tables': '\u{1F4CA}', 'Headings': '\u{1F4D1}', 'Navigation': '\u{1F9ED}',
|
|
'Fonts': '\u{1F524}', 'Security': '\u{1F512}', 'OCR Quality': '\u{1F50D}'
|
|
};
|
|
const key = Object.keys(icons).find(k => category.includes(k));
|
|
return key ? icons[key] : '\u{1F4CC}';
|
|
}
|