- Move all inline <script> blocks (layout head/theme, topbar dark toggle, foot, paste, new, history, mine, settings, admin, unlock) to external files under internal/web/static/. Page data reaches scripts via data-* attributes (data-paste-id, data-default-dark) instead of template vars. - Replace inline onclick handlers (copy, delete, stats toggle) with addEventListener wiring. - Convert inline style="" attributes to CSS utility classes; swatch colors are now set via CSSOM/DOM APIs instead of innerHTML strings. - script-src/style-src are now plain 'self'; img-src data: stays for the SVG data-URI backgrounds. Verified with headless chromium: zero CSP violations on all pages in dark and light presets, theme swatches, admin lock, tables and paste view render correctly.
48 lines
2.1 KiB
JavaScript
48 lines
2.1 KiB
JavaScript
// #53/#139: paste viewer page logic. Paste id arrives via <body data-paste-id>.
|
|
function toast(msg) {
|
|
let t = document.querySelector('.toast');
|
|
if (!t) { t = document.createElement('div'); t.className = 'toast'; document.body.appendChild(t); }
|
|
t.textContent = msg;
|
|
t.classList.add('show');
|
|
clearTimeout(t._h);
|
|
t._h = setTimeout(() => t.classList.remove('show'), 2000);
|
|
}
|
|
function toggleStats() {
|
|
const body = document.getElementById('stats-body');
|
|
const pill = document.getElementById('stats-pill');
|
|
const btn = document.getElementById('stats-toggle');
|
|
const open = body.hidden;
|
|
body.hidden = !open;
|
|
pill.classList.toggle('open', open);
|
|
btn.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
}
|
|
function copyContent(btn) {
|
|
navigator.clipboard.writeText(document.getElementById('raw-content').value);
|
|
// in-place success feedback (#53)
|
|
if (btn) {
|
|
btn.classList.add('ok');
|
|
btn.textContent = 'Success!';
|
|
clearTimeout(btn._okh);
|
|
btn._okh = setTimeout(() => { btn.classList.remove('ok'); btn.textContent = 'copy'; }, 2000);
|
|
} else {
|
|
toast('Copied', 'success');
|
|
}
|
|
}
|
|
function redeem() {
|
|
if (!confirm('Hard delete this paste immediately?')) return;
|
|
let tok = '';
|
|
try { tok = sessionStorage.getItem('deletion_token_' + PASTE_ID) || ''; } catch(e) {}
|
|
if (!tok) { alert('deletion token not available in this browser'); return; }
|
|
fetch('/api/pastes/' + PASTE_ID + '/redeem', {method: 'DELETE', headers: {'Authorization': 'Bearer ' + tok}})
|
|
.then(r => { if (r.ok) location.href = '/history'; else alert('delete failed'); });
|
|
}
|
|
|
|
// wiring (moved from inline handlers for CSP #139)
|
|
var PASTE_ID = document.currentScript.getAttribute('data-paste-id');
|
|
var copyBtn = document.getElementById('copy-btn');
|
|
if (copyBtn) copyBtn.addEventListener('click', function (e) { e.preventDefault(); copyContent(copyBtn); });
|
|
var delBtn = document.getElementById('delete-btn');
|
|
if (delBtn) delBtn.addEventListener('click', function (e) { e.preventDefault(); redeem(); });
|
|
var statsToggle = document.getElementById('stats-toggle');
|
|
if (statsToggle) statsToggle.addEventListener('click', toggleStats);
|