switch to es6 from coffeescript

This commit is contained in:
Xuan Sang LE
2020-05-24 13:17:59 +02:00
parent f11509120e
commit 759cd1fc6f
122 changed files with 10658 additions and 8702 deletions

View File

@ -0,0 +1,195 @@
/*
* decaffeinate suggestions:
* DS101: Remove unnecessary use of Array.from
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
CodePad.BaseExtension = class BaseExtension {
constructor(app) {
this.app = app;
}
preload() {
return Ant.OS.API.require(this.dependencies());
}
import(libs) {
return Ant.OS.API.require(libs);
}
basedir() {
return `${this.app.meta().path}/extensions`;
}
notify(m) {
return this.app.notify(m);
}
error(m, e) {
return this.app.error(m, e);
}
dependencies() {
return [];
}
cat(list, data) {
return new Promise((resolve, reject) => {
if (list.length === 0) { return resolve(data); }
const file = (list.splice(0, 1))[0].asFileHandle();
return file
.read()
.then(text => {
data = data + "\n" + text;
return this.cat(list, data)
.then(d => resolve(d))
.catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
});
}
copy(files, to) {
return new Promise((resolve, reject) => {
if (files.length === 0) { return resolve(); }
const file = (files.splice(0, 1))[0].asFileHandle();
const tof = `${to}/${file.basename}`.asFileHandle();
return file.onready().then(meta => {
if (meta.type === "dir") {
// copy directory
const desdir = to.asFileHandle();
return desdir.mk(file.basename).then(() => {
// read the dir content
return file.read().then(data => {
const list = (Array.from(data.result).map((v) => v.path));
return this.copy(list, `${desdir.path}/${file.basename}`)
.then(() => {
return this.copy(files, to)
.then(() => resolve())
.catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
} else {
// copy file
return file.read("binary")
.then(data => {
return tof.setCache(new Blob([data], { type: file.info.mime }))
.write(file.info.mime)
.then(d => {
return this.copy(files, to)
.then(() => resolve())
.catch(e => reject(__e(e)));
});
}).catch(e => reject(__e(e)));
}
}).catch(e => reject(__e(e)));
});
}
aradd(list, zip, base) {
return new Promise((resolve, reject) => {
if (list.length === 0) { return resolve(zip); }
const path = (list.splice(0, 1))[0];
const file = path.asFileHandle();
return file.onready().then(meta => {
if (meta.type === "dir") {
return file.read().then(d => {
const l = (Array.from(d.result).map((v) => v.path));
return this.aradd(l, zip, `${base}${file.basename}/`)
.then(() => {
return this.aradd(list, zip, base)
.then(() => resolve(zip))
.catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
} else {
return file.read("binary").then(d => {
const zpath = `${base}${file.basename}`.replace(/^\/+|\/+$/g, '');
zip.file(zpath, d, { binary: true });
return this.aradd(list, zip, base)
.then(() => resolve(zip))
.catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
}
}).catch(e => reject(__e(e)));
});
}
mkar(src, dest) {
this.notify(__("Preparing for release"));
return new Promise((resolve, reject) => {
return new Promise((r, e) => {
return this.import(["os://scripts/jszip.min.js"]).then(() => src.asFileHandle()
.read().then(d => r(d.result)).catch(ex => e(__e(ex)))).catch(ex => e(__e(ex)));
}).then(files => {
return new Promise((r, e) => {
const zip = new JSZip();
return this.aradd((Array.from(files).map((v) => v.path)), zip, "/")
.then(z => r(z))
.catch(ex => e(__e(ex)));
});
}).then(zip => {
return zip.generateAsync({ type: "base64" }).then(data => {
return dest.asFileHandle()
.setCache('data:application/zip;base64,' + data)
.write("base64").then(r => {
return this.notify(__("Archive is generated at: {0}", dest));
}).catch(e => reject(__e(e)));
});
}).catch(e => reject(__e(e)));
});
}
mkdirAll(list) {
return new Promise((resolve, reject) => {
if (list.length === 0) { return resolve(); }
const path = (list.splice(0, 1))[0].asFileHandle();
return path.parent().mk(path.basename)
.then(d => {
this.app.trigger("filechange", { file: path.parent(), type: "dir" });
return this.mkdirAll(list)
.then(() => resolve())
.catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
});
}
mkfileAll(list, path, name) {
return new Promise((resolve, reject) => {
if (list.length === 0) { return resolve(); }
const item = (list.splice(0, 1))[0];
return `${this.basedir()}/${item[0]}`
.asFileHandle()
.read()
.then(data => {
const file = item[1].asFileHandle();
return file
.setCache(data.format(name, `${path}/${name}`))
.write("text/plain")
.then(() => {
this.app.trigger("filechange", { file, type: "file" });
return this.mkfileAll(list, path, name)
.then(() => resolve())
.catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
});
}
metadata(file) {
return new Promise((resolve, reject) => {
if (!this.app.currdir) {
return reject(this.app._api.throwe(__("Current folder is not found")));
}
return `${this.app.currdir.path}/${file}`
.asFileHandle()
.read("json")
.then(data => resolve(data)).catch(e => {
return reject(this.app._api.throwe(__("Unable to read meta-data")));
});
});
}
};
CodePad.extensions = {};

View File

@ -0,0 +1,98 @@
/*
* decaffeinate suggestions:
* DS101: Remove unnecessary use of Array.from
* DS102: Remove unnecessary code created because of implicit returns
* DS205: Consider reworking code to avoid use of IIFEs
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
class CommandPalette extends this.OS.GUI.BasicDialog {
constructor() {
super("CommandPalete", CommandPalette.scheme);
}
main() {
super.main();
const offset = $(".afx-window-content", this.parent.scheme).offset();
const pw = this.parent.scheme.get("width") / 5;
this.scheme.set("width", 3 * pw);
$(this.scheme).offset({ top: offset.top - 2, left: offset.left + pw });
var cb = e => {
if (($(e.target)).closest(this.scheme).length > 0) {
return $(this.find("searchbox")).focus();
} else {
$(document).unbind("mousedown", cb);
return this.quit();
}
};
$(document).on("mousedown", cb);
$(this.find("searchbox")).focus();
this.cmdlist = this.find("container");
if (this.data) { this.cmdlist.set("data", (Array.from(this.data.child))); }
$(this.cmdlist).click(e => {
return this.selectCommand();
});
this.searchbox = this.find("searchbox");
return ($(this.searchbox)).keyup(e => {
return this.search(e);
});
}
search(e) {
let v;
switch (e.which) {
case 27:
// escape key
this.quit();
if (this.data.parent && this.data.parent.run) { return this.data.parent.run(this.parent); }
break;
case 37:
return e.preventDefault();
case 38:
this.cmdlist.selectPrev();
return e.preventDefault();
case 39:
return e.preventDefault();
case 40:
this.cmdlist.selectNext();
return e.preventDefault();
case 13:
e.preventDefault();
return this.selectCommand();
default:
var text = this.searchbox.value;
if (text.length === 2) { this.cmdlist.set("data", ((() => {
const result1 = [];
for (v of Array.from(this.data.child)) { result1.push(v);
}
return result1;
})())); }
if (text.length < 3) { return; }
var result = [];
var term = new RegExp(text, 'i');
for (v of Array.from(this.data.child)) { if (v.text.match(term)) { result.push(v); } }
return this.cmdlist.set("data", result);
}
}
selectCommand() {
const el = this.cmdlist.get("selectedItem");
if (!el) { return; }
el.set("selected", false);
let result = false;
if (this.handle) { result = this.handle({ data: { item: el } }); }
if (!result) { return this.quit(); }
}
}
CommandPalette.scheme = `\
<afx-app-window data-id = "cmd-win"
apptitle="" minimizable="false"
resizable = "false" width="200" height="200">
<afx-vbox>
<input data-height="25" type = "text" data-id="searchbox"/>
<afx-list-view data-id="container"></afx-list-view>
</afx-vbox>
</afx-app-window>\
`;

View File

@ -1,13 +1,10 @@
coffee_files = coffees/CommandPalette.coffee coffees/main.coffee coffees/BaseExtension.coffee
module_files = CommandPalette.js main.js BaseExtension.js
module_dir = extensions
module_dir_src = coffees/extensions
jsfiles =
libfiles =
cssfiles = css/main.css
copyfiles = assets/scheme.html package.json extensions.json
copyfiles = assets/scheme.html package.json extensions.json extensions
PKG_NAME=CodePad

View File

@ -1,168 +0,0 @@
class CodePad.BaseExtension
constructor: (@app) ->
preload: () ->
Ant.OS.API.require @dependencies()
import: (libs) ->
Ant.OS.API.require libs
basedir: () ->
"#{@app.meta().path}/extensions"
notify: (m) ->
@app.notify m
error: (m, e) ->
@app.error m, e
dependencies: () ->
[]
cat: (list, data) ->
new Promise (resolve, reject) =>
return resolve data if list.length is 0
file = (list.splice 0, 1)[0].asFileHandle()
file
.read()
.then (text) =>
data = data + "\n" + text
@cat list, data
.then (d) -> resolve d
.catch (e) -> reject __e e
.catch (e) -> reject __e e
copy: (files, to) ->
new Promise (resolve, reject) =>
return resolve() if files.length is 0
file = (files.splice 0, 1)[0].asFileHandle()
tof = "#{to}/#{file.basename}".asFileHandle()
file.onready().then (meta) =>
if meta.type is "dir"
# copy directory
desdir = to.asFileHandle()
desdir.mk(file.basename).then () =>
# read the dir content
file.read().then (data) =>
list = (v.path for v in data.result)
@copy list, "#{desdir.path}/#{file.basename}"
.then () =>
@copy files, to
.then () -> resolve()
.catch (e) -> reject __e e
.catch (e) ->
reject __e e
.catch (e) -> reject __e e
.catch (e) ->
reject __e e
else
# copy file
file.read("binary")
.then (data) =>
tof.setCache(new Blob [data], { type: file.info.mime })
.write(file.info.mime)
.then (d) =>
@copy files, to
.then () -> resolve()
.catch (e) -> reject __e e
.catch (e) -> reject __e e
.catch (e) ->
reject __e e
aradd: (list, zip, base) ->
new Promise (resolve, reject) =>
return resolve(zip) if list.length is 0
path = (list.splice 0, 1)[0]
file = path.asFileHandle()
file.onready().then (meta) =>
if meta.type is "dir"
file.read().then (d) =>
l = (v.path for v in d.result)
@aradd l, zip, "#{base}#{file.basename}/"
.then () =>
@aradd list, zip, base
.then () -> resolve(zip)
.catch (e) -> reject __e e
.catch (e) -> reject __e e
.catch (e) -> reject __e e
else
file.read("binary").then (d) =>
zpath = "#{base}#{file.basename}".replace(/^\/+|\/+$/g, '')
zip.file zpath, d, { binary: true }
@aradd list, zip, base
.then () -> resolve(zip)
.catch (e) -> reject __e e
.catch (e) -> reject __e e
.catch (e) -> reject __e e
mkar: (src, dest) ->
@notify __("Preparing for release")
new Promise (resolve, reject) =>
new Promise (r, e) =>
@import(["os://scripts/jszip.min.js"]).then () ->
src.asFileHandle()
.read().then (d) ->
r d.result
.catch (ex) -> e __e ex
.catch (ex) -> e __e ex
.then (files) =>
new Promise (r, e) =>
zip = new JSZip()
@aradd (v.path for v in files), zip, "/"
.then (z) -> r(z)
.catch (ex) -> e __e ex
.then (zip) =>
zip.generateAsync({ type: "base64" }).then (data) =>
dest.asFileHandle()
.setCache('data:application/zip;base64,' + data)
.write("base64").then (r) =>
@notify __("Archive is generated at: {0}", dest)
.catch (e) -> reject __e e
.catch (e) -> reject __e e
mkdirAll: (list) ->
new Promise (resolve, reject) =>
return resolve() if list.length is 0
path = (list.splice 0, 1)[0].asFileHandle()
path.parent().mk path.basename
.then (d) =>
@app.trigger "filechange", { file: path.parent(), type: "dir" }
@mkdirAll list
.then () -> resolve()
.catch (e) -> reject __e e
.catch (e) -> reject __e e
mkfileAll: (list, path, name) ->
new Promise (resolve, reject) =>
return resolve() if list.length is 0
item = (list.splice 0, 1)[0]
"#{@basedir()}/#{item[0]}"
.asFileHandle()
.read()
.then (data) =>
file = item[1].asFileHandle()
file
.setCache(data.format name, "#{path}/#{name}")
.write "text/plain"
.then () =>
@app.trigger "filechange", { file: file, type: "file" }
@mkfileAll list, path, name
.then () -> resolve()
.catch (e) -> reject __e e
.catch (e) -> reject __e e
.catch (e) -> reject __e e
metadata: (file) ->
new Promise (resolve, reject) =>
if not @app.currdir
return reject @app._api.throwe __("Current folder is not found")
"#{@app.currdir.path}/#{file}"
.asFileHandle()
.read("json")
.then (data) ->
resolve data
.catch (e) =>
reject @app._api.throwe __("Unable to read meta-data")
CodePad.extensions = {}

View File

@ -1,74 +0,0 @@
class CommandPalette extends this.OS.GUI.BasicDialog
constructor: () ->
super "CommandPalete", CommandPalette.scheme
main: () ->
super.main()
offset = $(".afx-window-content", @parent.scheme).offset()
pw = @parent.scheme.get("width") / 5
@scheme.set "width", 3 * pw
$(@scheme).offset { top: offset.top - 2, left: offset.left + pw }
cb = (e) =>
if ($ e.target).closest(@scheme).length > 0
$(@find "searchbox").focus()
else
$(document).unbind "mousedown", cb
@quit()
$(document).on "mousedown", cb
$(@find "searchbox").focus()
@cmdlist = @find("container")
@cmdlist.set "data", (v for v in @data.child) if @data
$(@cmdlist).click (e) =>
@selectCommand()
@searchbox = @find "searchbox"
($ @searchbox).keyup (e) =>
@search e
search: (e) ->
switch e.which
when 27
# escape key
@quit()
@data.parent.run(@parent) if @data.parent and @data.parent.run
when 37
e.preventDefault()
when 38
@cmdlist.selectPrev()
e.preventDefault()
when 39
e.preventDefault()
when 40
@cmdlist.selectNext()
e.preventDefault()
when 13
e.preventDefault()
@selectCommand()
else
text = @searchbox.value
@cmdlist.set "data", (v for v in @data.child) if text.length is 2
return if text.length < 3
result = []
term = new RegExp text, 'i'
result.push v for v in @data.child when v.text.match term
@cmdlist.set "data", result
selectCommand: () ->
el = @cmdlist.get "selectedItem"
return unless el
el.set "selected", false
result = false
result = @handle { data: { item: el } } if @handle
return @quit() unless result
CommandPalette.scheme = """
<afx-app-window data-id = "cmd-win"
apptitle="" minimizable="false"
resizable = "false" width="200" height="200">
<afx-vbox>
<input data-height="25" type = "text" data-id="searchbox"/>
<afx-list-view data-id="container"></afx-list-view>
</afx-vbox>
</afx-app-window>
"""

View File

@ -1,176 +0,0 @@
# import the CodePad application module
App = this.OS.APP.CodePad
# define the extension
class App.extensions.AntOSDK extends App.BaseExtension
constructor: (app) ->
super app
# public functions
create: () ->
@app.openDialog("FileDialog", {
title: "__(New Project at)",
file: { basename: __("ProjectName") },
mimes: ["dir"]
}).then (d) =>
@mktpl d.file.path, d.name, true
init: () ->
dir = @app.currdir
return @create() unless dir and dir.basename
dir.read()
.then (d) =>
return @notify __("Cannot read folder: {0}", dir.path) if d.error
return @notify __("The folder is not empty: {0}", dir.path) unless d.result.length is 0
@mktpl dir.parent().path, dir.basename
buildnrun: () ->
@metadata("project.json").then (meta) =>
@build(meta, true).then () =>
@run(meta).catch (e) => @error __("Unable to run project"), e
.catch (e) =>
@error __("Unable to build project"), e
.catch (e) => @error __("Unable to read meta-data"), e
release: () ->
@metadata("project.json").then (meta) =>
@build(meta, false).then () =>
@mkar("#{meta.root}/build/debug", "#{meta.root}/build/release/#{meta.name}.zip")
.then () ->
.catch (e) => @error __("Unable to create package archive"), e
.catch (e) =>
@error __("Unable to build project"), e
.catch (e) => @error __("Unable to read meta-data"), e
# private functions
mktpl: (path, name, flag) ->
rpath = "#{path}/#{name}"
dirs = [
"#{rpath}/javascripts",
"#{rpath}/css",
"#{rpath}/coffees",
"#{rpath}/assets"
]
dirs.unshift rpath if flag
files = [
["templates/sdk-main.tpl", "#{rpath}/coffees/main.coffee"],
["templates/sdk-package.tpl", "#{rpath}/package.json"],
["templates/sdk-project.tpl", "#{rpath}/project.json"],
["templates/sdk-README.tpl", "#{rpath}/README.md"],
["templates/sdk-scheme.tpl", "#{rpath}/assets/scheme.html"]
]
@mkdirAll dirs
.then () =>
@mkfileAll(files, path, name)
.then () =>
@app.currdir = rpath.asFileHandle()
@app.initSideBar()
@app.openFile "#{rpath}/README.md".asFileHandle()
.catch (e) => @error __("Unable to create template files"), e
.catch (e) => @error __("Unable to create project directory"), e
verify: (list) ->
new Promise (resolve, reject) =>
return resolve() if list.length is 0
file = (list.splice 0, 1)[0].asFileHandle()
@notify __("Verifying: {0}", file.path)
file.read().then (data) =>
try
CoffeeScript.nodes data
@verify list
.then () -> resolve()
.catch (e) -> reject __e e
catch ex
reject __e ex
.catch (e) -> reject __e e
compile: (meta) ->
new Promise (resolve, reject) =>
@import([
"#{@basedir()}/coffeescript.js",
"#{@basedir()}/terser.min.js"
]).then () =>
list = ("#{meta.root}/#{v}" for v in meta.coffees)
@verify((f for f in list)).then () =>
@cat(list).then (code) =>
jsrc = CoffeeScript.compile code
@notify __("Compiled successful")
resolve jsrc
.catch (e) -> reject __e e
.catch (e) -> reject __e e
.catch (e) -> reject __e e
build: (meta, debug) ->
dirs = [
"#{meta.root}/build",
"#{meta.root}/build/debug",
"#{meta.root}/build/release"
]
new Promise (resolve, reject) =>
@mkdirAll(dirs).then =>
@compile(meta).then (src) =>
@cat ("#{meta.root}/#{v}" for v in meta.javascripts), src
.then (jsrc) ->
new Promise (r, e) ->
code = jsrc
if not debug
options = {
toplevel: true,
compress: {
passes: 3,
#pure_getters: true,
#unsafe: true,
},
mangle: true,
output: {
#beautify: true,
},
}
result = Terser.minify(jsrc, options)
if result.error
@notify __("Unable to minify code: {0}", result.error)
else
code = result.code
"#{meta.root}/build/debug/main.js"
.asFileHandle()
.setCache code
.write("text/plain")
.then (d) ->
r()
.catch (ex) -> e __e ex
.then () =>
new Promise (r, e) =>
@cat ("#{meta.root}/#{v}" for v in meta.css), ""
.then (txt) ->
return r() if txt is ""
"#{meta.root}/build/debug/main.css"
.asFileHandle()
.setCache txt
.write("text/plain")
.then (d) ->
r()
.catch (ex) -> e __e ex
.then () =>
@copy ("#{meta.root}/#{v}" for v in meta.copies), "#{meta.root}/build/debug"
.then () -> resolve()
.catch (e) -> reject __e e
.catch (e) -> reject __e e
.catch (e) -> reject __e e
run: (meta) ->
"#{meta.root}/build/debug/package.json"
.asFileHandle()
.read("json")
.then (v) =>
v.text = v.name
v.path = "#{meta.root}/build/debug"
v.filename = meta.name
v.type = "app"
v.mime = "antos/app"
v.icon = "#{v.path}/#{v.icon}" if v.icon
v.iconclass = "fa fa-adn" unless v.iconclass or v.icon
@notify __("Installing...")
@app.systemsetting.system.packages[meta.name] = v
@notify __("Running {0}...", meta.name)
@app._gui.forceLaunch meta.name

View File

@ -1,217 +0,0 @@
# import the CodePad application module
App = this.OS.APP.CodePad
# define the extension
class App.extensions.ExtensionMaker extends App.BaseExtension
constructor: (app) ->
super app
# public functions
create: () ->
@app.openDialog("FileDialog", {
title: "__(New CodePad extension at)",
file: { basename: __("ExtensionName") },
mimes: ["dir"]
}).then (d) =>
@mktpl d.file.path, d.name
buildnrun: () ->
@metadata("extension.json").then (meta) =>
@build(meta).then () =>
@run(meta).catch (e) => @error __("Unable to run extension"), e
.catch (e) =>
@error __("Unable to build extension"), e
.catch (e) => @error __("Unable to read meta-data"), e
release: () ->
@metadata("extension.json").then (meta) =>
@build(meta).then () =>
@mkar("#{meta.root}/build/debug",
"#{meta.root}/build/release/#{meta.meta.name}.zip")
.then () ->
.catch (e) => @error __("Unable to create archive"), e
.catch (e) =>
@error __("Unable to build extension"), e
.catch (e) => @error __("Unable to read meta-data"), e
install: () ->
@app.openDialog("FileDialog", {
title: "__(Select extension archive)",
mimes: [".*/zip"]
}).then (d) =>
@installZip d.file.path
.then () =>
@notify __("Extension installed")
@app.loadExtensionMetaData()
.catch (e) => @error __("Unable to install extension"), e
# private functions
mktpl: (path, name) ->
rpath = "#{path}/#{name}"
dirs = [
rpath,
"#{rpath}/build",
"#{rpath}/build/release",
"#{rpath}/build/debug"
]
files = [
["templates/ext-main.tpl", "#{rpath}/#{name}.coffee"],
["templates/ext-extension.tpl", "#{rpath}/extension.json"],
]
@mkdirAll dirs
.then () =>
@mkfileAll(files, path, name)
.then () =>
@app.currdir = rpath.asFileHandle()
@app.initSideBar()
@app.openFile "#{rpath}/#{name}.coffee".asFileHandle()
.catch (e) => @error __("Unable to create extension template"), e
.catch (e) => @error __("Unable to create extension directories"), e
verify: (list) ->
new Promise (resolve, reject) =>
return resolve() if list.length is 0
file = (list.splice 0, 1)[0].asFileHandle()
@notify __("Verifying: {0}", file.path)
file.read().then (data) =>
try
CoffeeScript.nodes data
@verify list
.then () -> resolve()
.catch (e) -> reject __e e
catch ex
reject __e ex
.catch (e) -> reject __e e
compile: (meta) ->
new Promise (resolve, reject) =>
@import(["#{@basedir()}/coffeescript.js"]).then () =>
list = ("#{meta.root}/#{v}" for v in meta.coffees)
@verify((f for f in list)).then () =>
@cat(list).then (code) =>
jsrc = CoffeeScript.compile code
@notify __("Compiled successful")
resolve jsrc
.catch (e) -> reject __e e
.catch (e) -> reject __e e
.catch (e) -> reject __e e
build: (meta) ->
new Promise (resolve, reject) =>
@compile(meta).then (src) =>
@cat ("#{meta.root}/#{v}" for v in meta.javascripts), src
.then (jsrc) ->
new Promise (r, e) ->
"#{meta.root}/build/debug/#{meta.meta.name}.js"
.asFileHandle()
.setCache jsrc
.write("text/plain")
.then (d) ->
r()
.catch (ex) -> e __e ex
.then () ->
new Promise (r, e) ->
"#{meta.root}/build/debug/extension.json"
.asFileHandle()
.setCache meta.meta
.write("object")
.then (data) ->
r data
.catch (ex) -> e __e ex
.then () =>
@copy ("#{meta.root}/#{v}" for v in meta.copies), "#{meta.root}/build/debug"
.then () -> resolve()
.catch (e) -> reject __e e
.catch (e) -> reject __e e
run: (meta) ->
new Promise (resolve, reject) =>
path = "#{meta.root}/build/debug/#{meta.meta.name}.js"
delete @app._api.shared[path] if @app._api.shared[path]
@app._api.requires path
.then () =>
if @app.extensions[meta.meta.name]
@app.extensions[meta.meta.name].child = []
@app.extensions[meta.meta.name].addAction v for v in meta.meta.actions
else
@app.extensions[meta.meta.name] = new App.CMDMenu meta.meta.text
@app.extensions[meta.meta.name].name = meta.meta.name
@app.extensions[meta.meta.name].addAction v for v in meta.meta.actions
@app.spotlight.addAction @app.extensions[meta.meta.name]
@app.extensions[meta.meta.name].onchildselect (e) =>
@app.loadAndRunExtensionAction e.data.item.get "data"
@app.spotlight.run @app
resolve()
.catch (e) -> reject __e e
installExtension: (files, zip) ->
new Promise (resolve, reject) =>
idx = files.indexOf "extension.json"
reject(@app._api.throwe __("No meta-data found")) if idx < 0
metafile = (files.splice idx, 1)[0]
# read the meta file
zip.file(metafile).async("uint8array").then (d) =>
meta = JSON.parse(new TextDecoder("utf-8").decode(d))
@installFiles files, zip, meta
.then () -> resolve()
.catch (e) -> reject __e e
.catch (e) -> reject __e e
installFiles: (files, zip, meta) ->
return @installMeta(meta) if files.length is 0
new Promise (resolve, reject) =>
file = (files.splice 0, 1)[0]
path = "#{@basedir()}/#{file}"
zip.file(file).async("uint8array").then (d) =>
path.asFileHandle()
.setCache(new Blob [d], { type: "octet/stream" })
.write("text/plain").then (r) =>
return reject r.error if r.error
@installFiles files, zip, meta
.then () -> resolve()
.catch (e) -> reject __e e
.catch (e) -> reject __e e
.catch (e) -> reject __e e
installMeta: (meta) ->
new Promise (resolve, reject) =>
file = "#{@app.meta().path}/extensions.json".asFileHandle()
file.read("json").then (data) ->
names = (v.name) for v in data
idx = name.indexOf meta.name
data.splice idx, 1 if idx >= 0
data.push meta
file.setCache data
.write("object")
.then () -> resolve()
.catch (e) -> reject __e e
.catch (e) -> reject __e e
installZip: (path) ->
new Promise (resolve, reject) =>
@import(["os://scripts/jszip.min.js"]).then () =>
path.asFileHandle().read("binary").then (data) =>
JSZip.loadAsync(data).then (zip) =>
pth = @basedir()
dir = []
files = []
for name, file of zip.files
if file.dir
dir.push(pth + "/" + name)
else
files.push name
if dir.length > 0
@mkdirAll dir
.then () =>
@installExtension files, zip
.then () -> resolve()
.catch(e) -> reject(__e e)
.catch (e) -> reject __e e
else
@installExtension files, zip
.then () -> resolve()
.catch (e) -> reject(__e e)
.catch (e) -> reject __e e
.catch (e) -> reject __e e
.catch (e) -> reject __e e

