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 += `
${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
};