pastebin / static /js /app.js
Akay Borana
Initial upload
421b222
const ThemeManager = {
init() {
const saved = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
this.setTheme(saved || (prefersDark ? 'dark' : 'light'));
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
if (!localStorage.getItem('theme')) this.setTheme(e.matches ? 'dark' : 'light');
});
},
setTheme(t) { document.documentElement.setAttribute('data-theme', t); localStorage.setItem('theme', t); },
toggle() { this.setTheme(document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'); }
};
const Toast = {
container: null,
init() { this.container = document.createElement('div'); this.container.className = 'toast-container'; document.body.appendChild(this.container); },
show(msg, type = 'success', dur = 2500) {
const t = document.createElement('div');
t.className = `toast ${type}`;
t.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">${type === 'success' ? '<path d="M20 6L9 17l-5-5"/>' : '<circle cx="12" cy="12" r="10"/><path d="M15 9l-6 6M9 9l6 6"/>'}</svg><span>${msg}</span>`;
this.container.appendChild(t);
setTimeout(() => { t.style.animation = 'slideIn 0.3s reverse'; setTimeout(() => t.remove(), 300); }, dur);
}
};
async function copyToClipboard(text, btn) {
try {
await navigator.clipboard.writeText(text);
Toast.show('Copied!');
if (btn) { const o = btn.innerHTML; btn.innerHTML = '✓ Copied'; setTimeout(() => btn.innerHTML = o, 1500); }
} catch { Toast.show('Copy failed', 'error'); }
}
function toggleWrap() {
const body = document.querySelector('.code-body');
const btn = document.getElementById('wrap-toggle');
if (!body) return;
body.classList.toggle('wrap-text');
const isWrapped = body.classList.contains('wrap-text');
localStorage.setItem('wrapText', isWrapped);
if (btn) btn.classList.toggle('btn-primary', isWrapped);
}
const API = {
async request(url, opts = {}) {
const r = await fetch(url, { headers: { 'Content-Type': 'application/json' }, credentials: 'same-origin', ...opts });
const d = await r.json();
if (!r.ok) throw new Error(d.error || 'Error');
return d;
},
createPaste: (d) => API.request('/api/paste', { method: 'POST', body: JSON.stringify(d) }),
updatePaste: (id, d) => API.request(`/api/paste/${id}`, { method: 'PUT', body: JSON.stringify(d) }),
deletePaste: (id) => API.request(`/api/paste/${id}`, { method: 'DELETE' }),
forkPaste: (id) => API.request(`/api/paste/${id}/fork`, { method: 'POST' }),
login: (u, p) => API.request('/api/auth/login', { method: 'POST', body: JSON.stringify({ username: u, password: p }) }),
register: (u, p) => API.request('/api/auth/register', { method: 'POST', body: JSON.stringify({ username: u, password: p }) }),
logout: () => API.request('/api/auth/logout', { method: 'POST' })
};
function setupPasteForm() {
const f = document.getElementById('paste-form');
if (!f) return;
f.addEventListener('submit', async e => {
e.preventDefault();
const btn = f.querySelector('button[type="submit"]');
try {
btn.disabled = true; btn.textContent = 'Creating...';
const paste = await API.createPaste({
title: f.title.value || 'Untitled', content: f.content.value, language: f.language.value,
is_public: f.is_public.checked, expires_in: f.expires_in?.value || 'never', burn_after_read: f.burn_after_read?.checked || false
});
window.location.href = `/${paste.id}`;
} catch (err) { Toast.show(err.message, 'error'); btn.disabled = false; btn.textContent = 'Create Paste'; }
});
}
function setupEditForm() {
const f = document.getElementById('edit-form');
if (!f) return;
f.addEventListener('submit', async e => {
e.preventDefault();
const btn = f.querySelector('button[type="submit"]');
try {
btn.disabled = true; btn.textContent = 'Saving...';
await API.updatePaste(f.dataset.pasteId, { title: f.title.value, content: f.content.value, language: f.language.value, is_public: f.is_public.checked });
Toast.show('Saved!'); setTimeout(() => window.location.href = `/${f.dataset.pasteId}`, 800);
} catch (err) { Toast.show(err.message, 'error'); btn.disabled = false; btn.textContent = 'Save'; }
});
}
function setupAuthForms() {
const login = document.getElementById('login-form');
const reg = document.getElementById('register-form');
if (login) login.addEventListener('submit', async e => {
e.preventDefault();
const btn = login.querySelector('button[type="submit"]');
try { btn.disabled = true; await API.login(login.username.value, login.password.value); window.location.href = '/dashboard'; }
catch (err) { Toast.show(err.message, 'error'); btn.disabled = false; }
});
if (reg) reg.addEventListener('submit', async e => {
e.preventDefault();
const btn = reg.querySelector('button[type="submit"]');
try { btn.disabled = true; await API.register(reg.username.value, reg.password.value); window.location.href = '/dashboard'; }
catch (err) { Toast.show(err.message, 'error'); btn.disabled = false; }
});
}
function setupDeleteButton() {
const btn = document.getElementById('delete-paste');
if (!btn) return;
btn.addEventListener('click', async () => {
if (!confirm('Delete this paste?')) return;
try { await API.deletePaste(btn.dataset.pasteId); Toast.show('Deleted!'); setTimeout(() => window.location.href = '/dashboard', 800); }
catch (err) { Toast.show(err.message, 'error'); }
});
}
function setupForkButton() {
const btn = document.getElementById('fork-paste');
if (!btn) return;
btn.addEventListener('click', async () => {
try { const f = await API.forkPaste(btn.dataset.pasteId); Toast.show('Forked!'); setTimeout(() => window.location.href = `/${f.id}`, 800); }
catch (err) { Toast.show(err.message, 'error'); }
});
}
function setupLogout() {
const btn = document.getElementById('logout-btn');
if (!btn) return;
btn.addEventListener('click', async e => { e.preventDefault(); try { await API.logout(); window.location.href = '/'; } catch { } });
}
function setupKeyboardShortcuts() {
document.addEventListener('keydown', e => {
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
const f = document.querySelector('#paste-form, #edit-form');
if (f) { e.preventDefault(); f.dispatchEvent(new Event('submit')); }
}
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
const f = document.getElementById('edit-form');
if (f) { e.preventDefault(); f.dispatchEvent(new Event('submit')); }
}
});
}
function restoreWrapState() {
if (localStorage.getItem('wrapText') === 'true') {
const body = document.querySelector('.code-body');
const btn = document.getElementById('wrap-toggle');
if (body) body.classList.add('wrap-text');
if (btn) btn.classList.add('btn-primary');
}
}
document.addEventListener('DOMContentLoaded', () => {
ThemeManager.init();
Toast.init();
setupPasteForm();
setupEditForm();
setupAuthForms();
setupDeleteButton();
setupForkButton();
setupLogout();
setupKeyboardShortcuts();
restoreWrapState();
});
window.toggleTheme = () => ThemeManager.toggle();
window.copyToClipboard = copyToClipboard;
window.toggleWrap = toggleWrap;