View File

@ -1,502 +0,0 @@
Ant = this
class CodePad extends this.OS.GUI.BaseApplication
constructor: (args) ->
super "CodePad", args
@currfile = "Untitled".asFileHandle()
@currdir = undefined
if @args and @args.length > 0
if @args[0].type is "dir"
@currdir = @args[0].path.asFileHandle()
else
@currfile = @args[0].path.asFileHandle()
@currdir = @currfile.parent()
main: () ->
@extensions = {}
@fileview = @find("fileview")
@sidebar = @find("sidebar")
@tabbar = @find "tabbar"
@langstat = @find "langstat"
@editorstat = @find "editorstat"
@fileview.set "fetch", (path) ->
new Promise (resolve, reject) ->
dir = path
dir = path.asFileHandle() if typeof path is "string"
dir.read().then (d) ->
return reject d.error if d.error
resolve d.result
.catch (e) -> reject __e e
@setup()
setup: () ->
ace.config.set('basePath', '/scripts/ace')
ace.require "ace/ext/language_tools"
@editor = ace.edit @find("datarea")
@editor.setOptions {
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true,
highlightActiveLine: true,
highlightSelectedWord: true,
behavioursEnabled: true,
wrap: true,
fontSize: "11pt",
showInvisibles: true
}
#themes = ace.require "ace/ext/themelist"
@editor.setTheme "ace/theme/monokai"
@modes = ace.require "ace/ext/modelist"
@editor.completers.push { getCompletions: ( editor, session, pos, prefix, callback ) -> }
@editor.getSession().setUseWrapMode true
@editormux = false
@editor.on "input", () =>
if @editormux
@editormux = false
return false
if not @currfile.dirty
@currfile.dirty = true
@currfile.text += "*"
@tabbar.update()
@editor.getSession().selection.on "changeCursor", (e) =>
@updateStatus()
@tabbar.set "ontabselect", (e) =>
@selecteTab $(e.data.item).index()
@tabbar.set "ontabclose", (e) =>
it = e.data.item
return false unless it
return @closeTab it unless it.get("data").dirty
@openDialog("YesNoDialog", {
title: __("Close tab"),
text: __("Close without saving ?")
}).then (d) =>
return @closeTab it if d
@editor.focus()
return false
@fileview.set "onfileopen", (e) =>
return unless e.data and e.data.path
return if e.data.type is "dir"
@openFile e.data.path.asFileHandle()
@fileview.set "onfileselect", (e) =>
return unless e.data and e.data.path
return if e.data.type is "dir"
i = @findTabByFile e.data.path.asFileHandle()
return @tabbar.set "selected", i if i isnt -1
@on "resize", () => @editor.resize()
@on "focus", () => @editor.focus()
@spotlight = new CMDMenu __("Command palette")
@bindKey "ALT-P", () => @spotlight.run @
@find("datarea").contextmenuHandle = (e, m) =>
m.set "items", [{
text: __("Command palete"),
onmenuselect: (e) =>
@spotlight.run @
}]
m.show e
@fileview.contextmenuHandle = (e, m) =>
m.set "items", [
{ text: "__(New file)", id: "new" },
{ text: "__(New folder)", id: "newdir" },
{ text: "__(Rename)", id: "rename" },
{ text: "__(Delete)", id: "delete" }
]
m.set "onmenuselect", (e) =>
@ctxFileMenuHandle e
m.show e
@bindKey "ALT-N", () => @menuAction "new"
@bindKey "ALT-O", () => @menuAction "open"
@bindKey "ALT-F", () => @menuAction "opendir"
@bindKey "CTRL-S", () => @menuAction "save"
@bindKey "ALT-W", () => @menuAction "saveas"
@fileview.set "ondragndrop", (e) =>
src = e.data.from.get("data").path.asFileHandle()
des = e.data.to.get("data").path
src.move "#{des}/#{src.basename}"
.then (d) ->
e.data.to.update des
e.data.from.get("parent").update src.parent().path
.catch (e) => @error __("Unable to move file/folder"), e
@on "filechange", (data) =>
path = data.file.path
path = data.file.parent().path if data.type is "file"
@fileview.update path
@loadExtensionMetaData()
@initCommandPalete()
@initSideBar()
@openFile @currfile
openFile: (file) ->
#find tab
i = @findTabByFile file
return @tabbar.set "selected", i if i isnt -1
return @newTab file if file.path.toString() is "Untitled"
file.read()
.then (d) =>
file.cache = d or ""
@newTab file
.catch (e) =>
@error __("Unable to open: {0}", file.path), e
findTabByFile: (file) ->
lst = @tabbar.get "items"
its = ( i for d, i in lst when d.hash() is file.hash() )
return -1 if its.length is 0
return its[0]
newTab: (file) ->
file.text = if file.basename then file.basename else file.path
file.cache = "" unless file.cache
file.um = new ace.UndoManager()
@currfile.selected = false
file.selected = true
#console.log cnt
@tabbar.push file
closeTab: (it) ->
@tabbar.remove it
cnt = @tabbar.get("items").length
if cnt is 0
@openFile "Untitled".asFileHandle()
return false
@tabbar.set "selected", cnt - 1
return false
selecteTab: (i) ->
#return if i is @tabbar.get "selidx"
file = (@tabbar.get "items")[i]
return unless file
@scheme.set "apptitle", file.text.toString()
#return if file is @currfile
if @currfile isnt file
@currfile.cache = @editor.getValue()
@currfile.cursor = @editor.selection.getCursor()
@currfile.selected = false
@currfile = file
if not file.langmode
if file.path.toString() isnt "Untitled"
m = @modes.getModeForPath(file.path)
file.langmode = { caption: m.caption, mode: m.mode }
else
file.langmode = { caption: "Text", mode: "ace/mode/text" }
@editormux = true
@editor.getSession().setUndoManager new ace.UndoManager()
@editor.setValue file.cache, -1
@editor.getSession().setMode file.langmode.mode
if file.cursor
@editor.renderer.scrollCursorIntoView {
row: file.cursor.row, column: file.cursor.column
}, 0.5
@editor.selection.moveTo file.cursor.row, file.cursor.column
@editor.getSession().setUndoManager file.um
@updateStatus()
@editor.focus()
updateStatus: () ->
c = @editor.session.selection.getCursor()
l = @editor.session.getLength()
@editorstat.set "text", __("Row {0}, col {1}, lines: {2}", c.row + 1, c.column + 1, l)
@langstat.set "text", @currfile.langmode.caption
initSideBar: () ->
if @currdir
$(@sidebar).show()
@fileview.set "path", @currdir.path
else
$(@sidebar).hide()
@trigger "resize"
addAction: (action) ->
@spotlight.addAction action
@
addActions: (list) ->
@spotlight.addActions list
@
initCommandPalete: () ->
themes = ace.require "ace/ext/themelist"
cmdtheme = new CMDMenu __("Change theme")
cmdtheme.addAction { text: v.caption, theme: v.theme } for k, v of themes.themesByName
cmdtheme.onchildselect (d, r) ->
data = d.data.item.get("data")
r.editor.setTheme data.theme
r.editor.focus()
@spotlight.addAction cmdtheme
cmdmode = new CMDMenu __("Change language mode")
cmdmode.addAction { text: v.caption, mode: v.mode } for v in @modes.modes
cmdmode.onchildselect (d, r) ->
data = d.data.item.get("data")
r.editor.session.setMode data.mode
r.currfile.langmode = { caption: data.text, mode: data.mode }
r.updateStatus()
r.editor.focus()
@spotlight.addAction cmdmode
@addAction CMDMenu.fromMenu @fileMenu()
loadExtensionMetaData: () ->
"#{@meta().path}/extensions.json"
.asFileHandle()
.read("json")
.then (d) =>
for ext in d
if @extensions[ext.name]
@extensions[ext.name].child = []
@extensions[ext.name].addAction v for v in ext.actions
else
@extensions[ext.name] = new CMDMenu ext.text
@extensions[ext.name].name = ext.name
@extensions[ext.name].addAction v for v in ext.actions
@spotlight.addAction @extensions[ext.name]
@extensions[ext.name].onchildselect (e) =>
@loadAndRunExtensionAction e.data.item.get "data"
.catch (e) =>
@error __("Cannot load extension meta data"), e
runExtensionAction: (name, action) ->
return @error __("Unable to find extension: {0}", name) unless CodePad.extensions[name]
ext = new CodePad.extensions[name](@)
return @error __("Unable to find action: {0}", action) unless ext[action]
ext.preload()
.then () ->
ext[action]()
.catch (e) =>
@error __("Unable to preload extension"), e
loadAndRunExtensionAction: (data) ->
name = data.parent.name
action = data.name
#verify if the extension is load
if not CodePad.extensions[name]
#load the extension
path = "#{@meta().path}/extensions/#{name}.js"
@_api.requires path
.then () => @runExtensionAction name, action
.catch (e) =>
@error __("unable to load extension: {0}", name), e
else
@runExtensionAction name, action
fileMenu: () ->
{
text: __("File"),
child: [
{ text: __("New"), dataid: "new", shortcut: "A-N" },
{ text: __("Open"), dataid: "open", shortcut: "A-O" },
{ text: __("Open Folder"), dataid: "opendir", shortcut: "A-F" },
{ text: __("Save"), dataid: "save", shortcut: "C-S" },
{ text: __("Save as"), dataid: "saveas", shortcut: "A-W" }
],
onchildselect: (e, r) =>
@menuAction e.data.item.get("data").dataid, r
}
ctxFileMenuHandle: (e) ->
el = e.data.item
return unless el
data = el.get("data")
return unless data
file = @fileview.get "selectedFile"
dir = @currdir
dir = file.path.asFileHandle() if file and file.type is "dir"
dir = file.path.asFileHandle().parent() if file and file.type is "file"
switch data.id
when "new"
return unless dir
@openDialog("PromptDialog", {
title: "__(New file)",
label: "__(File name)"
})
.then (d) =>
fp = "#{dir.path}/#{d}".asFileHandle()
fp.write("text/plain")
.then (r) =>
@fileview.update dir.path
.catch (e) =>
@error __("Fail to create: {0}", e.stack), e
when "newdir"
return unless dir
@openDialog("PromptDialog", {
title: "__(New folder)",
label: "__(Folder name)"
})
.then (d) =>
dir.mk(d)
.then (r) =>
@fileview.update dir.path
.catch (e) =>
@error __("Fail to create: {0}", dir.path), e
when "rename"
return unless file
@openDialog("PromptDialog", {
title: "__(Rename)",
label: "__(File name)",
value: file.filename
})
.then (d) =>
return if d is file.filename
file = file.path.asFileHandle()
dir = file.parent()
file.move "#{dir.path}/#{d}"
.then (r) =>
@fileview.update dir.path
.catch (e) =>
@error __("Fail to rename: {0}", file.path), e
when "delete"
return unless file
@openDialog("YesNoDialog", {
title: "__(Delete)",
iconclass: "fa fa-question-circle",
text: __("Do you really want to delete: {0}?", file.filename)
})
.then (d) =>
return unless d
file = file.path.asFileHandle()
dir = file.parent()
file.remove()
.then (r) =>
@fileview.update dir.path
.catch (e) =>
@error __("Fail to delete: {0}", file.path), e
else
save: (file) ->
file.write("text/plain")
.then (d) =>
file.dirty = false
file.text = file.basename
@tabbar.update()
@scheme.set "apptitle", "#{@currfile.basename}"
.catch (e) => @error __("Unable to save file: {0}", file.path), e
saveAs: () ->
@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}"
@save @currfile
menuAction: (dataid, r) ->
me = @
me = r if r
switch dataid
when "new"
me.openFile "Untitled".asFileHandle()
when "open"
me.openDialog("FileDialog", {
title: __("Open file"),
mimes: (v for v in me.meta().mimes when v isnt "dir")
})
.then (f) ->
me.openFile f.file.path.asFileHandle()
when "opendir"
me.openDialog("FileDialog", {
title: __("Open folder"),
mimes: ["dir"]
})
.then (f) ->
me.currdir = f.file.path.asFileHandle()
me.initSideBar()
when "save"
me.currfile.cache = me.editor.getValue()
return me.save me.currfile if me.currfile.basename
me.saveAs()
when "saveas"
me.currfile.cache = me.editor.getValue()
me.saveAs()
else
console.log dataid
cleanup: (evt) ->
dirties = ( v for v in @tabbar.get "items" when v.dirty )
return if dirties.length is 0
evt.preventDefault()
@.openDialog("YesNoDialog", {
title: "__(Quit)",
text: __("Ignore all unsaved files: {0} ?", (v.filename() for v in dirties).join ", " )
}).then (d) =>
if d
v.dirty = false for v in dirties
@quit()
menu: () ->
menu = [
@fileMenu()
{
text: "__(View)",
child: [
{ text: "__(Command Palette)", dataid: "cmdpalette", shortcut: "A-P" }
],
onchildselect: (e, r) =>
@spotlight.run @
}
]
menu
class CMDMenu
constructor: (@text, @shortcut) ->
@child = []
@parent = undefined
@select = (e) ->
addAction: (v) ->
v.parent = @
@child.push v
@
addActions: (list) ->
@addAction v for v in list
onchildselect: (f) ->
@select = f
@
run: (root) ->
root.openDialog(new CommandPalette(), @)
.then (d) =>
data = d.data.item.get("data")
return data.run root if data.run
@select d, root
CMDMenu.fromMenu = (mn) ->
m = new CMDMenu mn.text, mn.shortcut
m.onchildselect mn.onchildselect
for v in mn.child
if v.child
m.addAction CMDMenu.fromMenu v
else
m.addAction v
m
CodePad.CMDMenu = CMDMenu
CodePad.dependencies = [
"os://scripts/ace/ace.js",
"os://scripts/ace/ext-language_tools.js",
"os://scripts/ace/ext-modelist.js",
"os://scripts/ace/ext-themelist.js"
]
this.OS.register "CodePad", CodePad

