2 Commits

Author SHA1 Message Date
23b64bef4a Merge branch 'origin/2.0.x' to master
All checks were successful
gitea-sync/antosdk-apps/pipeline/head This commit looks good
2024-04-29 11:43:45 +02:00
c15318f31b update to latest backend changes 2023-02-17 12:37:26 +01:00
33 changed files with 46 additions and 350 deletions

View File

@ -1,14 +0,0 @@
{
"name": "Blogger",
"css": ["main.css"],
"javascripts": [],
"coffees": ["main.coffee", "dialogs.coffee", "tags.coffee"],
"copies": [
"scheme.html",
"cvsection.html",
"api/sendmail.lua",
"sendmail.html",
"package.json",
"README.md"
]
}

View File

@ -1,5 +0,0 @@
<afx-app-window apptitle="DBDecoder" width="500" height="400" data-id="DBDecoder">
<afx-hbox >
<afx-button data-id="decoder" text="GO"></afx-button>
</afx-hbox>
</afx-app-window>

View File

@ -1,15 +0,0 @@
# DBDecoder
This is an example project, generated by AntOS Development Kit
## Howto
Use the CodePad command palette to access to the SDK functionalities:
1. Create new project
2. Init the project from the current folder located in side bar
3. Build and run the project
4. Release the project in zip package
## Set up build target
Open the `project.json` file from the current project tree and add/remove
build target entries. Save the file

View File

@ -1,54 +0,0 @@
(function() {
var DBDecoder;
DBDecoder = class DBDecoder extends this.OS.application.BaseApplication {
constructor(args) {
super("DBDecoder", args);
}
main() {
var bt;
bt = this.find("decoder");
this.db = new this._api.DB("blogs");
return bt.onbtclick = (e) => {
// decode the database
return this.db.find("1=1").then((data) => {
var i, len, v;
for (i = 0, len = data.length; i < len; i++) {
v = data[i];
v.content = atob(v.content);
v.rendered = atob(v.rendered);
}
return this.saveDB(data).then(() => {
return this.notify("Data base saved");
}).catch((e) => {
return this.error(e.toString(), e);
});
});
};
}
saveDB(list) {
return new Promise((resolve, reject) => {
var record;
if (list.length === 0) {
return resolve();
}
record = list.shift();
return this.db.save(record).then(() => {
return this.saveDB(list).then(() => {
return resolve();
}).catch((e) => {
return reject(__e(e));
});
}).catch((e) => {
return reject(__e(e));
});
});
}
};
this.OS.register("DBDecoder", DBDecoder);
}).call(this);

View File

@ -1,14 +0,0 @@
{
"app":"DBDecoder",
"name":"DBDecoder",
"description":"DBDecoder",
"info":{
"author": "",
"email": ""
},
"version":"0.0.2-a",
"category":"Other",
"iconclass":"fa fa-adn",
"mimes":["none"],
"locale": {}
}

View File

@ -1,5 +0,0 @@
<afx-app-window apptitle="DBDecoder" width="500" height="400" data-id="DBDecoder">
<afx-hbox >
<afx-button data-id="decoder" text="GO"></afx-button>
</afx-hbox>
</afx-app-window>

View File

@ -1,29 +0,0 @@
class DBDecoder extends this.OS.application.BaseApplication
constructor: ( args ) ->
super "DBDecoder", args
main: () ->
bt = @find "decoder"
@db = new @_api.DB("blogs")
bt.onbtclick = (e) =>
# decode the database
@db.find("1=1").then (data) =>
for v in data
v.content = atob(v.content)
v.rendered = atob(v.rendered)
@saveDB(data).then () =>
@notify "Data base saved"
.catch (e) => @error e.toString(), e
saveDB: (list) ->
new Promise (resolve, reject) =>
return resolve() if list.length is 0
record = list.shift()
@db.save(record).then () =>
@saveDB(list)
.then () => resolve()
.catch (e) => reject __e e
.catch (e) => reject __e e
this.OS.register "DBDecoder", DBDecoder

View File

