Sidenote-Start

This commit is contained in:
Simon Martens
2025-05-16 16:02:35 +02:00
parent 5dd9f446d7
commit d8d713bef6
2 changed files with 179 additions and 139 deletions

View File

@@ -1,6 +1,7 @@
package functions package functions
import ( import (
"math/rand"
"strconv" "strconv"
"strings" "strings"
@@ -8,13 +9,18 @@ import (
"github.com/Theodor-Springmann-Stiftung/lenz-web/xmlmodels" "github.com/Theodor-Springmann-Stiftung/lenz-web/xmlmodels"
) )
type outType int const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var endDivToken = outToken{ func RandString(length int) string {
Type: EndElement, b := make([]byte, length)
Name: "div", for i := range b {
b[i] = charset[rand.Intn(len(charset))]
}
return string(b)
} }
type outType int
const ( const (
NA outType = iota NA outType = iota
Text Text
@@ -24,11 +30,58 @@ const (
) )
type outToken struct { type outToken struct {
Type outType Type outType
Name string Name string
Classes []string Classes []string
Id string Id string
Value string Value string
Attributes map[string]string
}
func (o outToken) String() string {
switch o.Type {
case Text:
return o.Value
case Element:
builder := strings.Builder{}
builder.WriteString("<")
builder.WriteString(o.Name)
if len(o.Classes) > 0 {
builder.WriteString(" class=\"")
builder.WriteString(strings.Join(o.Classes, " "))
builder.WriteString("\"")
}
if len(o.Id) > 0 {
builder.WriteString(" id=\"")
builder.WriteString(o.Id)
builder.WriteString("\"")
}
builder.WriteString(">")
return builder.String()
case EndElement:
return "</" + o.Name + ">"
case EmptyElement:
builder := strings.Builder{}
builder.WriteString("<")
builder.WriteString(o.Name)
if len(o.Classes) > 0 {
builder.WriteString(" class=\"")
builder.WriteString(strings.Join(o.Classes, " "))
builder.WriteString("\"")
}
if len(o.Id) > 0 {
builder.WriteString(" id=\"")
builder.WriteString(o.Id)
builder.WriteString("\"")
}
builder.WriteString("/>")
}
return ""
} }
func (o *outToken) ClassesFromAttrs(attrs map[string]string) { func (o *outToken) ClassesFromAttrs(attrs map[string]string) {
@@ -67,45 +120,69 @@ type LenzParseState struct {
func (s *LenzParseState) String() string { func (s *LenzParseState) String() string {
builder := strings.Builder{} builder := strings.Builder{}
for _, token := range s.Out { for _, token := range s.Out {
switch token.Type { builder.WriteString(token.String())
case Text:
builder.WriteString(token.Value)
case Element:
builder.WriteString("<")
builder.WriteString(token.Name)
if len(token.Classes) > 0 {
builder.WriteString(" class=\"")
builder.WriteString(strings.Join(token.Classes, " "))
builder.WriteString("\"")
}
if len(token.Id) > 0 {
builder.WriteString(" id=\"")
builder.WriteString(token.Id)
builder.WriteString("\"")
}
builder.WriteString(">")
case EndElement:
builder.WriteString("</div>")
case EmptyElement:
builder.WriteString("<")
builder.WriteString(token.Name)
if len(token.Classes) > 0 {
builder.WriteString(" class=\"")
builder.WriteString(strings.Join(token.Classes, " "))
builder.WriteString("\"")
}
if len(token.Id) > 0 {
builder.WriteString(" id=\"")
builder.WriteString(token.Id)
builder.WriteString("\"")
}
builder.WriteString("/>")
}
} }
return builder.String() return builder.String()
} }
func (s *LenzParseState) AppendDefaultElement(token xmlparsing.Token, ids ...string) {
t := Default(token)
if len(ids) > 0 {
t.Id = ids[0]
}
s.Out = append(s.Out, t)
}
func (s *LenzParseState) AppendEndElement() {
for i := len(s.Out) - 1; i >= 0; i-- {
if s.Out[i].Type == Element {
s.Out = append(s.Out, outToken{
Name: s.Out[i].Name,
Type: EndElement,
})
return
}
}
}
func (s *LenzParseState) AppendDivElement(id string, classes ...string) {
s.Out = append(s.Out, outToken{
Name: "div",
Id: id,
Classes: classes,
})
}
func (s *LenzParseState) AppendEmptyElement(name string, id string, classes ...string) {
s.Out = append(s.Out, outToken{
Name: name,
Id: id,
Classes: classes,
Type: EmptyElement,
})
}
func (s *LenzParseState) AppendLink(href string, classes ...string) {
s.Out = append(s.Out, outToken{
Name: "a",
Attributes: map[string]string{"href": href},
Classes: classes,
Type: Element,
})
}
func (s *LenzParseState) AppendText(text string) {
s.Out = append(s.Out, outToken{
Type: Text,
Value: text,
})
}
func (s *LenzParseState) Append(token outToken) {
s.Out = append(s.Out, token)
}
func Parse(lib *xmlmodels.Library) func(s string) string { func Parse(lib *xmlmodels.Library) func(s string) string {
return func(s string) string { return func(s string) string {
if len(s) == 0 { if len(s) == 0 {
@@ -120,124 +197,87 @@ func Parse(lib *xmlmodels.Library) func(s string) string {
} }
if elem.Token.Type < 3 { if elem.Token.Type < 3 {
if !ps.CloseElement && elem.Token.Type == xmlparsing.EndElement { if elem.Token.Type == xmlparsing.EndElement {
ps.CloseElement = true if ps.CloseElement {
continue ps.AppendEndElement()
} else if elem.Token.Type == xmlparsing.EndElement { } else {
ps.Out = append(ps.Out, Default(elem.Token)) ps.CloseElement = true
}
continue continue
} }
defaultToken := Default(elem.Token)
switch elem.Token.Name { switch elem.Token.Name {
case "sidenote": case "sidenote":
nt := outToken{ id := RandString(8)
Type: Element, if elem.Token.Attributes["annotation"] != "" ||
Name: "div", elem.Token.Attributes["page"] != "" ||
Classes: []string{"sidenote"}, elem.Token.Attributes["pos"] != "" {
ps.AppendLink("#"+id, "nanchor-sidenote")
ps.AppendEndElement()
ps.AppendDivElement(id, "note-sidenote-meta")
if elem.Token.Attributes["annotation"] != "" {
ps.AppendDivElement("", "sidenote-note")
ps.AppendText(elem.Token.Attributes["annotation"])
ps.AppendEndElement()
}
if elem.Token.Attributes["page"] != "" {
ps.AppendDivElement("", "sidenote-page")
ps.AppendText(elem.Token.Attributes["page"])
ps.AppendEndElement()
}
if elem.Token.Attributes["pos"] != "" {
ps.AppendDivElement("", "sidenote-pos")
ps.AppendText(elem.Token.Attributes["pos"])
ps.AppendEndElement()
}
ps.AppendEndElement() // sidenote-meta
} }
ps.Out = append(ps.Out, nt) ps.AppendDefaultElement(elem.Token, id)
metatoken := outToken{
Type: Element,
Name: "div",
Classes: []string{"sidenote-meta"},
}
ps.Out = append(ps.Out, metatoken)
if elem.Token.Attributes["annotation"] != "" {
note := outToken{
Type: Element,
Name: "div",
Classes: []string{"sidenote-note"},
}
notecontent := outToken{
Type: Text,
Value: elem.Token.Attributes["annotation"],
}
ps.Out = append(ps.Out, note, notecontent, endDivToken)
}
if elem.Token.Attributes["page"] != "" {
note := outToken{
Type: Element,
Name: "div",
Classes: []string{"sidenote-page"},
}
notecontent := outToken{
Type: Text,
Value: elem.Token.Attributes["page"],
}
ps.Out = append(ps.Out, note, notecontent, endDivToken)
}
if elem.Token.Attributes["pos"] != "" {
note := outToken{
Type: Element,
Name: "div",
Classes: []string{"sidenote-pos"},
}
notecontent := outToken{
Type: Text,
Value: elem.Token.Attributes["pos"],
}
ps.Out = append(ps.Out, note, notecontent, endDivToken)
}
ps.Out = append(ps.Out, endDivToken)
case "note": case "note":
nt := outToken{ id := RandString(8)
Type: Element, ps.AppendLink("#"+id, "nanchor-note")
Name: "div", ps.AppendEndElement()
Classes: []string{"note-anchor"}, ps.AppendDivElement(id, "note", "note-note")
}
ps.Out = append(ps.Out, nt, endDivToken)
case "hand": case "hand":
nt := outToken{
Type: Element,
Name: "div",
Classes: []string{"hand"},
}
ps.Out = append(ps.Out, nt)
id := elem.Token.Attributes["ref"] id := elem.Token.Attributes["ref"]
idno, err := strconv.Atoi(id) idno, err := strconv.Atoi(id)
var person *xmlmodels.PersonDef var person *xmlmodels.PersonDef
if err == nil { if err == nil {
person = lib.Persons.Item(idno) person = lib.Persons.Item(idno)
} }
handtok := outToken{ ps.AppendLink("#"+id, "nanchor-hand")
Type: Element, ps.AppendEndElement()
Name: "div", ps.AppendDivElement(id, "note-hand")
Classes: []string{"hand-person"}, hand := "N/A"
}
defhand := outToken{
Type: Text,
Value: "N/A",
}
if person != nil { if person != nil {
defhand.Value = person.Name hand = person.Name
} }
ps.Out = append(ps.Out, handtok, defhand, endDivToken) ps.AppendText(hand)
ps.AppendEndElement()
ps.AppendDefaultElement(elem.Token)
case "line": case "line":
nt := outToken{
Type: EmptyElement,
Name: "br",
}
if val := elem.Token.Attributes["type"]; val != "empty" { if val := elem.Token.Attributes["type"]; val != "empty" {
ps.Out = append(ps.Out, nt)
ps.LC += 1 ps.LC += 1
ps.Out = append(ps.Out, defaultToken) ps.AppendEmptyElement("br", ps.PC+"-"+strconv.Itoa(ps.LC))
ps.AppendDefaultElement(elem.Token) // This is for indents, must be closed
} else { } else {
nt.Classes = []string{"empty"} ps.AppendEmptyElement("br", "", "empty")
ps.Out = append(ps.Out, nt) ps.CloseElement = false // Here Indents make no sense, so we dont open an element
ps.CloseElement = false
} }
case "page": case "page":
ps.LC = 0
ps.PC = elem.Token.Attributes["index"] ps.PC = elem.Token.Attributes["index"]
ps.Out = append(ps.Out, defaultToken) ps.AppendLink("#"+ps.PC, "eanchor-page")
nt := outToken{ ps.AppendEndElement()
Type: Text, ps.AppendDivElement(ps.PC, "page")
Value: "[" + ps.PC + "]", ps.AppendText(ps.PC)
}
ps.Out = append(ps.Out, nt)
default: default:
ps.Out = append(ps.Out, defaultToken) ps.AppendDefaultElement(elem.Token)
} }
} }
} }

View File

@@ -4,7 +4,7 @@
<div class="tradition-header font-bold"> <div class="tradition-header font-bold">
{{- (App $trad.Reference).Name -}} {{- (App $trad.Reference).Name -}}
</div> </div>
<div class="tradition-text"> <div class="tradition-text text hyphens-auto font-sans">
{{- Safe (ParseGeneric $trad.Content) -}} {{- Safe (ParseGeneric $trad.Content) -}}
</div> </div>
</div> </div>