Files
palette/internal/web/static/paste.js
T
fen 7a74ff3e74
CI / test (pull_request) Successful in 30s
CI / docker (pull_request) Skipped
Fix #139: drop unsafe-inline from script-src and style-src (#139)
- 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.
2026-09-10 09:08:17 -05:00

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);