class FilesystemDisplay { constructor(opts) { if (!opts.parentId) throw "Missing options"; const fs = require("fs"); const path = require("path"); this.cwd = []; this.iconcolor = `rgb(${window.theme.r}, ${window.theme.g}, ${window.theme.b})`; this.icons = { showDisks: ``, up: ``, dir: ``, symlink: ``, file: ``, other: ``, disk: ``, rom: ``, usb: ``, edex: { theme: ``, themesDir: ``, kblayout: ``, kblayoutsDir: ``, settings: `` } }; const container = document.getElementById(opts.parentId); container.innerHTML = `

FILESYSTEM

EXIT DISPLAY

Calculating available space...

`; this.filesContainer = document.getElementById("fs_disp_container"); this.space_bar = { text: document.querySelector("#fs_space_bar > h3"), bar: document.querySelector("#fs_space_bar > progress") }; this.fsBlock = {}; this.dirpath = ""; this.failed = false; this._noTracking = false; this._runNextTick = false; this._timer = setInterval(() => { if (this._runNextTick === true) { this._runNextTick = false; this.readFS(this.dirpath); } }, 1000); this._asyncFSwrapper = new Proxy(fs, { get: function(fs, prop) { if (prop in fs) { return function(...args) { return new Promise((resolve, reject) => { fs[prop](...args, (err, d) => { if (typeof err !== "undefined" && err !== null) reject(err); if (typeof d !== "undefined") resolve(d); if (typeof d === "undefined" && typeof err === "undefined") resolve(); }); }); } } }, set: function() { return false; } }); this.setFailedState = () => { this.failed = true; container.innerHTML = `

FILESYSTEM

EXECUTION FAILED

CANNOT ACCESS CURRENT WORKING DIRECTORY

`; }; this.followTab = () => { let num = window.currentTerm; window.term[num].oncwdchange = (cwd) => { if (cwd && window.currentTerm === num) { if (this._fsWatcher) { this._fsWatcher.close(); } if (cwd.startsWith("FALLBACK |-- ")) { this.readFS(cwd.slice(13)); this._noTracking = true; } else { this.readFS(cwd); this.watchFS(cwd); } } }; }; this.followTab(); this.watchFS = (dir) => { if (this._fsWatcher) { this._fsWatcher.close(); } this._fsWatcher = fs.watch(dir, () => { this._runNextTick = true; }); }; this.readFS = async dir => { if (this.failed === true) return false; let tcwd = dir; let content = await this._asyncFSwrapper.readdir(tcwd).catch(err => { console.warn(err); if (this._noTracking === true && this.dirpath) { // #262 this.setFailedState(); setTimeout(() => { this.readFS(this.dirpath); }, 1000); } else { this.setFailedState(); } }); this.cwd = []; await new Promise((resolve, reject) => { if (content.length === 0) resolve(); content.forEach(async (file, i) => { let fstat = await this._asyncFSwrapper.lstat(path.join(tcwd, file)).catch(reject); if (fstat.isDirectory()) { if (tcwd === settingsDir && file === "themes") { this.cwd.push({ name: window._escapeHtml(file), type: "edex-themesDir", category: "dir" }); } else if (tcwd === settingsDir && file === "keyboards") { this.cwd.push({ name: window._escapeHtml(file), type: "edex-kblayoutsDir", category: "dir" }); } else { this.cwd.push({ name: window._escapeHtml(file), type: "dir", category: "dir" }); } } else if (fstat.isSymbolicLink()) { this.cwd.push({ name: window._escapeHtml(file), type: "symlink", category: "symlink" }); } else if (fstat.isFile()) { if (tcwd === themesDir && file.endsWith(".json")) { this.cwd.push({ name: window._escapeHtml(file), type: "edex-theme", category: "file" }); } else if (tcwd === keyboardsDir && file.endsWith(".json")) { this.cwd.push({ name: window._escapeHtml(file), type: "edex-kblayout", category: "file" }); } else if (tcwd === settingsDir && file === "settings.json") { this.cwd.push({ name: window._escapeHtml(file), type: "edex-settings", category: "file" }); } else { this.cwd.push({ name: window._escapeHtml(file), type: "file", category: "file" }); } } else { this.cwd.push({ name: window._escapeHtml(file), type: "other", category: "other" }); } if (i === content.length-1) resolve(); }); }).catch(() => { this.setFailedState() }); if (this.failed) return false; let ordering = { dir: 0, symlink: 1, file: 2, other: 3 }; this.cwd.sort((a, b) => { return (ordering[a.category] - ordering[b.category] || a.name.localeCompare(b.name)); }); this.cwd.splice(0, 0, { name: "Show disks", type: "showDisks" }); if (tcwd !== "/" && tcwd !== "\\") { this.cwd.splice(1, 0, { name: "Go up", type: "up" }); } let d = await window.si.fsSize().catch(() => { this.setFailedState(); }); d.forEach(fsBlock => { if (tcwd.startsWith(fsBlock.mount)) { this.fsBlock = fsBlock; } }); this.dirpath = tcwd; this.render(this.cwd); }; this.readDevices = async () => { if (this.failed === true) return false; let blocks = await window.si.blockDevices(); let devices = []; blocks.forEach(block => { if (fs.existsSync(block.mount)) { let type = (block.type === "rom") ? "rom" : "disk"; if (block.removable && block.type !== "rom") { type = "usb"; } devices.push({ name: (block.label !== "") ? `${block.label} (${block.name})` : `${block.mount} (${block.name})`, type, path: block.mount }); } }); this.render(devices); }; this.render = async blockList => { if (this.failed === true) return false; document.getElementById("fs_disp_title_dir").innerText = this.dirpath; this.filesContainer.setAttribute("class", ""); if (this._noTracking) { document.querySelector("section#filesystem > h3.title > p:first-of-type").innerText = "FILESYSTEM - TRACKING FAILED, RUNNING DETACHED FROM TTY"; } let filesDOM = ``; blockList.forEach(e => { let hidden = ""; if (e.name.startsWith(".")) { hidden = " hidden"; } let cmd = `window.term[window.currentTerm].write('\\'${e.name}\\'')`; if (e.type === "dir" || e.type === "up" || e.type.endsWith("Dir")) { cmd = `window.term[window.currentTerm].writelr('cd \\'${e.name.replace("\\", "\\\\")}\\'')`; } if (e.type === "up") { cmd = `window.term[window.currentTerm].writelr('cd ..')`; } if (e.type === "up" && this._noTracking) { cmd = `window.fsDisp.readFS('${path.resolve(this.dirpath, '..').replace(/\\/g, '\\\\')}')`; } if ((e.type === "dir" || e.type.endsWith("Dir")) && this._noTracking) { cmd = `window.fsDisp.readFS('${path.resolve(this.dirpath, e.name).replace(/\\/g, '\\\\')}')`; } if (e.type === "showDisks") { cmd = `window.fsDisp.readDevices()`; } if (e.type === "disk" || e.type === "rom" || e.type === "usb") { let extraSwitch = (process.platform === "win32") ? " /D" : ""; cmd = `window.term[window.currentTerm].writelr('cd${extraSwitch} \\'${e.path.replace("\\", "\\\\")}\\'')`; document.getElementById("fs_disp_title_dir").innerText = "Showing available block devices"; this.filesContainer.setAttribute("class", "disks"); } if ((e.type === "disk" || e.type === "rom" || e.type === "usb") && this._noTracking) { cmd = `window.fsDisp.readFS('${e.path.replace(/\\/g, '\\\\')}')`; } if (e.type === "edex-theme") { cmd = `window.themeChanger('${e.name.slice(0, -5)}')`; } if (e.type === "edex-kblayout") { cmd = `window.remakeKeyboard('${e.name.slice(0, -5)}')`; } if (e.type === "edex-settings") { cmd = `window.openSettings()`; } let icon = ""; switch(e.type) { case "showDisks": icon = this.icons.showDisks; break; case "up": icon = this.icons.up; break; case "dir": icon = this.icons.dir; break; case "symlink": icon = this.icons.symlink; break; case "file": icon = this.icons.file; break; case "disk": icon = this.icons.disk; break; case "rom": icon = this.icons.rom; break; case "usb": icon = this.icons.usb; break; case "edex-theme": icon = this.icons.edex.theme; break; case "edex-kblayout": icon = this.icons.edex.kblayout; break; case "edex-settings": icon = this.icons.edex.settings; break; case "edex-themesDir": icon = this.icons.edex.themesDir; break; case "edex-kblayoutsDir": icon = this.icons.edex.kblayoutsDir; break; default: icon = this.icons.other; break; } filesDOM += `
${icon}

${e.name}

`; }); this.filesContainer.innerHTML = filesDOM; if (this.filesContainer.getAttribute("class").endsWith("disks")) { document.getElementById("fs_space_bar").setAttribute("onclick", "window.fsDisp.render(window.fsDisp.cwd)"); } else { document.getElementById("fs_space_bar").setAttribute("onclick", ""); let splitter = (process.platform === "win32") ? "\\" : "/"; let displayMount = (this.fsBlock.mount.length < 18) ? this.fsBlock.mount : "..."+splitter+this.fsBlock.mount.split(splitter).pop(); // See #226 if (!isNaN(this.fsBlock.use)) { this.space_bar.text.innerHTML = `Mount ${displayMount} used ${Math.round(this.fsBlock.use)}%`; this.space_bar.bar.value = Math.round(this.fsBlock.use); } else if (!isNaN((this.fsBlock.size / this.fsBlock.used) * 100)) { let usage = Math.round((this.fsBlock.size / this.fsBlock.used) * 100); this.space_bar.text.innerHTML = `Mount ${displayMount} used ${usage}%`; this.space_bar.bar.value = usage; } else { this.space_bar.text.innerHTML = "Could not calculate mountpoint usage."; this.space_bar.bar.value = 100; } } function delay(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms); }); } // Render animation let id = 0; while (this.filesContainer.childNodes[id]) { this.filesContainer.childNodes[id].setAttribute("class", this.filesContainer.childNodes[id].getAttribute("class").replace(" animationWait", "")); await delay(50); id++; } }; // Automatically start indexing supposed beginning CWD // See #365 // ...except if we're hot-reloading, in which case this can mess up the rendering // See #392 if (window.performance.navigation.type === 0) { this.readFS(window.term[window.currentTerm].cwd || window.settings.cwd); } } } module.exports = { FilesystemDisplay };