script-src 'self' blocks inline onclick handlers and <script> blocks. Migrate all interactive behavior to data-* attributes wired by mcias.js: - data-toggle-form: accounts, policies, pgcreds create-form buttons - data-href: audit clickable table rows - data-tab: policy form tab switching (moved showTab from inline script) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
88 lines
3.4 KiB
JavaScript
88 lines
3.4 KiB
JavaScript
// mcias.js — HTMX event wiring and CSP-safe UI helpers for the MCIAS web UI.
|
|
//
|
|
// All interactive behavior that would otherwise require inline onclick/onchange
|
|
// handlers is wired here via data-* attributes so that script-src 'self'
|
|
// (without 'unsafe-inline') is sufficient.
|
|
|
|
// Show server error responses in the global #htmx-error-banner.
|
|
//
|
|
// HTMX 2.x fires htmx:responseError for 4xx/5xx responses and does not swap
|
|
// the body into the target by default. The server's renderError() always
|
|
// returns a <div class="alert alert-error"> fragment whose message is
|
|
// HTML-escaped server-side, so setting innerHTML here is safe.
|
|
document.body.addEventListener('htmx:responseError', function (evt) {
|
|
var banner = document.getElementById('htmx-error-banner');
|
|
if (!banner) { return; }
|
|
var body = (evt.detail.xhr && evt.detail.xhr.responseText) || 'An unexpected error occurred.';
|
|
banner.innerHTML = body;
|
|
banner.style.display = '';
|
|
banner.scrollIntoView({ behavior: 'instant', block: 'nearest' });
|
|
});
|
|
|
|
// Clear the error banner whenever a successful HTMX swap completes so
|
|
// stale errors do not persist after the user corrects their input.
|
|
document.body.addEventListener('htmx:afterSwap', function () {
|
|
var banner = document.getElementById('htmx-error-banner');
|
|
if (banner) {
|
|
banner.style.display = 'none';
|
|
banner.innerHTML = '';
|
|
}
|
|
});
|
|
|
|
// --- Toggle-form buttons ---
|
|
//
|
|
// Usage: <button data-toggle-form="create-form"
|
|
// data-label-show="Add Item" data-label-hide="Cancel">
|
|
function toggleForm(btn) {
|
|
var id = btn.getAttribute('data-toggle-form');
|
|
var el = document.getElementById(id);
|
|
if (!el) { return; }
|
|
var show = el.hidden || el.style.display === 'none';
|
|
el.hidden = false;
|
|
el.style.display = show ? '' : 'none';
|
|
var labelShow = btn.getAttribute('data-label-show') || 'Create';
|
|
var labelHide = btn.getAttribute('data-label-hide') || 'Cancel';
|
|
btn.textContent = show ? labelHide : labelShow;
|
|
}
|
|
|
|
// --- Clickable rows ---
|
|
//
|
|
// Usage: <tr class="clickable-row" data-href="/audit/123">
|
|
function handleClickableRow(row) {
|
|
var href = row.getAttribute('data-href');
|
|
if (href) { window.location = href; }
|
|
}
|
|
|
|
// --- Policy form tab switching ---
|
|
//
|
|
// Usage: <button data-tab="form"> / <button data-tab="json">
|
|
function showTab(tab) {
|
|
var formMode = document.getElementById('pf-form-mode');
|
|
var jsonMode = document.getElementById('pf-json-mode');
|
|
var formBtn = document.getElementById('tab-form');
|
|
var jsonBtn = document.getElementById('tab-json');
|
|
if (!formMode || !jsonMode) { return; }
|
|
formMode.style.display = tab === 'form' ? '' : 'none';
|
|
jsonMode.style.display = tab === 'json' ? '' : 'none';
|
|
if (formBtn) { formBtn.className = tab === 'form' ? 'btn btn-sm btn-secondary' : 'btn btn-sm'; }
|
|
if (jsonBtn) { jsonBtn.className = tab === 'json' ? 'btn btn-sm btn-secondary' : 'btn btn-sm'; }
|
|
}
|
|
|
|
// --- Auto-wire on DOMContentLoaded ---
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
// Toggle-form buttons.
|
|
document.querySelectorAll('[data-toggle-form]').forEach(function (btn) {
|
|
btn.addEventListener('click', function () { toggleForm(btn); });
|
|
});
|
|
|
|
// Clickable table rows.
|
|
document.querySelectorAll('[data-href]').forEach(function (row) {
|
|
row.addEventListener('click', function () { handleClickableRow(row); });
|
|
});
|
|
|
|
// Tab buttons.
|
|
document.querySelectorAll('[data-tab]').forEach(function (btn) {
|
|
btn.addEventListener('click', function () { showTab(btn.getAttribute('data-tab')); });
|
|
});
|
|
});
|