2020-06-06 23:49:13 +02:00
|
|
|
class Booklet extends this.OS.application.BaseApplication
|
2019-11-24 20:33:14 +01:00
|
|
|
constructor: ( args ) ->
|
|
|
|
super "Booklet", args
|
|
|
|
|
|
|
|
main: () ->
|
2020-06-06 23:49:13 +02:00
|
|
|
|
2019-11-26 22:35:25 +01:00
|
|
|
@tree = @find "toc-ui"
|
|
|
|
@currentToc = undefined
|
2019-11-29 22:07:31 +01:00
|
|
|
@dirty = false
|
2019-11-28 23:11:46 +01:00
|
|
|
@emux = false
|
2020-06-06 23:49:13 +02:00
|
|
|
@on "treeselect", (evt) =>
|
|
|
|
e = evt.data.item.data
|
|
|
|
return @reloadEditor() if (@currentToc is e) or (e is undefined) or (e.treepath is 0)
|
2019-11-29 22:07:31 +01:00
|
|
|
e.treepath = e.path
|
2020-06-06 23:49:13 +02:00
|
|
|
@load(e).then ()=>
|
|
|
|
@open e
|
|
|
|
.catch (msg) =>
|
2019-11-29 22:07:31 +01:00
|
|
|
e.loaded = true
|
2020-06-06 23:49:13 +02:00
|
|
|
@open e
|
|
|
|
@error __("Error when loading '{0}': {1}", e.text, msg.toString()), msg
|
2019-11-28 18:35:55 +01:00
|
|
|
|
2020-07-10 14:01:52 +02:00
|
|
|
@tree.ondragndrop = (e) =>
|
|
|
|
@dndhandle(e)
|
|
|
|
|
2019-11-24 20:33:14 +01:00
|
|
|
@initEditor()
|
|
|
|
@resizeContent()
|
2020-06-06 23:49:13 +02:00
|
|
|
@tree.contextmenuHandle = (e, m) =>
|
|
|
|
menus = @contextMenu()
|
2019-11-26 22:35:25 +01:00
|
|
|
return unless menus
|
2020-06-06 23:49:13 +02:00
|
|
|
m.items = menus
|
|
|
|
m.onmenuselect = (evt) =>
|
|
|
|
@[evt.data.item.data.dataid]()
|
2019-11-26 22:35:25 +01:00
|
|
|
m.show e
|
2020-06-06 23:49:13 +02:00
|
|
|
@editor.codemirror.on "change", () =>
|
|
|
|
return if @emux
|
|
|
|
return unless @currentToc
|
|
|
|
@currentToc.descFile.dirty = true
|
|
|
|
@dirty = true
|
2019-11-26 22:35:25 +01:00
|
|
|
|
|
|
|
newChapter: () ->
|
2019-11-29 22:07:31 +01:00
|
|
|
return @error __("No book selected") unless @currentToc and @currentToc.type is "Book"
|
2019-11-28 18:35:55 +01:00
|
|
|
ch = new BookletChapter(@book)
|
|
|
|
@displayToc()
|
2019-11-28 23:11:46 +01:00
|
|
|
ch.treepath = ch.path
|
2019-11-26 22:35:25 +01:00
|
|
|
|
2019-11-28 18:35:55 +01:00
|
|
|
newSection: () ->
|
2019-11-29 22:07:31 +01:00
|
|
|
return @error __("No chapter selected") unless @currentToc and @currentToc.type is "Chapter"
|
2019-11-28 18:35:55 +01:00
|
|
|
sec = new BookletSection(@currentToc)
|
|
|
|
@displayToc()
|
2019-11-28 23:11:46 +01:00
|
|
|
sec.treepath = sec.path
|
2019-11-28 18:35:55 +01:00
|
|
|
|
|
|
|
newFile: () ->
|
2019-11-29 22:07:31 +01:00
|
|
|
return @error __("No section selected") unless @currentToc and @currentToc.type is "Section"
|
2019-11-28 18:35:55 +01:00
|
|
|
file = new BookletFile(@currentToc)
|
|
|
|
@displayToc()
|
2019-11-28 23:11:46 +01:00
|
|
|
file.treepath = file.path
|
|
|
|
|
|
|
|
delete: () ->
|
2020-06-06 23:49:13 +02:00
|
|
|
|
2019-11-28 23:11:46 +01:00
|
|
|
return @error __("No entrie select") unless @currentToc
|
2020-06-06 23:49:13 +02:00
|
|
|
fn = () =>
|
|
|
|
@currentToc = undefined
|
|
|
|
@displayToc()
|
|
|
|
@reloadEditor()
|
|
|
|
@currentToc.remove().then () =>
|
|
|
|
@notify __("Entrie deleted")
|
2019-11-28 23:11:46 +01:00
|
|
|
fn()
|
2020-06-06 23:49:13 +02:00
|
|
|
.catch (e) =>
|
|
|
|
@error e.toString(), e
|
2019-11-28 23:11:46 +01:00
|
|
|
fn()
|
|
|
|
|
2020-07-10 14:01:52 +02:00
|
|
|
goUp: () ->
|
|
|
|
return unless @currentToc and @currentToc.type isnt "Book"
|
|
|
|
@currentToc.parent.up @currentToc
|
|
|
|
@displayToc()
|
|
|
|
|
|
|
|
goDown: () ->
|
|
|
|
return unless @currentToc and @currentToc.type isnt "Book"
|
|
|
|
@currentToc.parent.down @currentToc
|
|
|
|
@displayToc()
|
|
|
|
|
2019-11-29 22:07:31 +01:00
|
|
|
load: (entry) ->
|
2020-06-06 23:49:13 +02:00
|
|
|
|
|
|
|
return new Promise (r, e) =>
|
2019-11-29 22:07:31 +01:00
|
|
|
return r() if entry.loaded
|
2020-06-06 23:49:13 +02:00
|
|
|
entry.descFile.meta().then (d) =>
|
|
|
|
entry.descFile.read().then (data) =>
|
2019-11-29 22:07:31 +01:00
|
|
|
entry.descFile.cache = data
|
|
|
|
entry.loaded = true
|
|
|
|
entry.descFile.dirty = false
|
|
|
|
r()
|
2020-06-06 23:49:13 +02:00
|
|
|
.catch (msg) -> e __e msg
|
|
|
|
.catch (msg) -> e __e msg
|
|
|
|
|
2019-11-29 22:07:31 +01:00
|
|
|
|
2020-07-10 14:01:52 +02:00
|
|
|
dndhandle: (e) ->
|
|
|
|
return unless e and e.data
|
|
|
|
from = e.data.from.data
|
|
|
|
to = e.data.to.data
|
|
|
|
return unless from and to
|
|
|
|
return if from.type is "Book" or from.type is "Chapter"
|
|
|
|
return if from.parent is to.parent or from.parent is to
|
|
|
|
if to.type is from.type
|
|
|
|
to = to.parent
|
|
|
|
|
|
|
|
if to.type is from.parent.type
|
|
|
|
from.parent.removeChild(from).then () =>
|
|
|
|
to.add from
|
|
|
|
@displayToc()
|
|
|
|
|
|
|
|
upload: () ->
|
|
|
|
return unless @currentToc and @currentToc.type isnt "File"
|
|
|
|
@currentToc.path.asFileHandle().upload()
|
|
|
|
.then () =>
|
|
|
|
@notify __("File uploaded")
|
|
|
|
.catch (e) =>
|
|
|
|
@error __("Unable to upload file {0}", e.toString()), e
|
|
|
|
|
2019-11-26 22:35:25 +01:00
|
|
|
contextMenu: () ->
|
|
|
|
return undefined unless @currentToc
|
|
|
|
switch @currentToc.type
|
2019-11-29 22:07:31 +01:00
|
|
|
when "Book"
|
2019-11-26 22:35:25 +01:00
|
|
|
return [
|
|
|
|
{ text: __("New chapter"), dataid: "newChapter" },
|
2020-07-10 14:01:52 +02:00
|
|
|
{ text: __("Delete book"), dataid: "delete" },
|
|
|
|
{ text: __("Upload media"), dataid: "upload" }
|
2019-11-26 22:35:25 +01:00
|
|
|
]
|
2019-11-29 22:07:31 +01:00
|
|
|
when "Chapter"
|
2019-11-26 22:35:25 +01:00
|
|
|
return [
|
|
|
|
{ text: __("New section"), dataid: "newSection" },
|
2020-07-10 14:01:52 +02:00
|
|
|
{ text: __("Delete chapter"), dataid: "delete" },
|
|
|
|
{ text: __("Go up"), dataid: "goUp" },
|
|
|
|
{ text: __("Go down"), dataid: "goDown" },
|
|
|
|
{ text: __("Upload media"), dataid: "upload" }
|
2019-11-26 22:35:25 +01:00
|
|
|
]
|
2019-11-29 22:07:31 +01:00
|
|
|
when "Section"
|
2019-11-26 22:35:25 +01:00
|
|
|
return [
|
|
|
|
{ text: __("New file"), dataid: "newFile" },
|
2020-07-10 14:01:52 +02:00
|
|
|
{ text: __("Delete section"), dataid: "delete" },
|
|
|
|
{ text: __("Go up"), dataid: "goUp" },
|
|
|
|
{ text: __("Go down"), dataid: "goDown" },
|
|
|
|
{ text: __("Upload media"), dataid: "upload" }
|
2019-11-28 23:11:46 +01:00
|
|
|
]
|
2019-11-29 22:07:31 +01:00
|
|
|
when "File"
|
2019-11-28 23:11:46 +01:00
|
|
|
return [
|
2020-07-10 14:01:52 +02:00
|
|
|
{ text: __("Delete file"), dataid: "delete" },
|
|
|
|
{ text: __("Go up"), dataid: "goUp" },
|
|
|
|
{ text: __("Go down"), dataid: "goDown" }
|
2019-11-26 22:35:25 +01:00
|
|
|
]
|
|
|
|
return undefined
|
2019-11-24 20:33:14 +01:00
|
|
|
|
2019-11-30 23:04:36 +01:00
|
|
|
shareFile: (mimes,f) ->
|
2020-06-06 23:49:13 +02:00
|
|
|
|
|
|
|
@openDialog "FileDialog", { title: __("Select a file"), mimes: mimes }
|
|
|
|
.then (d) =>
|
|
|
|
d.file.path.asFileHandle().publish().then (r) ->
|
2019-11-30 23:04:36 +01:00
|
|
|
f r.result
|
2020-06-06 23:49:13 +02:00
|
|
|
.catch (msg) =>
|
|
|
|
return @error __("Cannot export file for embedding to text"), msg
|
|
|
|
.catch (msg) =>
|
|
|
|
return @error msg.toString(), msg
|
|
|
|
|
2019-11-24 20:33:14 +01:00
|
|
|
initEditor: ()->
|
|
|
|
markarea = @find "markarea"
|
|
|
|
@container = @find "mycontainer"
|
|
|
|
@previewOn = false
|
|
|
|
@editormux = false
|
2020-06-06 23:49:13 +02:00
|
|
|
|
2019-11-24 20:33:14 +01:00
|
|
|
@editor = new SimpleMDE
|
|
|
|
element: markarea
|
2020-07-10 14:01:52 +02:00
|
|
|
autoDownloadFontAwesome: false
|
2019-11-24 20:33:14 +01:00
|
|
|
autofocus: true
|
|
|
|
tabSize: 4
|
|
|
|
indentWithTabs: true
|
|
|
|
toolbar: [
|
|
|
|
"bold", "italic", "heading", "|", "quote", "code",
|
|
|
|
"unordered-list", "ordered-list", "|", "link",
|
2019-11-30 23:04:36 +01:00
|
|
|
"image", "table", "horizontal-rule",
|
|
|
|
{
|
2020-07-10 14:01:52 +02:00
|
|
|
name: "shared image",
|
|
|
|
className: "fa fa-share-square",
|
2020-06-06 23:49:13 +02:00
|
|
|
action: (e) =>
|
|
|
|
@shareFile ["image/.*"], (path) =>
|
|
|
|
doc = @editor.codemirror.getDoc()
|
|
|
|
doc.replaceSelection "![](#{@_api.handler.shared}/#{path})"
|
2019-11-30 23:04:36 +01:00
|
|
|
},
|
2020-07-10 14:01:52 +02:00
|
|
|
{
|
|
|
|
name: "local image",
|
|
|
|
className: "fa fa-file-image-o",
|
|
|
|
action: (e) =>
|
|
|
|
return unless @book
|
|
|
|
@openDialog "FileDialog", {
|
|
|
|
title: __("Select image file"),
|
|
|
|
mimes: ["image/.*"],
|
|
|
|
root: @book.path
|
|
|
|
}
|
|
|
|
.then (d) =>
|
|
|
|
path = d.file.path.replace @book.path, ""
|
|
|
|
doc = @editor.codemirror.getDoc()
|
|
|
|
#selectedText = @editor.codemirror.getSelection()
|
|
|
|
doc.replaceSelection "[[@book:image:#{path}]]"
|
|
|
|
.catch (e) =>
|
|
|
|
@error e.toString(), e
|
|
|
|
},
|
2019-11-30 23:04:36 +01:00
|
|
|
{
|
|
|
|
name:"Youtube",
|
|
|
|
className: "fa fa-youtube",
|
2020-06-06 23:49:13 +02:00
|
|
|
action: (e) =>
|
|
|
|
doc = @editor.codemirror.getDoc()
|
2020-07-10 14:01:52 +02:00
|
|
|
selectedText = @editor.codemirror.getSelection() || ""
|
|
|
|
doc.replaceSelection "[[youtube:#{selectedText}]]"
|
2019-11-30 23:04:36 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "3d object",
|
2020-07-10 14:01:52 +02:00
|
|
|
className: "fa fa-cube",
|
2020-06-06 23:49:13 +02:00
|
|
|
action: (e) =>
|
2020-07-10 14:01:52 +02:00
|
|
|
return unless @book
|
|
|
|
@openDialog "FileDialog", {
|
|
|
|
title: __("Select 3d model"),
|
2020-09-13 17:10:45 +02:00
|
|
|
mimes: ["text/wavefront-obj", "model/gltf-binary"],
|
2020-07-10 14:01:52 +02:00
|
|
|
root: @book.path
|
|
|
|
}
|
|
|
|
.then (d) =>
|
|
|
|
path = d.file.path.replace @book.path, ""
|
2020-06-06 23:49:13 +02:00
|
|
|
doc = @editor.codemirror.getDoc()
|
2020-07-10 14:01:52 +02:00
|
|
|
doc.replaceSelection "[[@book:3dmodel:#{path}]]"
|
|
|
|
.catch (e) =>
|
|
|
|
@error e.toString(), e
|
2019-11-30 23:04:36 +01:00
|
|
|
},
|
|
|
|
"|",
|
2019-11-24 20:33:14 +01:00
|
|
|
{
|
2019-11-30 23:04:36 +01:00
|
|
|
name: __("Preview"),
|
2019-11-24 20:33:14 +01:00
|
|
|
className: "fa fa-eye no-disable",
|
2020-07-10 14:01:52 +02:00
|
|
|
action: (e) =>
|
2019-11-24 20:33:14 +01:00
|
|
|
SimpleMDE.togglePreview e
|
2020-06-06 23:49:13 +02:00
|
|
|
#/console.log @select ".editor-preview editor-preview-active"
|
|
|
|
renderMathInElement @find "mycontainer"
|
2020-07-10 14:01:52 +02:00
|
|
|
@renderLocalElement()
|
2019-11-24 20:33:14 +01:00
|
|
|
}
|
2020-07-10 14:01:52 +02:00
|
|
|
],
|
|
|
|
previewRender: (plainText, preview) =>
|
|
|
|
if @book
|
|
|
|
plainText = plainText.replace /\[\[@book:image:([^\]]*)\]\]/g, (a, b) =>
|
|
|
|
return "![](#{@_api.handle.get}/#{@book.path}/#{b})"
|
|
|
|
html = @editor.markdown plainText
|
|
|
|
|
|
|
|
preview.innerHTML = html
|
|
|
|
|
2020-06-06 23:49:13 +02:00
|
|
|
@on "hboxchange", (e) => @resizeContent()
|
|
|
|
@bindKey "ALT-N", () => @actionFile "#{@name}-New"
|
|
|
|
@bindKey "ALT-O", () => @actionFile "#{@name}-Open"
|
|
|
|
@bindKey "CTRL-S", () => @actionFile "#{@name}-Save"
|
2019-11-24 20:33:14 +01:00
|
|
|
|
2019-11-26 22:35:25 +01:00
|
|
|
reloadEditor: () ->
|
2019-11-28 18:35:55 +01:00
|
|
|
if @currentToc is undefined
|
|
|
|
@editor.value ""
|
2020-06-06 23:49:13 +02:00
|
|
|
return @scheme.apptitle = @name
|
2019-11-28 18:35:55 +01:00
|
|
|
@editor.value @currentToc.descFile.cache || ""
|
2020-06-06 23:49:13 +02:00
|
|
|
@scheme.apptitle = "Booklet - #{@currentToc.descFile.path}"
|
2019-11-24 20:33:14 +01:00
|
|
|
|
2019-11-26 22:35:25 +01:00
|
|
|
saveContext: () ->
|
2019-11-28 18:35:55 +01:00
|
|
|
return unless @currentToc
|
|
|
|
@currentToc.descFile.cache = @editor.value()
|
2019-11-24 20:33:14 +01:00
|
|
|
|
|
|
|
resizeContent: () ->
|
|
|
|
children = ($ @container).children()
|
|
|
|
titlebar = (($ @scheme).find ".afx-window-top")[0]
|
|
|
|
toolbar = children[1]
|
|
|
|
statusbar = children[4]
|
|
|
|
cheight = ($ @scheme).height() - ($ titlebar).height() - ($ toolbar).height() - ($ statusbar).height() - 40
|
|
|
|
($ children[2]).css("height", cheight + "px")
|
|
|
|
|
|
|
|
|
|
|
|
menu: () ->
|
2020-06-06 23:49:13 +02:00
|
|
|
|
2019-11-24 20:33:14 +01:00
|
|
|
menu = [{
|
|
|
|
text: "__(File)",
|
2020-06-06 23:49:13 +02:00
|
|
|
nodes: [
|
2019-11-24 20:33:14 +01:00
|
|
|
{ text: "__(New booklet)", dataid: "#{@name}-New", shortcut: "A-N" },
|
|
|
|
{ text: "__(Open a booklet)", dataid: "#{@name}-Open", shortcut: "A-O" }
|
2019-11-28 18:35:55 +01:00
|
|
|
{ text: "__(Save a booklet)", dataid: "#{@name}-Save", shortcut: "C-S" }
|
2019-11-24 20:33:14 +01:00
|
|
|
],
|
2020-06-06 23:49:13 +02:00
|
|
|
onchildselect: (e) => @actionFile e.data.item.data.dataid
|
2019-11-24 20:33:14 +01:00
|
|
|
}]
|
|
|
|
menu
|
|
|
|
|
|
|
|
actionFile: (e) ->
|
2020-06-06 23:49:13 +02:00
|
|
|
|
2019-11-24 20:33:14 +01:00
|
|
|
switch e
|
|
|
|
when "#{@name}-Open"
|
2020-06-06 23:49:13 +02:00
|
|
|
@checkForDirty () =>
|
2020-09-15 12:40:06 +02:00
|
|
|
@openDialog "FileDialog", { title:__("Open book"), mimes: ['dir'] }
|
2020-06-06 23:49:13 +02:00
|
|
|
.then (d) =>
|
|
|
|
@book = new BookletBook(d.file.path)
|
|
|
|
@book.read(d.file.path).then () =>
|
|
|
|
@book.treepath = @book.path
|
|
|
|
@tree.selectedItem = undefined
|
|
|
|
@displayToc()
|
|
|
|
@notify __("Book loaded")
|
|
|
|
.catch (msg) =>
|
|
|
|
@error __("Cannot load book: {0}", msg.toString()), msg
|
|
|
|
.catch (msg) => @error msg.toString(), msg
|
|
|
|
|
2019-11-24 20:33:14 +01:00
|
|
|
when "#{@name}-New"
|
2020-09-15 12:40:06 +02:00
|
|
|
@openDialog "FileDialog", { title: __("New book at"), mimes: ['dir'], file: { basename: __("BookName") }}
|
2020-06-06 23:49:13 +02:00
|
|
|
.then (d) =>
|
|
|
|
@newAt "#{d.file.path}/#{d.name}"
|
|
|
|
.catch (msg) => @error msg.toString(), msg
|
2019-11-28 18:35:55 +01:00
|
|
|
when "#{@name}-Save"
|
2020-06-06 23:49:13 +02:00
|
|
|
return unless @book
|
|
|
|
@saveContext() if @currentToc
|
|
|
|
@displayToc()
|
|
|
|
@book.save().then () =>
|
|
|
|
@dirty = false
|
|
|
|
@notify __("Book saved")
|
|
|
|
.catch (e) =>
|
|
|
|
@error __("Can't save the book : {0}", e.toString()), e
|
2019-11-26 22:35:25 +01:00
|
|
|
|
2019-11-29 22:07:31 +01:00
|
|
|
checkForDirty: (f) ->
|
|
|
|
return f() unless @dirty
|
2020-06-06 23:49:13 +02:00
|
|
|
@ask {title: __("Continue ?"), text: __("Book is unsaved, you want to continue ?") }
|
|
|
|
.then (d) =>
|
2019-11-29 22:07:31 +01:00
|
|
|
# console.log d
|
|
|
|
if d
|
|
|
|
f()
|
2020-06-06 23:49:13 +02:00
|
|
|
|
2019-11-29 22:07:31 +01:00
|
|
|
|
2019-11-28 18:35:55 +01:00
|
|
|
open: (toc) ->
|
2020-06-06 23:49:13 +02:00
|
|
|
@emux = true
|
|
|
|
@saveContext()
|
|
|
|
@currentToc = toc
|
|
|
|
@reloadEditor()
|
|
|
|
@displayToc()
|
|
|
|
@emux = false
|
|
|
|
|
2019-11-26 22:35:25 +01:00
|
|
|
|
|
|
|
newAt: (folder) ->
|
2020-06-06 23:49:13 +02:00
|
|
|
@tree.selectedItem = undefined
|
2019-11-29 22:07:31 +01:00
|
|
|
@book = new BookletBook(folder)
|
2019-11-28 18:35:55 +01:00
|
|
|
@book.treepath = @book.path
|
|
|
|
@currentToc = undefined
|
|
|
|
@reloadEditor()
|
2019-11-26 22:35:25 +01:00
|
|
|
@displayToc()
|
|
|
|
|
|
|
|
displayToc: () ->
|
2019-11-28 18:35:55 +01:00
|
|
|
@book.toc()
|
2020-06-06 23:49:13 +02:00
|
|
|
@tree.data = @book
|
|
|
|
@tree.expandAll()
|
2019-11-26 22:35:25 +01:00
|
|
|
|
2019-11-30 23:04:36 +01:00
|
|
|
cleanup: (evt) ->
|
|
|
|
return unless @dirty
|
|
|
|
evt.preventDefault()
|
2020-06-06 23:49:13 +02:00
|
|
|
@checkForDirty () =>
|
|
|
|
@dirty = false
|
|
|
|
@quit()
|
2019-11-30 23:04:36 +01:00
|
|
|
|
2020-07-10 14:01:52 +02:00
|
|
|
renderLocalElement: () ->
|
|
|
|
|
|
|
|
|
2020-06-06 23:49:13 +02:00
|
|
|
Booklet.dependencies = [
|
2020-12-18 21:52:44 +01:00
|
|
|
"pkg://SimpleMDE/main.js",
|
2021-01-17 09:19:58 +01:00
|
|
|
"pkg://SimpleMDE/main.css",
|
|
|
|
"pkg://Katex/main.js",
|
|
|
|
"pkg://Katex/main.css"
|
2020-06-06 23:49:13 +02:00
|
|
|
]
|
2019-11-24 20:33:14 +01:00
|
|
|
|
|
|
|
this.OS.register "Booklet", Booklet
|