@ -1,14 +0,0 @@
{
"app":"DBDecoder",
"name":"DBDecoder",
"description":"DBDecoder",
"info":{
"author": "",
"email": ""
},
"version":"0.0.2-a",
"category":"Other",
"iconclass":"fa fa-adn",
"mimes":["none"],
"locale": {}
}

View File

@ -1,7 +0,0 @@
{
"name": "DBDecoder",
"css": [],
"javascripts": [],
"coffees": ["coffees/main.coffee"],
"copies": ["assets/scheme.html", "package.json", "README.md"]
}

View File

@ -1,24 +0,0 @@
afx-app-window[data-id = "Docify"] .header .label-text
{
font-weight: bold;
}
div[data-id = "preview-container"]
{
overflow: auto;
display: block;
}
canvas[data-id = "preview-canvas"]
{
display: block;
margin:0 auto;
}
afx-app-window[data-id = "DocifyPrintDialog"] i.label-text {
font-weight: bold;
}
afx-app-window[data-id = "DocifyPrintDialog"] input[type="radio"] {
margin: 0;
height: 12px;
margin-left: 10px;
}

View File

@ -9,9 +9,6 @@ It support a wide range of documents.
![https://github.com/lxsang/antosdk-apps/blob/master/LibreOffice/libreoffice.png?raw=true](https://github.com/lxsang/antosdk-apps/blob/master/LibreOffice/libreoffice.png?raw=true) ![https://github.com/lxsang/antosdk-apps/blob/master/LibreOffice/libreoffice.png?raw=true](https://github.com/lxsang/antosdk-apps/blob/master/LibreOffice/libreoffice.png?raw=true)
## Change log ## Change log
- v 0.1.6-a:
* fix document exporting does not work
* fix missing toolbar icons
- v 0.1.5-a: - v 0.1.5-a:
* allow user to configure document server URI * allow user to configure document server URI
- v 0.1.4-a: - v 0.1.4-a:

View File

@ -13,20 +13,9 @@ local result = function(data)
end end
local error = function(msg) local error = function(msg)
LOG_ERROR("Error: "..msg)
return {error = msg, result = false} return {error = msg, result = false}
end end
local decode_path = function(str)
local encoded = str:gsub("%.(.*)$", ""):gsub('_', '/'):gsub('-', '+')
if #encoded % 4 == 2 then
encoded = encoded.."=="
elseif #encoded %4 == 3 then
encoded = encoded.."="
end
return tostring(enc.b64decode(encoded))
end
local fetch = function(url) local fetch = function(url)
local https = require('ssl.https') local https = require('ssl.https')
local body, code, headers = https.request(url) local body, code, headers = https.request(url)
@ -74,19 +63,15 @@ end
handle.file = function(data) handle.file = function(data)
local rq = REQUEST.r local rq = REQUEST.r
LOG_INFO("Request Data:"..JSON.encode(data))
if not rq then if not rq then
return error("Unknown request") return error("Unknown request")
end end
local ret, stat = vfs.fileinfo(data.file) local ret, stat = vfs.fileinfo(data.file)
if not ret then if not ret then
return error("Unable to query file info") return error("Unable to query file info")
end end
local path = vfs.ospath(data.file) local path = vfs.ospath(data.file)
LOG_INFO("Check for request GET/POST/or data")
if rq:match("/wopi/files/[^/]*/contents$") then if rq:match("/wopi/files/[^/]*/contents$") then
LOG_INFO("/wopi/files/[^/]*/contents$")
if REQUEST.method == "GET" then if REQUEST.method == "GET" then
std.sendFile(path) std.sendFile(path)
return nil return nil
@ -99,44 +84,14 @@ handle.file = function(data)
return error("Unknown request method") return error("Unknown request method")
end end
elseif rq:match("/wopi/files/[^/]*$") then elseif rq:match("/wopi/files/[^/]*$") then
LOG_INFO("Matching /wopi/files/[^/]*$") return {
if REQUEST.method == "GET" then BaseFileName = stat.name,
return { Size = math.floor(stat.size),
BaseFileName = stat.name, UserCanWrite = vfs.checkperm(data.file,"write"),
Size = math.floor(stat.size), mime = stat.mime,
UserCanWrite = vfs.checkperm(data.file,"write"), PostMessageOrigin = "*",
mime = stat.mime, UserCanNotWriteRelative = false
PostMessageOrigin = "*", }
UserCanNotWriteRelative = false
}
elseif REQUEST.method == "POST" then
LOG_INFO("Checking for header X_WOPI_SUGGESTEDTARGET")
local out_file = HEADER['X_WOPI_SUGGESTEDTARGET']
if not out_file then
return error("Unknown request")
end
LOG_INFO("Encoded path:"..out_file)
local dpath = decode_path(out_file)
LOG_INFO("Decoded path:"..dpath)
out_file = vfs.ospath(dpath)
LOG_INFO("Save file to:"..out_file)
local barr = REQUEST["application/octet-stream"]
barr:fileout(out_file)
local ret, stat = vfs.fileinfo(out_file)
if not ret then
return error("Unable to save file")
end
return {
BaseFileName = stat.name,
Size = math.floor(stat.size),
UserCanWrite = vfs.checkperm(data.file,"write"),
mime = stat.mime,
PostMessageOrigin = "*",
UserCanNotWriteRelative = false
}
else
return error("Unknown request method")
end
else else
return error("Unknown request") return error("Unknown request")
end end

View File

@ -52,9 +52,7 @@
"api", "api",
"main.css", "main.css",
"templates", "templates",
"icons/icon.png", "icon.png"
"icons/new.png",
"icons/open.png"
], ],
"dest":"build/debug" "dest":"build/debug"
} }

