Initial Commit
This commit is contained in:
commit
ddb78fe62a
|
@ -0,0 +1,247 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Page struct {
|
||||||
|
Title string
|
||||||
|
Body template.HTML
|
||||||
|
BodyOrig string
|
||||||
|
Concept template.HTML
|
||||||
|
}
|
||||||
|
|
||||||
|
var templates = template.Must(template.ParseFiles("templates/edit.html", "templates/view.html"))
|
||||||
|
|
||||||
|
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
|
||||||
|
err := templates.ExecuteTemplate(w, tmpl+".html", p)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Page) save() error {
|
||||||
|
filename := path.Join("data", p.Title+".kdb")
|
||||||
|
return ioutil.WriteFile(filename, []byte(p.Body), 0600)
|
||||||
|
}
|
||||||
|
|
||||||
|
var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9\\-]+)(/?)$")
|
||||||
|
|
||||||
|
func getTitle(w http.ResponseWriter, r *http.Request) (string, error) {
|
||||||
|
m := validPath.FindStringSubmatch(r.URL.Path)
|
||||||
|
if m == nil {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return "", errors.New("Invalid Page Title")
|
||||||
|
}
|
||||||
|
return m[2], nil // The title is the second subexpression.
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasPage(word string) bool {
|
||||||
|
|
||||||
|
if _, err := os.Stat(path.Join("data/", normalize(word)+".kdb")); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadPage(title string) (*Page, error) {
|
||||||
|
filename := title + ".kdb"
|
||||||
|
bodyb, err := ioutil.ReadFile(path.Join("data", filename))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body := template.HTMLEscapeString(string(bodyb))
|
||||||
|
|
||||||
|
concept := template.HTML(construct_graph(title))
|
||||||
|
|
||||||
|
for _, word := range strings.Fields(body) {
|
||||||
|
if hasPage(word) {
|
||||||
|
body = strings.ReplaceAll(body, word, fmt.Sprintf("<a href='/view/%v/'>%v</a>", normalize(word), normalize(word)))
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(word, "http") {
|
||||||
|
body = strings.ReplaceAll(body, word, fmt.Sprintf("<a href='%v'>%v</a>", word, word))
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(word, "data:image/png;base64,") {
|
||||||
|
body = strings.ReplaceAll(body, word, fmt.Sprintf("<img width=200px height=200px src='%v'/>", word))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Page{Title: title, Body: template.HTML(body), BodyOrig: string(bodyb), Concept: concept}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalize(word string) string {
|
||||||
|
if strings.HasPrefix(word, "typeof:") {
|
||||||
|
word = word[7:]
|
||||||
|
} else if strings.HasPrefix(word, "subtype:") {
|
||||||
|
word = word[8:]
|
||||||
|
} else if strings.HasPrefix(word, "instance:") {
|
||||||
|
word = word[9:]
|
||||||
|
}
|
||||||
|
word = strings.TrimSpace(word)
|
||||||
|
return word
|
||||||
|
}
|
||||||
|
|
||||||
|
func viewHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
title, err := getTitle(w, r)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p, err := loadPage(title)
|
||||||
|
if err != nil {
|
||||||
|
http.Redirect(w, r, "/edit/"+title, http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
renderTemplate(w, "view", p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func editHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
title, err := getTitle(w, r)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p, err := loadPage(title)
|
||||||
|
if err != nil {
|
||||||
|
p = &Page{Title: title}
|
||||||
|
}
|
||||||
|
renderTemplate(w, "edit", p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
title, err := getTitle(w, r)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
body := r.FormValue("body")
|
||||||
|
p := &Page{Title: title, Body: template.HTML(body)}
|
||||||
|
err = p.save()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, "/view/"+title, http.StatusFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func get_links(name string) (links []string) {
|
||||||
|
filename := name + ".kdb"
|
||||||
|
bodyb, err := ioutil.ReadFile(path.Join("data", filename))
|
||||||
|
if err != nil {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
body := template.HTMLEscapeString(string(bodyb))
|
||||||
|
for _, word := range strings.Fields(body) {
|
||||||
|
if hasPage(word) {
|
||||||
|
links = append(links, word)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func construct_graph(word string) string {
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
graph := "digraph {\n\""+word+"\";\n"
|
||||||
|
graph = construct_graph_sub(graph, word, seen, 1)
|
||||||
|
graph += "}"
|
||||||
|
ioutil.WriteFile("tmp.dot", []byte(graph), 0640)
|
||||||
|
subProcess := exec.Command("dot", "-Tsvg", "-otmp.svg", "tmp.dot")
|
||||||
|
subProcess.Run()
|
||||||
|
subProcess.Wait()
|
||||||
|
data, _ := ioutil.ReadFile("tmp.svg")
|
||||||
|
return "<div class=\"conceptgraph\">"+strings.ReplaceAll(string(data), `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||||
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">`, "")+"</svg>"
|
||||||
|
}
|
||||||
|
|
||||||
|
func construct_graph_sub(graph string, word string, seen map[string]bool, depth int) string {
|
||||||
|
if depth >= 0 {
|
||||||
|
} else {
|
||||||
|
return graph
|
||||||
|
}
|
||||||
|
links := get_links(normalize(word))
|
||||||
|
if !seen[normalize(word)] {
|
||||||
|
graph += fmt.Sprintf("\"%v\" [href=\"/view/%v\"];\n", normalize(word), normalize(word))
|
||||||
|
}
|
||||||
|
|
||||||
|
seen[normalize(word)] = true
|
||||||
|
seen[word] = true
|
||||||
|
graph += "# " + word + "\n"
|
||||||
|
for _, link := range links {
|
||||||
|
|
||||||
|
if strings.HasPrefix(word, "typeof:") || strings.HasPrefix(word, "instance:") || strings.HasPrefix(word, "subtype:") {
|
||||||
|
word = normalize(word)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !seen[normalize(link)] {
|
||||||
|
graph += fmt.Sprintf("\"%v\" [href=\"/view/%v\"];\n", normalize(link), normalize(link))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if !seen[normalize(word)+"++"+normalize(link)] || !seen[normalize(link)+"++"+normalize(word)] {
|
||||||
|
if strings.HasPrefix(link, "typeof:") {
|
||||||
|
graph += fmt.Sprintf("\"%v\" -> \"%v\" [style=dotted]\n", link[7:], word)
|
||||||
|
seen[word+"++"+link[7:]] = true
|
||||||
|
seen[link[7:]+"++"+word] = true
|
||||||
|
} else if strings.HasPrefix(link, "subtype:") {
|
||||||
|
graph += fmt.Sprintf("\"%v\" -> \"%v\" [style=dotted]\n", word, link[8:])
|
||||||
|
seen[word+"++"+link[8:]] = true
|
||||||
|
seen[link[8:]+"++"+word] = true
|
||||||
|
} else if strings.HasPrefix(link, "instance:") {
|
||||||
|
graph += fmt.Sprintf("\"%v\" -> \"%v\" [style=dotted]\n", word, link[9:])
|
||||||
|
seen[word+"++"+link[9:]] = true
|
||||||
|
seen[link[9:]+"++"+word] = true
|
||||||
|
} else {
|
||||||
|
graph += fmt.Sprintf("\"%v\" -> \"%v\"\n", word, link)
|
||||||
|
}
|
||||||
|
|
||||||
|
seen[normalize(word)+"++"+normalize(link)] = true
|
||||||
|
seen[normalize(link)+"++"+normalize(word)] = true
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
if !seen[link] {
|
||||||
|
graph = construct_graph_sub(graph, link, seen, depth-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return graph
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
root := "./data/"
|
||||||
|
var files []string
|
||||||
|
filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if path != "./data/" {
|
||||||
|
files = append(files, path[5:])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
body := ""
|
||||||
|
for _, file := range files {
|
||||||
|
name := strings.ReplaceAll(file, ".kdb", "")
|
||||||
|
|
||||||
|
body += fmt.Sprintf("<a href='/view/%v'>%v</a><br/>", name, name)
|
||||||
|
}
|
||||||
|
renderTemplate(w, "view", &Page{Title: "Sigil KDB", Body: template.HTML(body), BodyOrig: ""})
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/", indexHandler)
|
||||||
|
http.HandleFunc("/view/", viewHandler)
|
||||||
|
http.HandleFunc("/edit/", editHandler)
|
||||||
|
http.HandleFunc("/save/", saveHandler)
|
||||||
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
Observations: the 2/3m threshold doesn't seem to hold in particle models because of local isolation
|
||||||
|
|
||||||
|
See also: subtype:synchronization
|
||||||
|
|
||||||
|
instance:snowflake
|
|
@ -0,0 +1 @@
|
||||||
|
https://www.journals.uchicago.edu/doi/abs/10.1086/281977?journalCode=an
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,3 @@
|
||||||
|
The Lotka–Volterra equations, also known as the predator–prey equations, are a pair of first-order nonlinear differential equations, frequently used to describe the dynamics of biological systems in which two species interact, one as a predator and the other as
|
||||||
|
|
||||||
|
See Also: instance:predator-prey
|
|
@ -0,0 +1,3 @@
|
||||||
|
typeof:synchronization
|
||||||
|
|
||||||
|
See also: typeof:lotka-volterra
|
|
@ -0,0 +1,2 @@
|
||||||
|
Snowflake is a toy typeof:consensus model
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
Adjustment of a clock or watch to show the same time as another.
|
||||||
|
|
||||||
|
* instance:lightning-bugs
|
||||||
|
* instance:predator-prey
|
||||||
|
|
||||||
|
See also: typeof:consensus
|
|
@ -0,0 +1 @@
|
||||||
|
dfgdf
|
|
@ -0,0 +1,6 @@
|
||||||
|
<h1>Editing {{.Title}}</h1>
|
||||||
|
|
||||||
|
<form action="/save/{{.Title}}" method="POST">
|
||||||
|
<div><textarea name="body" rows="20" cols="80">{{printf "%s" .Body}}</textarea></div>
|
||||||
|
<div><input type="submit" value="Save"></div>
|
||||||
|
</form>
|
|
@ -0,0 +1,33 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
width:75%;
|
||||||
|
margin:100px auto;
|
||||||
|
font-family: Chilanka;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
textarea
|
||||||
|
{
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conceptgraph {
|
||||||
|
float:right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>{{.Title}}</h1>
|
||||||
|
<div class=".conceptgraph">{{.Concept}}</div>
|
||||||
|
<div style="white-space:pre-line" >{{.Body}}</div>
|
||||||
|
<br/><br/><br/><br/>
|
||||||
|
<hr/>
|
||||||
|
<h1>Editing {{.Title}}</h1>
|
||||||
|
|
||||||
|
<form style="width:100%;" action="/save/{{.Title}}" method="POST">
|
||||||
|
<div ><textarea name="body" rows="20" cols="80">{{printf "%s" .BodyOrig}}</textarea></div>
|
||||||
|
<div><input type="submit" value="Save"></div>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue