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 @@ - + + + + + + +
diff --git a/frontend/js/filebrowser.js b/frontend/js/filebrowser.js index 904901f..3c61542 100644 --- a/frontend/js/filebrowser.js +++ b/frontend/js/filebrowser.js @@ -1,4 +1,5 @@ //Default vars +var storeName = 'RetroArch'; var afs; BrowserFS.install(window); var fs = require('fs'); @@ -16,21 +17,36 @@ async function renderFiles(directory) { let items = await fs.readdirSync(directory); let baseName = directory.split('/').at(-1); let parentFolder = directory.replace(baseName,''); - let parentLink = $('
').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($(''); + for await (item of [parentLink, $(''); 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 = $('
').text(name)); + } + let parentRow = $('
').text('Parent'), $('')]) { + parentRow.append(item); + } + table.append(tableHeader,parentRow); + $('#filebrowser').append(table); if (items.length > 0) { for await (let item of items) { + let tableRow = $('
').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