mirror of
https://github.com/lxsang/antos-frontend.git
synced 2025-04-17 05:16:45 +02:00
567 lines
19 KiB
JavaScript
567 lines
19 KiB
JavaScript
/*
|
|
* decaffeinate suggestions:
|
|
* DS102: Remove unnecessary code created because of implicit returns
|
|
* DS205: Consider reworking code to avoid use of IIFEs
|
|
* DS208: Avoid top-level this
|
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
*/
|
|
// Copyright 2017-2018 Xuan Sang LE <xsang.le AT gmail DOT com>
|
|
|
|
// AnTOS Web desktop is is licensed under the GNU General Public
|
|
// License v3.0, see the LICENCE file for more information
|
|
|
|
// This program is free software: you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation, either version 3 of
|
|
// the License, or (at your option) any later version.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
//along with this program. If not, see https://www.gnu.org/licenses/.
|
|
String.prototype.asFileHandle = function() {
|
|
const list = this.split("://");
|
|
const handles = Ant.OS.API.VFS.findHandles(list[0]);
|
|
if (!handles || (handles.length === 0)) {
|
|
Ant.OS.announcer.osfail(__("VFS unknown handle: {0}", this), (Ant.OS.API.throwe("OS.VFS")), this);
|
|
return null;
|
|
}
|
|
return new (handles[0])(this);
|
|
};
|
|
|
|
this.OS.API.VFS = {
|
|
handles: { },
|
|
register( protos, cls ) {
|
|
return Ant.OS.API.VFS.handles[protos] = cls;
|
|
}, // if typeof protos is "string"
|
|
//Ant.OS.API.VFS.handles[v] = cls for v in protos
|
|
findHandles(proto) {
|
|
const l = ((() => {
|
|
const result = [];
|
|
for (let k in Ant.OS.API.VFS.handles) {
|
|
const v = Ant.OS.API.VFS.handles[k];
|
|
if (proto.trim().match((new RegExp(k , "g")))) {
|
|
result.push(v);
|
|
}
|
|
}
|
|
return result;
|
|
})());
|
|
return l;
|
|
}
|
|
};
|
|
|
|
class BaseFileHandle {
|
|
constructor(path) {
|
|
this.dirty = false;
|
|
this.cache = undefined;
|
|
this.setPath(path);
|
|
}
|
|
|
|
setPath(p) {
|
|
this.ready = false;
|
|
if (!p) { return; }
|
|
this.path = p.toString();
|
|
const list = this.path.split("://");
|
|
this.protocol = list[0];
|
|
if (!(list.length > 1)) { return; }
|
|
const re = list[1].replace(/^\/+|\/+$/g, '');
|
|
if (re === "") { return; }
|
|
this.genealogy = re.split("/");
|
|
if (!this.isRoot()) { this.basename = this.genealogy[this.genealogy.length - 1]; }
|
|
if ((this.basename.lastIndexOf(".") !== 0) && (this.basename.indexOf( "." ) !== -1)) { return this.ext = this.basename.split( "." ).pop(); }
|
|
}
|
|
|
|
filename() {
|
|
if (!this.basename) { return "Untitled"; }
|
|
return this.basename;
|
|
}
|
|
|
|
setCache(v) {
|
|
this.cache = v;
|
|
return this;
|
|
}
|
|
|
|
asFileHandle() { return this; }
|
|
|
|
isRoot() { return (!this.genealogy) || (this.genealogy.size === 0); }
|
|
|
|
child(name) {
|
|
if (this.isRoot()) {
|
|
return this.path + name;
|
|
} else {
|
|
return this.path + "/" + name;
|
|
}
|
|
}
|
|
|
|
isHidden() {
|
|
if (!this.basename) { return false; }
|
|
return this.basename[0] === ".";
|
|
}
|
|
|
|
hash() {
|
|
if (!this.path) { return -1; }
|
|
return this.path.hash();
|
|
}
|
|
|
|
b64(t) {
|
|
// t is object or mime type
|
|
return new Promise((resolve, reject) => {
|
|
const m = t === "object" ? "text/plain" : t;
|
|
if (!this.cache) { return resolve(""); }
|
|
if ((t === "object") || (typeof this.cache === "string")) {
|
|
let b64;
|
|
if (t === "object") {
|
|
b64 = JSON.stringify(this.cache, undefined, 4).asBase64();
|
|
} else {
|
|
b64 = this.cache.asBase64();
|
|
}
|
|
b64 = `data:${m};base64,${b64}`;
|
|
return resolve(b64);
|
|
} else {
|
|
const reader = new FileReader();
|
|
reader.readAsDataURL(this.cache);
|
|
reader.onload = () => resolve(reader.result);
|
|
return reader.onerror = e => reject(e);
|
|
}
|
|
});
|
|
}
|
|
|
|
parent() {
|
|
if (this.isRoot()) { return this; }
|
|
return (this.protocol + "://" + (this.genealogy.slice(0 , this.genealogy.length - 1)).join("/"))
|
|
.asFileHandle();
|
|
}
|
|
|
|
onready() {
|
|
// read meta data
|
|
return new Promise((resolve, reject) => {
|
|
if (this.ready) { return resolve(this.info); }
|
|
return this.meta()
|
|
.then(d => {
|
|
if (d.errors) { return reject(Ant.OS.API.throwe(__("{0}: {1}", d.error, this.path))); }
|
|
this.info = d.result;
|
|
this.ready = true;
|
|
return resolve(d.result);
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
read(t) {
|
|
return new Promise((resolve, reject) => {
|
|
return this.onready()
|
|
.then(r => {
|
|
return this._rd(t)
|
|
.then(d => // Ant.OS.announcer.ostrigger "VFS", { m: "read", file: me }
|
|
resolve(d)).catch(e => reject(__e(e)));
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
write(t) {
|
|
return new Promise((resolve, reject) => {
|
|
return this._wr(t)
|
|
.then(r => {
|
|
Ant.OS.announcer.ostrigger("VFS", { m: "write", file: this });
|
|
return resolve(r);
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
mk(d) {
|
|
return new Promise((resolve, reject) => {
|
|
return this.onready()
|
|
.then(r => {
|
|
return this._mk(d)
|
|
.then(d => {
|
|
Ant.OS.announcer.ostrigger("VFS", { m: "mk", file: this });
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
remove() {
|
|
return new Promise((resolve, reject) => {
|
|
return this.onready()
|
|
.then(r => {
|
|
return this._rm()
|
|
.then(d => {
|
|
Ant.OS.announcer.ostrigger("VFS", { m: "remove", file: this });
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
upload() {
|
|
return new Promise((resolve, reject) => {
|
|
return this.onready()
|
|
.then(r => {
|
|
return this._up()
|
|
.then(d => {
|
|
Ant.OS.announcer.ostrigger("VFS", { m: "upload", file: this });
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
publish() {
|
|
return new Promise((resolve, reject) => {
|
|
return this.onready()
|
|
.then(r => {
|
|
return this._pub()
|
|
.then(d => {
|
|
Ant.OS.announcer.ostrigger("VFS", { m: "publish", file: this });
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
download() {
|
|
return new Promise((resolve, reject) => {
|
|
return this.onready()
|
|
.then(r => {
|
|
return this._down()
|
|
.then(d => {
|
|
Ant.OS.announcer.ostrigger("VFS", { m: "download", file: this });
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
move(d) {
|
|
return new Promise((resolve, reject) => {
|
|
return this.onready()
|
|
.then(r => {
|
|
return this._mv(d)
|
|
.then(data => {
|
|
Ant.OS.announcer.ostrigger("VFS", { m: "move", file: d.asFileHandle() });
|
|
return resolve(data);
|
|
}).catch(e => reject(__e(e)));
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
execute() {
|
|
return new Promise((resolve, reject) => {
|
|
return this.onready()
|
|
.then(r => {
|
|
return this._exec()
|
|
.then(d => {
|
|
Ant.OS.announcer.ostrigger("VFS", { m: "execute", file: this });
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
getlink() { return this.path; }
|
|
|
|
unsupported(t) {
|
|
return new Promise((resolve, reject) => {
|
|
return reject(Ant.OS.API.throwe(__("Action {0} is unsupported on: {1}", t, this.path)));
|
|
});
|
|
}
|
|
// actions must be implemented by subclasses
|
|
|
|
_rd(t) { return this.unsupported("read"); }
|
|
_wr(d, t) { return this.unsupported("write"); }
|
|
_mk(d) { return this.unsupported("mk"); }
|
|
_rm() { return this.unsupported("remove"); }
|
|
_mv(d) { return this.unsupported("move"); }
|
|
_up() { return this.unsupported("upload"); }
|
|
_down() { return this.unsupported("download"); }
|
|
_exec() { return this.unsupported("execute"); }
|
|
_pub() { return this.unsupported("publish"); }
|
|
}
|
|
|
|
// now export the class
|
|
Ant.OS.API.VFS.BaseFileHandle = BaseFileHandle;
|
|
|
|
// Remote file handle
|
|
class RemoteFileHandle extends Ant.OS.API.VFS.BaseFileHandle {
|
|
constructor(path) {
|
|
super(path);
|
|
}
|
|
|
|
meta() {
|
|
return new Promise((resolve, reject) => {
|
|
return Ant.OS.API.handle.fileinfo(this.path)
|
|
.then(d => {
|
|
if (d.error) { return reject(Ant.OS.API.throwe(__("{0}: {1}", d.error, this.path))); }
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
|
|
getlink() {
|
|
return Ant.OS.API.handle.get + "/" + this.path;
|
|
}
|
|
|
|
_rd(t) {
|
|
// t: binary, text, any type
|
|
if (!this.info) {
|
|
return new Promise((resolve, reject) => {
|
|
return reject(Ant.OS.API.throwe(__(
|
|
"file meta-data not found: {0}", this.path)
|
|
)
|
|
);
|
|
});
|
|
}
|
|
if (this.info.type === "dir") { return Ant.OS.API.handle.scandir(this.path); }
|
|
//read the file
|
|
if (t === "binary") { return Ant.OS.API.handle.fileblob(this.path); }
|
|
return Ant.OS.API.handle.readfile(this.path, t ? t : "text");
|
|
}
|
|
|
|
_wr(t) {
|
|
// t is base64 or undefined
|
|
return new Promise((resolve, reject) => {
|
|
if (t === "base64") {
|
|
return Ant.OS.API.handle.write(this.path, this.cache).then(d => {
|
|
if (d.error) { return reject(Ant.OS.API.throwe(__("{0}: {1}", d.error, this.path))); }
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
} else {
|
|
return this.b64(t)
|
|
.then(r => {
|
|
return Ant.OS.API.handle.write(this.path, r)
|
|
.then(result => {
|
|
if (result.error) {
|
|
return reject(Ant.OS.API.throwe(__(
|
|
"{0}: {1}", result.error, this.path)
|
|
)
|
|
);
|
|
}
|
|
return resolve(result);
|
|
}).catch(e => reject(__e(e)));
|
|
}).catch(e => reject(__e(e)));
|
|
}
|
|
});
|
|
}
|
|
|
|
_mk(d) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!this.info) {
|
|
return reject(Ant.OS.API.throwe(__(
|
|
"file meta-data not found: {0}", this.path)
|
|
)
|
|
);
|
|
}
|
|
if (this.info.type === "file") {
|
|
return reject(Ant.OS.API.throwe(__("{0} is not a directory", this.path)));
|
|
}
|
|
return Ant.OS.API.handle.mkdir(`${this.path}/${d}`)
|
|
.then(d => {
|
|
if (d.error) { return reject(Ant.OS.API.throwe(__("{0}: {1}", d.error, this.path))); }
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
_rm() {
|
|
return new Promise((resolve, reject) => {
|
|
return Ant.OS.API.handle.delete(this.path)
|
|
.then(d => {
|
|
if (d.error) { return reject(Ant.OS.API.throwe(__("{0}: {1}", d.error, this.path))); }
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
|
|
_mv(d) {
|
|
return new Promise((resolve, reject) => {
|
|
return Ant.OS.API.handle.move(this.path, d)
|
|
.then(d => {
|
|
if (d.error) { return reject(Ant.OS.API.throwe(__("{0}: {1}", d.error, this.path))); }
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
|
|
_up() {
|
|
return new Promise((resolve, reject) => {
|
|
if (this.info.type !== "dir") {
|
|
return reject(Ant.OS.API.throwe(__("{0} is not a file", this.path)));
|
|
}
|
|
return Ant.OS.API.handle.upload(this.path)
|
|
.then(d => {
|
|
if (d.error) { return reject(Ant.OS.API.throwe(__("{0}: {1}", d.error, this.path))); }
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
_down() {
|
|
return new Promise((resolve, reject) => {
|
|
if (this.info.type === "dir") {
|
|
return Ant.OS.API.throwe(__("{0} is not a file", this.path));
|
|
}
|
|
return Ant.OS.API.handle.fileblob(this.path)
|
|
.then(d => {
|
|
const blob = new Blob([d], { type: "octet/stream" });
|
|
Ant.OS.API.saveblob(this.basename, blob);
|
|
return resolve();
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
_pub() {
|
|
return new Promise((resolve, reject) => {
|
|
return Ant.OS.API.handle.sharefile(this.path, true)
|
|
.then(d => {
|
|
if (d.error) { return reject(Ant.OS.API.throwe(__("{0}: {1}", d.error, this.path))); }
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
}
|
|
|
|
Ant.OS.API.VFS.register("^(home|desktop|os|Untitled)$", RemoteFileHandle);
|
|
|
|
// Application Handle
|
|
class ApplicationHandle extends Ant.OS.API.VFS.BaseFileHandle {
|
|
constructor(path) {
|
|
super(path);
|
|
if (this.basename) { this.info = Ant.OS.setting.system.packages[this.basename]; }
|
|
this.ready = true;
|
|
}
|
|
|
|
_rd(t) {
|
|
return new Promise((resolve, reject) => {
|
|
if (this.info) { return resolve({ result: this.info }); }
|
|
if (!this.isRoot()) { return reject(Ant.OS.API.throwe(__("Application meta data isnt found"))); }
|
|
return resolve({ result: ((() => {
|
|
const result = [];
|
|
for (let k in Ant.OS.setting.system.packages) {
|
|
const v = Ant.OS.setting.system.packages[k];
|
|
result.push(v);
|
|
}
|
|
return result;
|
|
})()) });
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
Ant.OS.API.VFS.register("^app$", ApplicationHandle);
|
|
|
|
class BufferFileHandle extends Ant.OS.API.VFS.BaseFileHandle {
|
|
constructor(path, mime, data) {
|
|
super(path);
|
|
if (data) { this.cache = data; }
|
|
this.info = {
|
|
mime,
|
|
path,
|
|
size: data ? data.length : 0,
|
|
name: this.basename,
|
|
type: "file"
|
|
};
|
|
}
|
|
|
|
_rd(t) {
|
|
return new Promise((resolve, reject) => {
|
|
return resolve({ result: this.cache });
|
|
});
|
|
}
|
|
|
|
_wr(d, t) {
|
|
this.cache = d;
|
|
if (this.onchange) { this.onchange(this); }
|
|
return new Promise((resolve, reject) => resolve({ result: true }));
|
|
}
|
|
|
|
_down() {
|
|
return new Promise((resolve, reject) => {
|
|
const blob = new Blob([this.cache], { type: "octet/stream" });
|
|
Ant.OS.API.saveblob(this.basename, blob);
|
|
return resolve();
|
|
});
|
|
}
|
|
|
|
onchange(f) {
|
|
return this.onchange = f;
|
|
}
|
|
}
|
|
|
|
Ant.OS.API.VFS.register("^mem$", BufferFileHandle);
|
|
|
|
class URLFileHandle extends Ant.OS.API.VFS.BaseFileHandle {
|
|
constructor(path) {
|
|
super(path);
|
|
this.ready = true;
|
|
}
|
|
|
|
_rd(t) {
|
|
return Ant.OS.API.get(this.path, t ? t : "text");
|
|
}
|
|
}
|
|
|
|
Ant.OS.API.VFS.register("^(http|https|ftp)$", URLFileHandle);
|
|
|
|
|
|
class SharedFileHandle extends Ant.OS.API.VFS.BaseFileHandle {
|
|
constructor(path) {
|
|
super(path);
|
|
if (this.isRoot()) { this.ready = true; }
|
|
}
|
|
|
|
meta() {
|
|
return Ant.OS.API.handle.fileinfo(this.path);
|
|
}
|
|
|
|
_rd(t) {
|
|
if (this.isRoot()) { return Ant.OS.API.get(`${Ant.OS.API.handle.shared}/all`, t); }
|
|
//read the file
|
|
if (t === "binary") { return Ant.OS.API.handle.fileblob(this.path); }
|
|
return Ant.OS.API.handle.readfile(this.path, t ? t : "text");
|
|
}
|
|
|
|
_wr(d, t) {
|
|
return new Promise((resolve, reject) => {
|
|
return Ant.OS.API.handle.write(this.path, d)
|
|
.then(d => {
|
|
if (d.error) { return reject(Ant.OS.API.throwe(__("{0}: {1}", d.error, this.path))); }
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
_rm() {
|
|
return new Promise((resolve, reject) => {
|
|
return Ant.OS.API.handle.sharefile(this.basename, false)
|
|
.then(d => {
|
|
if (d.error) { return reject(Ant.OS.API.throwe(__("{0}: {1}", d.error, this.path))); }
|
|
return resolve(d);
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
|
|
_down() {
|
|
return new Promise((resolve, reject) => {
|
|
if (this.info.type === "dir") {
|
|
return reject(Ant.OS.API.throwe(__("{0} is not a file", this.path)));
|
|
}
|
|
return Ant.OS.API.handle.fileblob(this.path)
|
|
.then(data => {
|
|
const blob = new Blob([data], { type: "octet/stream" });
|
|
Ant.OS.API.saveblob(this.basename, blob);
|
|
return resolve();
|
|
}).catch(e => reject(__e(e)));
|
|
});
|
|
}
|
|
_pub() {
|
|
return new Promise((resolve, reject) => resolve({ result: this.basename }));
|
|
}
|
|
}
|
|
|
|
Ant.OS.API.VFS.register("^shared$", SharedFileHandle); |