diff options
Diffstat (limited to 'web')
| -rw-r--r-- | web/js/admin.js | 23 | ||||
| -rw-r--r-- | web/js/app.js | 3 | ||||
| -rw-r--r-- | web/js/dom.js | 24 | ||||
| -rw-r--r-- | web/js/lightbox.js | 4 | ||||
| -rw-r--r-- | web/js/podcasts.js | 23 | ||||
| -rw-r--r-- | web/js/utils.js | 21 | ||||
| -rw-r--r-- | web/js/views/media-actions.js | 2 | ||||
| -rw-r--r-- | web/js/views/media-grid.js | 3 | ||||
| -rw-r--r-- | web/js/views/media-info.js | 3 | ||||
| -rw-r--r-- | web/js/views/sets.js | 2 | ||||
| -rw-r--r-- | web/js/views/shares.js | 3 | ||||
| -rw-r--r-- | web/js/views/tags.js | 2 | ||||
| -rw-r--r-- | web/js/views/upload.js | 2 | ||||
| -rw-r--r-- | web/sw.js | 3 |
14 files changed, 45 insertions, 73 deletions
diff --git a/web/js/admin.js b/web/js/admin.js index bc2913a..86ad471 100644 --- a/web/js/admin.js +++ b/web/js/admin.js @@ -1,4 +1,5 @@ import { API } from './api.js'; +import { escapeHtml, toast } from './utils.js'; export function initAdmin() { const btn = document.getElementById('admin-toggle'); @@ -54,16 +55,12 @@ async function refreshAdmin() { } catch (err) { toast(err.message, 'error'); } } -function esc(s) { - return (s ?? '').replace(/[&<>"']/g, (c) => ({ '&':'&','<':'<','>':'>','"':'"',"'":''' }[c])); -} - function renderUsers(users) { const el = document.getElementById('admin-users'); if (!el) return; el.innerHTML = `<ul class="admin-list"> ${users.map((u) => `<li class="${u.is_admin ? 'is-admin' : ''}"> - <span>${esc(u.username)}${u.is_admin ? ' <span class="badge">admin</span>' : ''}</span> + <span>${escapeHtml(u.username)}${u.is_admin ? ' <span class="badge">admin</span>' : ''}</span> <button class="btn btn-danger btn-sm" data-id="${u.id}">Remove</button> </li>`).join('')} </ul>`; @@ -102,12 +99,12 @@ function renderPermissions(data) { let html = '<table class="admin-table"><thead><tr><th>Set</th>'; data.users.forEach((u) => { - html += `<th>${esc(u.username)}</th>`; + html += `<th>${escapeHtml(u.username)}</th>`; }); html += '</tr></thead><tbody>'; data.sets.forEach((s) => { - html += `<tr><td>${esc(s.name)}</td>`; + html += `<tr><td>${escapeHtml(s.name)}</td>`; data.users.forEach((u) => { const role = roleMap[s.id]?.[u.id] || ''; const selectId = `perm-${s.id}-${u.id}`; @@ -152,8 +149,8 @@ function renderTrash(data) { } el.innerHTML = `<h4 class="text-90">Trash</h4><ul class="admin-list"> ${data.map((m) => `<li> - <span>${esc(m.file_name)}</span> - <span class="text-muted text-75">${esc(m.deleted_at || '')}</span> + <span>${escapeHtml(m.file_name)}</span> + <span class="text-muted text-75">${escapeHtml(m.deleted_at || '')}</span> <button class="btn btn-primary btn-sm" data-id="${m.id}">Restore</button> </li>`).join('')} </ul>`; @@ -169,11 +166,3 @@ function renderTrash(data) { }); }); } - -function toast(msg, type = 'info') { - const t = document.getElementById('toast'); - if (!t) return; - t.textContent = msg; - t.className = 'toast show ' + type; - setTimeout(() => t.classList.remove('show'), 2800); -} diff --git a/web/js/app.js b/web/js/app.js index 81799c6..ee515af 100644 --- a/web/js/app.js +++ b/web/js/app.js @@ -39,7 +39,8 @@ import { zoomOut as lightboxZoomOut, toggleSlideshow as lightboxToggleSlideshow, } from './lightbox.js'; -import { closeAllModals, toast } from './dom.js'; +import { closeAllModals } from './dom.js'; +import { toast } from './utils.js'; import { showAdmin } from './views/admin-status.js'; import { initHelp, showSearch, toggleHelp, toggleSidebar } from './views/help.js'; import { diff --git a/web/js/dom.js b/web/js/dom.js index 3d78bb4..1ea5840 100644 --- a/web/js/dom.js +++ b/web/js/dom.js @@ -1,3 +1,5 @@ +export { escapeHtml, fmtDur, toast } from './utils.js'; + export function fmtDate(d) { if (!d) return ''; const dt = typeof d === 'string' ? new Date(d) : d; @@ -11,16 +13,6 @@ export function fmtDateTime(d) { return dt.toLocaleString(); } -export function fmtDur(s) { - if (!s || s < 0) return '0:00'; - const h = Math.floor(s / 3600); - const m = Math.floor((s % 3600) / 60); - const sec = Math.floor(s % 60); - const mm = String(m).padStart(2, '0'); - const ss = String(sec).padStart(2, '0'); - return h > 0 ? `${h}:${mm}:${ss}` : `${mm}:${ss}`; -} - export function fmtSize(bytes) { if (!bytes || bytes <= 0) return ''; const kb = bytes / 1024; @@ -30,18 +22,6 @@ export function fmtSize(bytes) { return Math.round((mb / 1024) * 10) / 10 + ' GB'; } -export function escapeHtml(s) { - return (s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); -} - -export function toast(msg, type = 'info') { - const t = document.getElementById('toast'); - if (!t) return; - t.textContent = msg; - t.className = 'toast show ' + type; - setTimeout(() => t.classList.remove('show'), 2800); -} - export function closeAllModals() { document.querySelectorAll('.modal-overlay.open').forEach((m) => m.classList.remove('open')); } diff --git a/web/js/lightbox.js b/web/js/lightbox.js index 876f456..b8cda54 100644 --- a/web/js/lightbox.js +++ b/web/js/lightbox.js @@ -175,7 +175,3 @@ function fmtSize(bytes) { if (mb < 1024) return Math.round(mb * 10) / 10 + ' MB'; return Math.round((mb / 1024) * 10) / 10 + ' GB'; } - -function escapeHtml(s) { - return (s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); -} diff --git a/web/js/podcasts.js b/web/js/podcasts.js index 690b24e..1883e63 100644 --- a/web/js/podcasts.js +++ b/web/js/podcasts.js @@ -1,5 +1,6 @@ // podcastUI.js — Podcast feed manager + episode rendering. import { API } from './api.js'; +import { escapeHtml, fmtDur, toast } from './utils.js'; export function initPodcasts() { const modal = document.getElementById('podcast-modal'); @@ -88,7 +89,7 @@ export function renderPodcastEpisodes(grid, episodes) { function renderEpisodeHtml(ep) { const completed = ep.is_completed; const dateStr = ep.published_at ? new Date(ep.published_at).toLocaleDateString() : ''; - const duration = ep.duration_seconds ? fmtDuration(ep.duration_seconds) : ''; + const duration = ep.duration_seconds ? fmtDur(ep.duration_seconds) : ''; return ` <div class="thumb-wrap"> <span class="placeholder">🎙️</span> @@ -104,23 +105,3 @@ function renderEpisodeHtml(ep) { </div> `; } - -function fmtDuration(s) { - const m = Math.floor(s / 60); - const h = Math.floor(m / 60); - if (h > 0) return `${h}h ${m % 60}m`; - return `${m}m`; -} - -function escapeHtml(str) { - if (!str) return ''; - return str.replace(/[&<>"]/g, c => ({'&':'&','<':'<','>':'>','"':'"'}[c])); -} - -function toast(msg, type = 'info') { - const t = document.getElementById('toast'); - if (!t) return; - t.textContent = msg; - t.className = 'toast show ' + type; - setTimeout(() => t.classList.remove('show'), 2800); -} diff --git a/web/js/utils.js b/web/js/utils.js new file mode 100644 index 0000000..2851211 --- /dev/null +++ b/web/js/utils.js @@ -0,0 +1,21 @@ +export function fmtDur(s) { + if (!s || s < 0) return '0:00'; + const h = Math.floor(s / 3600); + const m = Math.floor((s % 3600) / 60); + const sec = Math.floor(s % 60); + const mm = String(m).padStart(2, '0'); + const ss = String(sec).padStart(2, '0'); + return h > 0 ? `${h}:${mm}:${ss}` : `${mm}:${ss}`; +} + +export function escapeHtml(s) { + return (s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); +} + +export function toast(msg, type = 'info') { + const t = document.getElementById('toast'); + if (!t) return; + t.textContent = msg; + t.className = 'toast show ' + type; + setTimeout(() => t.classList.remove('show'), 2800); +} diff --git a/web/js/views/media-actions.js b/web/js/views/media-actions.js index 7dab090..2367241 100644 --- a/web/js/views/media-actions.js +++ b/web/js/views/media-actions.js @@ -2,7 +2,7 @@ import { API } from '../api.js'; import { currentElement } from '../selection.js'; import { currentMediaId } from '../player.js'; import { open as openNotes } from '../notes.js'; -import { toast } from '../dom.js'; +import { toast } from '../utils.js'; export async function shareSelected() { const el = currentElement(); diff --git a/web/js/views/media-grid.js b/web/js/views/media-grid.js index f773f37..5c16de0 100644 --- a/web/js/views/media-grid.js +++ b/web/js/views/media-grid.js @@ -1,7 +1,8 @@ import { API } from '../api.js'; import { state, setMedia } from '../state.js'; import { clearSelection, selectByElement } from '../selection.js'; -import { escapeHtml, fmtDur, fmtSize, toast } from '../dom.js'; +import { fmtSize } from '../dom.js'; +import { escapeHtml, fmtDur, toast } from '../utils.js'; let callbacks = {}; diff --git a/web/js/views/media-info.js b/web/js/views/media-info.js index 06d88ad..51e576c 100644 --- a/web/js/views/media-info.js +++ b/web/js/views/media-info.js @@ -1,5 +1,6 @@ import { API } from '../api.js'; -import { escapeHtml, fmtDateTime, fmtDur, fmtSize, toast } from '../dom.js'; +import { fmtDateTime, fmtSize } from '../dom.js'; +import { escapeHtml, fmtDur, toast } from '../utils.js'; import { selectedMediaId } from './media-actions.js'; export function initMediaInfo() { diff --git a/web/js/views/sets.js b/web/js/views/sets.js index 9bb6db3..6badcb3 100644 --- a/web/js/views/sets.js +++ b/web/js/views/sets.js @@ -1,6 +1,6 @@ import { API } from '../api.js'; import { state } from '../state.js'; -import { escapeHtml, toast } from '../dom.js'; +import { escapeHtml, toast } from '../utils.js'; let loadMediaCallback = () => {}; diff --git a/web/js/views/shares.js b/web/js/views/shares.js index 12f717c..3cbf6b5 100644 --- a/web/js/views/shares.js +++ b/web/js/views/shares.js @@ -1,6 +1,7 @@ import { API } from '../api.js'; import { state } from '../state.js'; -import { escapeHtml, fmtDate, toast } from '../dom.js'; +import { fmtDate } from '../dom.js'; +import { escapeHtml, toast } from '../utils.js'; export function initShares() { const modal = document.getElementById('shares-modal'); diff --git a/web/js/views/tags.js b/web/js/views/tags.js index 21186bf..7d3ceba 100644 --- a/web/js/views/tags.js +++ b/web/js/views/tags.js @@ -1,6 +1,6 @@ import { API } from '../api.js'; import { currentElement } from '../selection.js'; -import { escapeHtml, toast } from '../dom.js'; +import { escapeHtml, toast } from '../utils.js'; let tagsCurrentMediaId = null; diff --git a/web/js/views/upload.js b/web/js/views/upload.js index 19ceb87..de88ba5 100644 --- a/web/js/views/upload.js +++ b/web/js/views/upload.js @@ -1,6 +1,6 @@ import { API } from '../api.js'; import { state } from '../state.js'; -import { toast } from '../dom.js'; +import { toast } from '../utils.js'; let loadMediaCallback = () => {}; @@ -1,4 +1,4 @@ -const CACHE = 'kiss-v7'; +const CACHE = 'kiss-v8'; const ASSETS = [ '/', '/index.html', @@ -22,6 +22,7 @@ const ASSETS = [ '/js/notes.js', '/js/admin.js', '/js/dom.js', + '/js/utils.js', '/js/podcasts.js', '/js/pwa.js', '/js/lightbox.js', |