View File

@ -9,9 +9,6 @@ It support a wide range of documents.
![https://github.com/lxsang/antosdk-apps/blob/master/LibreOffice/libreoffice.png?raw=true](https://github.com/lxsang/antosdk-apps/blob/master/LibreOffice/libreoffice.png?raw=true) ![https://github.com/lxsang/antosdk-apps/blob/master/LibreOffice/libreoffice.png?raw=true](https://github.com/lxsang/antosdk-apps/blob/master/LibreOffice/libreoffice.png?raw=true)
## Change log ## Change log
- v 0.1.6-a:
* fix document exporting does not work
* fix missing toolbar icons
- v 0.1.5-a: - v 0.1.5-a:
* allow user to configure document server URI * allow user to configure document server URI
- v 0.1.4-a: - v 0.1.4-a:

View File

@ -13,20 +13,9 @@ local result = function(data)
end end
local error = function(msg) local error = function(msg)
LOG_ERROR("Error: "..msg)
return {error = msg, result = false} return {error = msg, result = false}
end end
local decode_path = function(str)
local encoded = str:gsub("%.(.*)$", ""):gsub('_', '/'):gsub('-', '+')
if #encoded % 4 == 2 then
encoded = encoded.."=="
elseif #encoded %4 == 3 then
encoded = encoded.."="
end
return tostring(enc.b64decode(encoded))
end
local fetch = function(url) local fetch = function(url)
local https = require('ssl.https') local https = require('ssl.https')
local body, code, headers = https.request(url) local body, code, headers = https.request(url)
@ -74,19 +63,15 @@ end
handle.file = function(data) handle.file = function(data)
local rq = REQUEST.r local rq = REQUEST.r
LOG_INFO("Request Data:"..JSON.encode(data))
if not rq then if not rq then
return error("Unknown request") return error("Unknown request")
end end
local ret, stat = vfs.fileinfo(data.file) local ret, stat = vfs.fileinfo(data.file)
if not ret then if not ret then
return error("Unable to query file info") return error("Unable to query file info")
end end
local path = vfs.ospath(data.file) local path = vfs.ospath(data.file)
LOG_INFO("Check for request GET/POST/or data")
if rq:match("/wopi/files/[^/]*/contents$") then if rq:match("/wopi/files/[^/]*/contents$") then
LOG_INFO("/wopi/files/[^/]*/contents$")
if REQUEST.method == "GET" then if REQUEST.method == "GET" then
std.sendFile(path) std.sendFile(path)
return nil return nil
@ -99,44 +84,14 @@ handle.file = function(data)
return error("Unknown request method") return error("Unknown request method")
end end
elseif rq:match("/wopi/files/[^/]*$") then elseif rq:match("/wopi/files/[^/]*$") then
LOG_INFO("Matching /wopi/files/[^/]*$") return {
if REQUEST.method == "GET" then BaseFileName = stat.name,
return { Size = math.floor(stat.size),
BaseFileName = stat.name, UserCanWrite = vfs.checkperm(data.file,"write"),
Size = math.floor(stat.size), mime = stat.mime,
UserCanWrite = vfs.checkperm(data.file,"write"), PostMessageOrigin = "*",
mime = stat.mime, UserCanNotWriteRelative = false
PostMessageOrigin = "*", }
UserCanNotWriteRelative = false
}
elseif REQUEST.method == "POST" then
LOG_INFO("Checking for header X_WOPI_SUGGESTEDTARGET")
local out_file = HEADER['X_WOPI_SUGGESTEDTARGET']
if not out_file then
return error("Unknown request")
end
LOG_INFO("Encoded path:"..out_file)
local dpath = decode_path(out_file)
LOG_INFO("Decoded path:"..dpath)
out_file = vfs.ospath(dpath)
LOG_INFO("Save file to:"..out_file)
local barr = REQUEST["application/octet-stream"]
barr:fileout(out_file)
local ret, stat = vfs.fileinfo(out_file)
if not ret then
return error("Unable to save file")
end
return {
BaseFileName = stat.name,
Size = math.floor(stat.size),
UserCanWrite = vfs.checkperm(data.file,"write"),
mime = stat.mime,
PostMessageOrigin = "*",
UserCanNotWriteRelative = false
}
else
return error("Unknown request method")
end
else else
return error("Unknown request") return error("Unknown request")
end end

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -7,7 +7,7 @@
"author": "Dany LE", "author": "Dany LE",
"email": "contact@iohub.dev" "email": "contact@iohub.dev"
}, },
"version":"0.1.6-b", "version":"0.1.5-a",
"category":"Office", "category":"Office",
"icon":"icon.png", "icon":"icon.png",
"mimes":[ "mimes":[

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -2,8 +2,8 @@ namespace OS {
export namespace application { export namespace application {
const BUTTON_ICONS = { const BUTTON_ICONS = {
opendoc: "pkg://LibreOffice/open.png".asFileHandle().getlink(), opendoc: "data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAD8UlEQVR42q1Va2gcVRQ+587s7mRItNskfaY1trRi2sZHNYoQHzGmQkAMlNZK1R+l0R+K/REhRAmzIEElaqX2R/tDhZamov4QbV0a6V9Nm9DiI8QEEtpI8zDbPPfh7sw93juzOzv7Ugge2L3nztz7fed859w7CHmGiKocYHVGwswcvLwFWnNzc3tLS8sjaTIvcX4kBZvD4fBAf3//aeEmShGUd3Z2nu7q6noBHZPABOjA2/9ijs4+wiwrydehUKivp6fnqPCjOQSHPhrTmKI2XP/uA33/k9s6Ot58rSkNnvPzZlKQkTDDMM4XJXjp08m3aiqV9xUex3sqZqCtqZ6QMSeDdKQeEoISGQiCvo9PfnY0tjBVQNC9d7vf0Fkcq2AK9jXeR8wrEQB6Ii8gkJWV/skz4ZlLI8posKbOQmQfnnl9y/f2gsMnbnbvuctnaCwOm9QZaH3ifmAlpCklj7RvLw/Bn8lqSPAy+PVGKnT2ja0hN4MdmxTDDzGs1Wbh+aYHXVm8kebNM5m4En3TfxXGY1WUBB3HblmGyCBLsLUaDZViuLN8DvY/s9cL+p8SZQi+DF+BkaW1ZKKON/+iLIGUaEMQDMZjsHtNBA7ue6hAnlISCfldv++HK/DL7SBwpsP0PGQlOnh8onttBRpgruDD6xbgcGtDTgb5EkmXc27XVozus7MXr8Lg7BoCtRznlyF0/litgfUdAxcCqnovAK/l3IRKnWBz9Z155ZMZQEHUcnRcZz41twyROAJj8rZhN5KmOYxtvcMLLz5egxanCimrIiT2qUpBAT1sztzGppw1psXBIlkj0aQIsYHRSAqffnd4fOeWDRoRXy8iYgGBXab5PKDu1SAxwQXOji5BPGlCirNMB94em5xdxGffGx3as23zRotbLoGu+XMzQEgDOagZAqE/SplknUzLwtjfFqDiJ9kTDNn8bxO3JvC53vEfG+o21okF68VWTBN4znnaIUpzkJ2JBBZ1BlVVIJlMwUrCBKb43E2MscXBkekhPHBi8qvHdlc3Eqd1Ygtqoj56ma94Dci5FmT3ZEZRO4jGUwTMZzebXCIlFXkt/Tw8dwlfPjV9qrE+2MYYVckMHAK/q3uGwBN9uj3JliUaN4UsPoK8QyeT+un3hXPY/nmk59FdFa8GfBSUb/2MQ1lALXrXULYOIMDB0bz4BzCaoMTgSPQ4Hju31LHrbu0dXaM7ZDmJmxmof29T+wCq6QNCdmtmw0BcXOHJPyaTb2PvxciRYGX5J5wsvQRocYLSa5yTbiGPLifasfXAkada2g59kTKpuC6rNJ+C1uULX78iPlxKgHPrAfGssqiYqzOZRURRlGv/F2BJ+wdsRP1yLA0KOQAAAABJRU5ErkJggg==",
newdoc: "pkg://LibreOffice/new.png".asFileHandle().getlink() newdoc: "data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABmklEQVRIie2Vv0tbURTHP+fmJTGpIYM/KEoUHItOEtIu6uTk2r9BcNMWuro62X9D6OTWIhp00baLCI2giKKYVoMtJtTE93JPhyLSpyavLxE69Due+73nwzn3nnvhkSX+wNut2pugm9XapdkXicO/A3ysaZDk3UnD9yt77KETs9mOg4d8Jkiy+9TTCc8HIpmoSH7xU3Wo7QCA3k5DLtMY0hIgCKRlwB8QI6v+NSds0sKZpXBm/eFBf6AtFTTSf0BThT5kvxRw620G1FUREQxwUraUa3c9LbXoa0XZLbkUSpYBd4Pp+Bz6eTQJoPo7d0sVVD14FZ8mbc5JU8IRF6/CnpsfSXlr5iVsvw9VQa0Ox5eWRP0b/ZF9uqSII+7Nch+QwthJVSTUc31aUXqvN5npeE2Uexp/q5VQFfQkhCN9hquxZtb1f3PQzq+UQflCVK6bWcdCAboSQtHJMf/zHVV98pBt0Rnfmbx7TYWFZoC4A5m0oVh+mr2gP5viIpbUH7GIeAKcAilUPogQ6H9vqJuB8taGp9z88LJ/0H4BR3aNW1eB/4cAAAAASUVORK5CYII="
} }
const DEFAULT_LOO_URL = "https://loo.iohub.dev/hosting/discovery"; const DEFAULT_LOO_URL = "https://loo.iohub.dev/hosting/discovery";
/** /**
@ -163,7 +163,6 @@ namespace OS {
return; return;
let orgevt = e.originalEvent; let orgevt = e.originalEvent;
let data = JSON.parse(orgevt.data); let data = JSON.parse(orgevt.data);
console.log("receive data", data);
switch(data.MessageId) switch(data.MessageId)
{ {
case 'Action_Load_Resp': case 'Action_Load_Resp':
@ -221,7 +220,7 @@ namespace OS {
} }
break; break;
case "UI_SaveAs": case "UI_SaveAs":
this.check_dirty().then((_)=>this.save_as(data.Values.format)); this.check_dirty().then((_)=>this.save_as());
break; break;
default: default:
console.log(data); console.log(data);
@ -229,45 +228,28 @@ namespace OS {
//console.log(this.eid, e); //console.log(this.eid, e);
} }
private save_as(format:string = undefined) private save_as()
{ {
if(!format) { this.openDialog("FileDialog", {
format = this.curr_file.ext;
}
const out_file = `${this.curr_file.parent().path}/${this.curr_file.basename.replace(/\..*$/, '.'+format)}`;
console.log("export file data as ", out_file);
this.openDialog("FileDialog",
{
title: __("Save file as"), title: __("Save file as"),
type: "dir", type: "dir",
file: out_file.asFileHandle() file: this.curr_file.asFileHandle()
}) })
.then(async (d) => .then(async (d) => {
{
const file = `${d.file.path}/${d.name}`.asFileHandle(); const file = `${d.file.path}/${d.name}`.asFileHandle();
try try
{ {
if(this.curr_file.ext == file.ext)
const r = await this.exec({
action: 'duplicate',
args:{src: this.curr_file.path, dest: file.path}
});
if(r.error)
{ {
const r = await this.exec( throw r.error;
{
action: 'duplicate',
args:{src: this.curr_file.path, dest: file.path}
});
if(r.error)
{
throw r.error;
}
this.curr_file = file;
this.open();
}
else
{
this.post_message("Action_SaveAs", {
Filename: `${this.enb64u(file.path)}.${format}`,
Notify: true
});
} }
this.curr_file = file;
this.open();
} }
catch(e) catch(e)
{ {
@ -471,17 +453,13 @@ namespace OS {
args: {file: this.curr_file.path} args: {file: this.curr_file.path}
} }
} }
// encode data to URL safe base64 encoding // encode data to URL safe base64 encoding
let encoded = this.enb64u(JSON.stringify(cmd)); let encoded =
return `${this._api.REST}/system/apigateway/${encoded}/wopi/files/${this.curr_file.path.hash()}&${this.access_token}`; btoa(JSON.stringify(cmd))
}
private enb64u(input: string)
{
return btoa(input)
.trimBy("=") .trimBy("=")
.replace(/\+/g, '-') .replace(/\+/g, '-')
.replace(/\//g, '_'); .replace(/\//g, '_');
return `${this._api.REST}/system/apigateway/${encoded}/wopi/files/${this.curr_file.basename}&${this.access_token}`;
} }
private check_dirty():Promise<any> private check_dirty():Promise<any>
{ {

View File

@ -7,7 +7,7 @@
"author": "Dany LE", "author": "Dany LE",
"email": "contact@iohub.dev" "email": "contact@iohub.dev"
}, },
"version":"0.1.6-b", "version":"0.1.5-a",
"category":"Office", "category":"Office",
"icon":"icon.png", "icon":"icon.png",
"mimes":[ "mimes":[

View File

@ -6,7 +6,7 @@
"name": "batch", "name": "batch",
"data": { "data": {
"target": "release", "target": "release",
"modules": ["LibreOffice"] "modules": ["Blogger"]
} }
} }
] ]

View File

@ -1,8 +1,8 @@
# DBDecoder # AntunnelTestClient
This is an example project, generated by AntOS Development Kit This is an example project, generated by AntOS Development Kit
## Howto ## Howto
Use the CodePad command palette to access to the SDK functionalities: Use the Antedit command palette to access to the SDK functionalities:
1. Create new project 1. Create new project
2. Init the project from the current folder located in side bar 2. Init the project from the current folder located in side bar
@ -11,5 +11,5 @@ Use the CodePad command palette to access to the SDK functionalities:
## Set up build target ## Set up build target
Open the `project.json` file from the current project tree and add/remove Open the `build.json` file from the current project tree and add/remove
build target entries. Save the file build target entries and jobs. Save the file

Binary file not shown.

View File

@ -9,9 +9,6 @@ It support a wide range of documents.
![https://github.com/lxsang/antosdk-apps/blob/master/LibreOffice/libreoffice.png?raw=true](https://github.com/lxsang/antosdk-apps/blob/master/LibreOffice/libreoffice.png?raw=true) ![https://github.com/lxsang/antosdk-apps/blob/master/LibreOffice/libreoffice.png?raw=true](https://github.com/lxsang/antosdk-apps/blob/master/LibreOffice/libreoffice.png?raw=true)
## Change log ## Change log
- v 0.1.6-a:
* fix document exporting does not work
* fix missing toolbar icons
- v 0.1.5-a: - v 0.1.5-a:
* allow user to configure document server URI * allow user to configure document server URI
- v 0.1.4-a: - v 0.1.4-a:

Binary file not shown.

File diff suppressed because one or more lines are too long