Add minimal markup
This commit is contained in:
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/akikareha/himewiki/internal/format"
|
||||
)
|
||||
|
||||
func render(text string) string {
|
||||
func render(text string) (string, string) {
|
||||
if strings.HasPrefix(text, "=") {
|
||||
return format.Creole(text)
|
||||
} else if strings.HasPrefix(text, "#") {
|
||||
@@ -30,7 +30,7 @@ func View(cfg *config.Config, w http.ResponseWriter, r *http.Request, params *Pa
|
||||
|
||||
tmpl := template.Must(template.ParseFiles("templates/view.html"))
|
||||
escaped := url.PathEscape(params.Name)
|
||||
rendered := render(content)
|
||||
_, rendered := render(content)
|
||||
tmpl.Execute(w, struct {
|
||||
SiteName string
|
||||
Name string
|
||||
@@ -47,32 +47,35 @@ func View(cfg *config.Config, w http.ResponseWriter, r *http.Request, params *Pa
|
||||
func Edit(cfg *config.Config, w http.ResponseWriter, r *http.Request, params *Params) {
|
||||
var previewed bool
|
||||
var content string
|
||||
var preview string
|
||||
var save string
|
||||
if r.Method != http.MethodPost {
|
||||
previewed = false
|
||||
content, _ = data.Load(params.Name)
|
||||
preview = ""
|
||||
save = ""
|
||||
} else {
|
||||
previewed = r.FormValue("previewed") == "true"
|
||||
content = r.FormValue("content")
|
||||
preview := r.FormValue("preview")
|
||||
save := r.FormValue("save")
|
||||
if previewed && save != "" {
|
||||
if err := data.Save(params.Name, content); err != nil {
|
||||
http.Error(w, "Failed to save", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/"+url.PathEscape(params.Name), http.StatusFound)
|
||||
return
|
||||
} else if preview != "" {
|
||||
previewed = true
|
||||
} else {
|
||||
http.Error(w, "Invalid state", http.StatusInternalServerError)
|
||||
preview = r.FormValue("preview")
|
||||
save = r.FormValue("save")
|
||||
}
|
||||
|
||||
normalized, rendered := render(content)
|
||||
|
||||
if previewed && save != "" {
|
||||
if err := data.Save(params.Name, normalized); err != nil {
|
||||
http.Error(w, "Failed to save", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/"+url.PathEscape(params.Name), http.StatusFound)
|
||||
return
|
||||
} else if preview != "" {
|
||||
previewed = true
|
||||
}
|
||||
|
||||
tmpl := template.Must(template.ParseFiles("templates/edit.html"))
|
||||
escaped := url.PathEscape(params.Name)
|
||||
rendered := render(content)
|
||||
tmpl.Execute(w, struct {
|
||||
SiteName string
|
||||
Name string
|
||||
@@ -84,7 +87,7 @@ func Edit(cfg *config.Config, w http.ResponseWriter, r *http.Request, params *Pa
|
||||
SiteName: cfg.Site.Name,
|
||||
Name: params.Name,
|
||||
Escaped: escaped,
|
||||
Text: content,
|
||||
Text: normalized,
|
||||
Rendered: template.HTML(rendered),
|
||||
Previewed: previewed,
|
||||
})
|
||||
|
||||
@@ -66,7 +66,7 @@ func ViewRevision(cfg *config.Config, w http.ResponseWriter, r *http.Request, pa
|
||||
|
||||
tmpl := template.Must(template.ParseFiles("templates/revision.html"))
|
||||
escaped := url.PathEscape(params.Name)
|
||||
rendered := render(content)
|
||||
_, rendered := render(content)
|
||||
tmpl.Execute(w, struct {
|
||||
SiteName string
|
||||
Name string
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package format
|
||||
|
||||
func Creole(text string) string {
|
||||
// TODO
|
||||
return escapeHTML("(Creole)\n" + text)
|
||||
func Creole(text string) (string, string) {
|
||||
return Nomark(text) // TODO
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package format
|
||||
|
||||
func Markdown(text string) string {
|
||||
// TODO
|
||||
return escapeHTML("(Markdown)\n" + text)
|
||||
func Markdown(text string) (string, string) {
|
||||
return Nomark(text) // TODO
|
||||
}
|
||||
|
||||
@@ -1,6 +1,260 @@
|
||||
package format
|
||||
|
||||
func Nomark(text string) string {
|
||||
// TODO
|
||||
return escapeHTML("(Nomark)\n" + text)
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type blockMode int
|
||||
|
||||
const (
|
||||
blockNone blockMode = iota
|
||||
blockParagraph
|
||||
)
|
||||
|
||||
type state struct {
|
||||
data []byte
|
||||
index int
|
||||
text *bytes.Buffer
|
||||
html *bytes.Buffer
|
||||
block blockMode
|
||||
nextLine int
|
||||
lineEnd int
|
||||
}
|
||||
|
||||
func skip(s *state) {
|
||||
for s.index < len(s.data) {
|
||||
c := s.data[s.index]
|
||||
if c != '\r' && c != '\n' && c != ' ' && c != '\t' {
|
||||
break
|
||||
}
|
||||
s.index += 1
|
||||
}
|
||||
}
|
||||
|
||||
func skipEnd(s *state) {
|
||||
i := s.index
|
||||
for i < len(s.data) {
|
||||
c := s.data[i]
|
||||
if c != '\r' && c != '\n' && c != ' ' && c != '\t' {
|
||||
break
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
if i >= len(s.data) {
|
||||
s.index = i
|
||||
}
|
||||
}
|
||||
|
||||
func nextLine(s *state) {
|
||||
if s.nextLine < s.index {
|
||||
s.nextLine = s.index
|
||||
s.lineEnd = s.index
|
||||
}
|
||||
s.index = s.nextLine
|
||||
if s.nextLine >= len(s.data) {
|
||||
return
|
||||
}
|
||||
i := bytes.IndexByte(s.data[s.nextLine:], '\n')
|
||||
if i == -1 {
|
||||
s.nextLine = len(s.data)
|
||||
s.lineEnd = len(s.data)
|
||||
} else {
|
||||
end := s.nextLine + i
|
||||
s.nextLine = end + 1
|
||||
if end > 0 && s.data[end - 1] == '\r' {
|
||||
end -= 1
|
||||
}
|
||||
s.lineEnd = end
|
||||
}
|
||||
}
|
||||
|
||||
func blockEnd(s *state) {
|
||||
if s.block == blockParagraph {
|
||||
s.text.WriteString("\n")
|
||||
s.html.WriteString("\n</p>")
|
||||
skip(s)
|
||||
if s.index < len(s.data) {
|
||||
s.text.WriteString("\n")
|
||||
s.html.WriteString("\n")
|
||||
}
|
||||
}
|
||||
s.block = blockNone
|
||||
}
|
||||
|
||||
func blockBegin(s *state, block blockMode) {
|
||||
blockEnd(s)
|
||||
if block == blockParagraph {
|
||||
s.html.WriteString("<p>\n")
|
||||
}
|
||||
s.block = block
|
||||
}
|
||||
|
||||
func ignore(s *state) bool {
|
||||
c := s.data[s.index]
|
||||
if c == '\r' {
|
||||
s.index += 1
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func wikiLink(s *state) bool {
|
||||
line := s.data[s.index:s.lineEnd]
|
||||
if !bytes.HasPrefix(line, []byte("[[")) {
|
||||
return false
|
||||
}
|
||||
ket := bytes.Index(line[2:], []byte("]]"))
|
||||
if ket == -1 {
|
||||
return false
|
||||
}
|
||||
name := string(line[2:2 + ket])
|
||||
|
||||
s.text.WriteString("[[" + name + "]]")
|
||||
s.html.WriteString("<a href=\"/" + url.PathEscape(name) + "\">" + template.HTMLEscapeString(name) + "</a>")
|
||||
|
||||
s.index += 2 + ket + 2
|
||||
return true
|
||||
}
|
||||
|
||||
func spaceIndex(b []byte) int {
|
||||
for i := 0; i < len(b) ; i += 1 {
|
||||
c := b[i]
|
||||
if c == '\r' || c == '\n' || c == ' ' || c == '\t' {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func link(s *state) bool {
|
||||
line := s.data[s.index:s.lineEnd]
|
||||
if !bytes.HasPrefix(line, []byte("https:")) {
|
||||
return false
|
||||
}
|
||||
space := spaceIndex(line[6:])
|
||||
rawURL := string(line[:6 + space])
|
||||
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil || u.Scheme != "https" {
|
||||
return false
|
||||
}
|
||||
|
||||
checked := u.String()
|
||||
s.text.WriteString(checked)
|
||||
htmlURL := template.HTMLEscapeString(checked)
|
||||
s.html.WriteString("<a href=\"" + htmlURL + "\">" + htmlURL + "</a>")
|
||||
|
||||
s.index += len(rawURL)
|
||||
return true
|
||||
}
|
||||
|
||||
func html(s *state) bool {
|
||||
c := s.data[s.index]
|
||||
if c == '&' {
|
||||
s.index += 1
|
||||
s.text.WriteString("&")
|
||||
s.html.WriteString("&")
|
||||
return true
|
||||
} else if c == '<' {
|
||||
s.index += 1
|
||||
s.text.WriteString("<")
|
||||
s.html.WriteString("<")
|
||||
return true
|
||||
} else if c == '>' {
|
||||
s.index += 1
|
||||
s.text.WriteString(">")
|
||||
s.html.WriteString(">")
|
||||
return true
|
||||
} else if c == '"' {
|
||||
s.index += 1
|
||||
s.text.WriteString("\"")
|
||||
s.html.WriteString(""")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func raw(s *state) bool {
|
||||
c := s.data[s.index]
|
||||
s.index += 1
|
||||
s.text.WriteByte(c)
|
||||
s.html.WriteByte(c)
|
||||
return true
|
||||
}
|
||||
|
||||
func isBlank(b []byte) bool {
|
||||
for _, c := range b {
|
||||
if c != '\r' && c != '\n' && c != ' ' && c != '\t' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func nomarkLine(s *state) {
|
||||
for s.index < s.lineEnd {
|
||||
if ignore(s) {
|
||||
continue
|
||||
} else if wikiLink(s) {
|
||||
continue
|
||||
} else if link(s) {
|
||||
continue
|
||||
} else if html(s) {
|
||||
continue
|
||||
} else if raw(s) {
|
||||
continue
|
||||
} else {
|
||||
panic("program error")
|
||||
}
|
||||
}
|
||||
nextLine(s)
|
||||
skipEnd(s)
|
||||
if s.index < len(s.data) {
|
||||
line := s.data[s.index:s.lineEnd]
|
||||
if !isBlank(line) {
|
||||
s.text.WriteString("\n")
|
||||
s.html.WriteString("<br />\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Nomark(text string) (string, string) {
|
||||
d := []byte(text)
|
||||
s := state {
|
||||
data: d,
|
||||
index: 0,
|
||||
text: new(bytes.Buffer),
|
||||
html: new(bytes.Buffer),
|
||||
block: blockNone,
|
||||
nextLine: 0,
|
||||
lineEnd: 0,
|
||||
}
|
||||
|
||||
skip(&s)
|
||||
blockBegin(&s, blockParagraph)
|
||||
nextLine(&s)
|
||||
for s.index < len(s.data) {
|
||||
line := s.data[s.index:s.lineEnd]
|
||||
if isBlank(line) {
|
||||
blockEnd(&s)
|
||||
blockBegin(&s, blockParagraph)
|
||||
for s.index < len(s.data) {
|
||||
nextLine(&s)
|
||||
if s.index >= len(s.data) {
|
||||
break
|
||||
}
|
||||
line := s.data[s.index:s.lineEnd]
|
||||
if !isBlank(line) {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nomarkLine(&s)
|
||||
}
|
||||
}
|
||||
blockEnd(&s)
|
||||
|
||||
return s.text.String(), s.html.String()
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func escapeHTML(s string) string {
|
||||
r := strings.NewReplacer(
|
||||
"&", "&",
|
||||
"<", "<",
|
||||
">", ">",
|
||||
"\"", """,
|
||||
)
|
||||
return r.Replace(s)
|
||||
}
|
||||
@@ -28,7 +28,9 @@
|
||||
{{if .Previewed}}
|
||||
<h1><a href="/?a=search&t=content&w={{.Name | urlquery}}">{{.Name}}</a></h1>
|
||||
|
||||
<div>{{.Rendered}}</div>
|
||||
<div>
|
||||
{{.Rendered}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
</body>
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
|
||||
<h1>Old Revision - <a href="/?a=search&t=content&w={{.Name | urlquery}}">{{.Name}}</a></h1>
|
||||
|
||||
<div>{{.Rendered}}</div>
|
||||
<div>
|
||||
{{.Rendered}}
|
||||
</div>
|
||||
|
||||
<form action="/{{.Escaped}}?a=revert&i={{.ID}}" method="POST">
|
||||
<input type="submit" name="revert" value="Revert" />
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
|
||||
<h1><a href="/?a=search&t=content&w={{.Name | urlquery}}">{{.Name}}</a></h1>
|
||||
|
||||
<div>{{.Rendered}}</div>
|
||||
<div>
|
||||
{{.Rendered}}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user