mirror of
https://github.com/Theodor-Springmann-Stiftung/lenz-web.git
synced 2025-10-29 09:15:33 +00:00
finalversion marginalien
This commit is contained in:
@@ -122,6 +122,10 @@ type Tokens struct {
|
|||||||
Out []outToken
|
Out []outToken
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Tokens) Prepend(token outToken) {
|
||||||
|
s.Out = append([]outToken{token}, s.Out...)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Tokens) AppendDefaultElement(token xmlparsing.Token, ids ...string) {
|
func (s *Tokens) AppendDefaultElement(token xmlparsing.Token, ids ...string) {
|
||||||
t := Default(token)
|
t := Default(token)
|
||||||
if len(ids) > 0 {
|
if len(ids) > 0 {
|
||||||
@@ -131,6 +135,18 @@ func (s *Tokens) AppendDefaultElement(token xmlparsing.Token, ids ...string) {
|
|||||||
s.Out = append(s.Out, t)
|
s.Out = append(s.Out, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Tokens) AppendCustomAttribute(name, value string) {
|
||||||
|
if len(s.Out) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Out[len(s.Out)-1].Attributes == nil {
|
||||||
|
s.Out[len(s.Out)-1].Attributes = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Out[len(s.Out)-1].Attributes[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Tokens) AppendEndElement() {
|
func (s *Tokens) AppendEndElement() {
|
||||||
skip := 0
|
skip := 0
|
||||||
for i := len(s.Out) - 1; i >= 0; i-- {
|
for i := len(s.Out) - 1; i >= 0; i-- {
|
||||||
|
|||||||
@@ -30,15 +30,21 @@ type LenzParseState struct {
|
|||||||
LC int
|
LC int
|
||||||
PC string
|
PC string
|
||||||
CloseElement bool
|
CloseElement bool
|
||||||
|
Break bool
|
||||||
PageBreak bool
|
PageBreak bool
|
||||||
|
LineBreak bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LenzParseState) String() string {
|
func (s *LenzParseState) String() string {
|
||||||
builder := strings.Builder{}
|
builder := strings.Builder{}
|
||||||
|
s.Tokens.Prepend(outToken{Name: "div", Classes: []string{"fulltext"}, Type: Element})
|
||||||
|
s.Tokens.AppendEndElement()
|
||||||
builder.WriteString(s.Tokens.String())
|
builder.WriteString(s.Tokens.String())
|
||||||
|
builder.WriteString(outToken{Name: "div", Classes: []string{"notes"}, Type: Element}.String())
|
||||||
for _, note := range s.Notes {
|
for _, note := range s.Notes {
|
||||||
builder.WriteString(note.Tokens.String())
|
builder.WriteString(note.Tokens.String())
|
||||||
}
|
}
|
||||||
|
builder.WriteString(outToken{Name: "div", Classes: []string{"notes"}, Type: EndElement}.String())
|
||||||
return builder.String()
|
return builder.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +67,9 @@ func Parse(lib *xmlmodels.Library) func(s string) string {
|
|||||||
|
|
||||||
if elem.Token.Type < 3 {
|
if elem.Token.Type < 3 {
|
||||||
if elem.Token.Type == xmlparsing.EndElement {
|
if elem.Token.Type == xmlparsing.EndElement {
|
||||||
|
if elem.Token.Name == "sidenote" {
|
||||||
|
ps.LineBreak = true
|
||||||
|
}
|
||||||
if ps.CloseElement {
|
if ps.CloseElement {
|
||||||
ps.Tokens.AppendEndElement()
|
ps.Tokens.AppendEndElement()
|
||||||
} else {
|
} else {
|
||||||
@@ -73,12 +82,12 @@ func Parse(lib *xmlmodels.Library) func(s string) string {
|
|||||||
|
|
||||||
case "sidenote":
|
case "sidenote":
|
||||||
id := RandString(8)
|
id := RandString(8)
|
||||||
ps.Tokens.AppendDefaultElement(elem.Token, id)
|
ps.Tokens.AppendDefaultElement(elem.Token)
|
||||||
|
ps.Break = false
|
||||||
|
ps.Tokens.AppendCustomAttribute("aria-describedby", id)
|
||||||
if elem.Token.Attributes["annotation"] != "" ||
|
if elem.Token.Attributes["annotation"] != "" ||
|
||||||
elem.Token.Attributes["page"] != "" ||
|
elem.Token.Attributes["page"] != "" ||
|
||||||
elem.Token.Attributes["pos"] != "" {
|
elem.Token.Attributes["pos"] != "" {
|
||||||
ps.Tokens.AppendLink("#"+id, "nanchor-sidenote")
|
|
||||||
ps.Tokens.AppendEndElement()
|
|
||||||
note := Note{Id: id}
|
note := Note{Id: id}
|
||||||
note.Tokens.AppendDivElement(id, "note-sidenote-meta")
|
note.Tokens.AppendDivElement(id, "note-sidenote-meta")
|
||||||
if elem.Token.Attributes["annotation"] != "" {
|
if elem.Token.Attributes["annotation"] != "" {
|
||||||
@@ -113,8 +122,6 @@ func Parse(lib *xmlmodels.Library) func(s string) string {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
person = lib.Persons.Item(idno)
|
person = lib.Persons.Item(idno)
|
||||||
}
|
}
|
||||||
ps.Tokens.AppendLink("#"+id, "nanchor-hand")
|
|
||||||
ps.Tokens.AppendEndElement()
|
|
||||||
note := Note{Id: id}
|
note := Note{Id: id}
|
||||||
note.Tokens.AppendDivElement(id, "note-hand")
|
note.Tokens.AppendDivElement(id, "note-hand")
|
||||||
hand := "N/A"
|
hand := "N/A"
|
||||||
@@ -124,26 +131,46 @@ func Parse(lib *xmlmodels.Library) func(s string) string {
|
|||||||
note.Tokens.AppendText(hand)
|
note.Tokens.AppendText(hand)
|
||||||
note.Tokens.AppendEndElement()
|
note.Tokens.AppendEndElement()
|
||||||
ps.AppendNote(note)
|
ps.AppendNote(note)
|
||||||
ps.Tokens.AppendDefaultElement(elem.Token)
|
ps.Tokens.AppendDivElement("", "hand")
|
||||||
|
ps.Tokens.AppendCustomAttribute("aria-describedby", id)
|
||||||
|
|
||||||
case "line":
|
case "line":
|
||||||
if val := elem.Token.Attributes["type"]; val != "empty" {
|
if val := elem.Token.Attributes["type"]; val != "empty" {
|
||||||
ps.LC += 1
|
ps.LC += 1
|
||||||
ps.Tokens.AppendEmptyElement("br", ps.PC+"-"+strconv.Itoa(ps.LC))
|
if ps.Break {
|
||||||
|
ps.Tokens.AppendEmptyElement("br", ps.PC+"-"+strconv.Itoa(ps.LC))
|
||||||
|
}
|
||||||
ps.Tokens.AppendDefaultElement(elem.Token) // This is for indents, must be closed
|
ps.Tokens.AppendDefaultElement(elem.Token) // This is for indents, must be closed
|
||||||
} else {
|
} else {
|
||||||
ps.Tokens.AppendEmptyElement("br", "", "empty")
|
ps.Tokens.AppendEmptyElement("br", "", "empty")
|
||||||
ps.CloseElement = false // Here Indents make no sense, so we dont open an element
|
ps.CloseElement = false // Here Indents make no sense, so we dont open an element
|
||||||
}
|
}
|
||||||
|
ps.LineBreak = true
|
||||||
|
|
||||||
case "page":
|
case "page":
|
||||||
ps.PC = elem.Token.Attributes["index"]
|
ps.PC = elem.Token.Attributes["index"]
|
||||||
ps.Tokens.AppendLink("#"+ps.PC, "eanchor-page")
|
ps.PageBreak = true
|
||||||
ps.Tokens.AppendEndElement()
|
ps.CloseElement = false
|
||||||
ps.Tokens.AppendDivElement(ps.PC, "page")
|
|
||||||
ps.Tokens.AppendText(ps.PC)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
if !ps.Break && elem.Token.Type == xmlparsing.CharData && strings.TrimSpace(elem.Token.Data) != "" {
|
||||||
|
ps.Break = true
|
||||||
|
}
|
||||||
|
if ps.PageBreak && elem.Token.Type == xmlparsing.CharData && strings.TrimSpace(elem.Token.Data) != "" {
|
||||||
|
ps.PageBreak = false
|
||||||
|
if !ps.LineBreak {
|
||||||
|
ps.Tokens.AppendLink("#"+ps.PC, "eanchor-page")
|
||||||
|
ps.Tokens.AppendEndElement()
|
||||||
|
}
|
||||||
|
ps.Tokens.AppendDivElement(ps.PC, "page")
|
||||||
|
ps.Tokens.AppendText(ps.PC)
|
||||||
|
ps.Tokens.AppendEndElement()
|
||||||
|
strings.TrimLeft(elem.Token.Data, " \t\n\r")
|
||||||
|
}
|
||||||
|
if ps.LineBreak && elem.Token.Type == xmlparsing.CharData && strings.TrimSpace(elem.Token.Data) != "" {
|
||||||
|
strings.TrimLeft(elem.Token.Data, " \t\n\r")
|
||||||
|
ps.LineBreak = false
|
||||||
|
}
|
||||||
ps.Tokens.AppendDefaultElement(elem.Token)
|
ps.Tokens.AppendDefaultElement(elem.Token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -20,10 +20,59 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text">
|
<div class="text flex flex-row">
|
||||||
{{- Safe (ParseGeneric .text.Content) -}}
|
{{- Safe (ParseGeneric .text.Content) -}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="traditions mt-12 pt-3 border-t-gray-200 border-t-1 max-w-[90ch] print:border-none">
|
<div class="traditions mt-12 pt-3 border-t-gray-200 border-t-1 max-w-[90ch] print:border-none">
|
||||||
{{ template "_lettertrad" $model.meta -}}
|
{{ template "_lettertrad" $model.meta -}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
function alignSidenotes() {
|
||||||
|
const text = document.querySelector(".text");
|
||||||
|
if (!text) return;
|
||||||
|
const notes = text.querySelectorAll(".note-sidenote-meta,.note-hand");
|
||||||
|
|
||||||
|
// Only do margin notes if wide enough and not printing
|
||||||
|
// if (window.innerWidth < 900 || window.matchMedia("print").matches) return;
|
||||||
|
// Reset all notes for fallback
|
||||||
|
notes.forEach((note) => {
|
||||||
|
note.classList.remove("margin-note");
|
||||||
|
note.style.top = "";
|
||||||
|
});
|
||||||
|
|
||||||
|
notes.forEach((note) => {
|
||||||
|
const noteId = note.id;
|
||||||
|
if (!noteId) return;
|
||||||
|
const anchor = text.querySelector(
|
||||||
|
`.sidenote[aria-describedby="${noteId}"], .hand[aria-describedby="${noteId}"]`,
|
||||||
|
);
|
||||||
|
if (!anchor) return;
|
||||||
|
|
||||||
|
console.log(anchor);
|
||||||
|
// Mark as margin note
|
||||||
|
note.classList.add("margin-note");
|
||||||
|
// Position: relative to .text
|
||||||
|
const textRect = text.getBoundingClientRect();
|
||||||
|
const anchorRect = anchor.getBoundingClientRect();
|
||||||
|
// Get scroll offset for window
|
||||||
|
const top = anchorRect.top - textRect.top;
|
||||||
|
note.style.top = `${top}px`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run on load, resize, and before/after print
|
||||||
|
alignSidenotes();
|
||||||
|
window.addEventListener("resize", alignSidenotes);
|
||||||
|
window.addEventListener("scroll", alignSidenotes); // If container is scrollable
|
||||||
|
|
||||||
|
// Revert to fallback for print, reapply after
|
||||||
|
window.matchMedia("print").addEventListener("change", (e) => {
|
||||||
|
if (e.matches) {
|
||||||
|
alignSidenotes(); // fallback mode (will un-absolute)
|
||||||
|
} else {
|
||||||
|
setTimeout(alignSidenotes, 100); // recalc after print closes
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -87,7 +87,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
@apply font-serif max-w-[80ch] relative;
|
@apply font-serif relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text .fulltext {
|
||||||
|
@apply max-w-[80ch] mr-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text .notes {
|
||||||
|
@apply relative max-w-[80ch] pr-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text .page,
|
.text .page,
|
||||||
@@ -170,12 +178,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.text .page {
|
.text .page {
|
||||||
@apply font-sans text-sm text-gray-500 absolute left-0 ml-[-5%] w-[2%] mt-[0.4rem] leading-[1.2];
|
@apply font-sans text-sm text-slate-600 absolute -left-[2rem] w-[1rem] mt-[0.4rem] leading-[1.2];
|
||||||
}
|
}
|
||||||
|
|
||||||
.text .eanchor-page::before {
|
.text .eanchor-page::before {
|
||||||
content: " | ";
|
content: "|";
|
||||||
@apply text-gray-500 font-sans text-sm relative bottom-[0.1rem];
|
@apply text-slate-600 font-sans text-sm relative bottom-[0.1rem] pr-0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* .text .page + br { */
|
/* .text .page + br { */
|
||||||
@@ -255,7 +263,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.text .note-note {
|
.text .note-note {
|
||||||
@apply inline text-sm text-gray-500 relative left-[0.1rem] -top-[0.1rem];
|
@apply inline text-sm text-gray-500 relative left-[0.1rem];
|
||||||
}
|
}
|
||||||
|
|
||||||
.text .sidenote {
|
.text .sidenote {
|
||||||
@@ -263,14 +271,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.text .sidenote-page::before {
|
.text .sidenote-page::before {
|
||||||
content: "| S. ";
|
content: "; S. ";
|
||||||
}
|
}
|
||||||
|
|
||||||
.text .sidenote-pos {
|
.text .note-sidenote-meta > div {
|
||||||
|
@apply inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text .note-sidenote-meta .sidenote-pos {
|
||||||
@apply !hidden;
|
@apply !hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text .hand {
|
.text .hand {
|
||||||
@apply inline text-blue-950 font-didone;
|
@apply inline text-blue-950 font-didone text-[0.9rem];
|
||||||
|
}
|
||||||
|
|
||||||
|
.text .margin-note {
|
||||||
|
@apply absolute w-[13rem] text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text .note-hand {
|
||||||
|
@apply text-blue-950;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user