Add upload to navbar

This commit is contained in:
2026-09-13 12:12:45 +02:00
parent cb34bae784
commit 000f9d6984
5 changed files with 34 additions and 55 deletions
+16 -26
View File
@@ -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();