|
|
|
@ -17,10 +17,19 @@ func (t tmplData) Content() ([]string, error) { |
|
|
|
|
var files []string |
|
|
|
|
|
|
|
|
|
err := filepath.Walk(path.Join(t.Site.Root, "content"), func(name string, fi os.FileInfo, err error) error { |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if fi.IsDir() { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
name, err = filepath.Rel(t.Site.Root, name) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
files = append(files, name) |
|
|
|
|
return nil |
|
|
|
|
}) |
|
|
|
@ -34,6 +43,33 @@ func (t tmplData) Content() ([]string, error) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (t tmplData) ContentTypes() ([]string, error) { |
|
|
|
|
nameMap := map[string]struct{}{"default": struct{}{}} |
|
|
|
|
|
|
|
|
|
names, err := t.contentTypes(path.Join(t.Site.Root, "archetypes")) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
for _, name := range names { |
|
|
|
|
nameMap[name] = struct{}{} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
names, err = t.contentTypes(path.Join(t.Site.Root, "themes", "hugo-theme-minos", "archetypes")) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
for _, name := range names { |
|
|
|
|
nameMap[name] = struct{}{} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var out []string |
|
|
|
|
for name := range nameMap { |
|
|
|
|
out = append(out, name[:len(name)-len(filepath.Ext(name))]) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return out, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (t tmplData) contentTypes(dir string) ([]string, error) { |
|
|
|
|
layoutDir, err := os.Open(path.Join(t.Site.Root, "archetypes")) |
|
|
|
|
if err != nil { |
|
|
|
|
fmt.Println("opening layout dir", err) |
|
|
|
@ -47,12 +83,7 @@ func (t tmplData) ContentTypes() ([]string, error) { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
out := []string{"default"} |
|
|
|
|
for _, name := range names { |
|
|
|
|
out = append(out, name[:len(name)-len(filepath.Ext(name))]) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return out, nil |
|
|
|
|
return names, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type tmplData struct { |
|
|
|
@ -109,28 +140,64 @@ var EditPage = `<html> |
|
|
|
|
} |
|
|
|
|
</style> |
|
|
|
|
<link rel="stylesheet" href="/hugo/simplemde.css" /> |
|
|
|
|
<script src="https://unpkg.com/vue"></script> |
|
|
|
|
<script src="https://unpkg.com/moment"></script> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<body> |
|
|
|
|
<div id="container" > |
|
|
|
|
<div id="lastSaved"> |
|
|
|
|
<span v-if="ltime >serverLtime && (sendQueue.length > 0 || sentRecently.length > 0)">last saved ${ lastSaved.from(now) }, saving</span> |
|
|
|
|
<span v-else>saved</span> |
|
|
|
|
<span v-if="connectionError">, ${connectionError}</span> |
|
|
|
|
</div> |
|
|
|
|
<textarea id="editor">{{ .LoadContent }}</textarea> |
|
|
|
|
<div id="draft"><iframe src="{{ .IframeSource }}">Loading draft...</iframe></div> |
|
|
|
|
<div id="draft"><!--<iframe src="{{ .IframeSource }}">Loading draft...</iframe>--></div> |
|
|
|
|
</div> |
|
|
|
|
<script> |
|
|
|
|
var ltime = 0; |
|
|
|
|
var uiBindings = { |
|
|
|
|
ltime: 0, |
|
|
|
|
serverLtime: 0, |
|
|
|
|
lastSaved: moment(), |
|
|
|
|
now: moment(), |
|
|
|
|
connectionError: null, |
|
|
|
|
sendQueue: [], |
|
|
|
|
sentRecently: [], |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
var app = new Vue({ |
|
|
|
|
el: "#container", |
|
|
|
|
data: uiBindings, |
|
|
|
|
delimiters: ["${", "}"], |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
function getLtime() { |
|
|
|
|
ltime++ |
|
|
|
|
return ltime |
|
|
|
|
uiBindings.ltime++ |
|
|
|
|
return uiBindings.ltime |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function observeServer(l) { |
|
|
|
|
uiBindings.serverLtime = l; |
|
|
|
|
while (uiBindings.sentRecently.length > 0 && uiBindings.sentRecently[0].ltime < l) { |
|
|
|
|
uiBindings.sentRecently.pop(); |
|
|
|
|
} |
|
|
|
|
observe(l); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function observe(l) { |
|
|
|
|
if (l > ltime) {
|
|
|
|
|
ltime = l; |
|
|
|
|
if (l > uiBindings.ltime) {
|
|
|
|
|
uiBindings.now = moment(); |
|
|
|
|
uiBindings.lastSaved = moment(); |
|
|
|
|
uiBindings.ltime = l; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var editorElem = document.getElementById("editor"); |
|
|
|
|
var editor = new SimpleMDE({element: editorElem, forceSync: true}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create WebSocket connection.
|
|
|
|
|
const socket = new WebSocket('ws://' + location.host + location.pathname);
|
|
|
|
|
var socket = connect(); |
|
|
|
|
|
|
|
|
|
var iframe = document.querySelector("#draft > iframe"); |
|
|
|
|
|
|
|
|
@ -144,39 +211,76 @@ var EditPage = `<html> |
|
|
|
|
iframe.contentWindow.location.reload(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}, 50); |
|
|
|
|
|
|
|
|
|
// Listen for messages
|
|
|
|
|
socket.addEventListener('message', function (event) { |
|
|
|
|
var message = JSON.parse(event.data); |
|
|
|
|
observe(message.ltime); |
|
|
|
|
|
|
|
|
|
var deltas = []; |
|
|
|
|
deltas.push.apply(deltas, message.deltas); |
|
|
|
|
|
|
|
|
|
deltas.forEach(function(aceDelta) { |
|
|
|
|
var cmDelta = aceDeltaToCM(aceDelta) |
|
|
|
|
console.log(cmDelta); |
|
|
|
|
|
|
|
|
|
var content = "" |
|
|
|
|
var to = { |
|
|
|
|
line: aceDelta.start.row, |
|
|
|
|
ch: aceDelta.start.column, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (aceDelta.action == "insert") { |
|
|
|
|
content = aceDelta.lines.join("\n"); |
|
|
|
|
to = null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
editor.codemirror.doc.replaceRange(content, cmDelta.from, to, "dontreflect"); |
|
|
|
|
sawChanges = sawChangesBumpsTo; |
|
|
|
|
}) |
|
|
|
|
}); |
|
|
|
|
uiBindings.now = moment(); |
|
|
|
|
if (uiBindings.connectionError) { |
|
|
|
|
socket = connect(); |
|
|
|
|
} else if (uiBindings.sendQueue.length > 0) { |
|
|
|
|
var ltime = getLtime(); |
|
|
|
|
socket.send(JSON.stringify({ |
|
|
|
|
"deltas": uiBindings.sendQueue, |
|
|
|
|
"ltime": ltime, |
|
|
|
|
})); |
|
|
|
|
uiBindings.sentRecently.push({ |
|
|
|
|
"ltime": ltime, |
|
|
|
|
"sent": uiBindings.sendQueue, |
|
|
|
|
}); |
|
|
|
|
uiBindings.sendQueue = []; |
|
|
|
|
} |
|
|
|
|
}, 500); |
|
|
|
|
|
|
|
|
|
function connect() { |
|
|
|
|
const socket = new WebSocket((location.protocol == "https:" ? 'wss://' : 'ws://') + location.host + location.pathname);
|
|
|
|
|
|
|
|
|
|
// Listen for messages
|
|
|
|
|
socket.addEventListener('message', function (event) { |
|
|
|
|
var message = JSON.parse(event.data); |
|
|
|
|
observeServer(message.ltime); |
|
|
|
|
|
|
|
|
|
var deltas = []; |
|
|
|
|
deltas.push.apply(deltas, message.deltas); |
|
|
|
|
|
|
|
|
|
deltas.forEach(function(aceDelta) { |
|
|
|
|
var cmDelta = aceDeltaToCM(aceDelta) |
|
|
|
|
console.log(cmDelta); |
|
|
|
|
|
|
|
|
|
var content = "" |
|
|
|
|
var to = { |
|
|
|
|
line: aceDelta.start.row, |
|
|
|
|
ch: aceDelta.start.column, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (aceDelta.action == "insert") { |
|
|
|
|
content = aceDelta.lines.join("\n"); |
|
|
|
|
to = null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
editor.codemirror.doc.replaceRange(content, cmDelta.from, to, "dontreflect"); |
|
|
|
|
sawChanges = sawChangesBumpsTo; |
|
|
|
|
}) |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
socket.addEventListener('open', function () { |
|
|
|
|
uiBindings.connectionError = null; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
socket.addEventListener('close', function () { |
|
|
|
|
if (!uiBindings.connectionError) { |
|
|
|
|
getLtime(); |
|
|
|
|
uiBindings.connectionError = "server connection closed, reconnecting..."; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
socket.addEventListener('error', function (err) { |
|
|
|
|
if (!uiBindings.connectionError) { |
|
|
|
|
uiBindings.connectionError = err; |
|
|
|
|
getLtime(); |
|
|
|
|
} |
|
|
|
|
console.log(err); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
socket.addEventListener('error', function (err) { |
|
|
|
|
console.log(err); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return socket; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
editor.codemirror.on("change", function (cm, cmDelta) { |
|
|
|
|
if (cmDelta.origin == "dontreflect") { |
|
|
|
@ -187,10 +291,7 @@ var EditPage = `<html> |
|
|
|
|
console.log(cmDelta, "=>", aceDelta) |
|
|
|
|
|
|
|
|
|
sawChanges = sawChangesBumpsTo; |
|
|
|
|
socket.send(JSON.stringify({ |
|
|
|
|
"deltas": [aceDelta], |
|
|
|
|
"ltime": getLtime(), |
|
|
|
|
})) |
|
|
|
|
uiBindings.sendQueue.push(aceDelta); |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
function cmDeltaToAce(cmDelta) { |
|
|
|
|