aboutsummaryrefslogtreecommitdiffstats
path: root/internal/web/templates
diff options
context:
space:
mode:
Diffstat (limited to 'internal/web/templates')
-rw-r--r--internal/web/templates/dashboard/page_form.html7
-rw-r--r--internal/web/templates/dashboard/post_form.html7
-rw-r--r--internal/web/templates/partials/editor.html44
-rw-r--r--internal/web/templates/partials/mdhelp.html2
4 files changed, 51 insertions, 9 deletions
diff --git a/internal/web/templates/dashboard/page_form.html b/internal/web/templates/dashboard/page_form.html
index 38aac71..2692449 100644
--- a/internal/web/templates/dashboard/page_form.html
+++ b/internal/web/templates/dashboard/page_form.html
@@ -2,13 +2,13 @@
{{define "content"}}
<div class="card">
<h1>{{if .Data.page.ID}}Edit page{{else}}New page{{end}}</h1>
- <form method="post">
+ <form method="post" enctype="multipart/form-data">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
<label>Title<br><input name="title" value="{{.Data.page.Title}}" required maxlength="120"></label>
<label>Address <span class="muted">(leave empty to make one from the title; e.g. <code>about</code> → {{.BlogURL}}/about)</span><br>
<input name="slug" value="{{.Data.page.Slug}}" pattern="[a-z0-9]([a-z0-9-]*[a-z0-9])?" maxlength="80"></label>
- <label>Intro text <span class="muted">(Markdown, shown above the posts; optional)</span><br>
- <textarea name="intro" rows="8">{{.Data.page.IntroMD}}</textarea></label>
+ <label for="ed-intro">Intro text <span class="muted">(Markdown, shown above the posts; optional)</span></label>
+ {{template "editor" (dict "name" "intro" "value" .Data.page.IntroMD "rows" 8 "tall" false "upload" (print "/b/" .Blog.Subdomain "/images/upload") "csrf" .CSRF)}}
<label class="check"><input type="checkbox" name="show_in_nav"{{if .Data.page.ShowInNav}} checked{{end}}> Show in menu</label>
<p class="actionrow">
<button type="submit">Save</button>
@@ -17,5 +17,4 @@
</p>
</form>
</div>
-{{template "mdhelp"}}
{{end}}
diff --git a/internal/web/templates/dashboard/post_form.html b/internal/web/templates/dashboard/post_form.html
index 7e6f00f..8b4a593 100644
--- a/internal/web/templates/dashboard/post_form.html
+++ b/internal/web/templates/dashboard/post_form.html
@@ -2,7 +2,7 @@
{{define "content"}}
<div class="card">
<h1>{{if .Data.post.ID}}Edit post{{else}}New post{{end}}</h1>
- <form method="post">
+ <form method="post" enctype="multipart/form-data">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
<label>Title<br><input name="title" value="{{.Data.post.Title}}" required maxlength="200" autofocus></label>
<div class="row">
@@ -12,8 +12,8 @@
<label>Address <span class="muted">(optional, made from the title)</span><br>
<input name="slug" value="{{.Data.post.Slug}}" pattern="[a-z0-9]([a-z0-9-]*[a-z0-9])?" maxlength="80"></label>
</div>
- <label>Content <span class="muted">(Markdown)</span><br>
- <textarea name="body" rows="24" class="editor">{{.Data.post.BodyMD}}</textarea></label>
+ <label for="ed-body">Content <span class="muted">(Markdown)</span></label>
+ {{template "editor" (dict "name" "body" "value" .Data.post.BodyMD "rows" 24 "tall" true "upload" (print "/b/" .Blog.Subdomain "/images/upload") "csrf" .CSRF)}}
<label class="check"><input type="checkbox" name="published"{{if .Data.post.Published}} checked{{end}}> Visible on the blog</label>
<p class="actionrow">
<button type="submit">Save</button>
@@ -22,5 +22,4 @@
</p>
</form>
</div>
-{{template "mdhelp"}}
{{end}}
diff --git a/internal/web/templates/partials/editor.html b/internal/web/templates/partials/editor.html
new file mode 100644
index 0000000..8e0acaf
--- /dev/null
+++ b/internal/web/templates/partials/editor.html
@@ -0,0 +1,44 @@
+{{define "editor"}}<textarea name="{{.name}}" id="ed-{{.name}}" rows="{{.rows}}"{{if .tall}} class="editor"{{end}}>{{.value}}</textarea>
+<div class="editor-tools">
+ <label class="upload">Insert image <input type="file" name="inline_image" id="ed-{{.name}}-file" accept="image/*"></label>
+ <span class="upload-status" id="ed-{{.name}}-status"></span>
+ <span class="muted small">…or paste / drop an image into the text.</span>
+ <noscript><span class="muted small">The image is added at the end of the text when you save.</span></noscript>
+</div>
+{{template "mdhelp"}}
+<script>
+(function () {
+ var ta = document.getElementById("ed-{{.name}}"), inp = document.getElementById("ed-{{.name}}-file"), st = document.getElementById("ed-{{.name}}-status");
+ if (!window.fetch || !window.FormData || !ta || !inp) return; // old browser: the file is uploaded on save instead
+ function insert(text) {
+ var s = ta.selectionStart, e = ta.selectionEnd, pre = ta.value.slice(0, s), post = ta.value.slice(e);
+ var chunk = (pre && !/\n$/.test(pre) ? "\n" : "") + text + "\n";
+ ta.value = pre + chunk + post;
+ ta.selectionStart = ta.selectionEnd = (pre + chunk).length;
+ ta.focus();
+ }
+ function upload(file) {
+ st.textContent = "Uploading " + file.name + "…";
+ var fd = new FormData();
+ fd.append("_csrf", {{.csrf}});
+ fd.append("file", file);
+ fetch({{.upload}}, { method: "POST", body: fd, credentials: "same-origin", headers: { Accept: "application/json" } })
+ .then(function (r) { return r.json().then(function (j) { if (!r.ok || j.error) throw new Error(j.error || "upload failed"); return j; }); })
+ .then(function (j) { insert(j.markdown); st.textContent = "Inserted " + j.filename + "."; inp.value = ""; })
+ .catch(function (err) { st.textContent = "Could not upload: " + err.message; });
+ }
+ inp.addEventListener("change", function () { if (inp.files && inp.files[0]) upload(inp.files[0]); });
+ ta.addEventListener("paste", function (ev) {
+ var items = ev.clipboardData && ev.clipboardData.items;
+ if (!items) return;
+ for (var i = 0; i < items.length; i++) {
+ if (items[i].kind === "file" && /^image\//.test(items[i].type)) { ev.preventDefault(); upload(items[i].getAsFile()); return; }
+ }
+ });
+ ta.addEventListener("dragover", function (ev) { ev.preventDefault(); });
+ ta.addEventListener("drop", function (ev) {
+ var f = ev.dataTransfer && ev.dataTransfer.files && ev.dataTransfer.files[0];
+ if (f && /^image\//.test(f.type)) { ev.preventDefault(); upload(f); }
+ });
+})();
+</script>{{end}}
diff --git a/internal/web/templates/partials/mdhelp.html b/internal/web/templates/partials/mdhelp.html
index 4623c07..3584025 100644
--- a/internal/web/templates/partials/mdhelp.html
+++ b/internal/web/templates/partials/mdhelp.html
@@ -2,7 +2,7 @@
<summary>Formatting cheat-sheet</summary>
<table class="cheat">
<tr><td><code># Heading</code>, <code>## Smaller heading</code></td><td><code>**bold**</code>, <code>*italic*</code></td></tr>
- <tr><td><code>[link text](https://example.org)</code></td><td><code>![description](/media/…)</code> — or use <em>Insert image</em></td></tr>
+ <tr><td><code>[link text](https://example.org)</code></td><td><code>![description](/media/…)</code> — <em>Insert image</em> writes this for you</td></tr>
<tr><td><code>- list item</code> / <code>1. numbered</code></td><td><code>&gt; quote</code>, <code>`code`</code>, <code>---</code> for a line</td></tr>
</table>
<p class="muted small">Blank line = new paragraph. Press Enter once for a line break.</p>