diff --git a/frontend/css/filebrowser.css b/frontend/css/filebrowser.css
index e69de29..4162d82 100644
--- a/frontend/css/filebrowser.css
+++ b/frontend/css/filebrowser.css
@@ -0,0 +1,40 @@
+.hidden {
+ display: none;
+}
+
+.right {
+ float: right;
+ margin-right: 5px;
+}
+
+.directory, .file {
+ cursor: pointer;
+}
+
+button {
+ border: 2px solid #000;
+ background: transparent;
+ cursor: pointer;
+ margin: 5px;
+}
+
+.fileTable {
+ font-family: Arial, Helvetica, sans-serif;
+ border-collapse: collapse;
+ width: 100%;
+ margin-top: 10px;
+}
+
+td, th {
+ border: 2px solid #ddd;
+ padding: 8px;
+}
+
+tr:nth-child(even){
+ background-color: #f2f2f2;
+}
+
+tr:hover {
+ background-color: #ddd;
+}
+
diff --git a/frontend/filebrowser.html b/frontend/filebrowser.html
index f96f21c..f5770be 100644
--- a/frontend/filebrowser.html
+++ b/frontend/filebrowser.html
@@ -9,7 +9,13 @@
').addClass('directory').attr('onclick', 'renderFiles(\'' + parentFolder + '\');').text('..');
- if (directory == '/') {
- directory = '';
+ let parentLink = $('
').addClass('directory').attr('onclick', 'renderFiles(\'' + parentFolder + '\');').text('..');
+ if (directoryClean == '/') {
+ directoryClean = '';
}
- $('#filebrowser').append(parentLink);
+ let table = $('').addClass('fileTable');
+ let tableHeader = $('');
+ for await (name of ['Name', 'Type', 'Delete']) {
+ tableHeader.append($('| ').text(name));
+ }
+ let parentRow = $(' | ');
+ for await (item of [parentLink, $('| ').text('Parent'), $(' | ')]) {
+ parentRow.append(item);
+ }
+ table.append(tableHeader,parentRow);
+ $('#filebrowser').append(table);
if (items.length > 0) {
for await (let item of items) {
+ let tableRow = $(' | ');
let itemClean = item.replace("'","|");
if (fs.lstatSync(directory + '/' + item).isDirectory()) {
- let link = $('').addClass('directory').attr('onclick', 'renderFiles(\'' + directoryClean + '/' + itemClean + '\');').text(item);
- $('#filebrowser').append(link);
+ var link = $(' ').addClass('directory').attr('onclick', 'renderFiles(\'' + directoryClean + '/' + itemClean + '\');').text(item);
+ var type = $(' | ').text('Dir');
} else {
- let link = $('').addClass('file').attr('onclick', 'downloadFile(\'' + directoryClean + '/' + itemClean + '\');').text(item);
- $('#filebrowser').append(link);
+ var link = $(' ').addClass('file').attr('onclick', 'downloadFile(\'' + directoryClean + '/' + itemClean + '\');').text(item);
+ var type = $(' | ').text('File');
}
+ for await (item of [link, type, $(' | ')]) {
+ tableRow.append(item);
+ }
+ table.append(tableRow);
}
}
}
@@ -51,6 +67,48 @@ async function downloadFile(file) {
$("body").remove(a);
}
+// Upload file to current directory
+async function upload(input) {
+ let directory = $('#filebrowser').data('directory');
+ if (directory == '/') {
+ directoryUp = '';
+ } else {
+ directoryUp = directory;
+ }
+ if (input.files && input.files[0]) {
+ let reader = new FileReader();
+ reader.onload = function(e) {
+ let fileName = input.files[0].name;
+ let data = e.target.result;
+ fs.writeFileSync(directoryUp + '/' + fileName, Buffer.from(data));
+ renderFiles(directory);
+ }
+ reader.readAsArrayBuffer(input.files[0]);
+ }
+}
+
+// Create a directory
+async function createFolder() {
+ let folderName = $('#folderName').val();
+ $('#folderName').val('');
+ if ((folderName.length == 0) || (folderName.includes('/'))) {
+ alert('Bad or Null Directory Name');
+ return '';
+ }
+ let directory = $('#filebrowser').data('directory');
+ if (directory == '/') {
+ directoryUp = '';
+ } else {
+ directoryUp = directory;
+ }
+ let createD = directoryUp + '/' + folderName;
+ if (!fs.existsSync(createD)){
+ fs.mkdirSync(createD);
+ }
+ renderFiles(directory);
+}
+
+
// Download a full backup of all files
async function downloadBackup() {
var zip = new JSZip();
@@ -77,7 +135,7 @@ async function downloadBackup() {
let url = window.URL || window.webkitURL;
link = url.createObjectURL(blob);
let a = $("");
- a.attr("download", 'emulatorjs.zip');
+ a.attr("download", storeName + '.zip');
a.attr("href", link);
$("body").append(a);
a[0].click();
@@ -85,7 +143,59 @@ async function downloadBackup() {
});
}
-// Create IndexDB filestore
+// Upload a full backup
+async function uploadBackup(input) {
+ if (input.files && input.files[0]) {
+ let reader = new FileReader();
+ reader.onload = async function(e) {
+ let data = e.target.result;
+ var zip = new JSZip();
+ // Load zip from data
+ zip.loadAsync(data).then(async function(contents) {
+ // Purge current storage
+ async function rmDir(dirPath, removeSelf) {
+ try { var files = fs.readdirSync(dirPath); }
+ catch(e) { return; }
+ if (files.length > 0) {
+ for await (let file of files) {
+ var filePath = dirPath + '/' + file;
+ if (fs.statSync(filePath).isFile()) {
+ fs.unlinkSync(filePath);
+ } else {
+ rmDir(filePath);
+ }
+ }
+ }
+ if (dirPath !== '/') {
+ fs.rmdirSync(dirPath)
+ }
+ return '';
+ }
+ await rmDir('/');
+ // Unzip the files to the FS by name
+ for await (let fileName of Object.keys(contents.files)) {
+ if (fileName.endsWith('/')) {
+ if (! fs.existsSync('/' + fileName)) {
+ fs.mkdirSync('/' + fileName);
+ }
+ }
+ }
+ for await (let fileName of Object.keys(contents.files)) {
+ if (! fileName.endsWith('/')) {
+ zip.file(fileName).async('arraybuffer').then(function(content) {
+ fs.writeFileSync('/' + fileName, Buffer.from(content));
+ });
+ }
+ }
+ await new Promise(resolve => setTimeout(resolve, 2000));
+ renderFiles('/');
+ });
+ }
+ reader.readAsArrayBuffer(input.files[0]);
+ }
+}
+
+// Create Async filestore
async function setupFileSystem() {
var imfs = new BrowserFS.FileSystem.InMemory();
afs = new BrowserFS.FileSystem.AsyncMirror(imfs,
@@ -95,7 +205,7 @@ async function setupFileSystem() {
setupMounts();
});
},
- 'RetroArch'));
+ storeName));
};
// Setup mounts
| | |