diff --git a/Archive/README.md b/Archive/README.md
new file mode 100644
index 0000000..173cdc8
--- /dev/null
+++ b/Archive/README.md
@@ -0,0 +1,13 @@
+# Archive
+
+Small application for zip file manager
+
+## Features
+* Open, create zip file Archive
+* Add/remove file/folder to archive
+* Extract zip file content
+
+## Changle log
+
+### v0.0.1-a
+* First release
\ No newline at end of file
diff --git a/Archive/assets/scheme.html b/Archive/assets/scheme.html
new file mode 100644
index 0000000..ebd3753
--- /dev/null
+++ b/Archive/assets/scheme.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Archive/build/debug/README.md b/Archive/build/debug/README.md
new file mode 100644
index 0000000..173cdc8
--- /dev/null
+++ b/Archive/build/debug/README.md
@@ -0,0 +1,13 @@
+# Archive
+
+Small application for zip file manager
+
+## Features
+* Open, create zip file Archive
+* Add/remove file/folder to archive
+* Extract zip file content
+
+## Changle log
+
+### v0.0.1-a
+* First release
\ No newline at end of file
diff --git a/Archive/build/debug/main.css b/Archive/build/debug/main.css
new file mode 100644
index 0000000..feaf261
--- /dev/null
+++ b/Archive/build/debug/main.css
@@ -0,0 +1,20 @@
+
+afx-app-window[data-id="Archive"] afx-tree-view .afx-tree-view-folder-close:before{
+ content: "\f07b";
+ font-family: "FontAwesome";
+ color:#76D2F9;
+ font-size: 16px;
+}
+afx-app-window[data-id="Archive"] afx-tree-view .afx-tree-view-folder-open:before{
+ content: "\f07c";
+ font-family: "FontAwesome";
+ color:#76D2F9;
+ font-size: 16px;
+}
+afx-app-window[data-id="Archive"] afx-tree-view .afx-tree-view-item:before{
+ content: "\f016";
+ font-family: "FontAwesome";
+ font-size: 16px;
+ font-style: normal;
+ font-weight: normal;
+}
diff --git a/Archive/build/debug/main.js b/Archive/build/debug/main.js
new file mode 100644
index 0000000..25943ad
--- /dev/null
+++ b/Archive/build/debug/main.js
@@ -0,0 +1 @@
+(function(){var t;(t=class extends this.OS.application.BaseApplication{constructor(t){super("Archive",t),this.currfile="Untitled".asFileHandle(),this.args&&this.args.length>0&&this.args[0].path&&(this.currfile=t[0].path.asFileHandle())}main(){return this.btadd=this.find("btaradd"),this.btdel=this.find("btardel"),this.btxtract=this.find("btarxtract"),this.filetree=this.find("filetree"),this.zip=void 0,this.bindKey("ALT-N",()=>this.fileMenuHandle("new")),this.bindKey("ALT-O",()=>this.fileMenuHandle("open")),this.bindKey("CTRL-S",()=>this.fileMenuHandle("save")),this.bindKey("ALT-S",()=>this.fileMenuHandle("saveas")),this.btxtract.onbtclick=t=>{var e,i;return(e=this.filetree.selectedItem)?(i=e.data,this.openDialog("FileDialog",{title:__("Select a folder"),mimes:["dir"]}).then(t=>this.xtract(i,t.file.path).then(()=>this.notify(__("extract successful: {0}",i.path))).catch(t=>this.error(t.toString(),t))).catch(t=>this.error(t.toString(),t))):this.notify(__("Please select file/folder to extract"))},this.btadd.onbtclick=t=>this.actionAdd(),this.btdel.onbtclick=t=>this.actionDel(),this.filetree.contextmenuHandle=(t,e)=>{var i,r;if(i=this.filetree.selectedItem)return r=[{text:"__(Delete)",onmenuselect:()=>this.actionDel()},{text:"__(Info)",onmenuselect:()=>this.actionInfo()}],"dir"===i.data.type&&r.unshift({text:"__(Add)",onmenuselect:()=>this.actionAdd()}),e.items=r,e.show(t)},this.openar(this.currfile)}actionAdd(){var t,e;return(t=this.filetree.selectedItem)&&"dir"===t.data.type?(e=t.data,this.openDialog("FileDialog",{title:__("Select a file/folder")}).then(t=>this.addToZip(t.file,`${e.path}/${t.file.path.asFileHandle().basename}`).then(()=>(this.currfile.dirty=!0,this.refreshTreeFile())).catch(t=>this.error(t.toString(),t))).catch(t=>this.error(t.toString(),t))):this.notify(__("Please select a destination folder"))}actionDel(){var t,e;return(t=this.filetree.selectedItem)?(e=t.data).root?this.notify(__("You cannot delete the root node")):this.ask({title:"__(Delete)",text:__("Do you really want to delete: {0}?",e.text)}).then(t=>{if(t)return this.zip.remove(e.path.trimBy("/")),this.currfile.dirty=!0,this.refreshTreeFile()}).catch(t=>this.error(t.toString(),t)):this.notify(__("Please select a destination folder"))}actionInfo(){var t,e,i;return(t=this.filetree.selectedItem)?(e=t.data.path.trimBy("/"),"dir"===t.data.type&&(e+="/"),(i=this.zip.files[e])?this.openDialog("InfoDialog",{title:"About: "+i.name,name:i.name,date:i.date,dir:i.dir,dataBinary:i._dataBinary,size:i._data.uncompressedSize}):this.notify(__("Cannot get entry meta data"))):this.notify(__("Please select a file/folder"))}openar(t){return this.zip=void 0,"Untitled"===t.filename?(this.zip=new JSZip,this.currfile=t,this.refreshTreeFile()):t.read("binary").then(e=>JSZip.loadAsync(e).then(e=>(this.zip=e,this.currfile=t,this.refreshTreeFile())).catch(t=>this.error(__("Wrong zip format: {0}",t.toString()),t))).catch(e=>this.error(__("Unable to read archive: {0}",t.path),e))}refreshTreeFile(){var t,e,i,r,n;if(this.zip){for(t in r={text:this.currfile.filename.trimRight(".zip"),type:"dir",path:"",open:!0,root:!0,nodes:[]},i=this.zip.files)n=i[t],e=this.putFileInTree(t.split("/"),r),n.dir||(e.type="file",delete e.nodes);return this.filetree.data=r}}putFileInTree(t,e){var i,r,n,a;return i=function(){var t,i,r,n;for(n=[],t=0,i=(r=e.nodes).length;t"file"===t.type?this.zip.file(t.path.trimBy("/")).async("uint8array").then(n=>{var a;return(a=`${e}/${t.text}`.asFileHandle()).cache=new Blob([n],{type:"octet/stream"}),a.write().then((function(){return i()})).catch((function(t){return r(__e(t))}))}).catch((function(t){return r(__e(t))})):e.asFileHandle().mk(t.text).then(()=>{var n,a;return n=function(){var e,i,r,n;for(n=[],e=0,i=(r=t.nodes).length;e{var n;return 0===t.length?i():(n=t.shift(),this.xtract(n,e).then(()=>this.xtractall(t,e).then((function(){return i()})).catch((function(t){return r(__e(t))}))).catch((function(t){return r(__e(t))})))})}addToZip(t,e){return new Promise((i,r)=>"dir"===t.type?t.path.asFileHandle().read().then(t=>t.error?r(__e(this.throwe(t.error))):this.addFilesTozip(t.result,e).then((function(){return i()})).catch(t=>r(__e(t)))):t.path.asFileHandle().read("binary").then(t=>(this.zip.file(e.trimBy("/"),t,{binary:!0}),i())).catch((function(t){return i(__e(t))})))}addFilesTozip(t,e){return new Promise((i,r)=>{var n;return 0===t.length?i():(n=t.shift(),this.addToZip(n,`${e}/${n.path.asFileHandle().basename}`).then(()=>this.addFilesTozip(t,e).then((function(){return i()})).catch((function(t){return r(__e(t))}))).catch((function(t){return r(__e(t))})))})}saveZipAs(){return this.openDialog("FileDialog",{title:__("Save as"),file:this.currfile}).then(t=>{var e;return e=t.file.path.asFileHandle(),"file"===t.file.type&&(e=e.parent()),this.currfile.setPath(`${e.path}/${t.name}`),this.write()})}write(){if(this.zip&&"Untitled"!==this.currfile.path)return this.zip.generateAsync({type:"base64"}).then(t=>this.currfile.setCache("data:application/zip;base64,"+t).write("base64").then(()=>(this.currfile.dirty=!1,this.refreshTreeFile(),this.notify(__("zip file saved in {0}",this.currfile.path)))).catch(t=>this.error(__("Unable to save zip file: {0}",this.currfile.path))))}fileMenuHandle(t){switch(t){case"open":return this.openDialog("FileDialog",{title:__("Select a zip file"),mimes:["application/zip"]}).then(t=>this.openar(t.file.path.asFileHandle())).catch(t=>this.error(t.toString(),t));case"save":return"Untitled"!==this.currfile.path?this.write():this.saveZipAs();case"saveas":return this.saveZipAs()}}menu(){return[{text:"__(File)",nodes:[{text:"__(New)",id:"new",shortcut:"A-N"},{text:"__(Open)",id:"open",shortcut:"A-O"},{text:"__(Save)",id:"save",shortcut:"C-S"},{text:"__(Save as)",id:"saveas",shortcut:"A-S"}],onchildselect:t=>this.fileMenuHandle(t.data.item.data.id)}]}cleanup(t){if(this.currfile.dirty)return t.preventDefault(),this.ask({title:"__(Quit)",text:"__(Zip file has been modified. Quit without saving?)"}).then(t=>{if(t)return this.currfile.dirty=!1,this.quit()})}}).dependencies=["os://scripts/jszip.min.js"],this.OS.register("Archive",t)}).call(this);
\ No newline at end of file
diff --git a/Archive/build/debug/package.json b/Archive/build/debug/package.json
new file mode 100644
index 0000000..3c8e497
--- /dev/null
+++ b/Archive/build/debug/package.json
@@ -0,0 +1,15 @@
+{
+ "app":"Archive",
+ "name":"Archive",
+ "pkgname": "Archive",
+ "description":"Create of extract zip archive",
+ "info":{
+ "author": "Xuan Sang LE",
+ "email": "mrsang@lxsang.me"
+ },
+ "version":"0.0.1-a",
+ "category":"Other",
+ "iconclass":"fa fa-archive",
+ "mimes":["application/zip"],
+ "locale": {}
+}
\ No newline at end of file
diff --git a/Archive/build/debug/scheme.html b/Archive/build/debug/scheme.html
new file mode 100644
index 0000000..ebd3753
--- /dev/null
+++ b/Archive/build/debug/scheme.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Archive/build/release/Archive.zip b/Archive/build/release/Archive.zip
new file mode 100644
index 0000000..285a967
Binary files /dev/null and b/Archive/build/release/Archive.zip differ
diff --git a/Archive/coffees/main.coffee b/Archive/coffees/main.coffee
new file mode 100644
index 0000000..9ea987b
--- /dev/null
+++ b/Archive/coffees/main.coffee
@@ -0,0 +1,275 @@
+class Archive extends this.OS.application.BaseApplication
+ constructor: ( args ) ->
+ super "Archive", args
+ @currfile = "Untitled".asFileHandle()
+ if @args and @args.length > 0 and @args[0].path
+ @currfile = args[0].path.asFileHandle()
+
+ main: () ->
+ @btadd = @find "btaradd"
+ @btdel = @find "btardel"
+ @btxtract = @find "btarxtract"
+ @filetree = @find "filetree"
+ @zip = undefined
+ @bindKey "ALT-N", () => @fileMenuHandle "new"
+ @bindKey "ALT-O", () => @fileMenuHandle "open"
+ @bindKey "CTRL-S", () => @fileMenuHandle "save"
+ @bindKey "ALT-S", () => @fileMenuHandle "saveas"
+
+ @btxtract.onbtclick = (e) =>
+ item = @filetree.selectedItem
+ return @notify __("Please select file/folder to extract") unless item
+ treedata = item.data
+ @openDialog "FileDialog", { title: __("Select a folder"), mimes: ["dir"] }
+ .then (d) =>
+ @xtract(treedata, d.file.path)
+ .then () => @notify __("extract successful: {0}", treedata.path)
+ .catch (e) => @error e.toString(), e
+ .catch (e) => @error e.toString(), e
+
+
+ @btadd.onbtclick = (e) => @actionAdd()
+
+ @btdel.onbtclick = (e) => @actionDel()
+
+ @filetree.contextmenuHandle = (e, m) =>
+ item = @filetree.selectedItem
+ return unless item
+ mdata = [
+ { text: "__(Delete)", onmenuselect: () => @actionDel()},
+ { text: "__(Info)", onmenuselect: () => @actionInfo()}
+ ]
+ if item.data.type is "dir"
+ mdata.unshift { text: "__(Add)", onmenuselect: () => @actionAdd() }
+
+
+ m.items = mdata
+ m.show(e)
+
+ @openar(@currfile)
+
+ actionAdd: () ->
+ item = @filetree.selectedItem
+ return @notify __("Please select a destination folder") unless item and item.data.type is "dir"
+ treedata = item.data
+ @openDialog "FileDialog", { title: __("Select a file/folder") }
+ .then (d) =>
+ @addToZip d.file, "#{treedata.path}/#{d.file.path.asFileHandle().basename}"
+ .then () =>
+ @currfile.dirty = true
+ @refreshTreeFile()
+ .catch (e) => @error e.toString(), e
+ .catch (e) => @error e.toString(), e
+
+ actionDel: () ->
+ item = @filetree.selectedItem
+ return @notify __("Please select a destination folder") unless item
+ treedata = item.data
+ return @notify __("You cannot delete the root node") if treedata.root
+
+ @ask {title: "__(Delete)", text: __("Do you really want to delete: {0}?", treedata.text) }
+ .then (d) =>
+ return unless d
+ @zip.remove(treedata.path.trimBy("/"))
+ @currfile.dirty = true
+ @refreshTreeFile()
+ .catch (e) => @error e.toString(), e
+
+ actionInfo: () ->
+ item = @filetree.selectedItem
+ return @notify __("Please select a file/folder") unless item
+ key = item.data.path.trimBy("/")
+ key = "#{key}/" if item.data.type is "dir"
+ meta = @zip.files[key]
+ return @notify __("Cannot get entry meta data") unless meta
+ @openDialog "InfoDialog", {
+ title: "About: #{meta.name}",
+ name: meta.name,
+ date: meta.date,
+ dir: meta.dir,
+ dataBinary: meta._dataBinary,
+ size: meta._data.uncompressedSize
+ }
+
+ openar: (file) ->
+ @zip = undefined
+ if file.filename is "Untitled"
+ @zip = new JSZip()
+ @currfile = file
+ @refreshTreeFile()
+ else
+ # open the file and refresh filetree
+ file.read("binary")
+ .then (data) =>
+ JSZip.loadAsync(data)
+ .then (zip) =>
+ @zip = zip
+ @currfile = file
+ @refreshTreeFile()
+ .catch (e) =>
+ @error __("Wrong zip format: {0}", e.toString()), e
+ .catch (e) =>
+ @error __("Unable to read archive: {0}", file.path), e
+
+ refreshTreeFile: () ->
+ return unless @zip
+ treedata = {
+ text: @currfile.filename.trimRight(".zip"),
+ type: "dir"
+ path: "",
+ open: true,
+ root: true,
+ nodes: []
+ }
+
+ for k,v of @zip.files
+ leaf = @putFileInTree k.split("/"), treedata
+ if not v.dir
+ leaf.type = "file"
+ delete leaf.nodes
+ @filetree.data = treedata
+
+ putFileInTree: (patharr, treedata) ->
+ names = (v.text for v in treedata.nodes)
+ rep = patharr.shift()
+ return treedata unless rep
+ if names.includes rep
+ @putFileInTree patharr, treedata.nodes[names.indexOf rep]
+ else
+ subtree = {
+ text: rep,
+ path: "#{treedata.path}/#{rep}",
+ type: "dir",
+ nodes: []
+ }
+ treedata.nodes.push subtree
+ return @putFileInTree patharr, subtree
+
+
+ xtract: (treedata, to) ->
+ new Promise (resolve, reject) =>
+ if treedata.type is "file"
+ @zip.file(treedata.path.trimBy("/"))
+ .async("uint8array")
+ .then (data) =>
+ fp = "#{to}/#{treedata.text}".asFileHandle()
+ fp.cache = new Blob([data], { type: "octet/stream" })
+ fp.write()
+ .then () -> resolve()
+ .catch (e) -> reject __e e
+ .catch (e) -> reject __e e
+ else
+ #make the dir before extract
+ to.asFileHandle().mk treedata.text
+ .then () =>
+ nodes = (v for v in treedata.nodes)
+ @xtractall nodes, "#{to}/#{treedata.text}"
+ .then -> resolve()
+ .catch (e) -> reject __e e
+ .catch (e) -> reject __e e
+
+ xtractall: (list, to) ->
+ new Promise (resolve, reject) =>
+ return resolve() if list.length is 0
+ el = list.shift()
+ @xtract el, to
+ .then () =>
+ @xtractall list, to
+ .then () -> resolve()
+ .catch (e) -> reject __e e
+ .catch (e) -> reject __e e
+
+ addToZip: (file, to) ->
+ new Promise (resolve, reject) =>
+ if file.type is "dir"
+ file.path.asFileHandle().read().then (data) =>
+ return reject __e @throwe data.error if data.error
+ @addFilesTozip data.result, to
+ .then () -> resolve()
+ .catch (e) => reject __e e
+ else
+ file.path.asFileHandle()
+ .read("binary")
+ .then (data) =>
+ @zip.file(to.trimBy("/"), data, { binary: true })
+ resolve()
+ .catch (e) -> resolve __e e
+
+ addFilesTozip: (list, to) ->
+ new Promise (resolve, reject) =>
+ return resolve() if list.length is 0
+ el = list.shift()
+ @addToZip el, "#{to}/#{el.path.asFileHandle().basename}"
+ .then () =>
+ @addFilesTozip list, to
+ .then () -> resolve()
+ .catch (e) -> reject __e e
+ .catch (e) -> reject __e e
+
+ saveZipAs: () ->
+ @openDialog("FileDialog", {
+ title: __("Save as"),
+ file: @currfile
+ }).then (f) =>
+ d = f.file.path.asFileHandle()
+ d = d.parent() if f.file.type is "file"
+ @currfile.setPath "#{d.path}/#{f.name}"
+ @write()
+
+ write: () ->
+ return unless @zip and @currfile.path isnt "Untitled"
+ @zip .generateAsync({ type: "base64" })
+ .then (data) =>
+ @currfile
+ .setCache(
+ "data:application/zip;base64," + data
+ )
+ .write("base64")
+ .then () =>
+ @currfile.dirty = false
+ @refreshTreeFile()
+ @notify __("zip file saved in {0}", @currfile.path)
+ .catch (e) => @error __("Unable to save zip file: {0}", @currfile.path)
+
+ fileMenuHandle:(id) ->
+ switch id
+ when "open"
+ @openDialog "FileDialog", { title: __("Select a zip file"), mimes: ["application/zip"] }
+ .then (d) =>
+ @openar(d.file.path.asFileHandle())
+ .catch (e) => @error e.toString(), e
+ when "save"
+ return @write() if @currfile.path isnt "Untitled"
+ @saveZipAs()
+
+ when "saveas"
+ @saveZipAs()
+
+ menu: () ->
+ [
+ {
+ text: "__(File)",
+ nodes: [
+ { text: "__(New)", id:"new", shortcut: "A-N" },
+ { text: "__(Open)", id:"open", shortcut: "A-O"},
+ { text: "__(Save)", id:"save", shortcut: "C-S"},
+ { text: "__(Save as)", id:"saveas", shortcut: "A-S"}
+ ],
+ onchildselect: (e) => @fileMenuHandle e.data.item.data.id
+ }
+ ]
+
+ cleanup: (e) ->
+ return unless @currfile.dirty
+ e.preventDefault()
+ @ask { title: "__(Quit)", text: "__(Zip file has been modified. Quit without saving?)" }
+ .then (d) =>
+ return unless d
+ @currfile.dirty = false
+ @quit()
+
+Archive.dependencies = [
+ "os://scripts/jszip.min.js"
+]
+
+this.OS.register "Archive", Archive
\ No newline at end of file
diff --git a/Archive/css/main.css b/Archive/css/main.css
new file mode 100644
index 0000000..9fd300e
--- /dev/null
+++ b/Archive/css/main.css
@@ -0,0 +1,19 @@
+afx-app-window[data-id="Archive"] afx-tree-view .afx-tree-view-folder-close:before{
+ content: "\f07b";
+ font-family: "FontAwesome";
+ color:#76D2F9;
+ font-size: 16px;
+}
+afx-app-window[data-id="Archive"] afx-tree-view .afx-tree-view-folder-open:before{
+ content: "\f07c";
+ font-family: "FontAwesome";
+ color:#76D2F9;
+ font-size: 16px;
+}
+afx-app-window[data-id="Archive"] afx-tree-view .afx-tree-view-item:before{
+ content: "\f016";
+ font-family: "FontAwesome";
+ font-size: 16px;
+ font-style: normal;
+ font-weight: normal;
+}
diff --git a/Archive/package.json b/Archive/package.json
new file mode 100644
index 0000000..3c8e497
--- /dev/null
+++ b/Archive/package.json
@@ -0,0 +1,15 @@
+{
+ "app":"Archive",
+ "name":"Archive",
+ "pkgname": "Archive",
+ "description":"Create of extract zip archive",
+ "info":{
+ "author": "Xuan Sang LE",
+ "email": "mrsang@lxsang.me"
+ },
+ "version":"0.0.1-a",
+ "category":"Other",
+ "iconclass":"fa fa-archive",
+ "mimes":["application/zip"],
+ "locale": {}
+}
\ No newline at end of file
diff --git a/Archive/project.json b/Archive/project.json
new file mode 100644
index 0000000..6264e0e
--- /dev/null
+++ b/Archive/project.json
@@ -0,0 +1,8 @@
+{
+ "name": "Archive",
+ "root": "home://workspace/antosdk-apps/Archive",
+ "css": ["css/main.css"],
+ "javascripts": [],
+ "coffees": ["coffees/main.coffee"],
+ "copies": ["assets/scheme.html", "package.json", "README.md"]
+}
\ No newline at end of file
diff --git a/packages.json b/packages.json
index 0ff1705..90d8df8 100644
--- a/packages.json
+++ b/packages.json
@@ -17,6 +17,15 @@
"version": "0.0.6-a",
"download": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/ActivityMonitor/build/release/ActivityMonitor.zip"
},
+ {
+ "pkgname": "Archive",
+ "name": "Archive",
+ "description": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/Archive/README.md",
+ "category": "Other",
+ "author": "Xuan Sang LE",
+ "version": "0.0.1-a",
+ "download": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/Archive/build/release/Archive.zip"
+ },
{
"pkgname": "Blogger",
"name": "Blogging application",