mirror of
https://github.com/Theodor-Springmann-Stiftung/lenz-web.git
synced 2025-12-15 06:05:29 +00:00
LENZ BRIEFAUSGABE
This commit is contained in:
321
combin.txt
321
combin.txt
@@ -1,321 +0,0 @@
|
||||
opus
|
||||
opus/document
|
||||
opus/document/letterText
|
||||
opus/document/letterText/address
|
||||
opus/document/letterText/address/align
|
||||
opus/document/letterText/address/align/line
|
||||
opus/document/letterText/address/align/line/@type
|
||||
opus/document/letterText/address/align/@pos
|
||||
opus/document/letterText/address/align/ul
|
||||
opus/document/letterText/address/aq
|
||||
opus/document/letterText/address/aq/line
|
||||
opus/document/letterText/address/aq/line/@type
|
||||
opus/document/letterText/address/aq/ru
|
||||
opus/document/letterText/address/aq/ru/line
|
||||
opus/document/letterText/address/aq/ru/line/@type
|
||||
opus/document/letterText/address/aq/ul
|
||||
opus/document/letterText/address/del
|
||||
opus/document/letterText/address/dul
|
||||
opus/document/letterText/address/line
|
||||
opus/document/letterText/address/line/@type
|
||||
opus/document/letterText/address/sidenote
|
||||
opus/document/letterText/address/sidenote/@annotation
|
||||
opus/document/letterText/address/sidenote/aq
|
||||
opus/document/letterText/address/sidenote/@page
|
||||
opus/document/letterText/address/sidenote/@pos
|
||||
opus/document/letterText/address/tl
|
||||
opus/document/letterText/address/ul
|
||||
opus/document/letterText/address/ul/aq
|
||||
opus/document/letterText/align
|
||||
opus/document/letterText/align/align
|
||||
opus/document/letterText/align/align/@pos
|
||||
opus/document/letterText/align/aq
|
||||
opus/document/letterText/align/aq/fn
|
||||
opus/document/letterText/align/aq/fn/anchor
|
||||
opus/document/letterText/align/aq/fn/@index
|
||||
opus/document/letterText/align/del
|
||||
opus/document/letterText/align/del/nr
|
||||
opus/document/letterText/align/fn
|
||||
opus/document/letterText/align/fn/anchor
|
||||
opus/document/letterText/align/fn/@index
|
||||
opus/document/letterText/align/gr
|
||||
opus/document/letterText/align/hand
|
||||
opus/document/letterText/align/hand/line
|
||||
opus/document/letterText/align/hand/line/@type
|
||||
opus/document/letterText/align/hand/@ref
|
||||
opus/document/letterText/align/insertion
|
||||
opus/document/letterText/align/insertion/@pos
|
||||
opus/document/letterText/align/it
|
||||
opus/document/letterText/align/line
|
||||
opus/document/letterText/align/line/@tab
|
||||
opus/document/letterText/align/line/@type
|
||||
opus/document/letterText/align/note
|
||||
opus/document/letterText/align/page
|
||||
opus/document/letterText/align/page/@index
|
||||
opus/document/letterText/align/@pos
|
||||
opus/document/letterText/align/ru
|
||||
opus/document/letterText/align/ru/line
|
||||
opus/document/letterText/align/ru/line/@type
|
||||
opus/document/letterText/align/ul
|
||||
opus/document/letterText/aq
|
||||
opus/document/letterText/aq/align
|
||||
opus/document/letterText/aq/align/line
|
||||
opus/document/letterText/aq/align/line/@type
|
||||
opus/document/letterText/aq/align/@pos
|
||||
opus/document/letterText/aq/del
|
||||
opus/document/letterText/aq/del/nr
|
||||
opus/document/letterText/aq/dul
|
||||
opus/document/letterText/aq/gr
|
||||
opus/document/letterText/aq/insertion
|
||||
opus/document/letterText/aq/insertion/@pos
|
||||
opus/document/letterText/aq/line
|
||||
opus/document/letterText/aq/line/@tab
|
||||
opus/document/letterText/aq/line/@type
|
||||
opus/document/letterText/aq/note
|
||||
opus/document/letterText/aq/nr
|
||||
opus/document/letterText/aq/page
|
||||
opus/document/letterText/aq/page/@index
|
||||
opus/document/letterText/aq/pe
|
||||
opus/document/letterText/aq/pe/line
|
||||
opus/document/letterText/aq/pe/line/@tab
|
||||
opus/document/letterText/aq/pe/note
|
||||
opus/document/letterText/aq/pe/page
|
||||
opus/document/letterText/aq/pe/page/@index
|
||||
opus/document/letterText/aq/pe/tl
|
||||
opus/document/letterText/aq/ru
|
||||
opus/document/letterText/aq/ru/ul
|
||||
opus/document/letterText/aq/sidenote
|
||||
opus/document/letterText/aq/sidenote/@annotation
|
||||
opus/document/letterText/aq/sidenote/del
|
||||
opus/document/letterText/aq/sidenote/insertion
|
||||
opus/document/letterText/aq/sidenote/insertion/@pos
|
||||
opus/document/letterText/aq/sidenote/@page
|
||||
opus/document/letterText/aq/sidenote/pe
|
||||
opus/document/letterText/aq/sidenote/@pos
|
||||
opus/document/letterText/aq/tl
|
||||
opus/document/letterText/aq/ul
|
||||
opus/document/letterText/aq/ul/ru
|
||||
opus/document/letterText/b
|
||||
opus/document/letterText/del
|
||||
opus/document/letterText/del/del
|
||||
opus/document/letterText/del/del/nr
|
||||
opus/document/letterText/del/line
|
||||
opus/document/letterText/del/line/@tab
|
||||
opus/document/letterText/del/line/@type
|
||||
opus/document/letterText/del/nr
|
||||
opus/document/letterText/dul
|
||||
opus/document/letterText/er
|
||||
opus/document/letterText/er/nr
|
||||
opus/document/letterText/fn
|
||||
opus/document/letterText/fn/anchor
|
||||
opus/document/letterText/fn/anchor/note
|
||||
opus/document/letterText/fn/@index
|
||||
opus/document/letterText/gr
|
||||
opus/document/letterText/hand
|
||||
opus/document/letterText/hand/address
|
||||
opus/document/letterText/hand/address/aq
|
||||
opus/document/letterText/hand/address/line
|
||||
opus/document/letterText/hand/address/line/@type
|
||||
opus/document/letterText/hand/address/ul
|
||||
opus/document/letterText/hand/align
|
||||
opus/document/letterText/hand/align/line
|
||||
opus/document/letterText/hand/align/line/@type
|
||||
opus/document/letterText/hand/align/@pos
|
||||
opus/document/letterText/hand/aq
|
||||
opus/document/letterText/hand/del
|
||||
opus/document/letterText/hand/del/line
|
||||
opus/document/letterText/hand/del/line/@type
|
||||
opus/document/letterText/hand/del/nr
|
||||
opus/document/letterText/hand/fn
|
||||
opus/document/letterText/hand/fn/anchor
|
||||
opus/document/letterText/hand/fn/@index
|
||||
opus/document/letterText/hand/insertion
|
||||
opus/document/letterText/hand/insertion/@pos
|
||||
opus/document/letterText/hand/it
|
||||
opus/document/letterText/hand/line
|
||||
opus/document/letterText/hand/line/@tab
|
||||
opus/document/letterText/hand/line/@type
|
||||
opus/document/letterText/hand/note
|
||||
opus/document/letterText/hand/nr
|
||||
opus/document/letterText/hand/page
|
||||
opus/document/letterText/hand/page/@index
|
||||
opus/document/letterText/hand/pe
|
||||
opus/document/letterText/hand/pe/aq
|
||||
opus/document/letterText/hand/pe/del
|
||||
opus/document/letterText/hand/pe/del/line
|
||||
opus/document/letterText/hand/pe/del/line/@type
|
||||
opus/document/letterText/hand/pe/del/nr
|
||||
opus/document/letterText/hand/pe/line
|
||||
opus/document/letterText/hand/pe/line/@type
|
||||
opus/document/letterText/hand/pe/nr
|
||||
opus/document/letterText/hand/pe/sidenote
|
||||
opus/document/letterText/hand/pe/sidenote/@annotation
|
||||
opus/document/letterText/hand/pe/sidenote/line
|
||||
opus/document/letterText/hand/pe/sidenote/line/@type
|
||||
opus/document/letterText/hand/pe/sidenote/@page
|
||||
opus/document/letterText/hand/pe/sidenote/@pos
|
||||
opus/document/letterText/hand/pe/ul
|
||||
opus/document/letterText/hand/@ref
|
||||
opus/document/letterText/hand/sidenote
|
||||
opus/document/letterText/hand/sidenote/@annotation
|
||||
opus/document/letterText/hand/sidenote/line
|
||||
opus/document/letterText/hand/sidenote/line/@type
|
||||
opus/document/letterText/hand/sidenote/@page
|
||||
opus/document/letterText/hand/sidenote/@pos
|
||||
opus/document/letterText/hand/tabs
|
||||
opus/document/letterText/hand/tabs/line
|
||||
opus/document/letterText/hand/tabs/line/@index
|
||||
opus/document/letterText/hand/tabs/tab
|
||||
opus/document/letterText/hand/tabs/tab/@value
|
||||
opus/document/letterText/hand/tl
|
||||
opus/document/letterText/hand/ul
|
||||
opus/document/letterText/hand/ul/aq
|
||||
opus/document/letterText/hb
|
||||
opus/document/letterText/ink
|
||||
opus/document/letterText/ink/@ref
|
||||
opus/document/letterText/insertion
|
||||
opus/document/letterText/insertion/del
|
||||
opus/document/letterText/insertion/del/nr
|
||||
opus/document/letterText/insertion/fn
|
||||
opus/document/letterText/insertion/fn/anchor
|
||||
opus/document/letterText/insertion/fn/@index
|
||||
opus/document/letterText/insertion/line
|
||||
opus/document/letterText/insertion/line/@tab
|
||||
opus/document/letterText/insertion/@pos
|
||||
opus/document/letterText/it
|
||||
opus/document/letterText/@letter
|
||||
opus/document/letterText/line
|
||||
opus/document/letterText/line/@tab
|
||||
opus/document/letterText/line/@type
|
||||
opus/document/letterText/note
|
||||
opus/document/letterText/nr
|
||||
opus/document/letterText/p
|
||||
opus/document/letterText/page
|
||||
opus/document/letterText/page/@index
|
||||
opus/document/letterText/p/align
|
||||
opus/document/letterText/p/align/@pos
|
||||
opus/document/letterText/pe
|
||||
opus/document/letterText/pe/aq
|
||||
opus/document/letterText/pe/aq/del
|
||||
opus/document/letterText/pe/aq/insertion
|
||||
opus/document/letterText/pe/aq/insertion/@pos
|
||||
opus/document/letterText/pe/aq/line
|
||||
opus/document/letterText/pe/aq/line/@tab
|
||||
opus/document/letterText/pe/aq/line/@type
|
||||
opus/document/letterText/pe/aq/note
|
||||
opus/document/letterText/pe/aq/page
|
||||
opus/document/letterText/pe/aq/page/@index
|
||||
opus/document/letterText/pe/aq/ul
|
||||
opus/document/letterText/pe/hand
|
||||
opus/document/letterText/pe/hand/aq
|
||||
opus/document/letterText/pe/hand/@ref
|
||||
opus/document/letterText/pe/line
|
||||
opus/document/letterText/pe/line/@tab
|
||||
opus/document/letterText/pe/line/@type
|
||||
opus/document/letterText/pe/nr
|
||||
opus/document/letterText/pe/page
|
||||
opus/document/letterText/pe/page/@index
|
||||
opus/document/letterText/pe/ul
|
||||
opus/document/letterText/p/line
|
||||
opus/document/letterText/p/line/@tab
|
||||
opus/document/letterText/p/line/@type
|
||||
opus/document/letterText/p/page
|
||||
opus/document/letterText/p/page/@index
|
||||
opus/document/letterText/p/sidenote
|
||||
opus/document/letterText/p/sidenote/@annotation
|
||||
opus/document/letterText/p/sidenote/line
|
||||
opus/document/letterText/p/sidenote/line/@tab
|
||||
opus/document/letterText/p/sidenote/@page
|
||||
opus/document/letterText/p/sidenote/@pos
|
||||
opus/document/letterText/ru
|
||||
opus/document/letterText/ru/line
|
||||
opus/document/letterText/ru/line/@type
|
||||
opus/document/letterText/sidenote
|
||||
opus/document/letterText/sidenote/align
|
||||
opus/document/letterText/sidenote/align/gr
|
||||
opus/document/letterText/sidenote/align/@pos
|
||||
opus/document/letterText/sidenote/@annotation
|
||||
opus/document/letterText/sidenote/aq
|
||||
opus/document/letterText/sidenote/aq/ru
|
||||
opus/document/letterText/sidenote/del
|
||||
opus/document/letterText/sidenote/dul
|
||||
opus/document/letterText/sidenote/fn
|
||||
opus/document/letterText/sidenote/fn/anchor
|
||||
opus/document/letterText/sidenote/fn/anchor/note
|
||||
opus/document/letterText/sidenote/fn/@index
|
||||
opus/document/letterText/sidenote/gr
|
||||
opus/document/letterText/sidenote/hand
|
||||
opus/document/letterText/sidenote/hand/aq
|
||||
opus/document/letterText/sidenote/hand/aq/line
|
||||
opus/document/letterText/sidenote/hand/aq/line/@type
|
||||
opus/document/letterText/sidenote/hand/insertion
|
||||
opus/document/letterText/sidenote/hand/insertion/nr
|
||||
opus/document/letterText/sidenote/hand/insertion/@pos
|
||||
opus/document/letterText/sidenote/hand/line
|
||||
opus/document/letterText/sidenote/hand/line/@type
|
||||
opus/document/letterText/sidenote/hand/@ref
|
||||
opus/document/letterText/sidenote/ink
|
||||
opus/document/letterText/sidenote/ink/line
|
||||
opus/document/letterText/sidenote/ink/line/@type
|
||||
opus/document/letterText/sidenote/ink/@ref
|
||||
opus/document/letterText/sidenote/ink/ul
|
||||
opus/document/letterText/sidenote/insertion
|
||||
opus/document/letterText/sidenote/insertion/@pos
|
||||
opus/document/letterText/sidenote/line
|
||||
opus/document/letterText/sidenote/line/@tab
|
||||
opus/document/letterText/sidenote/line/@type
|
||||
opus/document/letterText/sidenote/note
|
||||
opus/document/letterText/sidenote/@page
|
||||
opus/document/letterText/sidenote/page
|
||||
opus/document/letterText/sidenote/page/@index
|
||||
opus/document/letterText/sidenote/pe
|
||||
opus/document/letterText/sidenote/@pos
|
||||
opus/document/letterText/sidenote/ru
|
||||
opus/document/letterText/sidenote/ru/aq
|
||||
opus/document/letterText/sidenote/ru/aq/ul
|
||||
opus/document/letterText/sidenote/ul
|
||||
opus/document/letterText/sidenote/ul/line
|
||||
opus/document/letterText/sidenote/ul/line/@tab
|
||||
opus/document/letterText/subst
|
||||
opus/document/letterText/subst/del
|
||||
opus/document/letterText/subst/del/nr
|
||||
opus/document/letterText/subst/del/nr/@extent
|
||||
opus/document/letterText/subst/insertion
|
||||
opus/document/letterText/tabs
|
||||
opus/document/letterText/tabs/line
|
||||
opus/document/letterText/tabs/line/@index
|
||||
opus/document/letterText/tabs/line/@tab
|
||||
opus/document/letterText/tabs/line/@type
|
||||
opus/document/letterText/tabs/note
|
||||
opus/document/letterText/tabs/page
|
||||
opus/document/letterText/tabs/page/@index
|
||||
opus/document/letterText/tabs/tab
|
||||
opus/document/letterText/tabs/tab/align
|
||||
opus/document/letterText/tabs/tab/align/aq
|
||||
opus/document/letterText/tabs/tab/align/@pos
|
||||
opus/document/letterText/tabs/tab/aq
|
||||
opus/document/letterText/tabs/tab/del
|
||||
opus/document/letterText/tabs/tab/fn
|
||||
opus/document/letterText/tabs/tab/fn/anchor
|
||||
opus/document/letterText/tabs/tab/fn/@index
|
||||
opus/document/letterText/tabs/tab/insertion
|
||||
opus/document/letterText/tabs/tab/insertion/@pos
|
||||
opus/document/letterText/tabs/tab/line
|
||||
opus/document/letterText/tabs/tab/line/@tab
|
||||
opus/document/letterText/tabs/tab/line/@type
|
||||
opus/document/letterText/tabs/tab/note
|
||||
opus/document/letterText/tabs/tab/ul
|
||||
opus/document/letterText/tabs/tab/ul/aq
|
||||
opus/document/letterText/tabs/tab/@value
|
||||
opus/document/letterText/tl
|
||||
opus/document/letterText/ul
|
||||
opus/document/letterText/ul/aq
|
||||
opus/document/letterText/ul/del
|
||||
opus/document/letterText/ul/fn
|
||||
opus/document/letterText/ul/fn/anchor
|
||||
opus/document/letterText/ul/fn/@index
|
||||
opus/document/letterText/ul/inc
|
||||
opus/document/letterText/ul/inc/@ref
|
||||
opus/document/letterText/ul/insertion
|
||||
opus/document/letterText/ul/insertion/@pos
|
||||
opus/document/letterText/ul/ru
|
||||
@@ -26,11 +26,19 @@ func GetLetter(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
}
|
||||
|
||||
html := ""
|
||||
text := fiber.Map{
|
||||
"count": "",
|
||||
"notes": "",
|
||||
"pages": []*xmlmodels.PageRender{},
|
||||
"html": "",
|
||||
}
|
||||
if state := letterData.HTML.Data(); state != nil {
|
||||
html = state.String()
|
||||
text["html"] = state.String()
|
||||
text["count"] = state.CountHTML()
|
||||
text["notes"] = state.NotesHTML()
|
||||
text["pages"] = state.Pages
|
||||
}
|
||||
tradition := lib.Traditions.Item(letter)
|
||||
|
||||
return c.Render("/brief/", fiber.Map{"meta": meta, "text": html, "tradition": tradition, "next": np.Next, "prev": np.Prev})
|
||||
return c.Render("/brief/", fiber.Map{"meta": meta, "text": text, "tradition": tradition, "next": np.Next, "prev": np.Prev})
|
||||
}
|
||||
|
||||
@@ -1,14 +1,136 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Theodor-Springmann-Stiftung/lenz-web/xmlmodels"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type filterItem struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func GetLetters(c *fiber.Ctx) error {
|
||||
lib := xmlmodels.Get()
|
||||
_, yearmap := lib.Years()
|
||||
rangeParam := c.Query("range", "")
|
||||
personParam := strings.TrimSpace(c.Query("person", ""))
|
||||
placeParam := strings.TrimSpace(c.Query("ort", ""))
|
||||
|
||||
personID := 0
|
||||
if personParam != "" {
|
||||
if id, err := strconv.Atoi(personParam); err == nil && id > 0 {
|
||||
personID = id
|
||||
} else {
|
||||
personParam = ""
|
||||
}
|
||||
}
|
||||
|
||||
placeID := 0
|
||||
if placeParam != "" {
|
||||
if id, err := strconv.Atoi(placeParam); err == nil && id > 0 {
|
||||
placeID = id
|
||||
} else {
|
||||
placeParam = ""
|
||||
}
|
||||
}
|
||||
|
||||
rawQuery := string(c.Context().URI().QueryString())
|
||||
lastPerson := strings.LastIndex(rawQuery, "person=")
|
||||
lastPlace := strings.LastIndex(rawQuery, "ort=")
|
||||
if lastPerson >= 0 && lastPlace >= 0 {
|
||||
if lastPlace > lastPerson {
|
||||
personID = 0
|
||||
personParam = ""
|
||||
} else {
|
||||
placeID = 0
|
||||
placeParam = ""
|
||||
}
|
||||
}
|
||||
|
||||
if personID > 0 && placeID > 0 {
|
||||
// Fallback if we couldn't determine query order.
|
||||
placeID = 0
|
||||
placeParam = ""
|
||||
}
|
||||
|
||||
personOptions := make(map[int]filterItem)
|
||||
placeOptions := make(map[int]filterItem)
|
||||
|
||||
addPerson := func(id int) {
|
||||
if id <= 0 {
|
||||
return
|
||||
}
|
||||
if _, ok := personOptions[id]; ok {
|
||||
return
|
||||
}
|
||||
if def := lib.Person(id); def != nil {
|
||||
name := strings.TrimSpace(def.Name)
|
||||
if name == "" {
|
||||
name = strings.TrimSpace(def.FirstName + " " + def.LastName)
|
||||
}
|
||||
if name == "" {
|
||||
name = strings.TrimSpace(def.Ref)
|
||||
}
|
||||
if name == "" {
|
||||
name = "Person " + strconv.Itoa(id)
|
||||
}
|
||||
personOptions[id] = filterItem{ID: id, Name: name}
|
||||
}
|
||||
}
|
||||
|
||||
addPlace := func(id int) {
|
||||
if id <= 0 {
|
||||
return
|
||||
}
|
||||
if _, ok := placeOptions[id]; ok {
|
||||
return
|
||||
}
|
||||
if def := lib.Place(id); def != nil {
|
||||
name := strings.TrimSpace(def.Name)
|
||||
if name == "" {
|
||||
name = strings.TrimSpace(def.Ref)
|
||||
}
|
||||
if name == "" {
|
||||
name = "Ort " + strconv.Itoa(id)
|
||||
}
|
||||
placeOptions[id] = filterItem{ID: id, Name: name}
|
||||
}
|
||||
}
|
||||
|
||||
collectRefs := func(meta xmlmodels.Meta) {
|
||||
for _, action := range meta.Sent {
|
||||
for _, ref := range action.Persons {
|
||||
addPerson(ref.Reference)
|
||||
}
|
||||
for _, ref := range action.Places {
|
||||
addPlace(ref.Reference)
|
||||
}
|
||||
}
|
||||
for _, action := range meta.Recieved {
|
||||
for _, ref := range action.Persons {
|
||||
addPerson(ref.Reference)
|
||||
}
|
||||
for _, ref := range action.Places {
|
||||
addPlace(ref.Reference)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filterLetter := func(meta xmlmodels.Meta) bool {
|
||||
if personID > 0 && !meta.HasPerson(personID) {
|
||||
return false
|
||||
}
|
||||
if placeID > 0 && !meta.HasPlace(placeID) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
// TODO: does not work ATM
|
||||
c.Locals("path", c.Path())
|
||||
|
||||
@@ -25,12 +147,21 @@ func GetLetters(c *fiber.Ctx) error {
|
||||
for year, letters := range yearmap {
|
||||
for i := range ranges {
|
||||
if year >= ranges[i].Start && year <= ranges[i].End {
|
||||
ranges[i].Letters = append(ranges[i].Letters, letters...)
|
||||
for _, letter := range letters {
|
||||
collectRefs(letter)
|
||||
if filterLetter(letter) {
|
||||
ranges[i].Letters = append(ranges[i].Letters, letter)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if personID > 0 || placeID > 0 {
|
||||
rangeParam = "all"
|
||||
}
|
||||
|
||||
// Handle specific range selection
|
||||
selectedRange := -1
|
||||
for i, r := range ranges {
|
||||
@@ -40,9 +171,41 @@ func GetLetters(c *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
querySuffix := ""
|
||||
if personParam != "" {
|
||||
querySuffix += "&person=" + personParam
|
||||
}
|
||||
if placeParam != "" {
|
||||
querySuffix += "&ort=" + placeParam
|
||||
}
|
||||
|
||||
personList := make([]filterItem, 0, len(personOptions))
|
||||
for _, item := range personOptions {
|
||||
personList = append(personList, item)
|
||||
}
|
||||
sort.Slice(personList, func(i, j int) bool {
|
||||
return strings.ToLower(personList[i].Name) < strings.ToLower(personList[j].Name)
|
||||
})
|
||||
|
||||
placeList := make([]filterItem, 0, len(placeOptions))
|
||||
for _, item := range placeOptions {
|
||||
placeList = append(placeList, item)
|
||||
}
|
||||
sort.Slice(placeList, func(i, j int) bool {
|
||||
return strings.ToLower(placeList[i].Name) < strings.ToLower(placeList[j].Name)
|
||||
})
|
||||
|
||||
personJSON, _ := json.Marshal(personList)
|
||||
placeJSON, _ := json.Marshal(placeList)
|
||||
|
||||
return c.Render(LETTERS_URL+"/", fiber.Map{
|
||||
"ranges": ranges,
|
||||
"ranges": ranges,
|
||||
"selectedRange": selectedRange,
|
||||
"all": rangeParam == "all",
|
||||
"all": rangeParam == "all",
|
||||
"person": personParam,
|
||||
"ort": placeParam,
|
||||
"query": querySuffix,
|
||||
"personJSON": string(personJSON),
|
||||
"placeJSON": string(placeJSON),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
--color-blue-900: oklch(37.9% 0.146 265.522);
|
||||
--color-blue-950: oklch(28.2% 0.091 267.935);
|
||||
--color-slate-50: oklch(98.4% 0.003 247.858);
|
||||
--color-slate-100: oklch(96.8% 0.007 247.896);
|
||||
--color-slate-200: oklch(92.9% 0.013 255.508);
|
||||
--color-slate-400: oklch(70.4% 0.04 256.788);
|
||||
--color-slate-500: oklch(55.4% 0.046 257.417);
|
||||
--color-slate-600: oklch(44.6% 0.043 257.281);
|
||||
--color-slate-700: oklch(37.2% 0.044 257.287);
|
||||
--color-slate-800: oklch(27.9% 0.041 260.031);
|
||||
@@ -43,10 +46,13 @@
|
||||
--text-2xl--line-height: calc(2 / 1.5);
|
||||
--text-6xl: 3.75rem;
|
||||
--text-6xl--line-height: 1;
|
||||
--font-weight-medium: 500;
|
||||
--font-weight-semibold: 600;
|
||||
--font-weight-bold: 700;
|
||||
--tracking-wide: 0.025em;
|
||||
--leading-normal: 1.5;
|
||||
--radius-md: 0.375rem;
|
||||
--radius-lg: 0.5rem;
|
||||
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
||||
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
||||
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
@@ -211,9 +217,23 @@
|
||||
.collapse {
|
||||
visibility: collapse;
|
||||
}
|
||||
.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip-path: inset(50%);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
@@ -423,6 +443,9 @@
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
.max-h-64 {
|
||||
max-height: calc(var(--spacing) * 64);
|
||||
}
|
||||
.min-h-screen {
|
||||
min-height: 100vh;
|
||||
}
|
||||
@@ -435,9 +458,15 @@
|
||||
.max-w-none {
|
||||
max-width: none;
|
||||
}
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
.flex-shrink {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.shrink-0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
@@ -481,9 +510,53 @@
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.gap-2 {
|
||||
gap: calc(var(--spacing) * 2);
|
||||
}
|
||||
.gap-8 {
|
||||
gap: calc(var(--spacing) * 8);
|
||||
}
|
||||
.space-y-1 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-block-start: calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));
|
||||
margin-block-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)));
|
||||
}
|
||||
}
|
||||
.space-y-1\.5 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-block-start: calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));
|
||||
margin-block-end: calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)));
|
||||
}
|
||||
}
|
||||
.space-y-2 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));
|
||||
margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)));
|
||||
}
|
||||
}
|
||||
.space-y-4 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));
|
||||
margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));
|
||||
}
|
||||
}
|
||||
.space-y-5 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-block-start: calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));
|
||||
margin-block-end: calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)));
|
||||
}
|
||||
}
|
||||
.space-y-6 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-y-reverse: 0;
|
||||
@@ -504,6 +577,20 @@
|
||||
.gap-x-1\.5 {
|
||||
column-gap: calc(var(--spacing) * 1.5);
|
||||
}
|
||||
.divide-y {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-divide-y-reverse: 0;
|
||||
border-bottom-style: var(--tw-border-style);
|
||||
border-top-style: var(--tw-border-style);
|
||||
border-top-width: calc(1px * var(--tw-divide-y-reverse));
|
||||
border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
|
||||
}
|
||||
}
|
||||
.divide-slate-100 {
|
||||
:where(& > :not(:last-child)) {
|
||||
border-color: var(--color-slate-100);
|
||||
}
|
||||
}
|
||||
.self-end {
|
||||
align-self: flex-end;
|
||||
}
|
||||
@@ -513,6 +600,9 @@
|
||||
.justify-self-end {
|
||||
justify-self: flex-end;
|
||||
}
|
||||
.overflow-y-auto {
|
||||
overflow-y: auto;
|
||||
}
|
||||
.rounded {
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
@@ -542,6 +632,15 @@
|
||||
.border-gray-300 {
|
||||
border-color: var(--color-gray-300);
|
||||
}
|
||||
.border-slate-100 {
|
||||
border-color: var(--color-slate-100);
|
||||
}
|
||||
.border-slate-200 {
|
||||
border-color: var(--color-slate-200);
|
||||
}
|
||||
.border-slate-900 {
|
||||
border-color: var(--color-slate-900);
|
||||
}
|
||||
.border-t-gray-200 {
|
||||
border-top-color: var(--color-gray-200);
|
||||
}
|
||||
@@ -551,18 +650,30 @@
|
||||
.bg-gray-900 {
|
||||
background-color: var(--color-gray-900);
|
||||
}
|
||||
.bg-slate-900 {
|
||||
background-color: var(--color-slate-900);
|
||||
}
|
||||
.bg-white {
|
||||
background-color: var(--color-white);
|
||||
}
|
||||
.mask-repeat {
|
||||
mask-repeat: repeat;
|
||||
}
|
||||
.p-2 {
|
||||
padding: calc(var(--spacing) * 2);
|
||||
}
|
||||
.p-3 {
|
||||
padding: calc(var(--spacing) * 3);
|
||||
}
|
||||
.px-0\.5 {
|
||||
padding-inline: calc(var(--spacing) * 0.5);
|
||||
}
|
||||
.px-2 {
|
||||
padding-inline: calc(var(--spacing) * 2);
|
||||
}
|
||||
.px-3 {
|
||||
padding-inline: calc(var(--spacing) * 3);
|
||||
}
|
||||
.px-8 {
|
||||
padding-inline: calc(var(--spacing) * 8);
|
||||
}
|
||||
@@ -581,6 +692,9 @@
|
||||
.pb-0 {
|
||||
padding-bottom: calc(var(--spacing) * 0);
|
||||
}
|
||||
.pb-1 {
|
||||
padding-bottom: calc(var(--spacing) * 1);
|
||||
}
|
||||
.pb-1\.5 {
|
||||
padding-bottom: calc(var(--spacing) * 1.5);
|
||||
}
|
||||
@@ -593,6 +707,9 @@
|
||||
.text-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
.align-bottom {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
@@ -632,6 +749,19 @@
|
||||
font-size: var(--text-xl);
|
||||
line-height: var(--tw-leading, var(--text-xl--line-height));
|
||||
}
|
||||
.text-xs {
|
||||
font-size: var(--text-xs);
|
||||
line-height: var(--tw-leading, var(--text-xs--line-height));
|
||||
}
|
||||
.text-\[0\.7rem\] {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
.text-\[0\.65rem\] {
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
.text-\[0\.75rem\] {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.leading-none {
|
||||
--tw-leading: 1;
|
||||
line-height: 1;
|
||||
@@ -640,10 +770,18 @@
|
||||
--tw-font-weight: var(--font-weight-bold);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
.font-medium {
|
||||
--tw-font-weight: var(--font-weight-medium);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
.font-semibold {
|
||||
--tw-font-weight: var(--font-weight-semibold);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
.tracking-wide {
|
||||
--tw-tracking: var(--tracking-wide);
|
||||
letter-spacing: var(--tracking-wide);
|
||||
}
|
||||
.text-wrap {
|
||||
text-wrap: wrap;
|
||||
}
|
||||
@@ -669,6 +807,15 @@
|
||||
.text-gray-800 {
|
||||
color: var(--color-gray-800);
|
||||
}
|
||||
.text-slate-500 {
|
||||
color: var(--color-slate-500);
|
||||
}
|
||||
.text-slate-600 {
|
||||
color: var(--color-slate-600);
|
||||
}
|
||||
.text-slate-700 {
|
||||
color: var(--color-slate-700);
|
||||
}
|
||||
.text-white {
|
||||
color: var(--color-white);
|
||||
}
|
||||
@@ -733,9 +880,17 @@
|
||||
-webkit-font-smoothing: auto;
|
||||
-moz-osx-font-smoothing: auto;
|
||||
}
|
||||
.placeholder-slate-400 {
|
||||
&::placeholder {
|
||||
color: var(--color-slate-400);
|
||||
}
|
||||
}
|
||||
.opacity-0 {
|
||||
opacity: 0%;
|
||||
}
|
||||
.opacity-70 {
|
||||
opacity: 70%;
|
||||
}
|
||||
.opacity-100 {
|
||||
opacity: 100%;
|
||||
}
|
||||
@@ -797,6 +952,20 @@
|
||||
-webkit-user-select: all;
|
||||
user-select: all;
|
||||
}
|
||||
.hover\:border-slate-400 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
border-color: var(--color-slate-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:bg-slate-100 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
background-color: var(--color-slate-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:text-slate-900 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
@@ -811,6 +980,38 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.focus\:border-slate-400 {
|
||||
&:focus {
|
||||
border-color: var(--color-slate-400);
|
||||
}
|
||||
}
|
||||
.focus\:ring-1 {
|
||||
&:focus {
|
||||
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
}
|
||||
}
|
||||
.focus\:ring-slate-400 {
|
||||
&:focus {
|
||||
--tw-ring-color: var(--color-slate-400);
|
||||
}
|
||||
}
|
||||
.focus\:outline-none {
|
||||
&:focus {
|
||||
--tw-outline-style: none;
|
||||
outline-style: none;
|
||||
}
|
||||
}
|
||||
.lg\:w-72 {
|
||||
@media (width >= 64rem) {
|
||||
width: calc(var(--spacing) * 72);
|
||||
}
|
||||
}
|
||||
.lg\:flex-row {
|
||||
@media (width >= 64rem) {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
.print\:hidden {
|
||||
@media print {
|
||||
display: none;
|
||||
@@ -1293,7 +1494,8 @@
|
||||
line-height: var(--tw-leading, var(--text-sm--line-height));
|
||||
color: var(--color-slate-800);
|
||||
}
|
||||
.text .page {
|
||||
.text .count .page,
|
||||
.text .notes .page {
|
||||
visibility: hidden;
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--text-sm);
|
||||
@@ -1392,6 +1594,11 @@
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
@property --tw-divide-y-reverse {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
@property --tw-border-style {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
@@ -1405,6 +1612,10 @@
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-tracking {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-ordinal {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
@@ -1604,9 +1815,11 @@
|
||||
--tw-skew-x: initial;
|
||||
--tw-skew-y: initial;
|
||||
--tw-space-y-reverse: 0;
|
||||
--tw-divide-y-reverse: 0;
|
||||
--tw-border-style: solid;
|
||||
--tw-leading: initial;
|
||||
--tw-font-weight: initial;
|
||||
--tw-tracking: initial;
|
||||
--tw-ordinal: initial;
|
||||
--tw-slashed-zero: initial;
|
||||
--tw-numeric-figure: initial;
|
||||
|
||||
@@ -30,7 +30,26 @@
|
||||
</div>
|
||||
|
||||
<div class="text flex flex-row print:flex-col">
|
||||
{{- Safe $model.text -}}
|
||||
<div class="count">
|
||||
{{- Safe $model.text.count -}}
|
||||
</div>
|
||||
<div class="fulltext">
|
||||
{{- if $model.text.pages }}
|
||||
{{- range $page := $model.text.pages }}
|
||||
<div
|
||||
class="page"
|
||||
data-page-index="{{ $page.Index }}"
|
||||
data-starts-inline="{{ if $page.StartsInline }}true{{ else }}false{{ end }}">
|
||||
{{- $page.HTML | Safe -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{- else }}
|
||||
{{- Safe $model.text.html -}}
|
||||
{{- end -}}
|
||||
</div>
|
||||
<div class="notes">
|
||||
{{- Safe $model.text.notes -}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="traditions mt-12 pt-3 border-t-gray-200 border-t-1 max-w-[90ch] print:border-none">
|
||||
|
||||
@@ -1,45 +1,194 @@
|
||||
{{ $model := . }}
|
||||
{{ $query := $model.query }}
|
||||
{{ $personJSON := Safe .personJSON }}
|
||||
{{ $placeJSON := Safe .placeJSON }}
|
||||
|
||||
<nav class="print:hidden">
|
||||
{{- range $i, $range := .ranges -}}
|
||||
{{- if $range.Letters -}}
|
||||
<a
|
||||
class="inline-block stdlink px-0.5"
|
||||
href="/briefe?range={{ $range.Label }}"
|
||||
{{ if eq $model.selectedRange $i -}}aria-current="page"{{- end }}
|
||||
>{{ $range.Label }}</a
|
||||
>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
<a
|
||||
class="inline-block stdlink px-0.5"
|
||||
href="/briefe?range=all"
|
||||
{{ if .all -}}aria-current="page"{{- end }}
|
||||
>Alle</a
|
||||
>
|
||||
</nav>
|
||||
|
||||
{{- if .ranges -}}
|
||||
{{- if ne .selectedRange -1 -}}
|
||||
{{- $selectedRangeData := index .ranges .selectedRange -}}
|
||||
<div class="flex flex-row gap-x-1">
|
||||
<div>Briefe für {{ $selectedRangeData.Label }}</div>
|
||||
<div>({{ len $selectedRangeData.Letters }})</div>
|
||||
<div class="flex flex-col gap-8 lg:flex-row">
|
||||
<aside class="lg:w-72 shrink-0 space-y-5 print:hidden font-sans">
|
||||
<nav class="border border-slate-200 rounded bg-white shadow p-3 space-y-2">
|
||||
<div class="text-[0.75rem] font-semibold uppercase tracking-wide text-slate-600">Jahre</div>
|
||||
<ul class="space-y-1 text-xs font-medium text-slate-700">
|
||||
{{- range $i, $range := .ranges -}}
|
||||
{{- if $range.Letters -}}
|
||||
<li>
|
||||
<a
|
||||
class="flex items-center justify-between rounded border px-2 py-1 transition {{ if eq $model.selectedRange $i }}border-slate-900 bg-slate-900 text-white{{ else }}border-slate-200 hover:border-slate-400{{ end }}"
|
||||
href="/briefe?range={{ $range.Label }}{{ $query }}">
|
||||
<span>{{ $range.Label }}</span>
|
||||
<span class="text-[0.65rem] opacity-70">{{ len $range.Letters }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
<li>
|
||||
<a
|
||||
class="flex items-center justify-between rounded border px-2 py-1 transition {{ if .all }}border-slate-900 bg-slate-900 text-white{{ else }}border-slate-200 hover:border-slate-400{{ end }}"
|
||||
href="/briefe?range=all{{ $query }}">
|
||||
<span>Alle</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="space-y-4">
|
||||
<filter-list
|
||||
class="block"
|
||||
data-title="Personen"
|
||||
data-param="person"
|
||||
data-active="{{ .person }}"
|
||||
data-items='{{ $personJSON }}'>
|
||||
<div class="text-sm text-slate-500">Filter werden geladen …</div>
|
||||
</filter-list>
|
||||
<filter-list
|
||||
class="block"
|
||||
data-title="Orte"
|
||||
data-param="ort"
|
||||
data-active="{{ .ort }}"
|
||||
data-items='{{ $placeJSON }}'>
|
||||
<div class="text-sm text-slate-500">Filter werden geladen …</div>
|
||||
</filter-list>
|
||||
</div>
|
||||
{{ template "_letterlist" $selectedRangeData.Letters -}}
|
||||
{{- end -}}
|
||||
</aside>
|
||||
|
||||
{{- if .all -}}
|
||||
{{- range $range := .ranges -}}
|
||||
{{- if $range.Letters -}}
|
||||
<div class="mb-8">
|
||||
<div class="flex flex-row gap-x-1 mb-4">
|
||||
<div class="font-semibold">{{ $range.Label }}</div>
|
||||
<div>({{ len $range.Letters }})</div>
|
||||
</div>
|
||||
{{ template "_letterlist" $range.Letters -}}
|
||||
<section class="flex-1">
|
||||
{{- if .ranges -}}
|
||||
{{- if ne .selectedRange -1 -}}
|
||||
{{- $selectedRangeData := index .ranges .selectedRange -}}
|
||||
<div class="flex flex-row gap-x-1">
|
||||
<div>Briefe für {{ $selectedRangeData.Label }}</div>
|
||||
<div>({{ len $selectedRangeData.Letters }})</div>
|
||||
</div>
|
||||
{{ template "_letterlist" $selectedRangeData.Letters -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if .all -}}
|
||||
{{- range $range := .ranges -}}
|
||||
{{- if $range.Letters -}}
|
||||
<div class="mb-8">
|
||||
<div class="flex flex-row gap-x-1 mb-4">
|
||||
<div class="font-semibold">{{ $range.Label }}</div>
|
||||
<div>({{ len $range.Letters }})</div>
|
||||
</div>
|
||||
{{ template "_letterlist" $range.Letters -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
(() => {
|
||||
if (customElements.get("filter-list")) {
|
||||
return;
|
||||
}
|
||||
|
||||
class FilterList extends HTMLElement {
|
||||
connectedCallback() {
|
||||
if (this._init) {
|
||||
return;
|
||||
}
|
||||
this._init = true;
|
||||
this.param = this.dataset.param || "";
|
||||
this.title = this.dataset.title || "";
|
||||
this.activeValue = this.dataset.active || "";
|
||||
|
||||
try {
|
||||
this.items = JSON.parse(this.dataset.items || "[]");
|
||||
} catch (err) {
|
||||
console.error("filter-list: failed to parse dataset items", err);
|
||||
this.items = [];
|
||||
}
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
this.innerHTML = `
|
||||
<div class="border border-slate-200 rounded bg-white shadow p-3 space-y-1.5 font-sans">
|
||||
<div class="flex items-center justify-between gap-2 pb-1 border-b border-slate-100">
|
||||
<h3 class="text-[0.75rem] font-semibold uppercase tracking-wide text-slate-600">${this.title}</h3>
|
||||
<button type="button" data-role="clear" class="text-[0.7rem] text-slate-500 hover:text-slate-900 ${this.activeValue ? "" : "invisible"}">Zurücksetzen</button>
|
||||
</div>
|
||||
<div>
|
||||
<label class="sr-only" for="${this.param}-filter-input">Filter ${this.title}</label>
|
||||
<input
|
||||
id="${this.param}-filter-input"
|
||||
type="search"
|
||||
placeholder="Suchen"
|
||||
class="w-full rounded border border-slate-200 bg-white px-3 py-1 text-xs text-slate-700 placeholder-slate-400 focus:border-slate-400 focus:outline-none focus:ring-1 focus:ring-slate-400"/>
|
||||
</div>
|
||||
<ul class="max-h-64 overflow-y-auto divide-y divide-slate-100 text-[0.7rem]" data-role="list"></ul>
|
||||
<p class="text-xs text-slate-500 hidden" data-role="empty">Keine Treffer</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.list = this.querySelector('[data-role="list"]');
|
||||
this.empty = this.querySelector('[data-role="empty"]');
|
||||
this.input = this.querySelector('input[type="search"]');
|
||||
this.clearButton = this.querySelector('[data-role="clear"]');
|
||||
|
||||
this.input?.addEventListener("input", () => this.update());
|
||||
this.clearButton?.addEventListener("click", () => {
|
||||
this.activeValue = "";
|
||||
this.navigate();
|
||||
});
|
||||
|
||||
this.update();
|
||||
}
|
||||
|
||||
update() {
|
||||
if (!this.list) {
|
||||
return;
|
||||
}
|
||||
const query = (this.input?.value || "").trim().toLowerCase();
|
||||
const results = this.items.filter((item) => item.name?.toLowerCase().includes(query));
|
||||
|
||||
this.list.innerHTML = "";
|
||||
results.forEach((item) => {
|
||||
const li = document.createElement("li");
|
||||
li.className = "";
|
||||
const btn = document.createElement("button");
|
||||
btn.type = "button";
|
||||
const isActive = String(item.id) === String(this.activeValue);
|
||||
btn.className = `w-full text-left px-2 py-1 rounded transition ${
|
||||
isActive
|
||||
? "bg-slate-900 text-white"
|
||||
: "text-slate-700 hover:bg-slate-100"
|
||||
}`;
|
||||
btn.textContent = item.name;
|
||||
btn.addEventListener("click", () => {
|
||||
this.activeValue = isActive ? "" : String(item.id);
|
||||
this.navigate();
|
||||
});
|
||||
li.appendChild(btn);
|
||||
this.list.appendChild(li);
|
||||
});
|
||||
|
||||
if (this.empty) {
|
||||
this.empty.classList.toggle("hidden", results.length > 0);
|
||||
}
|
||||
}
|
||||
|
||||
navigate() {
|
||||
if (!this.param) {
|
||||
return;
|
||||
}
|
||||
const url = new URL(window.location.href);
|
||||
const otherParam = this.param === "person" ? "ort" : "person";
|
||||
url.searchParams.delete(otherParam);
|
||||
|
||||
if (this.activeValue) {
|
||||
url.searchParams.set(this.param, this.activeValue);
|
||||
url.searchParams.set("range", "all");
|
||||
} else {
|
||||
url.searchParams.delete(this.param);
|
||||
}
|
||||
|
||||
window.location.href = url.pathname + url.search;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("filter-list", FilterList);
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -405,7 +405,8 @@
|
||||
@apply text-sm text-slate-800;
|
||||
}
|
||||
|
||||
.text .page {
|
||||
.text .count .page,
|
||||
.text .notes .page {
|
||||
@apply font-sans text-sm text-slate-600 invisible;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,31 @@ type Note struct {
|
||||
Tokens Tokens
|
||||
}
|
||||
|
||||
type PageRender struct {
|
||||
Index string
|
||||
StartsInline bool
|
||||
Tokens Tokens
|
||||
rendered string
|
||||
}
|
||||
|
||||
func (p *PageRender) HTML() string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
if p.rendered != "" {
|
||||
return p.rendered
|
||||
}
|
||||
p.rendered = p.Tokens.String()
|
||||
return p.rendered
|
||||
}
|
||||
|
||||
type LenzParseState struct {
|
||||
Tokens Tokens
|
||||
Notes []Note
|
||||
Count []Note
|
||||
Pages []*PageRender
|
||||
currentPage *PageRender
|
||||
paging bool
|
||||
LC int
|
||||
PC string
|
||||
CloseElement bool
|
||||
@@ -46,9 +67,7 @@ func (s *LenzParseState) String() string {
|
||||
}
|
||||
builder := strings.Builder{}
|
||||
builder.WriteString(outToken{Name: "div", Classes: []string{"count"}, Type: Element}.String())
|
||||
for _, c := range s.Count {
|
||||
builder.WriteString(c.Tokens.String())
|
||||
}
|
||||
builder.WriteString(s.CountHTML())
|
||||
builder.WriteString(outToken{Name: "div", Classes: []string{"count"}, Type: EndElement}.String())
|
||||
|
||||
tokens := s.Tokens
|
||||
@@ -57,18 +76,118 @@ func (s *LenzParseState) String() string {
|
||||
builder.WriteString(tokens.String())
|
||||
|
||||
builder.WriteString(outToken{Name: "div", Classes: []string{"notes"}, Type: Element}.String())
|
||||
for _, note := range s.Notes {
|
||||
builder.WriteString(note.Tokens.String())
|
||||
}
|
||||
builder.WriteString(s.NotesHTML())
|
||||
builder.WriteString(outToken{Name: "div", Classes: []string{"notes"}, Type: EndElement}.String())
|
||||
s.rendered = builder.String()
|
||||
return s.rendered
|
||||
}
|
||||
|
||||
func (s *LenzParseState) CountHTML() string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
builder := strings.Builder{}
|
||||
for _, c := range s.Count {
|
||||
builder.WriteString(c.Tokens.String())
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func (s *LenzParseState) NotesHTML() string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
builder := strings.Builder{}
|
||||
for _, note := range s.Notes {
|
||||
builder.WriteString(note.Tokens.String())
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func (s *LenzParseState) AppendNote(note Note) {
|
||||
s.Notes = append(s.Notes, note)
|
||||
}
|
||||
|
||||
func (s *LenzParseState) ensureCurrentPage() *PageRender {
|
||||
if s.currentPage == nil {
|
||||
s.startPage(s.PC)
|
||||
}
|
||||
return s.currentPage
|
||||
}
|
||||
|
||||
func (s *LenzParseState) startPage(index string) *PageRender {
|
||||
if index == "" {
|
||||
index = strconv.Itoa(len(s.Pages) + 1)
|
||||
}
|
||||
page := &PageRender{Index: index}
|
||||
s.Pages = append(s.Pages, page)
|
||||
s.currentPage = page
|
||||
s.paging = true
|
||||
return page
|
||||
}
|
||||
|
||||
func (s *LenzParseState) currentPageTokens() *Tokens {
|
||||
if !s.paging {
|
||||
return nil
|
||||
}
|
||||
return &s.ensureCurrentPage().Tokens
|
||||
}
|
||||
|
||||
func (s *LenzParseState) appendDefaultElement(token *xmlparsing.Token, ids ...string) {
|
||||
s.Tokens.AppendDefaultElement(token, ids...)
|
||||
if pageTokens := s.currentPageTokens(); pageTokens != nil {
|
||||
pageTokens.AppendDefaultElement(token, ids...)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LenzParseState) appendDivElement(id string, classes ...string) {
|
||||
s.Tokens.AppendDivElement(id, classes...)
|
||||
if pageTokens := s.currentPageTokens(); pageTokens != nil {
|
||||
pageTokens.AppendDivElement(id, classes...)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LenzParseState) appendEndElement() {
|
||||
s.Tokens.AppendEndElement()
|
||||
if pageTokens := s.currentPageTokens(); pageTokens != nil {
|
||||
pageTokens.AppendEndElement()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LenzParseState) appendCustomAttribute(name, value string) {
|
||||
s.Tokens.AppendCustomAttribute(name, value)
|
||||
if pageTokens := s.currentPageTokens(); pageTokens != nil {
|
||||
pageTokens.AppendCustomAttribute(name, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LenzParseState) appendLink(href string, classes ...string) {
|
||||
s.Tokens.AppendLink(href, classes...)
|
||||
if pageTokens := s.currentPageTokens(); pageTokens != nil {
|
||||
pageTokens.AppendLink(href, classes...)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LenzParseState) appendEmptyElement(name string, id string, classes ...string) {
|
||||
s.Tokens.AppendEmptyElement(name, id, classes...)
|
||||
if pageTokens := s.currentPageTokens(); pageTokens != nil {
|
||||
pageTokens.AppendEmptyElement(name, id, classes...)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LenzParseState) appendText(text string) {
|
||||
s.Tokens.AppendText(text)
|
||||
if pageTokens := s.currentPageTokens(); pageTokens != nil {
|
||||
pageTokens.AppendText(text)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LenzParseState) markCurrentPageInline(inline bool) {
|
||||
if page := s.ensureCurrentPage(); page != nil {
|
||||
page.StartsInline = inline
|
||||
}
|
||||
}
|
||||
|
||||
type LenzTextHandler struct {
|
||||
Lib *Library
|
||||
}
|
||||
@@ -86,54 +205,54 @@ func (h LenzTextHandler) OnOpenElement(state *xmlparsing.ParseState[*LenzParseSt
|
||||
|
||||
switch elem.Name {
|
||||
case "insertion":
|
||||
ps.Tokens.AppendDefaultElement(elem)
|
||||
ps.Tokens.AppendDivElement("", "insertion-marker")
|
||||
ps.Tokens.AppendEndElement()
|
||||
ps.appendDefaultElement(elem)
|
||||
ps.appendDivElement("", "insertion-marker")
|
||||
ps.appendEndElement()
|
||||
case "sidenote":
|
||||
id := randString(8)
|
||||
ps.Tokens.AppendDefaultElement(elem)
|
||||
ps.appendDefaultElement(elem)
|
||||
ps.Break = false
|
||||
ps.Tokens.AppendCustomAttribute("aria-describedby", id)
|
||||
ps.appendCustomAttribute("aria-describedby", id)
|
||||
if elem.Attributes["annotation"] != "" ||
|
||||
elem.Attributes["page"] != "" ||
|
||||
elem.Attributes["pos"] != "" {
|
||||
note := Note{Id: id}
|
||||
note.Tokens.AppendDivElement(id, "note-sidenote-meta")
|
||||
ps.Tokens.AppendDivElement(id, "inline-sidenote-meta")
|
||||
ps.appendDivElement(id, "inline-sidenote-meta")
|
||||
if elem.Attributes["page"] != "" {
|
||||
note.Tokens.AppendDivElement("", "sidenote-page")
|
||||
note.Tokens.AppendText(elem.Attributes["page"])
|
||||
note.Tokens.AppendEndElement()
|
||||
ps.Tokens.AppendDivElement("", "sidenote-page")
|
||||
ps.Tokens.AppendText(elem.Attributes["page"])
|
||||
ps.Tokens.AppendEndElement()
|
||||
ps.appendDivElement("", "sidenote-page")
|
||||
ps.appendText(elem.Attributes["page"])
|
||||
ps.appendEndElement()
|
||||
}
|
||||
if elem.Attributes["annotation"] != "" {
|
||||
note.Tokens.AppendDivElement("", "sidenote-note")
|
||||
note.Tokens.AppendText(elem.Attributes["annotation"])
|
||||
note.Tokens.AppendEndElement()
|
||||
ps.Tokens.AppendDivElement("", "sidenote-note")
|
||||
ps.Tokens.AppendText(elem.Attributes["annotation"])
|
||||
ps.Tokens.AppendEndElement()
|
||||
ps.appendDivElement("", "sidenote-note")
|
||||
ps.appendText(elem.Attributes["annotation"])
|
||||
ps.appendEndElement()
|
||||
}
|
||||
if elem.Attributes["pos"] != "" {
|
||||
note.Tokens.AppendDivElement("", "sidenote-pos")
|
||||
note.Tokens.AppendText(elem.Attributes["pos"])
|
||||
note.Tokens.AppendEndElement()
|
||||
ps.Tokens.AppendDivElement("", "sidenote-pos")
|
||||
ps.Tokens.AppendText(elem.Attributes["pos"])
|
||||
ps.Tokens.AppendEndElement()
|
||||
ps.appendDivElement("", "sidenote-pos")
|
||||
ps.appendText(elem.Attributes["pos"])
|
||||
ps.appendEndElement()
|
||||
}
|
||||
note.Tokens.AppendEndElement()
|
||||
ps.Tokens.AppendEndElement()
|
||||
ps.appendEndElement()
|
||||
ps.AppendNote(note)
|
||||
}
|
||||
|
||||
case "note":
|
||||
id := randString(8)
|
||||
ps.Tokens.AppendLink("#"+id, "nanchor-note")
|
||||
ps.Tokens.AppendEndElement()
|
||||
ps.Tokens.AppendDivElement(id, "note", "note-note")
|
||||
ps.appendLink("#"+id, "nanchor-note")
|
||||
ps.appendEndElement()
|
||||
ps.appendDivElement(id, "note", "note-note")
|
||||
|
||||
case "nr":
|
||||
ext := elem.Attributes["extent"]
|
||||
@@ -145,9 +264,9 @@ func (h LenzTextHandler) OnOpenElement(state *xmlparsing.ParseState[*LenzParseSt
|
||||
extno = 1
|
||||
}
|
||||
|
||||
ps.Tokens.AppendDefaultElement(elem)
|
||||
ps.appendDefaultElement(elem)
|
||||
for i := 0; i < extno; i++ {
|
||||
ps.Tokens.AppendText(" ")
|
||||
ps.appendText(" ")
|
||||
}
|
||||
|
||||
case "hand":
|
||||
@@ -167,21 +286,21 @@ func (h LenzTextHandler) OnOpenElement(state *xmlparsing.ParseState[*LenzParseSt
|
||||
note.Tokens.AppendText(hand)
|
||||
note.Tokens.AppendEndElement()
|
||||
ps.AppendNote(note)
|
||||
ps.Tokens.AppendDivElement(id, "inline-hand")
|
||||
ps.Tokens.AppendText(hand)
|
||||
ps.Tokens.AppendEndElement()
|
||||
ps.Tokens.AppendDivElement("", "hand")
|
||||
ps.Tokens.AppendCustomAttribute("aria-describedby", id)
|
||||
ps.appendDivElement(id, "inline-hand")
|
||||
ps.appendText(hand)
|
||||
ps.appendEndElement()
|
||||
ps.appendDivElement("", "hand")
|
||||
ps.appendCustomAttribute("aria-describedby", id)
|
||||
|
||||
case "line":
|
||||
if val := elem.Attributes["type"]; val != "empty" {
|
||||
ps.LC += 1
|
||||
if ps.Break {
|
||||
ps.Tokens.AppendEmptyElement("br", ps.PC+"-"+strconv.Itoa(ps.LC))
|
||||
ps.appendEmptyElement("br", ps.PC+"-"+strconv.Itoa(ps.LC))
|
||||
}
|
||||
ps.Tokens.AppendDefaultElement(elem)
|
||||
ps.appendDefaultElement(elem)
|
||||
} else {
|
||||
ps.Tokens.AppendEmptyElement("br", "", "empty")
|
||||
ps.appendEmptyElement("br", "", "empty")
|
||||
ps.CloseElement = false
|
||||
}
|
||||
ps.LineBreak = true
|
||||
@@ -190,9 +309,10 @@ func (h LenzTextHandler) OnOpenElement(state *xmlparsing.ParseState[*LenzParseSt
|
||||
ps.PC = elem.Attributes["index"]
|
||||
ps.PageBreak = true
|
||||
ps.CloseElement = false
|
||||
ps.startPage(ps.PC)
|
||||
|
||||
default:
|
||||
ps.Tokens.AppendDefaultElement(elem)
|
||||
ps.appendDefaultElement(elem)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -204,7 +324,7 @@ func (h LenzTextHandler) OnCloseElement(state *xmlparsing.ParseState[*LenzParseS
|
||||
ps.LineBreak = true
|
||||
}
|
||||
if ps.CloseElement {
|
||||
ps.Tokens.AppendEndElement()
|
||||
ps.appendEndElement()
|
||||
} else {
|
||||
ps.CloseElement = true
|
||||
}
|
||||
@@ -223,17 +343,19 @@ func (h LenzTextHandler) OnText(state *xmlparsing.ParseState[*LenzParseState], e
|
||||
}
|
||||
if ps.PageBreak && ps.PC != "1" {
|
||||
ps.PageBreak = false
|
||||
inline := !ps.LineBreak
|
||||
ps.markCurrentPageInline(inline)
|
||||
note := Note{Id: ps.PC}
|
||||
quality := "outside"
|
||||
if !ps.LineBreak {
|
||||
if inline {
|
||||
quality = "inside"
|
||||
}
|
||||
ps.Tokens.AppendDivElement("", "eanchor-page", "eanchor-page-"+quality)
|
||||
ps.Tokens.AppendCustomAttribute("aria-describedby", ps.PC)
|
||||
ps.Tokens.AppendEndElement()
|
||||
ps.Tokens.AppendDivElement("", "page-counter", "page-"+quality)
|
||||
ps.Tokens.AppendText(ps.PC)
|
||||
ps.Tokens.AppendEndElement()
|
||||
ps.appendDivElement("", "eanchor-page", "eanchor-page-"+quality)
|
||||
ps.appendCustomAttribute("aria-describedby", ps.PC)
|
||||
ps.appendEndElement()
|
||||
ps.appendDivElement("", "page-counter", "page-"+quality)
|
||||
ps.appendText(ps.PC)
|
||||
ps.appendEndElement()
|
||||
note.Tokens.AppendDivElement(ps.PC, "page", "page-"+quality)
|
||||
note.Tokens.AppendText(ps.PC)
|
||||
note.Tokens.AppendEndElement()
|
||||
@@ -243,7 +365,7 @@ func (h LenzTextHandler) OnText(state *xmlparsing.ParseState[*LenzParseState], e
|
||||
ps.LineBreak = false
|
||||
}
|
||||
|
||||
ps.Tokens.AppendDefaultElement(elem)
|
||||
ps.appendDefaultElement(elem)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
23
xmlmodels/letter_text_test.go
Normal file
23
xmlmodels/letter_text_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package xmlmodels
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPageRendering(t *testing.T) {
|
||||
content := `<letterText><page index="1"/><line>First</line><page index="2"/><line>Second</line></letterText>`
|
||||
parsed, err := parseText(nil, content)
|
||||
if err != nil {
|
||||
t.Fatalf("parse error: %v", err)
|
||||
}
|
||||
state := parsed.Data()
|
||||
for i, page := range state.Pages {
|
||||
t.Logf("page %d idx=%s html=%q", i, page.Index, page.HTML())
|
||||
}
|
||||
if len(state.Pages) != 2 {
|
||||
t.Fatalf("expected 2 pages, got %d", len(state.Pages))
|
||||
}
|
||||
for i, page := range state.Pages {
|
||||
if page.HTML() == "" {
|
||||
t.Fatalf("page %d empty", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,6 +86,40 @@ type Action struct {
|
||||
Persons []RefElement `xml:"person"`
|
||||
}
|
||||
|
||||
func (m Meta) HasPerson(id int) bool {
|
||||
if id <= 0 {
|
||||
return false
|
||||
}
|
||||
for _, action := range m.Sent {
|
||||
if action.HasPerson(id) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, action := range m.Recieved {
|
||||
if action.HasPerson(id) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m Meta) HasPlace(id int) bool {
|
||||
if id <= 0 {
|
||||
return false
|
||||
}
|
||||
for _, action := range m.Sent {
|
||||
if action.HasPlace(id) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, action := range m.Recieved {
|
||||
if action.HasPlace(id) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *Action) Earliest() *Date {
|
||||
if len(a.Dates) == 0 {
|
||||
return nil
|
||||
@@ -93,3 +127,21 @@ func (a *Action) Earliest() *Date {
|
||||
|
||||
return &a.Dates[0]
|
||||
}
|
||||
|
||||
func (a Action) HasPerson(id int) bool {
|
||||
for _, ref := range a.Persons {
|
||||
if ref.Reference == id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a Action) HasPlace(id int) bool {
|
||||
for _, ref := range a.Places {
|
||||
if ref.Reference == id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user