|  | @@ -11,6 +11,15 @@ function chi(me,tag) { return me.getElementsByTagName(tag)[0]; }
 | 
	
		
			
				|  |  |  // Find a sibling element with a specific tag
 | 
	
		
			
				|  |  |  function sib(me,tag) { return chi(me.parentElement, tag); }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// Add/remove class entries in bulk; tags is an Array each containing
 | 
	
		
			
				|  |  | +// an Array of arguments to toggle. On return the second element of
 | 
	
		
			
				|  |  | +// each Array will be updated to the current value.
 | 
	
		
			
				|  |  | +function classmod(elem,tags) {
 | 
	
		
			
				|  |  | +    for (var i = 0; i < tags.length; i++)
 | 
	
		
			
				|  |  | +	tags[i][1] = elem.classList.toggle(tags[i]);
 | 
	
		
			
				|  |  | +    return tags;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // Read a key=value text file and return it as a Promise of a Map
 | 
	
		
			
				|  |  |  function fetchconfig(url) {
 | 
	
		
			
				|  |  |      return fetch(url, {redirect: "follow"})
 | 
	
	
		
			
				|  | @@ -122,25 +131,24 @@ function upload(form,data) {
 | 
	
		
			
				|  |  |  	}, PassiveListener);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    var result = chi(form,'output');
 | 
	
		
			
				|  |  | -    if (result)
 | 
	
		
			
				|  |  | -	result.classList.remove('result');
 | 
	
		
			
				|  |  | +    classmod(form, [['started',1],['done',0],['ok',0],['err',0],['running',1]]);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    xhr.addEventListener('readystatechange', (e) => {
 | 
	
		
			
				|  |  | -	if (xhr.readyState != XMLHttpRequest.DONE)
 | 
	
		
			
				|  |  | -	    return;
 | 
	
		
			
				|  |  | +    xhr.addEventListener('loadend', (e) => {
 | 
	
		
			
				|  |  |  	const ok = xhr.status >= 200 && xhr.status < 400;
 | 
	
		
			
				|  |  | -	if (progress)
 | 
	
		
			
				|  |  | -	    progress.value = ok ? progress.max : 0;
 | 
	
		
			
				|  |  | +	if (progress && ok)
 | 
	
		
			
				|  |  | +	    progress.value = progress.max;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	var result = chi(form,'output');
 | 
	
		
			
				|  |  |  	if (result) {
 | 
	
		
			
				|  |  |  	    var msg = xhr.responseText.trimEnd();
 | 
	
		
			
				|  |  |  	    if (!msg)
 | 
	
		
			
				|  |  |  		msg = xhr.status + ' ' + xhr.statusText;
 | 
	
		
			
				|  |  |  	    result.textContent = msg;
 | 
	
		
			
				|  |  | -	    result.classList.toggle('ok', ok);
 | 
	
		
			
				|  |  | -	    result.classList.toggle('err', !ok);
 | 
	
		
			
				|  |  | -	    result.classList.add('result');
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	classmod(form, [['ok',ok],['err',!ok],['running',0],['done',1]]);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// Automatically reload the page after successful upload
 | 
	
		
			
				|  |  |  	const rf = parseInt(xhr.getResponseHeader('Refresh'));
 | 
	
		
			
				|  |  |  	if (rf && ok)
 | 
	
		
			
				|  |  |  	    setTimeout(() => window.location.reload(), rf * 1000);
 |