View File

@ -0,0 +1,222 @@
(function() {
/*
* decaffeinate suggestions:
* DS101: Remove unnecessary use of Array.from
* 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
*/
// import the CodePad application module
const App = this.OS.APP.CodePad;
// define the extension
App.extensions.AntOSDK = class AntOSDK extends App.BaseExtension {
constructor(app) {
super(app);
}
// public functions
create() {
return this.app.openDialog("FileDialog", {
title: "__(New Project at)",
file: { basename: __("ProjectName") },
mimes: ["dir"]
}).then(d => {
return this.mktpl(d.file.path, d.name, true);
});
}
init() {
const dir = this.app.currdir;
if (!dir || !dir.basename) { return this.create(); }
return dir.read()
.then(d => {
if (d.error) { return this.notify(__("Cannot read folder: {0}", dir.path)); }
if (d.result.length !== 0) { return this.notify(__("The folder is not empty: {0}", dir.path)); }
return this.mktpl(dir.parent().path, dir.basename);
});
}
buildnrun() {
return this.metadata("project.json").then(meta => {
return this.build(meta, true).then(() => {
return this.run(meta).catch(e => this.error(__("Unable to run project"), e));
}).catch(e => {
return this.error(__("Unable to build project"), e);
});
}).catch(e => this.error(__("Unable to read meta-data"), e));
}
release() {
return this.metadata("project.json").then(meta => {
return this.build(meta, false).then(() => {
return this.mkar(`${meta.root}/build/debug`, `${meta.root}/build/release/${meta.name}.zip`)
.catch(e => this.error(__("Unable to create package archive"), e));
}).catch(e => {},
this.error(__("Unable to build project"), e)
);
}).catch(e => this.error(__("Unable to read meta-data"), e));
}
// private functions
mktpl(path, name, flag) {
const rpath = `${path}/${name}`;
const dirs = [
`${rpath}/javascripts`,
`${rpath}/css`,
`${rpath}/coffees`,
`${rpath}/assets`
];
if (flag) { dirs.unshift(rpath); }
const files = [
["templates/sdk-main.tpl", `${rpath}/coffees/main.coffee`],
["templates/sdk-package.tpl", `${rpath}/package.json`],
["templates/sdk-project.tpl", `${rpath}/project.json`],
["templates/sdk-README.tpl", `${rpath}/README.md`],
["templates/sdk-scheme.tpl", `${rpath}/assets/scheme.html`]
];
return this.mkdirAll(dirs)
.then(() => {
return this.mkfileAll(files, path, name)
.then(() => {
this.app.currdir = rpath.asFileHandle();
this.app.initSideBar();
return this.app.openFile(`${rpath}/README.md`.asFileHandle());
}).catch(e => this.error(__("Unable to create template files"), e));
}).catch(e => this.error(__("Unable to create project directory"), e));
}
verify(list) {
return new Promise((resolve, reject) => {
if (list.length === 0) { return resolve(); }
const file = (list.splice(0, 1))[0].asFileHandle();
this.notify(__("Verifying: {0}", file.path));
return file.read().then(data => {
try {
CoffeeScript.nodes(data);
return this.verify(list)
.then(() => resolve())
.catch(e => reject(__e(e)));
} catch (ex) {
return reject(__e(ex));
}
}).catch(e => reject(__e(e)));
});
}
compile(meta) {
return new Promise((resolve, reject) => {
return this.import([
`${this.basedir()}/coffeescript.js`,
`${this.basedir()}/terser.min.js`
]).then(() => {
const list = (Array.from(meta.coffees).map((v) => `${meta.root}/${v}`));
return this.verify((Array.from(list))).then(() => {
return this.cat(list).then(code => {
const jsrc = CoffeeScript.compile(code);
this.notify(__("Compiled successful"));
return resolve(jsrc);
}).catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
});
}
build(meta, debug) {
const dirs = [
`${meta.root}/build`,
`${meta.root}/build/debug`,
`${meta.root}/build/release`
];
return new Promise((resolve, reject) => {
return this.mkdirAll(dirs).then(() => {
return this.compile(meta).then(src => {
let v;
return this.cat(((() => {
const result = [];
for (v of Array.from(meta.javascripts)) { result.push(`${meta.root}/${v}`);
}
return result;
})()), src)
.then(jsrc => new Promise(function(r, e) {
let code = jsrc;
if (!debug) {
const options = {
toplevel: true,
compress: {
passes: 3,
//pure_getters: true,
//unsafe: true,
},
mangle: true,
output: {
//beautify: true,
},
};
const result = Terser.minify(jsrc, options);
if (result.error) {
this.notify(__("Unable to minify code: {0}", result.error));
} else {
({
code
} = result);
}
}
return `${meta.root}/build/debug/main.js`
.asFileHandle()
.setCache(code)
.write("text/plain")
.then(d => r()).catch(ex => e(__e(ex)));
})).then(() => {
return new Promise((r, e) => {
return this.cat(((() => {
const result1 = [];
for (v of Array.from(meta.css)) { result1.push(`${meta.root}/${v}`);
}
return result1;
})()), "")
.then(function(txt) {
if (txt === "") { return r(); }
return `${meta.root}/build/debug/main.css`
.asFileHandle()
.setCache(txt)
.write("text/plain")
.then(d => r()).catch(ex => e(__e(ex)));
});
});
}).then(() => {
return this.copy(((() => {
const result1 = [];
for (v of Array.from(meta.copies)) { result1.push(`${meta.root}/${v}`);
}
return result1;
})()), `${meta.root}/build/debug`);
}).then(() => resolve())
.catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
});
}
run(meta) {
return `${meta.root}/build/debug/package.json`
.asFileHandle()
.read("json")
.then(v => {
v.text = v.name;
v.path = `${meta.root}/build/debug`;
v.filename = meta.name;
v.type = "app";
v.mime = "antos/app";
if (v.icon) { v.icon = `${v.path}/${v.icon}`; }
if (!v.iconclass && !v.icon) { v.iconclass = "fa fa-adn"; }
this.notify(__("Installing..."));
this.app.systemsetting.system.packages[meta.name] = v;
this.notify(__("Running {0}...", meta.name));
return this.app._gui.forceLaunch(meta.name);
});
}
};
}).call(this);

View File

@ -0,0 +1,261 @@
(function() {
/*
* decaffeinate suggestions:
* DS101: Remove unnecessary use of Array.from
* 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
*/
// import the CodePad application module
const App = this.OS.APP.CodePad;
// define the extension
App.extensions.ExtensionMaker = class ExtensionMaker extends App.BaseExtension {
constructor(app) {
super(app);
}
// public functions
create() {
return this.app.openDialog("FileDialog", {
title: "__(New CodePad extension at)",
file: { basename: __("ExtensionName") },
mimes: ["dir"]
}).then(d => {
return this.mktpl(d.file.path, d.name);
});
}
buildnrun() {
return this.metadata("extension.json").then(meta => {
return this.build(meta).then(() => {
return this.run(meta).catch(e => this.error(__("Unable to run extension"), e));
}).catch(e => {
return this.error(__("Unable to build extension"), e);
});
}).catch(e => this.error(__("Unable to read meta-data"), e));
}
release() {
return this.metadata("extension.json").then(meta => {
return this.build(meta).then(() => {
return this.mkar(`${meta.root}/build/debug`,
`${meta.root}/build/release/${meta.meta.name}.zip`)
.catch(e => this.error(__("Unable to create archive"), e));
}).catch(e => {},
this.error(__("Unable to build extension"), e)
);
}).catch(e => this.error(__("Unable to read meta-data"), e));
}
install() {
return this.app.openDialog("FileDialog", {
title: "__(Select extension archive)",
mimes: [".*/zip"]
}).then(d => {
return this.installZip(d.file.path)
.then(() => {
this.notify(__("Extension installed"));
return this.app.loadExtensionMetaData();
}).catch(e => this.error(__("Unable to install extension"), e));
});
}
// private functions
mktpl(path, name) {
const rpath = `${path}/${name}`;
const dirs = [
rpath,
`${rpath}/build`,
`${rpath}/build/release`,
`${rpath}/build/debug`
];
const files = [
["templates/ext-main.tpl", `${rpath}/${name}.coffee`],
["templates/ext-extension.tpl", `${rpath}/extension.json`],
];
return this.mkdirAll(dirs)
.then(() => {
return this.mkfileAll(files, path, name)
.then(() => {
this.app.currdir = rpath.asFileHandle();
this.app.initSideBar();
return this.app.openFile(`${rpath}/${name}.coffee`.asFileHandle());
}).catch(e => this.error(__("Unable to create extension template"), e));
}).catch(e => this.error(__("Unable to create extension directories"), e));
}
verify(list) {
return new Promise((resolve, reject) => {
if (list.length === 0) { return resolve(); }
const file = (list.splice(0, 1))[0].asFileHandle();
this.notify(__("Verifying: {0}", file.path));
return file.read().then(data => {
try {
CoffeeScript.nodes(data);
return this.verify(list)
.then(() => resolve())
.catch(e => reject(__e(e)));
} catch (ex) {
return reject(__e(ex));
}
}).catch(e => reject(__e(e)));
});
}
compile(meta) {
return new Promise((resolve, reject) => {
return this.import([`${this.basedir()}/coffeescript.js`]).then(() => {
const list = (Array.from(meta.coffees).map((v) => `${meta.root}/${v}`));
return this.verify((Array.from(list))).then(() => {
return this.cat(list).then(code => {
const jsrc = CoffeeScript.compile(code);
this.notify(__("Compiled successful"));
return resolve(jsrc);
}).catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
});
}
build(meta) {
return new Promise((resolve, reject) => {
return this.compile(meta).then(src => {
let v;
return this.cat(((() => {
const result = [];
for (v of Array.from(meta.javascripts)) { result.push(`${meta.root}/${v}`);
}
return result;
})()), src)
.then(jsrc => new Promise((r, e) => `${meta.root}/build/debug/${meta.meta.name}.js`
.asFileHandle()
.setCache(jsrc)
.write("text/plain")
.then(d => r()).catch(ex => e(__e(ex))))).then(() => new Promise((r, e) => `${meta.root}/build/debug/extension.json`
.asFileHandle()
.setCache(meta.meta)
.write("object")
.then(data => r(data)).catch(ex => e(__e(ex))))).then(() => {
return this.copy(((() => {
const result1 = [];
for (v of Array.from(meta.copies)) { result1.push(`${meta.root}/${v}`);
}
return result1;
})()), `${meta.root}/build/debug`);
}).then(() => resolve())
.catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
});
}
run(meta) {
return new Promise((resolve, reject) => {
const path = `${meta.root}/build/debug/${meta.meta.name}.js`;
if (this.app._api.shared[path]) { delete this.app._api.shared[path]; }
return this.app._api.requires(path)
.then(() => {
let v;
if (this.app.extensions[meta.meta.name]) {
this.app.extensions[meta.meta.name].child = [];
for (v of Array.from(meta.meta.actions)) { this.app.extensions[meta.meta.name].addAction(v); }
} else {
this.app.extensions[meta.meta.name] = new App.CMDMenu(meta.meta.text);
this.app.extensions[meta.meta.name].name = meta.meta.name;
for (v of Array.from(meta.meta.actions)) { this.app.extensions[meta.meta.name].addAction(v); }
this.app.spotlight.addAction(this.app.extensions[meta.meta.name]);
this.app.extensions[meta.meta.name].onchildselect(e => {
return this.app.loadAndRunExtensionAction(e.data.item.get("data"));
});
}
this.app.spotlight.run(this.app);
return resolve();
}).catch(e => reject(__e(e)));
});
}
installExtension(files, zip) {
return new Promise((resolve, reject) => {
const idx = files.indexOf("extension.json");
if (idx < 0) { reject(this.app._api.throwe(__("No meta-data found"))); }
const metafile = (files.splice(idx, 1))[0];
// read the meta file
return zip.file(metafile).async("uint8array").then(d => {
const meta = JSON.parse(new TextDecoder("utf-8").decode(d));
return this.installFiles(files, zip, meta)
.then(() => resolve())
.catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
});
}
installFiles(files, zip, meta) {
if (files.length === 0) { return this.installMeta(meta); }
return new Promise((resolve, reject) => {
const file = (files.splice(0, 1))[0];
const path = `${this.basedir()}/${file}`;
return zip.file(file).async("uint8array").then(d => {
return path.asFileHandle()
.setCache(new Blob([d], { type: "octet/stream" }))
.write("text/plain").then(r => {
if (r.error) { return reject(r.error); }
return this.installFiles(files, zip, meta)
.then(() => resolve())
.catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
});
}
installMeta(meta) {
return new Promise((resolve, reject) => {
const file = `${this.app.meta().path}/extensions.json`.asFileHandle();
return file.read("json").then(function(data) {
for (let v of Array.from(data)) { const names = (v.name); }
const idx = name.indexOf(meta.name);
if (idx >= 0) { data.splice(idx, 1); }
data.push(meta);
return file.setCache(data)
.write("object")
.then(() => resolve())
.catch(e => reject(__e(e)));}).catch(e => reject(__e(e)));
});
}
installZip(path) {
return new Promise((resolve, reject) => {
return this.import(["os://scripts/jszip.min.js"]).then(() => {
return path.asFileHandle().read("binary").then(data => {
return JSZip.loadAsync(data).then(zip => {
const pth = this.basedir();
const dir = [];
const files = [];
for (let name in zip.files) {
const file = zip.files[name];
if (file.dir) {
dir.push(pth + "/" + name);
} else {
files.push(name);
}
}
if (dir.length > 0) {
return this.mkdirAll(dir)
.then(() => {
return this.installExtension(files, zip)
.then(() => resolve())
.catch(e)(() => reject(__e(e)));
}).catch(e => reject(__e(e)));
} else {
return this.installExtension(files, zip)
.then(() => resolve())
.catch(e => reject(__e(e)));
}
}).catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
}).catch(e => reject(__e(e)));
});
}
};
}).call(this);

View File

@ -0,0 +1,628 @@
/*
* decaffeinate suggestions:
* DS101: Remove unnecessary use of Array.from
* 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
*/
const Ant = this;
class CodePad extends this.OS.GUI.BaseApplication {
constructor(args) {
super("CodePad", args);
this.currfile = "Untitled".asFileHandle();
this.currdir = undefined;
if (this.args && (this.args.length > 0)) {
if (this.args[0].type === "dir") {
this.currdir = this.args[0].path.asFileHandle();
} else {
this.currfile = this.args[0].path.asFileHandle();
this.currdir = this.currfile.parent();
}
}
}
main() {
this.extensions = {};
this.fileview = this.find("fileview");
this.sidebar = this.find("sidebar");
this.tabbar = this.find("tabbar");
this.langstat = this.find("langstat");
this.editorstat = this.find("editorstat");
this.fileview.set("fetch", path => new Promise(function(resolve, reject) {
let dir = path;
if (typeof path === "string") { dir = path.asFileHandle(); }
return dir.read().then(function(d) {
if (d.error) { return reject(d.error); }
return resolve(d.result);}).catch(e => reject(__e(e)));
}));
return this.setup();
}
setup() {
ace.config.set('basePath', '/scripts/ace');
ace.require("ace/ext/language_tools");
this.editor = ace.edit(this.find("datarea"));
this.editor.setOptions({
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true,
highlightActiveLine: true,
highlightSelectedWord: true,
behavioursEnabled: true,
wrap: true,
fontSize: "11pt",
showInvisibles: true
});
//themes = ace.require "ace/ext/themelist"
this.editor.setTheme("ace/theme/monokai");
this.modes = ace.require("ace/ext/modelist");
this.editor.completers.push({ getCompletions( editor, session, pos, prefix, callback ) {} });
this.editor.getSession().setUseWrapMode(true);
this.editormux = false;
this.editor.on("input", () => {
if (this.editormux) {
this.editormux = false;
return false;
}
if (!this.currfile.dirty) {
this.currfile.dirty = true;
this.currfile.text += "*";
return this.tabbar.update();
}
});
this.editor.getSession().selection.on("changeCursor", e => {
return this.updateStatus();
});
this.tabbar.set("ontabselect", e => {
return this.selecteTab($(e.data.item).index());
});
this.tabbar.set("ontabclose", e => {
const it = e.data.item;
if (!it) { return false; }
if (!it.get("data").dirty) { return this.closeTab(it); }
this.openDialog("YesNoDialog", {
title: __("Close tab"),
text: __("Close without saving ?")
}).then(d => {
if (d) { return this.closeTab(it); }
return this.editor.focus();
});
return false;
});
this.fileview.set("onfileopen", e => {
if (!e.data || !e.data.path) { return; }
if (e.data.type === "dir") { return; }
return this.openFile(e.data.path.asFileHandle());
});
this.fileview.set("onfileselect", e => {
if (!e.data || !e.data.path) { return; }
if (e.data.type === "dir") { return; }
const i = this.findTabByFile(e.data.path.asFileHandle());
if (i !== -1) { return this.tabbar.set("selected", i); }
});
this.on("resize", () => this.editor.resize());
this.on("focus", () => this.editor.focus());
this.spotlight = new CMDMenu(__("Command palette"));
this.bindKey("ALT-P", () => this.spotlight.run(this));
this.find("datarea").contextmenuHandle = (e, m) => {
m.set("items", [{
text: __("Command palete"),
onmenuselect: e => {
return this.spotlight.run(this);
}
}]);
return m.show(e);
};
this.fileview.contextmenuHandle = (e, m) => {
m.set("items", [
{ text: "__(New file)", id: "new" },
{ text: "__(New folder)", id: "newdir" },
{ text: "__(Rename)", id: "rename" },
{ text: "__(Delete)", id: "delete" }
]);
m.set("onmenuselect", e => {
return this.ctxFileMenuHandle(e);
});
return m.show(e);
};
this.bindKey("ALT-N", () => this.menuAction("new"));
this.bindKey("ALT-O", () => this.menuAction("open"));
this.bindKey("ALT-F", () => this.menuAction("opendir"));
this.bindKey("CTRL-S", () => this.menuAction("save"));
this.bindKey("ALT-W", () => this.menuAction("saveas"));
this.fileview.set("ondragndrop", e => {
const src = e.data.from.get("data").path.asFileHandle();
const des = e.data.to.get("data").path;
return src.move(`${des}/${src.basename}`)
.then(function(d) {
e.data.to.update(des);
return e.data.from.get("parent").update(src.parent().path);}).catch(e => this.error(__("Unable to move file/folder"), e));
});
this.on("filechange", data => {
let {
path
} = data.file;
if (data.type === "file") { ({
path
} = data.file.parent()); }
return this.fileview.update(path);
});
this.loadExtensionMetaData();
this.initCommandPalete();
this.initSideBar();
return this.openFile(this.currfile);
}
openFile(file) {
//find tab
const i = this.findTabByFile(file);
if (i !== -1) { return this.tabbar.set("selected", i); }
if (file.path.toString() === "Untitled") { return this.newTab(file); }
return file.read()
.then(d => {
file.cache = d || "";
return this.newTab(file);
}).catch(e => {
return this.error(__("Unable to open: {0}", file.path), e);
});
}
findTabByFile(file) {
const lst = this.tabbar.get("items");
const its = ((() => {
const result = [];
for (let i = 0; i < lst.length; i++) {
const d = lst[i];
if (d.hash() === file.hash()) {
result.push(i);
}
}
return result;
})());
if (its.length === 0) { return -1; }
return its[0];
}
newTab(file) {
file.text = file.basename ? file.basename : file.path;
if (!file.cache) { file.cache = ""; }
file.um = new ace.UndoManager();
this.currfile.selected = false;
file.selected = true;
//console.log cnt
return this.tabbar.push(file);
}
closeTab(it) {
this.tabbar.remove(it);
const cnt = this.tabbar.get("items").length;
if (cnt === 0) {
this.openFile("Untitled".asFileHandle());
return false;
}
this.tabbar.set("selected", cnt - 1);
return false;
}
selecteTab(i) {
//return if i is @tabbar.get "selidx"
const file = (this.tabbar.get("items"))[i];
if (!file) { return; }
this.scheme.set("apptitle", file.text.toString());
//return if file is @currfile
if (this.currfile !== file) {
this.currfile.cache = this.editor.getValue();
this.currfile.cursor = this.editor.selection.getCursor();
this.currfile.selected = false;
this.currfile = file;
}
if (!file.langmode) {
if (file.path.toString() !== "Untitled") {
const m = this.modes.getModeForPath(file.path);
file.langmode = { caption: m.caption, mode: m.mode };
} else {
file.langmode = { caption: "Text", mode: "ace/mode/text" };
}
}
this.editormux = true;
this.editor.getSession().setUndoManager(new ace.UndoManager());
this.editor.setValue(file.cache, -1);
this.editor.getSession().setMode(file.langmode.mode);
if (file.cursor) {
this.editor.renderer.scrollCursorIntoView({
row: file.cursor.row, column: file.cursor.column
}, 0.5);
this.editor.selection.moveTo(file.cursor.row, file.cursor.column);
}
this.editor.getSession().setUndoManager(file.um);
this.updateStatus();
return this.editor.focus();
}
updateStatus() {
const c = this.editor.session.selection.getCursor();
const l = this.editor.session.getLength();
this.editorstat.set("text", __("Row {0}, col {1}, lines: {2}", c.row + 1, c.column + 1, l));
return this.langstat.set("text", this.currfile.langmode.caption);
}
initSideBar() {
if (this.currdir) {
$(this.sidebar).show();
this.fileview.set("path", this.currdir.path);
} else {
$(this.sidebar).hide();
}
return this.trigger("resize");
}
addAction(action) {
this.spotlight.addAction(action);
return this;
}
addActions(list) {
this.spotlight.addActions(list);
return this;
}
initCommandPalete() {
let v;
const themes = ace.require("ace/ext/themelist");
const cmdtheme = new CMDMenu(__("Change theme"));
for (let k in themes.themesByName) { v = themes.themesByName[k]; cmdtheme.addAction({ text: v.caption, theme: v.theme }); }
cmdtheme.onchildselect(function(d, r) {
const data = d.data.item.get("data");
r.editor.setTheme(data.theme);
return r.editor.focus();
});
this.spotlight.addAction(cmdtheme);
const cmdmode = new CMDMenu(__("Change language mode"));
for (v of Array.from(this.modes.modes)) { cmdmode.addAction({ text: v.caption, mode: v.mode }); }
cmdmode.onchildselect(function(d, r) {
const data = d.data.item.get("data");
r.editor.session.setMode(data.mode);
r.currfile.langmode = { caption: data.text, mode: data.mode };
r.updateStatus();
return r.editor.focus();
});
this.spotlight.addAction(cmdmode);
return this.addAction(CMDMenu.fromMenu(this.fileMenu()));
}
loadExtensionMetaData() {
return `${this.meta().path}/extensions.json`
.asFileHandle()
.read("json")
.then(d => {
return (() => {
const result = [];
for (var ext of Array.from(d)) {
if (this.extensions[ext.name]) {
this.extensions[ext.name].child = [];
result.push((() => {
const result1 = [];
for (let v of Array.from(ext.actions)) { result1.push(this.extensions[ext.name].addAction(v));
}
return result1;
})());
} else {
this.extensions[ext.name] = new CMDMenu(ext.text);
this.extensions[ext.name].name = ext.name;
for (let v of Array.from(ext.actions)) { this.extensions[ext.name].addAction(v); }
this.spotlight.addAction(this.extensions[ext.name]);
result.push(this.extensions[ext.name].onchildselect(e => {
return this.loadAndRunExtensionAction(e.data.item.get("data"));
}));
}
}
return result;
})();
}).catch(e => {
return this.error(__("Cannot load extension meta data"), e);
});
}
runExtensionAction(name, action) {
if (!CodePad.extensions[name]) { return this.error(__("Unable to find extension: {0}", name)); }
const ext = new (CodePad.extensions[name])(this);
if (!ext[action]) { return this.error(__("Unable to find action: {0}", action)); }
return ext.preload()
.then(() => ext[action]()).catch(e => {
return this.error(__("Unable to preload extension"), e);
});
}
loadAndRunExtensionAction(data) {
const {
name
} = data.parent;
const action = data.name;
//verify if the extension is load
if (!CodePad.extensions[name]) {
//load the extension
const path = `${this.meta().path}/extensions/${name}.js`;
return this._api.requires(path)
.then(() => this.runExtensionAction(name, action))
.catch(e => {
return this.error(__("unable to load extension: {0}", name), e);
});
} else {
return this.runExtensionAction(name, action);
}
}
fileMenu() {
return {
text: __("File"),
child: [
{ text: __("New"), dataid: "new", shortcut: "A-N" },
{ text: __("Open"), dataid: "open", shortcut: "A-O" },
{ text: __("Open Folder"), dataid: "opendir", shortcut: "A-F" },
{ text: __("Save"), dataid: "save", shortcut: "C-S" },
{ text: __("Save as"), dataid: "saveas", shortcut: "A-W" }
],
onchildselect: (e, r) => {
return this.menuAction(e.data.item.get("data").dataid, r);
}
};
}
ctxFileMenuHandle(e) {
const el = e.data.item;
if (!el) { return; }
const data = el.get("data");
if (!data) { return; }
let file = this.fileview.get("selectedFile");
let dir = this.currdir;
if (file && (file.type === "dir")) { dir = file.path.asFileHandle(); }
if (file && (file.type === "file")) { dir = file.path.asFileHandle().parent(); }
switch (data.id) {
case "new":
if (!dir) { return; }
return this.openDialog("PromptDialog", {
title: "__(New file)",
label: "__(File name)"
})
.then(d => {
const fp = `${dir.path}/${d}`.asFileHandle();
return fp.write("text/plain")
.then(r => {
return this.fileview.update(dir.path);
}).catch(e => {
return this.error(__("Fail to create: {0}", e.stack), e);
});
});
case "newdir":
if (!dir) { return; }
return this.openDialog("PromptDialog", {
title: "__(New folder)",
label: "__(Folder name)"
})
.then(d => {
return dir.mk(d)
.then(r => {
return this.fileview.update(dir.path);
}).catch(e => {
return this.error(__("Fail to create: {0}", dir.path), e);
});
});
case "rename":
if (!file) { return; }
return this.openDialog("PromptDialog", {
title: "__(Rename)",
label: "__(File name)",
value: file.filename
})
.then(d => {
if (d === file.filename) { return; }
file = file.path.asFileHandle();
dir = file.parent();
return file.move(`${dir.path}/${d}`)
.then(r => {
return this.fileview.update(dir.path);
}).catch(e => {
return this.error(__("Fail to rename: {0}", file.path), e);
});
});
case "delete":
if (!file) { return; }
return this.openDialog("YesNoDialog", {
title: "__(Delete)",
iconclass: "fa fa-question-circle",
text: __("Do you really want to delete: {0}?", file.filename)
})
.then(d => {
if (!d) { return; }
file = file.path.asFileHandle();
dir = file.parent();
return file.remove()
.then(r => {
return this.fileview.update(dir.path);
}).catch(e => {
return this.error(__("Fail to delete: {0}", file.path), e);
});
});
default:
}
}
save(file) {
return file.write("text/plain")
.then(d => {
file.dirty = false;
file.text = file.basename;
this.tabbar.update();
return this.scheme.set("apptitle", `${this.currfile.basename}`);
}).catch(e => this.error(__("Unable to save file: {0}", file.path), e));
}
saveAs() {
return this.openDialog("FileDialog", {
title: __("Save as"),
file: this.currfile
})
.then(f => {
let d = f.file.path.asFileHandle();
if (f.file.type === "file") { d = d.parent(); }
this.currfile.setPath(`${d.path}/${f.name}`);
return this.save(this.currfile);
});
}
menuAction(dataid, r) {
let me = this;
if (r) { me = r; }
switch (dataid) {
case "new":
return me.openFile("Untitled".asFileHandle());
case "open":
return me.openDialog("FileDialog", {
title: __("Open file"),
mimes: (Array.from(me.meta().mimes).filter((v) => v !== "dir"))
})
.then(f => me.openFile(f.file.path.asFileHandle()));
case "opendir":
return me.openDialog("FileDialog", {
title: __("Open folder"),
mimes: ["dir"]
})
.then(function(f) {
me.currdir = f.file.path.asFileHandle();
return me.initSideBar();
});
case "save":
me.currfile.cache = me.editor.getValue();
if (me.currfile.basename) { return me.save(me.currfile); }
return me.saveAs();
case "saveas":
me.currfile.cache = me.editor.getValue();
return me.saveAs();
default:
return console.log(dataid);
}
}
cleanup(evt) {
let v;
const dirties = ((() => {
const result = [];
for (v of Array.from(this.tabbar.get("items"))) { if (v.dirty) {
result.push(v);
}
}
return result;
})());
if (dirties.length === 0) { return; }
evt.preventDefault();
return this.openDialog("YesNoDialog", {
title: "__(Quit)",
text: __("Ignore all unsaved files: {0} ?", ((() => {
const result1 = [];
for (v of Array.from(dirties)) { result1.push(v.filename());
}
return result1;
})()).join(", ") )
}).then(d => {
if (d) {
for (v of Array.from(dirties)) { v.dirty = false; }
return this.quit();
}
});
}
menu() {
const menu = [
this.fileMenu(),
{
text: "__(View)",
child: [
{ text: "__(Command Palette)", dataid: "cmdpalette", shortcut: "A-P" }
],
onchildselect: (e, r) => {
return this.spotlight.run(this);
}
}
];
return menu;
}
}
class CMDMenu {
constructor(text, shortcut) {
this.text = text;
this.shortcut = shortcut;
this.child = [];
this.parent = undefined;
this.select = function(e) {};
}
addAction(v) {
v.parent = this;
this.child.push(v);
return this;
}
addActions(list) {
return Array.from(list).map((v) => this.addAction(v));
}
onchildselect(f) {
this.select = f;
return this;
}
run(root) {
return root.openDialog(new CommandPalette(), this)
.then(d => {
const data = d.data.item.get("data");
if (data.run) { return data.run(root); }
return this.select(d, root);
});
}
}
CMDMenu.fromMenu = function(mn) {
const m = new CMDMenu(mn.text, mn.shortcut);
m.onchildselect(mn.onchildselect);
for (let v of Array.from(mn.child)) {
if (v.child) {
m.addAction(CMDMenu.fromMenu(v));
} else {
m.addAction(v);
}
}
return m;
};
CodePad.CMDMenu = CMDMenu;
CodePad.dependencies = [
"os://scripts/ace/ace.js",
"os://scripts/ace/ext-language_tools.js",
"os://scripts/ace/ext-modelist.js",
"os://scripts/ace/ext-themelist.js"
];
this.OS.register("CodePad", CodePad);