antos-frontend/src/core/vfs.coffee
2020-05-18 18:53:59 +02:00

395 lines
13 KiB
CoffeeScript

# 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 = () ->
list = @split "://"
handles = Ant.OS.API.VFS.findHandles list[0]
if not handles or handles.length is 0
Ant.OS.announcer.osfail __("VFS unknown handle: {0}", @), (Ant.OS.API.throwe "OS.VFS"), @
return null
return new handles[0](@)
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) ->
l = (v for k, v of Ant.OS.API.VFS.handles when proto.trim().match (new RegExp k , "g"))
return l
class BaseFileHandle
constructor: (path) ->
@dirty = false
@cache = undefined
@setPath path
setPath: (p) ->
@ready = false
return unless p
@path = p.toString()
list = @path.split "://"
@protocol = list[0]
return unless list.length > 1
re = list[1].replace(/^\/+|\/+$/g, '')
return if re is ""
@genealogy = re.split("/")
@basename = @genealogy[@genealogy.length - 1] unless @isRoot()
@ext = @basename.split( "." ).pop() unless @basename.lastIndexOf(".") is 0 or @basename.indexOf( "." ) is -1
setCache: (v) ->
@cache = v
@
asFileHandle: () -> @
isRoot: () -> (not @genealogy) or (@genealogy.size is 0)
child: (name) ->
if @isRoot()
return @path + name
else
return @path + "/" + name
isHidden: () ->
return false if not @basename
@basename[0] is "."
hash: () ->
return -1 unless @path
return @path.hash()
b64: (t) ->
# t is object or mime type
new Promise (resolve, reject) =>
m = if t is "object" then "text/plain" else t
return resolve "" unless @cache
if t is "object" or typeof @cache is "string"
if t is "object"
b64 = JSON.stringify(@cache, undefined, 4).asBase64()
else
b64 = @cache.asBase64()
b64 = "data:#{m};base64,#{b64}"
resolve(b64)
else
reader = new FileReader()
reader.readAsDataURL(@cache)
reader.onload = () ->
resolve reader.result
reader.onerror = (e) ->
reject e
parent: () ->
return @ if @isRoot()
return (@protocol + "://" + (@genealogy.slice 0 , @genealogy.length - 1).join "/")
.asFileHandle()
onready: () ->
# read meta data
new Promise (resolve, reject) =>
return resolve(@info) if @ready
@meta()
.then (d) =>
return reject d if d.errors
@info = d.result
@ready = true
resolve(d.result)
.catch (e) -> reject e
read: (t) ->
new Promise (resolve, reject) =>
@onready()
.then (r) =>
@_rd(t)
.then (d) ->
# Ant.OS.announcer.ostrigger "VFS", { m: "read", file: me }
resolve d
.catch (e) -> reject e
.catch (e) -> reject e
write: (t) ->
new Promise (resolve, reject) =>
@_wr(t)
.then (r) =>
Ant.OS.announcer.ostrigger "VFS", { m: "write", file: @ }
resolve r
.catch (e) -> reject e
mk: (d) ->
new Promise (resolve, reject) =>
@onready()
.then (r) =>
@_mk(d)
.then (d) =>
Ant.OS.announcer.ostrigger "VFS", { m: "mk", file: @ }
resolve d
.catch (e) -> reject e
.catch (e) -> reject e
remove: () ->
new Promise (resolve, reject) =>
@onready()
.then (r) =>
@_rm()
.then (d) =>
Ant.OS.announcer.ostrigger "VFS", { m: "remove", file: @ }
resolve d
.catch (e) -> reject e
.catch (e) -> reject e
upload: () ->
new Promise (resolve, reject) =>
@onready()
.then (r) =>
@_up()
.then (d) =>
Ant.OS.announcer.ostrigger "VFS", { m: "upload", file: @ }
resolve d
.catch (e) -> reject e
.catch (e) -> reject e
publish: () ->
new Promise (resolve, reject) =>
@onready()
.then (r) =>
@_pub()
.then (d) =>
Ant.OS.announcer.ostrigger "VFS", { m: "publish", file: @ }
resolve d
.catch (e) -> reject e
.catch (e) -> reject e
download: () ->
new Promise (resolve, reject) =>
@onready()
.then (r) =>
@_down()
.then (d) =>
Ant.OS.announcer.ostrigger "VFS", { m: "download", file: @ }
resolve d
.catch (e) -> reject e
.catch (e) -> reject e
move: (d) ->
new Promise (resolve, reject) =>
@onready()
.then (r) =>
@_mv(d)
.then (data) =>
Ant.OS.announcer.ostrigger "VFS", { m: "move", file: @ }
resolve data
.catch (e) -> reject e
.catch (e) -> reject e
execute: () ->
new Promise (resolve, reject) =>
@onready()
.then (r) =>
@_exec()
.then (d) =>
Ant.OS.announcer.ostrigger "VFS", { m: "execute", file: @ }
resolve d
.catch (e) -> reject e
.catch (e) -> reject e
getlink: () -> @path
unsupported: (t) ->
new Promise (resolve, reject) =>
reject { error: __("Action {0} is unsupported on: {1}", t, @path) }
# actions must be implemented by subclasses
_rd: (t) -> @unsupported "read"
_wr: (d, t) -> @unsupported "write"
_mk: (d) -> @unsupported "mk"
_rm: () -> @unsupported "remove"
_mv: (d) -> @unsupported "move"
_up: () -> @unsupported "upload"
_down: () -> @unsupported "download"
_exec: () -> @unsupported "execute"
_pub: () -> @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: () ->
Ant.OS.API.handle.fileinfo @path
getlink: () ->
Ant.OS.API.handle.get + "/" + @path
_rd: (t) ->
# t: binary, text, any type
if not @info
return new Promise (resolve, reject) =>
reject Ant.OS.API.throwe __(
"file meta-data not found: {0}", @path)
return Ant.OS.API.handle.scandir @path if @info.type is "dir"
#read the file
return Ant.OS.API.handle.fileblob @path if t is "binary"
Ant.OS.API.handle.readfile @path, if t then t else "text"
_wr: (t) ->
# t is base64 or undefined
return Ant.OS.API.handle.write @path, @cache if t is "base64"
new Promise (resolve, reject) =>
@b64(t)
.then (r) =>
Ant.OS.API.handle.write @path, r
.then (result) ->
resolve result
.catch (e) -> reject e
.catch (e) -> reject e
_mk: (d) ->
if not @info
return new Promise (resolve, reject) =>
reject Ant.OS.API.throwe __(
"file meta-data not found: {0}", @path)
if @info.type is "file"
return new Promise (resolve, reject) =>
reject Ant.OS.API.throwe __("{0} is not a directory", @path)
Ant.OS.API.handle.mkdir "#{@path}/#{d}"
_rm: () ->
Ant.OS.API.handle.delete @path
_mv: (d) ->
Ant.OS.API.handle.move @path, d
_up: () ->
if @info.type isnt "dir"
return new Promise (resolve, reject) =>
reject Ant.OS.API.throwe __("{0} is not a file", @path)
Ant.OS.API.handle.upload @path
_down: () ->
new Promise (resolve, reject) =>
if @info.type is "dir"
return Ant.OS.API.throwe __("{0} is not a file", @path)
Ant.OS.API.handle.fileblob(@path)
.then (d) =>
blob = new Blob [d], { type: "octet/stream" }
Ant.OS.API.saveblob @basename, blob
resolve()
.catch (e) ->
reject e
_pub: () ->
Ant.OS.API.handle.sharefile @path, true
Ant.OS.API.VFS.register "^(home|desktop|os|Untitled)$", RemoteFileHandle
# Application Handle
class ApplicationHandle extends Ant.OS.API.VFS.BaseFileHandle
constructor: (path) ->
super path
@info = Ant.OS.setting.system.packages[@basename] if @basename
@ready = true
_rd: (t) ->
new Promise (resolve, reject) =>
return resolve { result: @info } if @info
return reject Ant.OS.API.throwe(__("Application meta data isnt found")) unless @isRoot()
resolve { result: ( v for k, v of Ant.OS.setting.system.packages ) }
Ant.OS.API.VFS.register "^app$", ApplicationHandle
class BufferFileHandle extends Ant.OS.API.VFS.BaseFileHandle
constructor: (path, mime, data) ->
super path
@cache = data if data
@info =
mime: mime
path: path
size: if data then data.length else 0
name: @basename
type: "file"
_rd: (t) ->
new Promise (resolve, reject) =>
resolve { result: @cache }
_wr: (d, t) ->
@cache = d
@onchange @ if @onchange
new Promise (resolve, reject) ->
resolve { result: true }
_down: () ->
new Promise (resolve, reject) =>
blob = new Blob [@cache], { type: "octet/stream" }
Ant.OS.API.saveblob @basename, blob
resolve()
onchange: (f) ->
@onchange = f
Ant.OS.API.VFS.register "^mem$", BufferFileHandle
class URLFileHandle extends Ant.OS.API.VFS.BaseFileHandle
constructor: (path) ->
super path
@ready = true
_rd: (t) ->
Ant.OS.API.get @path, if t then t else "text"
Ant.OS.API.VFS.register "^(http|https|ftp)$", URLFileHandle
class SharedFileHandle extends Ant.OS.API.VFS.BaseFileHandle
constructor: (path) ->
super path
@ready = true if @isRoot()
meta: () ->
Ant.OS.API.handle.fileinfo @path
_rd: (t) ->
return Ant.OS.API.get "#{Ant.OS.API.handle.shared}/all", t if @isRoot()
#read the file
return Ant.OS.API.handle.fileblob @path if t is "binary"
Ant.OS.API.handle.readfile @path, if t then t else "text"
_wr: (d, t) ->
Ant.OS.API.handle.write @path, d
_rm: () ->
Ant.OS.API.handle.sharefile @basename, false
_down: () ->
new Promise (resolve, reject) =>
if @info.type is "dir"
return reject Ant.OS.API.throwe __("{0} is not a file", @path)
Ant.OS.API.handle.fileblob @path
.then (data) =>
blob = new Blob [data], { type: "octet/stream" }
Ant.OS.API.saveblob @basename, blob
resolve()
.catch (e) -> reject e
_pub: () ->
return new Promise (resolve, reject) => resolve { result: @basename }
Ant.OS.API.VFS.register "^shared$", SharedFileHandle