diff --git a/web/static/app.js b/web/static/app.js index afbf94d..21061cd 100644 --- a/web/static/app.js +++ b/web/static/app.js @@ -1,11 +1,11 @@ -// Progressive enhancement for the upload form. With JavaScript off the plain -// multipart POST still works; everything here is additive. +// Everything here is additive: with JavaScript off, the plain multipart POST +// still works. (function () { 'use strict'; - // Copy buttons appear on the server-rendered result page too, which has no - // upload form, so they are wired before anything form-specific bails out. - // They ship hidden so that without this script there is no dead button. + // Wired before the form-specific code bails out, because the result page has + // copy buttons and no form. They ship hidden, so without this there is no + // dead button. function wireCopy() { var buttons = document.querySelectorAll('button.copy[data-copy]'); Array.prototype.forEach.call(buttons, function (button) { @@ -20,12 +20,9 @@ timer = setTimeout(function () { button.textContent = label; }, 1500); } - // Copying the selection needs neither a permission nor a secure context, - // so it answers for both of the ways the clipboard API can let us down: - // missing entirely (this service is reached over plain HTTP as well as - // HTTPS, and the API exists only on the secure side) or present and - // refusing (an unfocused document, a permissions policy). If even that - // fails, the text is selected either way, so say what to press. + // Answers both ways the clipboard API fails us: absent over plain HTTP, + // where it is not a secure context, and present but refusing. Needs no + // permission. If it fails too, the text is selected, so say what to press. function copySelection() { var ok = false; try { ok = document.execCommand('copy'); } catch (e) { /* ok stays false */ } @@ -47,8 +44,6 @@ } wireCopy(); - // Destructive buttons ask first. The attribute carries the question, so the - // markup decides what is worth confirming and this stays generic. Array.prototype.forEach.call(document.querySelectorAll('[data-confirm]'), function (button) { button.addEventListener('click', function (e) { if (!window.confirm(button.dataset.confirm)) e.preventDefault(); @@ -67,8 +62,7 @@ var submit = document.getElementById('submit'); var errorBox = document.getElementById('error'); - // The session's limits are rendered by the server, so this script never has - // to ask who the visitor is. Zero means unlimited. + // Zero means unlimited. var maxSize = parseInt(form.dataset.maxSize, 10) || 0; function formatSize(n) { @@ -82,7 +76,6 @@ errorBox.hidden = !msg; } - // --- drag and drop ------------------------------------------------------ function describeSelection() { var f = fileInput.files[0]; dropHint.textContent = f ? f.name + ' - ' + formatSize(f.size) @@ -109,7 +102,6 @@ }); fileInput.addEventListener('change', describeSelection); - // --- upload ------------------------------------------------------------- form.addEventListener('submit', function (e) { var file = fileInput.files[0]; if (!file) return; // let the browser's own validation speak @@ -127,16 +119,15 @@ progress.hidden = false; // FormData follows markup order, not painted order, so the options and the - // token precede the file part exactly as the server requires. The drop - // zone only looks like it comes first; see the order rules in style.css. + // token precede the file part as the server requires. The drop zone only + // looks like it comes first; see the order rules in style.css. var data = new FormData(form); var started = Date.now(); var xhr = new XMLHttpRequest(); xhr.open('POST', form.action); - // Ask for the page a plain form post would have been given. What a - // finished upload looks like is the server's business, and it already - // renders it; this script only adds the progress bar on the way there. + // Ask for the page a plain form post would get: the server owns what a + // finished upload looks like, and this only adds a progress bar. xhr.setRequestHeader('Accept', 'text/html'); xhr.upload.onprogress = function (ev) { @@ -153,10 +144,9 @@ }; xhr.onload = function () { - // Whatever came back, the result page or an error page, is the page the - // browser should be showing now. Writing it into this document rather - // than navigating leaves the address on the form's own URL, so a reload - // offers a fresh form instead of posting the file a second time. + // Writing the response into this document rather than navigating leaves + // the address on the form's URL, so a reload offers a fresh form instead + // of posting the file again. document.open(); document.write(xhr.responseText); document.close(); diff --git a/web/static/style.css b/web/static/style.css index 5adfd43..13584f8 100644 --- a/web/static/style.css +++ b/web/static/style.css @@ -83,10 +83,9 @@ input[type=text], input[type=password] { .row { display: flex; gap: 1rem; flex-wrap: wrap; } .row > .field { flex: 1 1 12rem; } -/* The file input has to be the last part in the submitted body, so it sits at - the bottom of the form's markup and is lifted back to the top here. Only the - painting order moves; the submission order, which the server depends on, is - the markup's. */ +/* The file input must be the last part in the submitted body, so it sits last + in the markup and is lifted back to the top here. Only the painting order + moves; the server depends on the markup's. */ #upload { display: flex; flex-direction: column; } #upload > .drop { order: -1; } #upload > button { align-self: flex-start; } @@ -184,17 +183,13 @@ button.link { cursor: pointer; } -/* Result card ------------------------------------------------------------ */ - .result .links { margin: 1.25rem 0 0; } .result .links .field { margin-bottom: 1rem; } -/* A read-only URL with its copy button alongside. */ .copyrow { display: flex; gap: .5rem; align-items: stretch; } .copyrow input { flex: 1 1 auto; min-width: 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .8125rem; } .copyrow .copy { flex: 0 0 auto; } -/* Keep the detail list clear of the copy buttons above it. */ .result dl { margin-top: 1.75rem; padding-top: 1.25rem; border-top: 1px solid var(--line); } .result dl dd { margin-bottom: .25rem; } @@ -205,8 +200,6 @@ button.link { .copyrow input { flex-basis: 100%; } } -/* Administration --------------------------------------------------------- */ - header { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap; } header nav { margin-left: auto; @@ -244,8 +237,6 @@ table.admin .nowrap { white-space: nowrap; } table.admin form { margin: 0; } table.admin .actions-cell { text-align: right; width: 1%; } -/* The filename carries the link and the identifier sits under it, which saves - a whole column of UUID. */ table.admin .filecell a { overflow-wrap: anywhere; font-weight: 500; } table.admin .filecell .id { display: block; @@ -264,8 +255,8 @@ button.small { padding: .2rem .5rem; font-size: .75rem; } overflow: hidden; } -/* Narrow screens get one card per row rather than a horizontal scrollbar, - which is unusable for picking a row and pressing a button in it. */ +/* One card per row beats a horizontal scrollbar, which is unusable for picking + a row and pressing a button in it. */ @media (max-width: 46rem) { table.admin, table.admin tbody, @@ -289,10 +280,8 @@ button.small { padding: .2rem .5rem; font-size: .75rem; } .cli code, .hint code { background: var(--bg); padding: .1rem .3rem; border-radius: 4px; } -/* Disclosure panels: the one-off token on the upload form, "Remove this file" - on the info page. Closed, a panel is a single line and needs nothing; open, - it needs an edge, or its contents read as loose page furniture and there is - no telling where it ends. */ +/* Disclosure panels. Open, a panel needs an edge, or there is no telling where + its contents end. */ details.panel { border: 1px solid var(--line); border-radius: var(--radius); @@ -309,20 +298,17 @@ details.panel[open] > summary { border-bottom: 1px solid var(--line); margin-bottom: .75rem; } -/* The summary carries the panel's padding itself; everything else is inset to - match, and the panel closes the gap under the last of them. */ details.panel > :not(summary) { padding-left: .75rem; padding-right: .75rem; } details.panel[open] { padding-bottom: .75rem; } details.panel > :last-child { margin-bottom: 0; } details.panel .field { max-width: 24rem; } -/* A hint that introduces a control is an instruction for what follows, not a - caption for what came before, so it must not sit against it. */ +/* A hint before a control explains what follows, so it must not sit against + it. */ .hint + form, .hint + .field { margin-top: .75rem; } .limits .hint { margin-top: .75rem; } .limits dl, .card dl { row-gap: .375rem; } -/* Actions that mix a link-button with a form-button. */ .actions { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; } .actions form { margin: 0; } diff --git a/web/templates/index.html b/web/templates/index.html index b83ebd0..1a58fc3 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -6,10 +6,10 @@