summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2016-11-15 00:16:06 -0500
committerLuke Shumaker <lukeshu@sbcglobal.net>2016-11-15 00:16:06 -0500
commit5e1e95852821e138caf20c3f8ee23e3941e42bbc (patch)
treea88f3a3d6284e76116df24c54bd6a6ddc2184770
parent711269a7eb2e151512bcaf1afcf45b13540d8f8e (diff)
better progressive enhancement of editor
-rw-r--r--enhancers.txt91
-rw-r--r--got/page.html.got1
-rw-r--r--got/view_blob.got3
-rw-r--r--src/edit/enhancers.go79
-rw-r--r--src/edit/views.go38
-rw-r--r--static/mde.js20
-rw-r--r--static/style.css35
7 files changed, 225 insertions, 42 deletions
diff --git a/enhancers.txt b/enhancers.txt
new file mode 100644
index 0000000..22c684b
--- /dev/null
+++ b/enhancers.txt
@@ -0,0 +1,91 @@
+--boundary
+X-Thing: Pattern
+
+text/markdown
+--boundary
+X-Thing: Head
+
+<link rel="stylesheet" href="/static/font-awesome/css/font-awesome.min.css">
+<link rel="stylesheet" href="/static/simplemde/dist/simplemde.min.css">
+<script src="/static/simplemde/dist/simplemde.min.js"></script>
+--boundary
+X-Thing: Tail
+
+<script>
+(function() {
+ var textarea = document.getElementsByTagName('textarea')[0];
+ var form = textarea.form;
+ var container = document.createElement('div');
+ form.insertBefore(container, textarea);
+ container.appendChild(textarea);
+
+ var tip = document.createElement('aside');
+ tip.innerHTML =
+ '<p>Tip: To set the page title (what appears in the tab '+
+ 'name/window bar), put this at the top of the page:</p>\n'+
+ '<pre>---\ntitle: "Your Title Here"\n---\n</pre>'+
+ '<p>I apologize that it looks funny on this page and in the '+
+ 'preview.</p>';
+ container.appendChild(tip);
+
+ var simplemde = new SimpleMDE({
+ autoDownloadFontAwesome: false,
+ element: textarea,
+ promptURLs: true,
+ forceSync: true,
+ showIcons: ['code', 'table'],
+ });
+})();
+</script>
+--boundary
+X-Thing: Pattern
+
+text/*; */*+xml
+--boundary
+X-Thing: Head
+
+<link rel="stylesheet" href="/static/codemirror/lib/codemirror.css">
+<script src="/static/codemirror/lib/codemirror.js"></script>
+
+
+<script src="/static/codemirror/mode/xml/xml.js"></script>
+<script src="/static/codemirror/mode/javascript/javascript.js"></script>
+<script src="/static/codemirror/mode/css/css.js"></script>
+<script src="/static/codemirror/mode/sass/sass.js"></script>
+<script src="/static/codemirror/mode/htmlmixed/htmlmixed.js"></script>
+<script src="/static/codemirror/addon/edit/matchbrackets.js"></script>
+<script src="/static/codemirror/addon/edit/trailingspace.js"></script>
+<script src="/static/codemirror/addon/display/rulers.js"></script>
+
+<script src="/static/codemirror/addon/fold/foldcode.js"></script>
+<script src="/static/codemirror/addon/fold/foldgutter.js"></script>
+<link rel="stylesheet" href="/static/codemirror/addon/fold/foldgutter.css">
+<script src="/static/codemirror/addon/fold/brace-fold.js"></script>
+<script src="/static/codemirror/addon/fold/xml-fold.js"></script>
+<script src="/static/codemirror/addon/fold/markdown-fold.js"></script>
+<script src="/static/codemirror/addon/fold/comment-fold.js"></script>
+--boundary
+X-Thing: Tail
+
+<script>
+(function() {
+ var textarea = document.getElementsByTagName('textarea')[0];
+ var form = textarea.form;
+ var container = document.createElement('div');
+ form.insertBefore(container, textarea);
+ container.appendChild(textarea);
+ var cm = CodeMirror.fromTextArea(textarea, {
+ mode: ctype,
+ lineNumbers: true,
+ matchBrackets: true,
+ foldGutter: true,
+ rulers: [{column: 80, lineStyle: "dashed"}],
+ showTrailingSpace: true,
+ gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
+ });
+ cm.on("change", function() {
+ cm.save();
+ });
+})();
+</script>
+--boundary--
diff --git a/got/page.html.got b/got/page.html.got
index c061d50..7f2c16c 100644
--- a/got/page.html.got
+++ b/got/page.html.got
@@ -13,5 +13,6 @@
</head>
<body>
{{.body}}
+ {{.tail}}
</body>
</html>
diff --git a/got/view_blob.got b/got/view_blob.got
index 95692b0..752d5c7 100644
--- a/got/view_blob.got
+++ b/got/view_blob.got
@@ -32,6 +32,3 @@
</label>
<input type="submit" value="Delete">
</form>
-{{if eq .ctype "text/markdown"}}
-<script src="/static/mde.js"></script>
-{{end}}
diff --git a/src/edit/enhancers.go b/src/edit/enhancers.go
new file mode 100644
index 0000000..b732ab2
--- /dev/null
+++ b/src/edit/enhancers.go
@@ -0,0 +1,79 @@
+package main
+
+import (
+ "io"
+ "io/ioutil"
+ "os"
+ "mime/multipart"
+ "strings"
+ "path"
+
+ "fmt"
+)
+
+type enhancer struct {
+ pattern []string
+ head string
+ tail string
+}
+
+func loadEnhancers(filename string) []enhancer {
+ file, err := os.Open(filename)
+ errcheck(err)
+
+ reader := multipart.NewReader(file, "boundary")
+
+ var s uint8 = 0
+ var e enhancer
+ var ret []enhancer
+ for {
+ part, err := reader.NextPart()
+ if err == io.EOF {
+ return ret
+ }
+ errcheck(err)
+
+ k := part.Header.Get("X-Thing")
+ v, err := ioutil.ReadAll(part)
+ errcheck(err)
+
+ switch k {
+ case "Pattern":
+ patterns := strings.Split(string(v), ";")
+ for i := range patterns {
+ patterns[i] = strings.TrimSpace(patterns[i])
+ }
+ s |= 1<<0
+ e.pattern = patterns
+ case "Head":
+ s |= 1<<1
+ e.head = string(v)
+ case "Tail":
+ s |= 1<<2
+ e.tail = string(v)
+ default:
+ panic("unknown X-Thing: "+k)
+ }
+ if s == 1<<0 | 1<<1 | 1<<2 {
+ ret = append(ret, e)
+ s = 0
+ }
+ }
+}
+
+var enhancers = loadEnhancers("enhancers.txt")
+
+func getEnhancer(ctype string) (head, tail string) {
+ fmt.Fprintf(os.Stderr, "enhancers = %q\n", enhancers)
+ for _, enhancer := range enhancers {
+ for _, pattern := range enhancer.pattern {
+ matched, err := path.Match(pattern, ctype)
+ errcheck(err)
+ fmt.Fprintf(os.Stderr, "Match(%q, %q) => %q\n", pattern, ctype, matched)
+ if matched {
+ return enhancer.head, enhancer.tail
+ }
+ }
+ }
+ return "", ""
+}
diff --git a/src/edit/views.go b/src/edit/views.go
index 1d3176d..a2890d4 100644
--- a/src/edit/views.go
+++ b/src/edit/views.go
@@ -1,6 +1,7 @@
package main
import (
+ "fmt"
"bytes"
"io"
"path"
@@ -24,11 +25,12 @@ var (
tmplDeleted = newTemplate("got/deleted.got")
)
-func renderPage(w io.Writer, title, head, body string) error {
+func renderPage(w io.Writer, title, head, body, tail string) error {
return tmplPage.Execute(w, map[string]string{
"title": title,
"head": head,
"body": body,
+ "tail": tail,
})
}
@@ -47,8 +49,8 @@ func renderViewTree(w io.Writer, upath string, tree GitTree) error {
}
// Component Render
- var buf bytes.Buffer
- err := tmplViewTree.Execute(&buf, map[string]interface{}{
+ var body bytes.Buffer
+ err := tmplViewTree.Execute(&body, map[string]interface{}{
"path": upath,
"files": files,
})
@@ -56,7 +58,7 @@ func renderViewTree(w io.Writer, upath string, tree GitTree) error {
return err
}
// Page render
- return renderPage(w, upath, "", buf.String())
+ return renderPage(w, upath, "", body.String(), "")
}
func renderViewBlob(w io.Writer, upath string, file GitFile) error {
@@ -67,8 +69,8 @@ func renderViewBlob(w io.Writer, upath string, file GitFile) error {
}
ctype := getctype(upath, content)
// Component render
- var buf bytes.Buffer
- err = tmplViewBlob.Execute(&buf, map[string]string{
+ var body bytes.Buffer
+ err = tmplViewBlob.Execute(&body, map[string]string{
"path": upath,
"ctype": ctype,
"content": string(content),
@@ -76,38 +78,36 @@ func renderViewBlob(w io.Writer, upath string, file GitFile) error {
if err != nil {
return err
}
- head := ""
- if ctype == "text/markdown" {
- head += "<link rel=\"stylesheet\" href=\"/static/font-awesome/css/font-awesome.min.css\">\n"
- head += "<link rel=\"stylesheet\" href=\"/static/simplemde/dist/simplemde.min.css\">\n"
- head += "<script src=\"/static/simplemde/dist/simplemde.min.js\"></script>\n"
- }
+
+ head, tail := getEnhancer(ctype)
+ head = fmt.Sprintf("<script>ctype = \"%s\"</script>\n%s", template.JSEscapeString(ctype), head);
+
// Page render
- return renderPage(w, upath, head, buf.String())
+ return renderPage(w, upath, head, body.String(), tail)
}
func renderModified(w io.Writer, upath string) error {
// Component render
- var buf bytes.Buffer
- err := tmplModified.Execute(&buf, map[string]string{
+ var body bytes.Buffer
+ err := tmplModified.Execute(&body, map[string]string{
"path": upath,
})
if err != nil {
return err
}
// Page render
- return renderPage(w, upath, "", buf.String())
+ return renderPage(w, upath, "", body.String(), "")
}
func renderDeleted(w io.Writer, upath string) error {
// Component render
- var buf bytes.Buffer
- err := tmplDeleted.Execute(&buf, map[string]string{
+ var body bytes.Buffer
+ err := tmplDeleted.Execute(&body, map[string]string{
"path": upath,
})
if err != nil {
return err
}
// Page render
- return renderPage(w, upath, "", buf.String())
+ return renderPage(w, upath, "", body.String(), "")
}
diff --git a/static/mde.js b/static/mde.js
deleted file mode 100644
index 7471110..0000000
--- a/static/mde.js
+++ /dev/null
@@ -1,20 +0,0 @@
-(function() {
- var textarea = document.getElementsByTagName("textarea")[0];
- var form = textarea.form;
- var container = document.createElement('div');
- /*container.innerHTML =
- "<p>I know it looks funny in the editor, but to set the page "+
- "title (what appears in the tab name/window bar), put this at "+
- "the top of the page:</p>\n"+
- "<pre>---\ntitle: \"Your Title Here\"\n---\n</pre>";*/
- form.insertBefore(container, textarea);
- container.appendChild(textarea);
-
- var simplemde = new SimpleMDE({
- autoDownloadFontAwesome: false,
- element: textarea,
- promptURLs: true,
- forceSync: true,
- showIcons: ["code", "table"],
- });
-})();
diff --git a/static/style.css b/static/style.css
index cad83c0..2ce7fa8 100644
--- a/static/style.css
+++ b/static/style.css
@@ -34,3 +34,38 @@ input[type=file] {
background: #EEEEEE;
border-radius: 4px;
}
+
+kbd, code, samp, tt, pre {
+ background: #DDDDFF;
+ white-space: pre;
+}
+pre {
+ margin: auto 2em;
+ padding: .5em;
+ overflow: auto;
+ border: solid 1px #AAAAAA;
+}
+
+aside {
+ padding: 10px;
+ border-radius: 4px;
+ border: solid 1px #ddd;
+ background: rgb(240, 240, 240);
+ opacity: 0.7;
+}
+
+.CodeMirror {
+ /* match Firefox's built-in style for textearea */
+ border: 1px solid rgb(232, 232, 231);
+}
+.CodeMirror .cm-trailingspace {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QUXCToH00Y1UgAAACFJREFUCNdjPMDBUc/AwNDAAAFMTAwMDA0OP34wQgX/AQBYgwYEx4f9lQAAAABJRU5ErkJggg==);
+ background-position: bottom left;
+ background-repeat: repeat-x;
+}
+.CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word) {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QUXCToH00Y1UgAAACFJREFUCNdjPMDBUc/AwNDAAAFMTAwMDA0OP34wQgX/AQBYgwYEx4f9lQAAAABJRU5ErkJggg==) !important;
+ background-position: bottom left !important;
+ background-repeat: repeat-x !important;
+ background-color: transparent !important;
+}