diff options
Diffstat (limited to 'plugins')
| -rw-r--r-- | plugins/static-files/Makefile | 8 | ||||
| -rw-r--r-- | plugins/webbox/Makefile | 16 | ||||
| -rw-r--r-- | plugins/webbox/html/directory.png | bin | 0 -> 683 bytes | |||
| -rw-r--r-- | plugins/webbox/html/favicon.ico | bin | 0 -> 2238 bytes | |||
| -rw-r--r-- | plugins/webbox/html/file.png | bin | 0 -> 530 bytes | |||
| -rw-r--r-- | plugins/webbox/html/index.html | 108 | ||||
| -rw-r--r-- | plugins/webbox/html/menu.png | bin | 0 -> 456 bytes | |||
| -rw-r--r-- | plugins/webbox/html/refresh-inverted.png | bin | 0 -> 771 bytes | |||
| -rw-r--r-- | plugins/webbox/html/webbox.css | 289 | ||||
| -rw-r--r-- | plugins/webbox/html/webbox.js | 889 | 
10 files changed, 1297 insertions, 13 deletions
diff --git a/plugins/static-files/Makefile b/plugins/static-files/Makefile index b928506..6577785 100644 --- a/plugins/static-files/Makefile +++ b/plugins/static-files/Makefile @@ -92,14 +92,8 @@ googletest/src/%.o: googletest/src/%.cc  ADD_DEP=Makefile  install: -	mkdir -p $(DESTDIR)/usr/bin -	cp webserver $(DESTDIR)/usr/bin -  	mkdir -p $(DESTDIR)/usr/lib/webserver/plugins -	mkdir -p $(DESTDIR)/usr/local/lib/webserver/plugins - -	mkdir -p $(DESTDIR)/etc -	cp webserver.conf $(DESTDIR)/etc/webserver.conf +	cp $(PROJECTNAME).so $(DESTDIR)/usr/lib/webserver/plugins  # misc ---------------------------------------------------  deb: diff --git a/plugins/webbox/Makefile b/plugins/webbox/Makefile index 2ddec0e..1850018 100644 --- a/plugins/webbox/Makefile +++ b/plugins/webbox/Makefile @@ -94,14 +94,18 @@ googletest/src/%.o: googletest/src/%.cc  ADD_DEP=Makefile  install: -	mkdir -p $(DESTDIR)/usr/bin -	cp webserver $(DESTDIR)/usr/bin -  	mkdir -p $(DESTDIR)/usr/lib/webserver/plugins -	mkdir -p $(DESTDIR)/usr/local/lib/webserver/plugins +	cp $(PROJECTNAME).so $(DESTDIR)/usr/lib/webserver/plugins + +	mkdir -p $(DESTDIR)/var/www/webbox +	cp -r html/* $(DESTDIR)/var/www/webbox/ + +	# Minify, conditionally +	command -v uglifyjs && uglifyjs html/webbox.js -m -c > $(DESTDIR)/var/www/webbox/webbox.js || true +	command -v htmlmin && \ +		htmlmin html/index.html $(DESTDIR)/var/www/webbox/index.html || true +	command -v cleancss && cleancss -o $(DESTDIR)/var/www/webbox/webbox.css html/webbox.css || true -	mkdir -p $(DESTDIR)/etc -	cp webserver.conf $(DESTDIR)/etc/webserver.conf  # misc ---------------------------------------------------  deb: diff --git a/plugins/webbox/html/directory.png b/plugins/webbox/html/directory.png Binary files differnew file mode 100644 index 0000000..5b4dc10 --- /dev/null +++ b/plugins/webbox/html/directory.png diff --git a/plugins/webbox/html/favicon.ico b/plugins/webbox/html/favicon.ico Binary files differnew file mode 100644 index 0000000..e8cbddb --- /dev/null +++ b/plugins/webbox/html/favicon.ico diff --git a/plugins/webbox/html/file.png b/plugins/webbox/html/file.png Binary files differnew file mode 100644 index 0000000..a5c92e6 --- /dev/null +++ b/plugins/webbox/html/file.png diff --git a/plugins/webbox/html/index.html b/plugins/webbox/html/index.html new file mode 100644 index 0000000..e9ea819 --- /dev/null +++ b/plugins/webbox/html/index.html @@ -0,0 +1,108 @@ +<!DOCTYPE html> +<html> +	<head> +		<meta charset="utf-8"/> +		<meta name="viewport" content="width=device-width, initial-scale=1"> +		<title>Webbox</title> +		<link rel="stylesheet" type="text/css" href="webbox.css"/> +		<script src="webbox.js"></script> +	</head> +	<body onload="initMainpage();"> +		<div> +			<table> +				<tr> +				<td class="title">Webbox</td> +				<td class="menusymbol" onclick="showMenu();"><img src="menu.png" alt=""/></td> +				</tr> +			</table> +			<input id="uploadfile" type="file" onchange="onUploadFile();" multiple hidden/> +			<table class="menu"> +				<tr> +					<td class="firsttd"></td> +					<td class="entry" onclick="refresh();"><img src="refresh-inverted.png" alt=""/></td> +				</tr> +			</table> +		</div> +		<div id="list"> +		</div> + +		<div class="greyout fullscreen" id="greyout" hidden> +		</div> + +		<div class="dialogwindow" id="dialogwindow" hidden> +			<div id="dialog" class="dialog"> +			</div> +			<button class="button leftbutton" id="cancelbutton">Cancel</button> +			<button class="button rightbutton" id="okbutton">OK</button> +		</div> + +		<div class="menuwindow" id="menuwindow" hidden> +			<div class="menudiv"> +				<table class="menudialog"> +					<tr class="writecommand"><td class="entry" onclick="hideMenu(); createDir();">New Folder</td></tr> +					<tr><td class="entry" onclick="hideMenu(); download();">Download</td></tr> +					<tr class="writecommand"><td class="entry" onclick="hideMenu(); upload();">Upload</td></tr> +					<tr class="writecommand"><td class="entry" onclick="hideMenu(); deleteItems();">Delete</td></tr> +					<tr class="writecommand"><td class="entry" onclick="hideMenu(); move();">Move</td></tr> +					<tr class="writecommand"><td class="entry" onclick="hideMenu(); rename();">Rename</td></tr> +					<tr><td class="entry" onclick="hideMenu(); info();">Info</td></tr> +					<tr><td class="entry" onclick="hideMenu(); selectAll();">Select/Unselect All</td></tr> +					<tr id="logoutcommand" hidden><td class="entry" onclick="hideMenu(); logout();">Logout</td></tr> +					<tr><td class="entry" onclick="hideMenu();">Close Menu</td></tr> +				</table> +			</div> +		</div> + +		<div class="progresswindow" id="progresswindow" hidden> +			<canvas id="progresscanvas" class="progresscanvas" width="100" height="100"></canvas> +		</div> + +		<div id="create-dir-dialog" hidden> +			New folder:<br> +			<input type="text" id="newdir" class="textinput"></input> +		</div> + +		<div id="download-zip-dialog" hidden> +			Download multiple files as ZIP?<br> +		</div> + +		<div id="move-dialog" hidden> +			Moving files to folder:<br> +			<input type="text" id="movedir" class="textinput"></input> +			<br/> +			Files to move: +			<br/> +			<div id="movefiles"></div> +		</div> + +		<div id="rename-dialog" hidden> +			Old name: +			<br/> +			<div id="renameold"></div> +			<br/> +			New name:<br> +			<input type="text" id="renamenew" class="textinput"></input> +			<br/> +		</div> +		 +		<div id="login-dialog" hidden> +			<span id="logintitle">Webbox</span><br/> +			<br/> +			Username:<br/> +			<input type="text" id="loginusername" class="textinput"></input> +			Password:<br/> +			<input type="password" id="loginpassword" class="textinput"></input> +		</div> + +		<div id="logout-dialog" hidden> +			Are you sure you want to log out? +		</div> + +		<a id="download-a" hidden></a> + +		<div class="footer"> +		</div> +		 +	</body> +</html> + diff --git a/plugins/webbox/html/menu.png b/plugins/webbox/html/menu.png Binary files differnew file mode 100644 index 0000000..907434c --- /dev/null +++ b/plugins/webbox/html/menu.png diff --git a/plugins/webbox/html/refresh-inverted.png b/plugins/webbox/html/refresh-inverted.png Binary files differnew file mode 100644 index 0000000..fd51afd --- /dev/null +++ b/plugins/webbox/html/refresh-inverted.png diff --git a/plugins/webbox/html/webbox.css b/plugins/webbox/html/webbox.css new file mode 100644 index 0000000..89f4885 --- /dev/null +++ b/plugins/webbox/html/webbox.css @@ -0,0 +1,289 @@ +div, td, h1 { +	font-family: "sans-serif"; +} + +.title { +	font-size: 16pt; +	width: 100%; +} + +.greyout { +	opacity: 0.7; +	background-color: #FFFFFF; +	z-index: 9; /* behind dialog window which is 10 */ +} + +.button { +	background-color: #303060; +	border: none; +	color: white; +	padding: 18px; +	text-align: center; +	text-decoration: none; +	display: inline-block; +	font-size: 16px; +	margin: 2px 2px; +	border-radius: 6px; +	cursor: pointer; +} + +.leftbutton { +	position: absolute; +	left: 20px; +	bottom: 20px; +} + +.rightbutton { +	position: absolute; +	right: 20px; +	bottom: 20px; +} + +.menuwindow { +	position: fixed; +	top: 50%; +	left: 50%; +	width: 400px; +	height: 400px; +	margin-top: -200px; +	margin-left: -200px; +	background-color: #FFFFFF; +	opacity: 1; +	z-index: 10; +	border-width: 3px; +	border-style: solid; +	border-color: #808080; +	padding: 10pt; +	box-sizing: border-box; +} + +.menusymbol { +	cursor: pointer; +} + +.dialogwindow { +	position: fixed; +	top: 50%; +	left: 50%; +	width: 400px; +	height: 400px; +	margin-top: -200px; +	margin-left: -200px; +	background-color: #FFFFFF; +	opacity: 1; +	z-index: 10; +	border-width: 3px; +	border-style: solid; +	border-color: #808080; +	padding: 10pt; +	box-sizing: border-box; +} + + +.progresswindow { +	position: fixed; +	top: 0; +	left: 0; +	width: 100%; +	height: 100%; +	background-color: rgba(0, 0, 0, 0); +	opacity: 1; +	z-index: 20; +	border-width: 0; +	border-collapse: collapse; +	box-sizing: border-box; +} + +.progresscanvas { +	position: absolute; +	left: 50%; +	top: 50%; +	transform: translate(-50%, -50%); +	background-color: rgba(0, 0, 0, 0); +} +table.menudialog { +	width: 100%; +} + +.menudiv { +        height: 100%; +        overflow-y: scroll; +} + +table.menudialog td.entry { +	cursor: pointer; +	border-width: 0; +	border-bottom-width: 1px; +	border-style: solid; +	border-color: #808080; +	padding: 10px; +} + +.dialog { +        height: calc(100% - 85px); +        overflow-y: scroll; +} + +.textinput { +	width: calc(100% - 20px); +} + +.fullscreen { +	position: fixed; +	top: 0; +	left: 0; +	width: 100%; +	height: 100%; +	border: 0; +} + +div.footer { +	font-size: 8pt; +	padding-top: 30pt; +	text-align: center; +} + +table.menu { +	border: 0; /* 1px solid #000000; */ +	border-collapse: collapse; +	margin: 0; +	padding: 0; +	table-layout: fixed; +} + +table.menu tr { +	border: 0; /* 1px solid #000000; */ +	margin: 0; +	padding: 0; +} + +table.menu td.firsttd { +	width: 100%; +	border: 0; /* 1px solid #000000; */ +	color: #FFFFFF; +	background-color: #404070; +	margin: 0; +	padding: 0 5px 0 5px; +} + +table.menu td.entry { +	border: 0; /* 1px solid #000000; */ +	color: #FFFFFF; +	background-color: #404070; +	padding: 9px; +	margin: 0; +	cursor: pointer; +} + +table.list { +	width: 100%; +	table-layout: fixed; +	border: 0; /* 1px solid #000000; */ +	cursor: pointer; +	background-color: #FFFFFF; +	border-collapse: collapse; +} + +table.list tr { +	background-color: #FFFFFF; +} + +table.list tr.selectedrow { +	background-color: #CCCCFF; +} + +table.list td { +	border-width: 0; +	border-bottom-width: 1px; +	border-style: solid; +	border-color: #808080; +} + +table.list td.type { +	width: 30px; +} + +table.list td.name { +	width: 100%; +	color: #0000FF; +	white-space: pre; +	overflow: hidden; +} + +@media only screen and (min-width: 1px) and (max-width: 630px) { +	.menuwindow { +		position: fixed; +		top: 0; +		left: 0; +		width: 100%; +		height: 100%; +		margin: 0; +	} + +	.dialogwindow { +		position: fixed; +		top: 0; +		left: 0; +		width: 100%; +		height: 100%; +		margin: 0; +	} + +	.progresswindow { +		position: fixed; +		top: 0; +		left: 0; +		width: 100%; +		height: 100%; +		margin: 0; +	} + +	table.list td { +		padding: 5px; +	} +} + +/* Tooltip container */ +.tooltip { +	position: relative; +	display: inline-block; +} + +/* Tooltip text */ +.tooltip .tooltiptext { +	visibility: hidden; +	background-color: #FFFFDD; +	color: #000000; +	text-align: center; +	padding: 5px; +	border-style: solid; +	border-width: 1px; +	border-color: #000000; +	position: fixed; +	right: 20px; +	bottom: 20px; +	z-index: 1; +} + +/* Show the tooltip text when you mouse over the tooltip container */ +.tooltip:hover .tooltiptext { +	visibility: visible; +} + +.fullwidth { +	width: 100%; +	border: 0; +	margin: 0; +	padding: 0; +	border-collapse: collapse; +} + +::-moz-selection { /* Firefox */ +	color: #000000; +	background: #FFFFFF; +} + +::selection { +	color: #000000; +	background: #FFFFFF; +} diff --git a/plugins/webbox/html/webbox.js b/plugins/webbox/html/webbox.js new file mode 100644 index 0000000..9a38051 --- /dev/null +++ b/plugins/webbox/html/webbox.js @@ -0,0 +1,889 @@ +var currentDir = "/"; +var listElements; +var numberOfSelectedRows = 0; +var username = "notaname"; +var password = "password"; + +function clearContents() { +	var result = "<table class=\"list\">"; +	// empty list +	result += "</table>" +	 +	var listElement = document.getElementById("list"); + +	listElement.innerHTML = result; +} + +function loadContents(dir) { +	numberOfSelectedRows = 0; + +	var xhr = new XMLHttpRequest(); + +	xhr.onreadystatechange = function() { +		if (this.readyState != 4 || this.status != 200) { +			return; +		} + +		var list = xhr.responseXML; +		listElements = list.getElementsByTagName("listentry"); +		 +		var result = "<div class=\"fullwidth tooltip\"><table class=\"list\">"; + +		if (listElements.length == 0) { +			result += "<tr><td class=\"type\"></td><td class=\"name\">(empty)</td></tr>"; +		} else { +			for (var i = 0; i < listElements.length; i++) { +				var type = listElements[i].getAttribute("type"); +				if (type == "file") { +					type = "<img src=\"file.png\"/>"; +				} else if (type == "dir") { +					type = "<img src=\"directory.png\"/>"; +				} else { +					type = ""; +				} + +				result += "<tr " + +					"onmousedown=\"entryMouseDown('" + listElements[i].textContent + "')\" " + +					"onmouseup=\"entryMouseUp('" + listElements[i].textContent + "')\"" + +				"><td class=\"type\">" + type + "</td><td class=\"name\">" + listElements[i].textContent + "</td></tr>"; +			} +		} + +		result += "</table><div class=\"tooltiptext\">To select entry,<br/>click and hold</div></div>" + +		var listElement = document.getElementById("list"); + +		listElement.innerHTML = result; + +	} + +	xhr.open("GET", "bin" + dir + "?command=list", true); +	xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); +	xhr.send(); +} + +// return list of file names +function getFileList() { +	var result = []; + +	for (var i = 0; i < listElements.length; i++) { +		result.push(listElements[i].childNodes[0].nodeValue); +	} + +	return result; +} + +function getFileType(filename) { +	for (var i = 0; i < listElements.length; i++) { +		if (listElements[i].childNodes[0].nodeValue == filename) { +			return listElements[i].getAttribute("type"); +		} +	} +	return ""; +} + +function getRow(filename) { +	var list = document.getElementById("list"); +	var rows = list.getElementsByTagName("tr"); + +	for (var i = 0; i < rows.length; i++) { +		var nameElement = rows[i].getElementsByClassName("name")[0]; +		if (nameElement.childNodes[0].nodeValue == filename) { +			return rows[i]; +		} +	} +	return ""; +} + +// return list of files +function getSelectedFiles() { +	var result = []; +	var list = document.getElementById("list"); +	var rows = list.getElementsByTagName("tr"); + +	for (var i = 0; i < rows.length; i++) { +		if (rows[i].classList.contains("selectedrow")) { +			result.push(rows[i].getElementsByClassName("name")[0].childNodes[0].nodeValue); +		} +	} + +	return result; +} + +// As long as this is 1, the mouse was pressed less than 1 second ago +var mouseShortFlag = 0; +var mouseTimeout = undefined; + +function getSelection(filename) { +	var row = getRow(filename); + +	if (row.classList.contains("selectedrow")) { +		return true; +	} +	return false; +} + +function clearSelection(filename) { +	var row = getRow(filename); + +	if (row.classList.contains("selectedrow")) { +		row.classList.remove("selectedrow"); +		numberOfSelectedRows--; +	} +} + +function setSelection(filename) { +	if (filename == "..") { // don't select ".." +		return; +	} + +	var row = getRow(filename); + +	if (!row.classList.contains("selectedrow")) { +		row.classList.add("selectedrow"); +		numberOfSelectedRows++; +	} +} + +function toggleSelection(filename) { +	if (filename == "..") { // don't select ".." +		return; +	} + +	var row = getRow(filename); + +	if (row.classList.contains("selectedrow")) { +		row.classList.remove("selectedrow"); +		numberOfSelectedRows--; +	} else { +		row.classList.add("selectedrow"); +		numberOfSelectedRows++; +	} +} + +function mouseTimeoutFunction(filename) { +	mouseShortFlag = 0; +	toggleSelection(filename); +} + +function entryMouseDown(filename) { +	if (mouseTimeout !== undefined) { +		clearTimeout(mouseTimeout); +	} + +	if (numberOfSelectedRows > 0) { +		toggleSelection(filename); +	} else { +		mouseShortFlag = 1; +		mouseTimeout = setTimeout(function(){ mouseTimeoutFunction(filename); }, 1000); +	} +} + +function entryMouseUp(filename) { +	if (mouseTimeout !== undefined) { +		clearTimeout(mouseTimeout); +	} + +	// short click: download / change dir +	if (mouseShortFlag) { +		var type = getFileType(filename); +		if (type == "file") { +			download(filename); +		} else if (type == "dir") { +			if (filename == "..") { +				if (!currentDir.includes("/")) { +					// error: this shouldn't happen +					alert("Bad path " + currentDir + " for " + filename); +					return; +				} +				currentDir = currentDir.substr(0, currentDir.lastIndexOf("/")); + +				if (currentDir == "") { +					currentDir = "/"; +				} + +				setCurrentDir(currentDir); +				return; +			} + +			if (!currentDir.endsWith("/")) { +				currentDir += "/"; +			} +			setCurrentDir(currentDir + filename); +		} +	} + +	mouseShortFlag = 0; +} + +function showDialog() { +	document.getElementById("greyout").style.display = 'block'; +	document.getElementById("dialogwindow").style.display = 'block'; +	document.getElementById("okbutton").focus(); +} + +function hideDialog() { +	document.getElementById("greyout").style.display = 'none'; +	document.getElementById("dialogwindow").style.display = 'none'; +} + +function showMenu() { +	document.getElementById("greyout").style.display = 'block'; +	document.getElementById("menuwindow").style.display = 'block'; +} + +function hideMenu() { +	document.getElementById("greyout").style.display = 'none'; +	document.getElementById("menuwindow").style.display = 'none'; +} + +// if readOnly = "1", disable respective controls since this setting is global and constant +function prepareReadOnly(readOnly) { +	if (readOnly == "1") { +		var writecommands = document.getElementsByClassName("writecommand"); +		for (var i = 0; i < writecommands.length; i++) { +			writecommands[i].style.display = "none"; +		} +	} +} + +function login(title) { +	showDialog(); + +	document.getElementById("okbutton").onclick = function() { +		// restore dialog buttons +		document.getElementById("cancelbutton").style.display = "block"; +		document.getElementById("okbutton").childNodes[0].nodeValue = "OK"; +		hideDialog(); + +		username = document.getElementById("loginusername").value; +		password = document.getElementById("loginpassword").value; +		 +		initMainpage(); +	} + +	// rearrange dialog buttons +	document.getElementById("cancelbutton").style.display = "none"; +	document.getElementById("okbutton").childNodes[0].nodeValue = "Login"; +	 +	document.getElementById("dialog").innerHTML = document.getElementById("login-dialog").innerHTML; +	 +	document.getElementById("logintitle").childNodes[0].nodeValue = title; +	 +	document.getElementById("loginusername").focus(); + +	document.getElementById("loginusername").onkeydown = function(evt) { +		if (evt.key == "Enter") { +			document.getElementById("okbutton").click(); +		} +	} +	 +	document.getElementById("loginpassword").onkeydown = function(evt) { +		if (evt.key == "Enter") { +			document.getElementById("okbutton").click(); +		} +	} +} + +function initMainpage() { +	// load title +	var xhrTitle = new XMLHttpRequest(); + +	xhrTitle.onreadystatechange = function() { +		if (this.readyState != 4) { +			return; +		} +		if (this.status == 401) { // login error: goto login page +			var authheader = this.getResponseHeader("WWW-Authenticate"); +			var title = "Webbox"; +			// For web servers with standard AUTH BASIC, triggering problems in +			// client browsers, popping up the browser's "Authenticate" window +			// but we want our own +			if (authheader.startsWith("Basic realm=\"") && authheader.endsWith("\"")) { +				title = authheader.substr(13, authheader.length - 14); +			} else +			// Fixed up Apache server +			if (authheader.startsWith("SR_Basic realm=\"") && authheader.endsWith("\"")) { +				title = authheader.substr(16, authheader.length - 17); +			} + +			// enable logout function if logging in +			document.getElementById("logoutcommand").style.display = "table-row"; + +			login(title); +			return; +		} else +		if (this.status != 200) { +			document.getElementsByClassName("title")[0].innerHTML = "HTTP error"; +			return; +		} + +		var serverInfo = xhrTitle.responseXML; +		var title = serverInfo.getElementsByTagName("title")[0].textContent; +		document.getElementsByClassName("title")[0].innerHTML = title; + +		var readOnly = serverInfo.getElementsByTagName("readonly")[0].textContent; +		prepareReadOnly(readOnly); + +		// if successful: continue loading +		initMainpage2(); +	} + +	xhrTitle.open("GET", "bin?command=server-info", true); +	xhrTitle.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); +	xhrTitle.send(); + +} + +// deferred initialization after successful login +function initMainpage2() { +	// fill file list initially +	setCurrentDir("/"); + +	// load footer +	var xhrFooter = new XMLHttpRequest(); + +	xhrFooter.onreadystatechange = function() { +		if (this.readyState != 4 || this.status != 200) { +			return; +		} +		document.getElementsByClassName("footer")[0].innerHTML = xhrFooter.responseText; +	} + +	xhrFooter.open("GET", "bin?command=version", true); +	xhrFooter.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); +	xhrFooter.send(); +	 +	// default action for "Cancel" button: hide dialog window +	document.getElementById("cancelbutton").onclick = hideDialog; + +	// on click outside of menu, close menu +	document.getElementById("greyout").onclick = function() { +		hideDialog(); +		hideMenu(); +	} +	 +	// on Escape, globally hide dialog and menu window +	document.onkeydown = function(evt) { +		if (evt.key == "Escape") { +			hideDialog(); +			hideMenu(); +		} +	} +} + +function setCurrentDir(newDir) { +	currentDir = newDir; +	loadContents(newDir); + +	var menu = document.getElementsByClassName("menu")[0]; +	var firsttd = menu.getElementsByClassName("firsttd")[0]; +	firsttd.innerHTML = newDir; +} + +function download(filename) { +	var files = getSelectedFiles(); + +	// if activated via menu, download directly (not as zip) +	if (files.length == 1 && filename === undefined) { +		filename = files[0]; +	} + +	if (filename === undefined) { // download selection as ZIP +		showDialog(); + +		if (files.length == 0) { +			document.getElementById("dialog").innerHTML = "No files selected."; +			document.getElementById("okbutton").onclick = hideDialog; +			return; +		} + +		document.getElementById("dialog").innerHTML = document.getElementById("download-zip-dialog").innerHTML; + +		document.getElementById("okbutton").onclick = function() { +			hideDialog(); + +			// send info request for files +			var xhr = new XMLHttpRequest(); + +			xhr.onreadystatechange = function() { +				if (this.readyState != 4 || this.status != 200) { +					return; +				} + +				var a = document.getElementById("download-a"); +				a.setAttribute("download", "webbox-download.zip"); +				var file = new Blob([this.response]); +				a.href = window.URL.createObjectURL(file); +				a.click(); +				progressOff(); +			} + +			progressOn(); +			var files = getSelectedFiles(); +			var parser = new DOMParser(); +			var xmlDocument = parser.parseFromString("<files></files>", "text/xml"); +			var filesElement = xmlDocument.getElementsByTagName("files")[0]; + +			for (var i = 0; i < files.length; i++) { +				var fileElement = xmlDocument.createElement("file"); +				fileElement.appendChild(document.createTextNode(files[i])); +				filesElement.appendChild(fileElement); +			} + +			xhr.open("POST", "bin" + currentDir + "?command=download-zip", true); +			xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); +			xhr.setRequestHeader("Content-type", "text/xml"); +			xhr.responseType = 'blob'; +			xhr.send(xmlDocument); +		} +	} else { // single file download +		var xhr = new XMLHttpRequest(); + +		xhr.onreadystatechange = function() { +			if (this.readyState != 4 || this.status != 200) { +				return; +			} + +			var a = document.getElementById("download-a"); +			a.setAttribute("download", filename); +			var file = new Blob([this.response]); +			a.href = window.URL.createObjectURL(file); +			a.click(); +			progressOff(); +		} + +		var dir = currentDir; +		if (dir != "/") { +			dir += "/" +		} +		progressOn(); +		xhr.open("GET", "bin" + dir + filename, true); +		xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); +		xhr.responseType = 'blob'; +		xhr.send(); +	} +} + +function createDir() { +	showDialog(); + +	// hide dialog when done +	document.getElementById("okbutton").onclick = function() { +		// send new folder request for directory +		var xhr = new XMLHttpRequest(); + +		xhr.onreadystatechange = function() { +			if (this.readyState != 4) { +				return; +			} +			if (this.status != 200) { +				document.getElementById("dialog").innerHTML = "HTTP error"; +			} else { +				document.getElementById("dialog").innerHTML = xhr.responseText; +			} +			document.getElementById("okbutton").onclick = hideDialog; +			document.getElementById("okbutton").focus(); +			loadContents(currentDir); // load new file list with new dir +		} + +		var parser = new DOMParser(); +		var xmlDocument = parser.parseFromString("<dirname></dirname>", "text/xml"); +		var dirElement = xmlDocument.getElementsByTagName("dirname")[0]; + +		dirElement.appendChild(document.createTextNode(document.getElementById("newdir").value)); + +		xhr.open("POST", "bin" + currentDir + "?command=newdir", true); +		xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); +		xhr.setRequestHeader("Content-type", "text/xml"); +		xhr.send(xmlDocument); +	} +	 +	document.getElementById("dialog").innerHTML = document.getElementById("create-dir-dialog").innerHTML; +	document.getElementById("newdir").focus(); + +	document.getElementById("newdir").onkeydown = function(evt) { +		if (evt.key == "Enter") { +			document.getElementById("okbutton").click(); +		} +	} +} + +function upload() { +	var uploadfile = document.getElementById("uploadfile"); + +	uploadfile.click(); +} + +// Callback on selected and uploaded file +function onUploadFile() { +	var xhr = new XMLHttpRequest(); + +	xhr.onreadystatechange = function() { +		if (this.readyState != 4) { +			return; +		} + +		showDialog(); +		var message = ""; + +		if (this.status != 200) { +			message = "HTTP error"; +		} else { +			if (xhr.responseText == "") { +				message = "Upload successful."; +				loadContents(currentDir); // load new file list with uploaded file +			} else { +				message = xhr.responseText; +			} +		} + +		document.getElementById("dialog").innerHTML = message; +		document.getElementById("cancelbutton").style.display = "none"; +		document.getElementById("okbutton").onclick = function() { +			hideDialog(); +			document.getElementById("cancelbutton").style.display = "block"; +		} +		document.getElementById("okbutton").focus(); +		progressOff(); +	} + +	progressOn(); + +        var uploadfile = document.getElementById("uploadfile"); +        var formData = new FormData(); + +	for (var i = 0; i < uploadfile.files.length; i++) { +        	formData.append("uploadfile", uploadfile.files[i]); +	} + +        xhr.open("POST", "bin" + currentDir + "?command=upload", true); +	xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); +        xhr.send(formData); +} + +function deleteItems() { +	showDialog(); +	 +	var files = getSelectedFiles(); +	if (files.length == 0) { +		document.getElementById("dialog").innerHTML = "No files selected."; +		document.getElementById("okbutton").onclick = hideDialog; +		return; +	} + +	var message = "Are sure to delete the following files and directories?<br/><br/>"; + +	for (var i = 0; i < files.length; i++) { +		message += files[i] + "<br/>"; +	} + +	document.getElementById("dialog").innerHTML = message; +	 +	document.getElementById("okbutton").onclick = function() { + +		var xhr = new XMLHttpRequest(); + +		xhr.onreadystatechange = function() { +			if (this.readyState != 4) { +				return; +			} +			if (this.status != 200) { +				document.getElementById("dialog").innerHTML = "HTTP error"; +			} else { +				document.getElementById("dialog").innerHTML = xhr.responseText; +			} +			document.getElementById("okbutton").onclick = hideDialog; +			loadContents(currentDir); // load new file list with deleted items +		} + +		var parser = new DOMParser(); +		var xmlDocument = parser.parseFromString("<files></files>", "text/xml"); +		var filesElement = xmlDocument.getElementsByTagName("files")[0]; + +		for (var i = 0; i < files.length; i++) { +			var fileElement = xmlDocument.createElement("file"); +			fileElement.appendChild(document.createTextNode(files[i])); +			filesElement.appendChild(fileElement); +		} + +		xhr.open("POST", "bin" + currentDir + "?command=delete", true); +		xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); +		xhr.setRequestHeader("Content-type", "text/xml"); +		xhr.send(xmlDocument); +	} +} + +function move() { +	showDialog(); +	 +	var files = getSelectedFiles(); +	if (files.length == 0) { +		document.getElementById("dialog").innerHTML = "No files selected."; +		document.getElementById("okbutton").onclick = hideDialog; +		return; +	} + +	var message = ""; +	for (var i = 0; i < files.length; i++) { +		message += files[i] + "<br/>"; +	} + +	document.getElementById("dialog").innerHTML = document.getElementById("move-dialog").innerHTML; +	document.getElementById("movedir").focus(); +	document.getElementById("movedir").onkeydown = function(evt) { +		if (evt.key == "Enter") { +			document.getElementById("okbutton").click(); +		} +	} + +	document.getElementById("movefiles").innerHTML = message; +	 +	document.getElementById("okbutton").onclick = function() { + +		var xhr = new XMLHttpRequest(); + +		xhr.onreadystatechange = function() { +			if (this.readyState != 4) { +				return; +			} +			if (this.status != 200) { +				document.getElementById("dialog").innerHTML = "HTTP error"; +			} else { +				document.getElementById("dialog").innerHTML = xhr.responseText; +			} + +			document.getElementById("okbutton").onclick = hideDialog; +			document.getElementById("okbutton").focus(); +			loadContents(currentDir); // load new file list with deleted items +		} + +		var parser = new DOMParser(); +		var xmlDocument = parser.parseFromString("<request></request>", "text/xml"); +		var filesElement = xmlDocument.getElementsByTagName("request")[0]; +			 +		var targetElement = xmlDocument.createElement("target"); +		targetElement.appendChild(document.createTextNode(document.getElementById("movedir").value)); +		filesElement.appendChild(targetElement); + +		for (var i = 0; i < files.length; i++) { +			var fileElement = xmlDocument.createElement("file"); +			fileElement.appendChild(document.createTextNode(files[i])); +			filesElement.appendChild(fileElement); +		} + +		xhr.open("POST", "bin" + currentDir + "?command=move", true); +		xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); +		xhr.setRequestHeader("Content-type", "text/xml"); +		xhr.send(xmlDocument); +	} +} + +function rename() { +	showDialog(); +	 +	var files = getSelectedFiles(); +	if (files.length == 0) { +		document.getElementById("dialog").innerHTML = "No files selected."; +		document.getElementById("okbutton").onclick = hideDialog; +		return; +	} +	if (files.length > 1) { +		document.getElementById("dialog").innerHTML = "Only one file can be renamed at once."; +		document.getElementById("okbutton").onclick = hideDialog; +		return; +	} + +	document.getElementById("renameold").innerHTML = files[0]; + +	document.getElementById("dialog").innerHTML = document.getElementById("rename-dialog").innerHTML; +	document.getElementById("renamenew").value = files[0]; +	document.getElementById("renamenew").focus(); +	document.getElementById("renamenew").onkeydown = function(evt) { +		if (evt.key == "Enter") { +			document.getElementById("okbutton").click(); +		} +	} + +	document.getElementById("okbutton").onclick = function() { + +		var xhr = new XMLHttpRequest(); + +		xhr.onreadystatechange = function() { +			if (this.readyState != 4) { +				return; +			} +			if (this.status != 200) { +				document.getElementById("dialog").innerHTML = "HTTP error"; +			} else { +				document.getElementById("dialog").innerHTML = xhr.responseText; +			} + +			document.getElementById("okbutton").onclick = hideDialog; +			document.getElementById("okbutton").focus(); +			loadContents(currentDir); // load new file list with deleted items +		} + +		var parser = new DOMParser(); +		var xmlDocument = parser.parseFromString("<request></request>", "text/xml"); +		var filesElement = xmlDocument.getElementsByTagName("request")[0]; +			 +		var oldnameElement = xmlDocument.createElement("oldname"); +		oldnameElement.appendChild(document.createTextNode(document.getElementById("renameold").childNodes[0].nodeValue)); +		filesElement.appendChild(oldnameElement); +		 +		var newnameElement = xmlDocument.createElement("newname"); +		newnameElement.appendChild(document.createTextNode(document.getElementById("renamenew").value)); +		filesElement.appendChild(newnameElement); + +		xhr.open("POST", "bin" + currentDir + "?command=rename", true); +		xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); +		xhr.setRequestHeader("Content-type", "text/xml"); +		xhr.send(xmlDocument); +	} +} + +// File info: date, size, type +function info() { +	showDialog(); +	document.getElementById("cancelbutton").style.display = "none"; // hide "cancel" button in info dialog, only provide "OK" + +	// hide dialog when done +	document.getElementById("okbutton").onclick = function() { +		hideDialog(); +		document.getElementById("cancelbutton").style.display = "block"; +	} +	 +	var files = getSelectedFiles(); +	if (files.length == 0) { +		document.getElementById("dialog").innerHTML = "No files selected."; +		return; +	} + +	// send info request for files +	var xhr = new XMLHttpRequest(); + +	xhr.onreadystatechange = function() { +		if (this.readyState != 4) { +			return; +		} +		if (this.status != 200) { +			document.getElementById("dialog").innerHTML = "HTTP error"; +		} else { +			document.getElementById("dialog").innerHTML = xhr.responseText; +		} +	} + +	var parser = new DOMParser(); +	var xmlDocument = parser.parseFromString("<files></files>", "text/xml"); +	var filesElement = xmlDocument.getElementsByTagName("files")[0]; + +	for (var i = 0; i < files.length; i++) { +		var fileElement = xmlDocument.createElement("file"); +		fileElement.appendChild(document.createTextNode(files[i])); +		filesElement.appendChild(fileElement); +	} + +	xhr.open("POST", "bin" + currentDir + "?command=info", true); +	xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); +	xhr.setRequestHeader("Content-type", "text/xml"); +	xhr.send(xmlDocument); +} + +// select all files, except if all are selected, unselect all +function selectAll() { +	var files = getFileList(); +	 +	var allSelected = true; + +	for (var i = 0; i < files.length; i++) { +		if (getSelection(files[i]) === false && files[i] != "..") { +			allSelected = false; +		} +	} + +	for (var i = 0; i < files.length; i++) { +		if (allSelected) { +			clearSelection(files[i]); +		} else { +			setSelection(files[i]); +		} +	} +} + +function refresh() { +	loadContents(currentDir); // load new file list +} + +function logout() { +	showDialog(); + +	document.getElementById("okbutton").onclick = function() { +		hideDialog(); + +		var menu = document.getElementsByClassName("menu")[0]; +		var firsttd = menu.getElementsByClassName("firsttd")[0]; +		firsttd.innerHTML = "/"; + +		clearContents(); + +		username = "notaname"; +		password = "password"; +		initMainpage(); +	} + +	document.getElementById("dialog").innerHTML = document.getElementById("logout-dialog").innerHTML; +} + +// Progress indication + +function drawDot(c, offsetx, offsety, radius, distance, phase) { +        c.beginPath(); +        c.arc(distance * Math.sin(phase) + offsetx, +                - distance * Math.cos(phase) + offsety, radius, 0, 2 * Math.PI); +        c.fill(); +} + +function drawLogo(canvasid, rotation) { +        var canvas = document.getElementById(canvasid); + +        var height = canvas.height; +        var width = canvas.width; +        var radius = height * 0.1; +        var distance = height * 0.1 * 4 / 3; +         +        var c = canvas.getContext("2d"); + +        c.clearRect(0, 0, width, height); +        c.fillStyle = "#1132A7"; +        drawDot(c, width / 2, height / 2, radius, distance, rotation); +        drawDot(c, width / 2, height / 2, radius, distance, rotation + 2 * Math.PI / 3); +        drawDot(c, width / 2, height / 2, radius, distance, rotation + 2 * Math.PI / 3 * 2); +        drawDot(c, width / 2, height / 2, radius, distance * 2, rotation + Math.PI); +        drawDot(c, width / 2, height / 2, radius, distance * 2, rotation + Math.PI + 2 * Math.PI / 3); +        drawDot(c, width / 2, height / 2, radius, distance * 2, rotation + Math.PI + 2 * Math.PI / 3 * 2); +} + +var progressState = 0; + +function drawAni(timeStamp) { +        var rotation = timeStamp / 1000 * 6; + +        drawLogo("progresscanvas", rotation); + +	if (progressState == 1) { +	        requestAnimationFrame(drawAni); +	} +} + +function progressOn() { +	progressState = 1; +	document.getElementById("progresswindow").style.display = "block"; +        requestAnimationFrame(drawAni); +} + +function progressOff() { +	progressState = 0; +	document.getElementById("progresswindow").style.display = "none"; +} +  | 
