mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2025-10-29 09:15:33 +00:00
Statische Seiten
This commit is contained in:
67
helpers/functions/datetime.go
Normal file
67
helpers/functions/datetime.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package functions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Weekday struct {
|
||||
Name string
|
||||
ShortName string
|
||||
Number int
|
||||
}
|
||||
|
||||
type Month struct {
|
||||
Name string
|
||||
ShortName string
|
||||
Number int
|
||||
}
|
||||
|
||||
var Months = []Month{
|
||||
{"Januar", "Jan", 1},
|
||||
{"Februar", "Feb", 2},
|
||||
{"März", "Mär", 3},
|
||||
{"April", "Apr", 4},
|
||||
{"Mai", "Mai", 5},
|
||||
{"Juni", "Jun", 6},
|
||||
{"Juli", "Jul", 7},
|
||||
{"August", "Aug", 8},
|
||||
{"September", "Sep", 9},
|
||||
{"Oktober", "Okt", 10},
|
||||
{"November", "Nov", 11},
|
||||
{"Dezember", "Dez", 12},
|
||||
{"N/A", "N/A", 0},
|
||||
}
|
||||
|
||||
var Weekdays = []Weekday{
|
||||
{"Sonntag", "So", 0},
|
||||
{"Montag", "Mo", 1},
|
||||
{"Dienstag", "Di", 2},
|
||||
{"Mittwoch", "Mi", 3},
|
||||
{"Donnerstag", "Do", 4},
|
||||
{"Freitag", "Fr", 5},
|
||||
{"Samstag", "Sa", 6},
|
||||
{"N/A", "N/A", 7},
|
||||
}
|
||||
|
||||
func Today() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
func GetMonth(month any) Month {
|
||||
if val, ok := month.(int); ok {
|
||||
val -= 1
|
||||
if val < 0 || val > 11 {
|
||||
val = 12
|
||||
}
|
||||
return Months[val]
|
||||
}
|
||||
|
||||
if val, ok := month.(time.Time); ok {
|
||||
m := val.Month() - 1
|
||||
return Months[m]
|
||||
}
|
||||
|
||||
fmt.Println("Invalid month value", month)
|
||||
return Months[12]
|
||||
}
|
||||
11
helpers/functions/int.go
Normal file
11
helpers/functions/int.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package functions
|
||||
|
||||
func Add(a, b any) int {
|
||||
val1, ok1 := a.(int)
|
||||
val2, ok2 := b.(int)
|
||||
if !ok1 || !ok2 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return val1 + val2
|
||||
}
|
||||
@@ -6,8 +6,8 @@ func Arr(els ...any) []any {
|
||||
return els
|
||||
}
|
||||
|
||||
// Must have even number of args: key, value, key, value, ...
|
||||
func Dict(values ...interface{}) (map[string]interface{}, error) {
|
||||
// Must have even number of args: key, value, key, value, ...
|
||||
if len(values)%2 != 0 {
|
||||
return nil, fmt.Errorf("invalid dict call: must have even number of args")
|
||||
}
|
||||
|
||||
48
helpers/functions/toc.go
Normal file
48
helpers/functions/toc.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package functions
|
||||
|
||||
import (
|
||||
"golang.org/x/net/html"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TOCEntry struct {
|
||||
Level int
|
||||
Title string
|
||||
}
|
||||
|
||||
func getText(n *html.Node) string {
|
||||
if n.Type == html.TextNode {
|
||||
return n.Data
|
||||
}
|
||||
var sb strings.Builder
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
sb.WriteString(getText(c))
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func TOCFromHTML(htmlStr string) ([]TOCEntry, error) {
|
||||
doc, err := html.Parse(strings.NewReader(htmlStr))
|
||||
toc := []TOCEntry{}
|
||||
if err != nil {
|
||||
return toc, err
|
||||
}
|
||||
var f func(*html.Node)
|
||||
f = func(n *html.Node) {
|
||||
if n.Type == html.ElementNode {
|
||||
if len(n.Data) == 2 && n.Data[0] == 'h' && n.Data[1] >= '1' && n.Data[1] <= '6' {
|
||||
level, err := strconv.Atoi(n.Data[1:])
|
||||
if err == nil {
|
||||
title := strings.TrimSpace(getText(n))
|
||||
toc = append(toc, TOCEntry{Level: level, Title: title})
|
||||
}
|
||||
}
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
f(c)
|
||||
}
|
||||
}
|
||||
f(doc)
|
||||
return toc, nil
|
||||
}
|
||||
@@ -12,6 +12,7 @@ func init() {
|
||||
RegisterTextPage("/edition/danksagungen/", pagemodels.P_DANK_NAME)
|
||||
RegisterTextPage("/edition/literatur/", pagemodels.P_LIT_NAME)
|
||||
RegisterTextPage("/edition/einfuehrung/", pagemodels.P_EINFUEHRUNG_NAME)
|
||||
RegisterTextPage("/edition/dokumentation/", pagemodels.P_DOK_NAME)
|
||||
}
|
||||
|
||||
func RegisterStaticPage(url, name string) {
|
||||
|
||||
@@ -31,9 +31,9 @@ Modelle umwandeln (zzt RecordProxy)
|
||||
- Technologie-Stack auf Server-Rendering / Go Templates umgestellt
|
||||
- Die Seiten sollten jetzt insgesamt schneller laden
|
||||
|
||||
- Man kann auf der Startseite nach Almanach-Nummern suchen
|
||||
- Man kann auf der Startseite und in der Suche nach Almanach-Nummern suchen
|
||||
- Überall werden die Almanachnummer und Inhaltsnummer angezeigt
|
||||
- Die URL referenziert die Almanachnummern, nicht mher die DB-IDs
|
||||
- Die URL referenziert die Almanachnummern, nicht mher die Datenbank-IDs
|
||||
|
||||
- In der Almanach-Ansicht werden die Abkürzungen erklärt
|
||||
- In der Almanach- und Suchansicht werden Sammlungen abgehoben
|
||||
|
||||
@@ -47,11 +47,25 @@ func NewEngine(layouts, templates *fs.FS) *Engine {
|
||||
func (e *Engine) funcs() error {
|
||||
e.mu.Lock()
|
||||
e.mu.Unlock()
|
||||
|
||||
// Passing HTML
|
||||
e.AddFunc("Safe", functions.Safe)
|
||||
// Creating an array or dict (to pass to a template)
|
||||
e.AddFunc("Arr", functions.Arr)
|
||||
e.AddFunc("HasPrefix", strings.HasPrefix)
|
||||
e.AddFunc("Dict", functions.Dict)
|
||||
|
||||
// Datatype Functions
|
||||
e.AddFunc("HasPrefix", strings.HasPrefix)
|
||||
e.AddFunc("Contains", functions.Contains)
|
||||
e.AddFunc("Add", functions.Add)
|
||||
|
||||
// Time & Date Functions
|
||||
e.AddFunc("Today", functions.Today)
|
||||
e.AddFunc("GetMonth", functions.GetMonth)
|
||||
|
||||
// TOC
|
||||
e.AddFunc("TOCFromHTML", functions.TOCFromHTML)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
var L = (s) => {
|
||||
var _ = (s) => {
|
||||
throw TypeError(s);
|
||||
};
|
||||
var T = (s, e, t) => e.has(s) || L("Cannot " + t);
|
||||
var g = (s, e, t) => (T(s, e, "read from private field"), t ? t.call(s) : e.get(s)), c = (s, e, t) => e.has(s) ? L("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(s) : e.set(s, t), u = (s, e, t, i) => (T(s, e, "write to private field"), i ? i.call(s, t) : e.set(s, t), t), f = (s, e, t) => (T(s, e, "access private method"), t);
|
||||
const w = "script[xslt-onload]", v = "xslt-template", A = "xslt-transformed", k = "filter-list", m = "filter-list-list", M = "filter-list-item", y = "filter-list-input", x = "filter-list-searchable", B = "scroll-button", P = "tool-tip", I = "abbrev-tooltips";
|
||||
var T = (s, e, t) => e.has(s) || _("Cannot " + t);
|
||||
var f = (s, e, t) => (T(s, e, "read from private field"), t ? t.call(s) : e.get(s)), c = (s, e, t) => e.has(s) ? _("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(s) : e.set(s, t), d = (s, e, t, i) => (T(s, e, "write to private field"), i ? i.call(s, t) : e.set(s, t), t), g = (s, e, t) => (T(s, e, "access private method"), t);
|
||||
const k = "script[xslt-onload]", v = "xslt-template", w = "xslt-transformed", A = "filter-list", m = "filter-list-list", M = "filter-list-item", y = "filter-list-input", x = "filter-list-searchable", B = "scroll-button", I = "tool-tip", P = "abbrev-tooltips", C = "int-link";
|
||||
var h, b, E;
|
||||
class R {
|
||||
class z {
|
||||
constructor() {
|
||||
c(this, b);
|
||||
c(this, h);
|
||||
u(this, h, /* @__PURE__ */ new Map());
|
||||
d(this, h, /* @__PURE__ */ new Map());
|
||||
}
|
||||
setup() {
|
||||
let e = htmx.findAll(w);
|
||||
let e = htmx.findAll(k);
|
||||
for (let t of e)
|
||||
f(this, b, E).call(this, t);
|
||||
g(this, b, E).call(this, t);
|
||||
}
|
||||
hookupHTMX() {
|
||||
htmx.on("htmx:load", (e) => {
|
||||
@@ -23,26 +23,26 @@ class R {
|
||||
}
|
||||
}
|
||||
h = new WeakMap(), b = new WeakSet(), E = function(e) {
|
||||
if (e.getAttribute(A) === "true" || !e.hasAttribute(v))
|
||||
if (e.getAttribute(w) === "true" || !e.hasAttribute(v))
|
||||
return;
|
||||
let t = "#" + e.getAttribute(v), i = g(this, h).get(t);
|
||||
let t = "#" + e.getAttribute(v), i = f(this, h).get(t);
|
||||
if (!i) {
|
||||
let l = htmx.find(t);
|
||||
if (l) {
|
||||
let S = l.innerHTML ? new DOMParser().parseFromString(l.innerHTML, "application/xml") : l.contentDocument;
|
||||
i = new XSLTProcessor(), i.importStylesheet(S), g(this, h).set(t, i);
|
||||
i = new XSLTProcessor(), i.importStylesheet(S), f(this, h).set(t, i);
|
||||
} else
|
||||
throw new Error("Unknown XSLT template: " + t);
|
||||
}
|
||||
let r = new DOMParser().parseFromString(e.innerHTML, "application/xml"), n = i.transformToFragment(r, document), a = new XMLSerializer().serializeToString(n);
|
||||
e.outerHTML = a;
|
||||
let r = new DOMParser().parseFromString(e.innerHTML, "application/xml"), n = i.transformToFragment(r, document), o = new XMLSerializer().serializeToString(n);
|
||||
e.outerHTML = o;
|
||||
};
|
||||
var o, p, _;
|
||||
var a, p, L;
|
||||
class H extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
c(this, p);
|
||||
c(this, o, !1);
|
||||
c(this, a, !1);
|
||||
this._items = [], this._url = "", this._filterstart = !1, this._placeholder = "Liste filtern...", this.render();
|
||||
}
|
||||
static get observedAttributes() {
|
||||
@@ -55,7 +55,7 @@ class H extends HTMLElement {
|
||||
return this._items;
|
||||
}
|
||||
connectedCallback() {
|
||||
this._url = this.getAttribute("data-url") || "./", this._filterstart = this.getAttribute("data-filterstart") === "true", this._placeholder = this.getAttribute("data-placeholder") || "Liste filtern...", this._filterstart && u(this, o, !0), this.addEventListener("input", this.onInput.bind(this)), this.addEventListener("keydown", this.onEnter.bind(this)), this.addEventListener("focusin", this.onGainFocus.bind(this)), this.addEventListener("focusout", this.onLoseFocus.bind(this));
|
||||
this._url = this.getAttribute("data-url") || "./", this._filterstart = this.getAttribute("data-filterstart") === "true", this._placeholder = this.getAttribute("data-placeholder") || "Liste filtern...", this._filterstart && d(this, a, !0), this.addEventListener("input", this.onInput.bind(this)), this.addEventListener("keydown", this.onEnter.bind(this)), this.addEventListener("focusin", this.onGainFocus.bind(this)), this.addEventListener("focusout", this.onLoseFocus.bind(this));
|
||||
}
|
||||
attributeChangedCallback(t, i, r) {
|
||||
t === "data-url" && i !== r && (this._url = r, this.render()), t === "data-filterstart" && i !== r && (this._filterstart = r === "true", this.render()), t === "data-placeholder" && i !== r && (this._placeholder = r, this.render());
|
||||
@@ -64,14 +64,14 @@ class H extends HTMLElement {
|
||||
t.target && t.target.tagName.toLowerCase() === "input" && (this._filter = t.target.value, this.renderList());
|
||||
}
|
||||
onGainFocus(t) {
|
||||
t.target && t.target.tagName.toLowerCase() === "input" && (u(this, o, !1), this.renderList());
|
||||
t.target && t.target.tagName.toLowerCase() === "input" && (d(this, a, !1), this.renderList());
|
||||
}
|
||||
onLoseFocus(t) {
|
||||
let i = this.querySelector("input");
|
||||
if (t.target && t.target === i) {
|
||||
if (relatedElement = t.relatedTarget, relatedElement && this.contains(relatedElement))
|
||||
return;
|
||||
i.value = "", this._filter = "", this._filterstart && u(this, o, !0), this.renderList();
|
||||
i.value = "", this._filter = "", this._filterstart && d(this, a, !0), this.renderList();
|
||||
}
|
||||
}
|
||||
onEnter(t) {
|
||||
@@ -139,7 +139,7 @@ class H extends HTMLElement {
|
||||
`;
|
||||
}
|
||||
ActiveDot(t) {
|
||||
return f(this, p, _).call(this, t), "";
|
||||
return g(this, p, L).call(this, t), "";
|
||||
}
|
||||
NoItems(t) {
|
||||
return t.length === 0 ? '<div class="px-2 py-0.5 italic text-gray-500">Keine Einträge gefunden</div>' : "";
|
||||
@@ -167,13 +167,13 @@ class H extends HTMLElement {
|
||||
t = this._items.filter((r) => i.every((n) => this.getSearchText(r).toLowerCase().includes(n.toLowerCase())));
|
||||
}
|
||||
return `
|
||||
<div id="${m}" class="${m} pt-1 min-h-[19rem] max-h-60 overflow-auto border-b border-zinc-300 bg-stone-50 ${g(this, o) ? "hidden" : ""}">
|
||||
<div id="${m}" class="${m} pt-1 min-h-[19rem] max-h-60 overflow-auto border-b border-zinc-300 bg-stone-50 ${f(this, a) ? "hidden" : ""}">
|
||||
${t.map(
|
||||
(i, r) => `
|
||||
<a
|
||||
href="${this._url}${this.getHREF(i)}"
|
||||
class="${M} block px-2.5 py-0.5 hover:bg-slate-200 no-underline ${r % 2 === 0 ? "bg-stone-100" : "bg-stone-50"}"
|
||||
${f(this, p, _).call(this, i) ? 'aria-current="page"' : ""}>
|
||||
${g(this, p, L).call(this, i) ? 'aria-current="page"' : ""}>
|
||||
${this.ActiveDot(i)}
|
||||
${this.getLinkText(i)}
|
||||
</a>
|
||||
@@ -184,13 +184,13 @@ class H extends HTMLElement {
|
||||
`;
|
||||
}
|
||||
}
|
||||
o = new WeakMap(), p = new WeakSet(), _ = function(t) {
|
||||
a = new WeakMap(), p = new WeakSet(), L = function(t) {
|
||||
if (!t)
|
||||
return !1;
|
||||
let i = this.getHREF(t);
|
||||
return !(i === "" || !window.location.href.endsWith(i));
|
||||
};
|
||||
class C extends HTMLElement {
|
||||
class $ extends HTMLElement {
|
||||
constructor() {
|
||||
super(), this.handleScroll = this.handleScroll.bind(this), this.scrollToTop = this.scrollToTop.bind(this);
|
||||
}
|
||||
@@ -334,7 +334,7 @@ class F extends HTMLElement {
|
||||
}
|
||||
}
|
||||
}
|
||||
class d extends HTMLElement {
|
||||
class u extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return ["data-text", "data-abbrevmap"];
|
||||
}
|
||||
@@ -397,7 +397,7 @@ class d extends HTMLElement {
|
||||
};
|
||||
}
|
||||
constructor() {
|
||||
super(), this._abbrevMap = d.defaultAbbrevMap;
|
||||
super(), this._abbrevMap = u.defaultAbbrevMap;
|
||||
}
|
||||
connectedCallback() {
|
||||
this.render();
|
||||
@@ -407,13 +407,13 @@ class d extends HTMLElement {
|
||||
}
|
||||
_parseAndSetAbbrevMap(e) {
|
||||
if (!e) {
|
||||
this._abbrevMap = d.defaultAbbrevMap;
|
||||
this._abbrevMap = u.defaultAbbrevMap;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this._abbrevMap = JSON.parse(e);
|
||||
} catch {
|
||||
this._abbrevMap = d.defaultAbbrevMap;
|
||||
this._abbrevMap = u.defaultAbbrevMap;
|
||||
}
|
||||
}
|
||||
setAbbrevMap(e) {
|
||||
@@ -437,17 +437,17 @@ class d extends HTMLElement {
|
||||
}
|
||||
const n = this.findLongestAbbrevAt(e, r, t);
|
||||
if (n) {
|
||||
const { match: a, meaning: l } = n;
|
||||
const { match: o, meaning: l } = n;
|
||||
i += `
|
||||
<tool-tip position="top" class="!inline" timeout="300">
|
||||
<div class="data-tip p-2 text-sm text-white bg-gray-700 rounded shadow">
|
||||
${l}
|
||||
</div>
|
||||
<span class="cursor-help text-blue-900 hover:text-slate-800">
|
||||
${a}
|
||||
${o}
|
||||
</span>
|
||||
</tool-tip>
|
||||
`, r += a.length;
|
||||
`, r += o.length;
|
||||
} else
|
||||
i += e[r], r++;
|
||||
}
|
||||
@@ -455,21 +455,40 @@ class d extends HTMLElement {
|
||||
}
|
||||
findLongestAbbrevAt(e, t, i) {
|
||||
let r = null, n = 0;
|
||||
for (const a of Object.keys(i))
|
||||
e.startsWith(a, t) && a.length > n && (r = a, n = a.length);
|
||||
for (const o of Object.keys(i))
|
||||
e.startsWith(o, t) && o.length > n && (r = o, n = o.length);
|
||||
return r ? { match: r, meaning: i[r] } : null;
|
||||
}
|
||||
isSpaceOrPunct(e) {
|
||||
return /\s|[.,;:!?]/.test(e);
|
||||
}
|
||||
}
|
||||
customElements.define(I, d);
|
||||
customElements.define(k, H);
|
||||
customElements.define(B, C);
|
||||
customElements.define(P, F);
|
||||
class N extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
connectedCallback() {
|
||||
this.style.cursor = "pointer", this.addEventListener("click", this.handleClick);
|
||||
}
|
||||
disconnectedCallback() {
|
||||
this.removeEventListener("click", this.handleClick);
|
||||
}
|
||||
handleClick(e) {
|
||||
const t = this.getAttribute("data-jump");
|
||||
if (t) {
|
||||
const i = document.querySelector(t);
|
||||
i ? i.scrollIntoView({ behavior: "smooth" }) : console.warn(`No element found for selector: ${t}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define(C, N);
|
||||
customElements.define(P, u);
|
||||
customElements.define(A, H);
|
||||
customElements.define(B, $);
|
||||
customElements.define(I, F);
|
||||
export {
|
||||
d as AbbreviationTooltips,
|
||||
u as AbbreviationTooltips,
|
||||
H as FilterList,
|
||||
C as ScrollButton,
|
||||
R as XSLTParseProcess
|
||||
$ as ScrollButton,
|
||||
z as XSLTParseProcess
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1,22 @@
|
||||
<div class="mt-12 pt-3 border-t">Datenschutz, Impressum.</div>
|
||||
{{- $date := Today -}}
|
||||
<footer class="container-normal pb-1.5 text-base text-gray-800">
|
||||
<div class="mt-12 pt-3 flex flex-row justify-between">
|
||||
<div>
|
||||
<i class="ri-creative-commons-line"></i>
|
||||
<i class="ri-creative-commons-by-line"></i>
|
||||
<a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>
|
||||
<span>·</span>
|
||||
<span>{{- (GetMonth $date).Name }} {{ $date.Year }}</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span>{{- .site.title }} – ein Projekt der</span>
|
||||
<a href="https://theodor-springmann-stiftung.de">Theodor Springmann Stiftung</a>
|
||||
<span>·</span>
|
||||
<a href="/datenschutz/">Impressum & Datenschutz</a>
|
||||
<span>·</span>
|
||||
<i class="ri-code-line"></i>
|
||||
<a href="https://github.com/Theodor-Springmann-Stiftung/musenalm">Code</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<button
|
||||
{{ if and $model.page (HasPrefix $model.page.Path "/edition") -}}
|
||||
aria-current="true"
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
data-url="/edition/"
|
||||
class="text-slate-600 hover:text-slate-900 hover:cursor-pointer hover:bg-slate-100
|
||||
!pr-2.5"
|
||||
|
||||
@@ -29,29 +29,6 @@
|
||||
<link href="/assets/css/remixicon.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" type="text/css" href="/assets/css/fonts.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/assets/style.css" />
|
||||
|
||||
{{ if .isDev -}}
|
||||
{{/* INFO: this runs airs injected script on page load, in dev mode */}}
|
||||
{{/* INFO: Make sure airs proxy runs on port 8081 */}}
|
||||
<script type="module">
|
||||
function hookupAIR() {
|
||||
const port = window.location.port;
|
||||
if (!port || port !== "8081") {
|
||||
return;
|
||||
}
|
||||
document.body.addEventListener("htmx:afterSwap", (event) => {
|
||||
if (event.detail.target === document.body) {
|
||||
const scriptEl = document.querySelector("body > script:last-of-type");
|
||||
if (scriptEl && scriptEl.textContent.trim()) {
|
||||
new Function(scriptEl.textContent)();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
hookupAIR();
|
||||
</script>
|
||||
{{- end }}
|
||||
</head>
|
||||
|
||||
<body class="w-full text-lg" hx-ext="response-targets" hx-boost="true">
|
||||
@@ -68,10 +45,9 @@
|
||||
{{ end }}
|
||||
</main>
|
||||
|
||||
<footer class="container-normal">
|
||||
{{ block "_footer" . }}
|
||||
{{ end }}
|
||||
</footer>
|
||||
{{ block "_footer" . }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
<scroll-button></scroll-button>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
{{- $bd := index $entries $rel.Entry -}}
|
||||
|
||||
{{- if $bd -}}
|
||||
<div class="flex flex-row odd:bg-zinc-100 px-3 py-0.5 justify-between w-full">
|
||||
<div class="flex flex-row odd:bg-zinc-100 px-3 ml-2 py-0.5 justify-between w-full">
|
||||
<a href="/almanach/{{ $bd.MusenalmID }}" class="no-underline">
|
||||
<div class="">
|
||||
{{- if $bd.PreferredTitle -}}
|
||||
|
||||
50
views/routes/components/_toc.gohtml
Normal file
50
views/routes/components/_toc.gohtml
Normal file
@@ -0,0 +1,50 @@
|
||||
{{ $toc := . }}
|
||||
|
||||
{{- if $toc -}}
|
||||
<div class="block border text-base w-max">
|
||||
<div class="text-sm py-1.5 italic bg-slate-100 px-4 border-b">Auf dieser Seite:</div>
|
||||
{{- $h1cnt := 0 -}}
|
||||
{{- $h2cnt := 0 -}}
|
||||
{{- $h3cnt := 0 -}}
|
||||
{{- $h4cnt := 0 -}}
|
||||
{{- $h5cnt := 0 -}}
|
||||
{{- $h6cnt := 0 -}}
|
||||
<div class="py-2 px-4">
|
||||
{{- range $i, $t := $toc -}}
|
||||
<div class="">
|
||||
{{- if eq $t.Level 1 -}}
|
||||
{{- $h1cnt = Add $h1cnt 1 -}}
|
||||
<int-link data-jump=".jumptext h1:nth-of-type({{- $h1cnt -}})" class="font-serif"
|
||||
>{{ $t.Title }}</int-link
|
||||
>
|
||||
{{- else if eq $t.Level 2 -}}
|
||||
{{- $h2cnt = Add $h2cnt 1 -}}
|
||||
<int-link data-jump=".jumptext h2:nth-of-type({{- $h2cnt -}})" class="font-serif"
|
||||
>{{ $t.Title }}</int-link
|
||||
>
|
||||
{{- else if eq $t.Level 3 -}}
|
||||
{{- $h3cnt = Add $h3cnt 1 -}}
|
||||
<int-link data-jump=".jumptext h3:nth-of-type({{- $h3cnt -}})" class="font-serif"
|
||||
>{{ $t.Title }}</int-link
|
||||
>
|
||||
{{- else if eq $t.Level 4 -}}
|
||||
{{- $h4cnt = Add $h4cnt 1 -}}
|
||||
<int-link data-jump=".jumptext h4:nth-of-type({{- $h4cnt -}})" class="font-serif"
|
||||
>{{ $t.Title }}</int-link
|
||||
>
|
||||
{{- else if eq $t.Level 5 -}}
|
||||
{{- $h5cnt = Add $h5cnt 1 -}}
|
||||
<int-link data-jump=".jumptext h5:nth-of-type({{- $h5cnt -}})" class="font-serif"
|
||||
>{{ $t.Title }}</int-link
|
||||
>
|
||||
{{- else if eq $t.Level 6 -}}
|
||||
{{- $h6cnt = Add $h6cnt 1 -}}
|
||||
<int-link data-jump=".jumptext h6:nth-of-type({{- $h6cnt -}})" class="font-serif"
|
||||
>{{ $t.Title }}</int-link
|
||||
>
|
||||
{{- end -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
</div>
|
||||
{{- end -}}
|
||||
@@ -1 +1,238 @@
|
||||
Hello from Datenschutz
|
||||
<div class="container-normal">
|
||||
<div class="text [&_*]:!indent-0 border px-8 py-7">
|
||||
<h1 class="!mt-0">Impressum</h1>
|
||||
<div>
|
||||
<p>
|
||||
<strong>Telemedienanbieter im Sinne des § 5 TMG:</strong><br />Theodor Springmann
|
||||
Stiftung<br />Hirschgasse 2<br />69120 Heidelberg<br /><br />Telefon +49 6221 436235<br />Email <a
|
||||
href="mailto:info@theodor-springmann-stiftung.de"
|
||||
>info@theodor-springmann-stiftung.de</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Rechtsform und Sitz:</strong><br />Die Theodor Springmann Stiftung ist eine
|
||||
rechtsfähige Stiftung bürgerlichen Rechts. Sitz der Stiftung ist Heidelberg.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Vorstand der Theodor Springmann Stiftung:</strong><br />Dr. Randolf Straky
|
||||
(Präsident)
|
||||
</p>
|
||||
<p>
|
||||
<strong>Geschäftsführung:</strong><br />Dr. Randolf Straky<br />Theodor Springmann
|
||||
Stiftung<br />Hirschgasse 2<br />69120 Heidelberg
|
||||
</p>
|
||||
<p>
|
||||
<strong>Zuständige Aufsichtsbehörde:</strong><br />Regierungspräsidium Karlsruhe<br />Schlossplatz
|
||||
1-3<br />76131 Karlsruhe<br /><br /><a
|
||||
href="https://rp.baden-wuerttemberg.de/Themen/Stiftung/Seiten/Ansprechpartner.aspx"
|
||||
>Ansprechpartner</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Redaktionelle Verantwortung nach § 55 Abs. 2 RStV:</strong><br />Adrian
|
||||
Braunbehrens<br />Theodor Springmann Stiftung<br />Hirschgasse 2<br />69120 Heidelberg
|
||||
</p>
|
||||
</div>
|
||||
2024-10-17 09:52:17.412Z
|
||||
</div>
|
||||
<div class="text">
|
||||
<h1 class="mt-12">Datenschutzerklärung</h1>
|
||||
<div class="ma-prose-prose">
|
||||
<div>
|
||||
<h2>Präambel</h2>
|
||||
<p>
|
||||
Diese Datenschutzerklärung informiert Sie über die Art, den Umfang und den Zweck der
|
||||
personenbezogenen Daten, die im Rahmen dieser Onlinepräsenz von Ihnen erhoben und von uns
|
||||
verarbeitet werden, sowie die Ihnen zustehenden Rechte.<br />Personenbezogene Daten sind
|
||||
alle Informationen, die sich auf eine identifizierte oder identifizierbare natürliche
|
||||
Person beziehen. Als identifizierbar wird eine natürliche Person angesehen, die direkt
|
||||
oder indirekt identifiziert werden kann. Im Hinblick auf weitere verwendete
|
||||
Begrifflichkeiten verweisen wir auf die Definitionen der Datenschutz-Grundverordnung
|
||||
(DSGVO), Artikel 4.<br />Erfolgt die Verarbeitung personenbezogener Daten auf Grundlage
|
||||
des Art. 6 Abs. 1 lit. f DSGVO, so besteht unser berechtigtes Interesse in der Erfüllung
|
||||
unseres satzungsgemäßen Stiftungszwecks. Zweck der Stiftung ist es, in gemeinnütziger
|
||||
Weise Wissenschaft und Kunst, Völkerverständigung und Entwicklungshilfe zu fördern und zum
|
||||
menschlichen Selbstverständnis sowie zum Erkennen und Lindern strukturell bedingter Not
|
||||
und Bedürftigkeit von Menschen in aller Welt beizutragen.
|
||||
</p>
|
||||
<h2>Verantwortlicher</h2>
|
||||
<p>
|
||||
Verantwortlich im Sinne von Art. 4 Abs. 7 DSGVO für die Verarbeitung personenbezogener
|
||||
Daten ist:<br />Theodor Springmann Stiftung<br />Hirschgasse 2<br />69120 Heidelberg<br /><br /><a
|
||||
href="mailto:info@theodor-springmann-stiftung.de"
|
||||
>info@theodor-springmann-stiftung.de</a
|
||||
>
|
||||
</p>
|
||||
<h2>Automatisch bei Nutzung unserer Onlinepräsenz erfasste Daten</h2>
|
||||
<p>
|
||||
Mit der Nutzung unserer Onlinepräsenz werden automatisch personenbezogene und allgemeine
|
||||
Daten vom Nutzer übermittelt und von uns erfasst und gespeichert. Die von uns erhobenen
|
||||
Daten umfassen:
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
die IP-Adresse des ans Internet angeschlossenen Netzwerkgeräts (Computer oder Router des
|
||||
Nutzers),
|
||||
</li>
|
||||
<li>den verwendeten Browsertyp und dessen Version,</li>
|
||||
<li>das verwendete Betriebssystem und dessen Version,</li>
|
||||
<li>die Internetseite, die den Nutzer auf unsere Onlinepräsenz verweist (Referrer),</li>
|
||||
<li>die Unterwebseiten, die der Nutzer auf unserer Onlinepräsenz aufruft,</li>
|
||||
<li>das Datum und die Uhrzeit des Aufrufs,</li>
|
||||
<li>den Internet-Service-Provider des Nutzers,</li>
|
||||
<li>sonstige ähnliche Daten.</li>
|
||||
</ol>
|
||||
<p>
|
||||
Die Theodor Springmann Stiftung zieht aus diesen Daten keine Rückschlüsse auf die
|
||||
betroffenen Nutzer. Diese Informationen werden benötigt, um
|
||||
</p>
|
||||
<ol>
|
||||
<li>die vom Nutzer angeforderten Inhalte korrekt auszuliefern,</li>
|
||||
<li>
|
||||
technische Probleme zu diagnostizieren, unsere IT-Systeme vor Angriffen zu schützen und
|
||||
im Falle eines Angriffs den zuständigen Behörden notwendige Informationen zur
|
||||
Strafverfolgung bereitstellen zu können,
|
||||
</li>
|
||||
<li>die Inhalte und die Bedienung unserer Onlinepräsenz zu optimieren.</li>
|
||||
</ol>
|
||||
<p>
|
||||
Die oben genannten Verarbeitungszwecke werden von Subsystemen erfüllt, die unabhängig von
|
||||
anderen Subsystemen ihre jeweils eigene Kopie der vom Nutzer erhobenen Daten erhalten,
|
||||
verarbeiten und löschen. Dabei werden den Subsystemen lediglich die zur Ausführung ihrer
|
||||
Aufgabe erforderlichen Daten übergeben. Alle Subsysteme befinden sich auf Servern der
|
||||
Theodor Springmann Stiftung.
|
||||
</p>
|
||||
<h3>Auslieferung der vom Nutzer angeforderten Inhalte</h3>
|
||||
<p>
|
||||
Mit der Anforderung einer Seite dieser Onlinepräsenz übermittelt der Browser des Nutzers
|
||||
automatisch die IP-Adresse seines ans Internet angeschlossenen Netzwerkgeräts (Computer
|
||||
oder Router) und andere Daten. Ohne diese IP-Adresse können die Inhalte dieser
|
||||
Online-Präsenz nicht an den Nutzer zurückgesendet werden. Daher ist die Speicherung und
|
||||
Verarbeitung der IP-Adresse für den Betrieb dieser Onlinepräsenz notwendig. Ebenfalls
|
||||
übermittelte Daten über den verwendeten Browser usw. werden gegebenenfalls verwendet, um
|
||||
die Anzeige der Webseite an das Anzeigegerät oder den Browser anzupassen. Die zum Zwecke
|
||||
der Auslieferung von Inhalten gesammelten Daten werden direkt nach Übertragung der Inhalte
|
||||
vom Subsystem gelöscht. Die Verarbeitung dieser Daten erfolgt auf der Grundlage eines
|
||||
berechtigten Interesses nach Art. 6 Abs. 1 lit. f DSGVO.
|
||||
</p>
|
||||
<h3>Diagnose und Schutz der IT-Systeme</h3>
|
||||
<p>
|
||||
Daneben erfolgt eine Speicherung der oben genannten Daten in Diagnose-Protokollen. Die
|
||||
Protokollierung ist notwendig, um etwaige technische Probleme analysieren oder Angriffe
|
||||
erkennen und abwehren zu können. Bei Angriffen auf unsere IT-Systeme übergeben wir die
|
||||
Diagnose-Protokolle den zuständigen Strafverfolgungsbehörden. Die Diagnose-Protokolle
|
||||
werden nach 14 Tagen automatisch gelöscht. Die Speicherung und Verarbeitung dieser Daten
|
||||
erfolgt auf Grundlage eines berechtigten Interesses nach Art. 6 Abs. 1 lit. f DSGVO.
|
||||
</p>
|
||||
<h3>Analyse zur Optimierung der Inhalte und der Bedienung</h3>
|
||||
<p>
|
||||
Die oben genannten Daten werden von einem Webanalyseprogramm verarbeitet. Dabei werden die
|
||||
Daten pseudonymisiert und aggregiert, so dass sie einzelnen Nutzern nicht mehr zugeordnet
|
||||
werden können. Das Analyseprogramm wird auf einem von uns betriebenen Server ausgeführt,
|
||||
daher werden keine Daten an Dritte übermittelt. Der Zweck dieser Erfassung besteht in der
|
||||
Analyse der Nutzung unseres Angebots, durch die eine Verbesserung unserer Webseiten, der
|
||||
Inhalte und der Bedienung ermöglicht wird. Das Analyseprogramm speichert die
|
||||
anonymisierten Daten ohne zeitliche Begrenzung. Die Verarbeitung dieser Daten erfolgt auf
|
||||
der Grundlage eines berechtigten Interesses (Art. 6 Abs. 1 lit. f DSGVO).
|
||||
</p>
|
||||
<h2>Sonstige Daten</h2>
|
||||
<p>
|
||||
Sämtliche Daten, die mit dem Aufruf einer Webseite von unserer Onlinepräsenz an den Nutzer
|
||||
übertragen werden, werden von unseren Servern bereitgestellt. Wir nutzen kein von Dritten
|
||||
bereitgestelltes Content Delivery Network (CDN), um Teile unserer Onlinepräsenz (z. B.
|
||||
Javascript- oder Webfont-Dateien) von dort an den Nutzer zu übermitteln. Wir verlinken
|
||||
auch nicht in soziale Netzwerke. Insofern können von Dritten keine personenbezogenen Daten
|
||||
über unsere Onlinepräsenz erhoben werden, es sei denn, die Datenerfassung durch Dritte
|
||||
basiert auf einer gesetzlichen Grundlage und/oder wurde behördlich angeordnet.<br />In
|
||||
unseren Inhalten können sich jedoch Hyperlinks („Links“) auf fremde Onlineangebote
|
||||
befinden. Mit dem Anklicken eines solchen Links verlässt der Nutzer unsere Onlinepräsenz
|
||||
und damit den Geltungsbereich dieser Datenschutzerklärung.
|
||||
</p>
|
||||
<h2>Erhobene Daten bei Kontakt</h2>
|
||||
<p>
|
||||
Unsere Onlinepräsenz hält unter anderem aufgrund gesetzlicher Vorgaben verschiedene
|
||||
Möglichkeiten bereit, mit uns in Kontakt zu treten. Sofern eine betroffene Person Kontakt
|
||||
zu uns aufnimmt, werden die von der betroffenen Person übermittelten personenbezogenen
|
||||
Daten automatisch gespeichert (z. B. Telefonnummer, E-Mail-Adresse). Diese Angaben werden
|
||||
zum Zwecke der Bearbeitung sowie für sich möglicherweise anschließende Kommunikation von
|
||||
uns gespeichert. Sobald die Speicherung dieser Daten nicht mehr erforderlich ist und keine
|
||||
gesetzlichen Archivierungsgründe vorliegen, werden die Daten gelöscht. Die
|
||||
Erforderlichkeit wird alle zwei Jahre überprüft. Es erfolgt keine Weitergabe dieser
|
||||
personenbezogenen Daten an Dritte.<br />Von betroffenen Personen übermittelte
|
||||
Informationen zur Bearbeitung einer Kontaktanfrage werden gemäß Art. 6 Abs. 1 lit. b
|
||||
(vertragliche und vorvertragliche Beziehungen) oder lit. f (andere Anfragen) der DSGVO
|
||||
verarbeitet.
|
||||
</p>
|
||||
<h2>Speicherung und Löschung von personenbezogenen Daten</h2>
|
||||
<p>
|
||||
Personenbezogene Daten werden von uns für die Dauer der entsprechenden gesetzlichen
|
||||
Aufbewahrungspflichten oder für den zur Vertragserfüllung erforderlichen Zeitraum
|
||||
gespeichert. Liegen solche Gründe nicht vor, werden personenbezogene Daten für den
|
||||
Zeitraum, der zur Erreichung des Speicherzwecks erforderlich ist, verarbeitet und
|
||||
gespeichert, sofern in dieser Datenschutzerklärung nicht ausdrücklich anderes angegeben
|
||||
ist. Sobald die Daten für ihre Zweckbestimmung nicht mehr erforderlich sind oder der
|
||||
Speicherungszweck entfällt, werden sie nach Maßgabe der gesetzlichen Vorschriften gelöscht
|
||||
oder in ihrer Verarbeitung eingeschränkt.<br />Sollten die Daten nicht gelöscht werden
|
||||
dürfen, weil sie einer gesetzlichen Aufbewahrungspflicht unterliegen, wird deren
|
||||
Verarbeitung durch Archivierung eingeschränkt. Die Daten werden erst nach Ablauf der
|
||||
gesetzlich vorgeschriebenen Speicherfrist gelöscht.<br />Alle sechs Monate wird
|
||||
routinemäßig geprüft, ob der Speicherungszweck weggefallen bzw. die Aufbewahrungspflicht
|
||||
abgelaufen ist. Anschließend wird gegebenenfalls die Löschung durchgeführt.
|
||||
</p>
|
||||
<h2>Rechte der betroffenen Personen</h2>
|
||||
<p>
|
||||
Unter den angegebenen Kontaktdaten können betroffene Personen jederzeit nachgenannte
|
||||
Rechte ausüben. Eine betroffene Person kann von uns verlangen, dass
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Auskunft über sie betreffende, bei uns gespeicherte Daten und deren Verarbeitung erteilt
|
||||
wird (Art. 15 DSGVO),
|
||||
</li>
|
||||
<li>
|
||||
sie betreffende unrichtige personenbezogene Daten berichtigt werden (Art. 16 DSGVO),
|
||||
</li>
|
||||
<li>
|
||||
sie betreffende, bei uns gespeicherte personenbezogene Daten gelöscht werden (Art. 17
|
||||
DSGVO),
|
||||
</li>
|
||||
<li>
|
||||
die Verarbeitung sie betreffender, bei uns gespeicherter Daten, die aufgrund
|
||||
gesetzlicher Vorschriften oder anderer Gründe nicht gelöscht werden dürfen,
|
||||
eingeschränkt wird (Art. 18 DSGVO),
|
||||
</li>
|
||||
<li>
|
||||
sie betreffende Daten übertragbar sind, soweit sie in die Datenverarbeitung eingewilligt
|
||||
oder einen Vertrag mit uns geschlossen hat (Art. 20 DSGVO),
|
||||
</li>
|
||||
<li>
|
||||
sie betreffende Daten nach einem Widerspruch nicht weiter von uns verarbeitet werden
|
||||
(Art. 21 DSGVO).
|
||||
</li>
|
||||
</ul>
|
||||
<p>Eine betroffene Person hat ferner jederzeit das Recht,</p>
|
||||
<ul>
|
||||
<li>
|
||||
eine erteilte Einwilligung in die Erhebung und Verarbeitung personenbezogener Daten nach
|
||||
Art. 6 Abs. 1 lit. a oder Art. 9 Abs. 2 lit. a DSGVO für die Zukunft zu widerrufen (Art.
|
||||
7 Abs. 3 DSGVO),
|
||||
</li>
|
||||
<li>
|
||||
sich mit einer Beschwerde an eine Aufsichtsbehörde zu wenden (Art. 15 Abs. 1 lit. f
|
||||
DSGVO). Eine Liste der Aufsichtsbehörden lässt sich unter <a
|
||||
href="https://www.bfdi.bund.de/DE/Infothek/Anschriften_Links/anschriften_links-node.html"
|
||||
>diesem Link</a
|
||||
> abrufen.
|
||||
</li>
|
||||
</ul>
|
||||
<h2>Änderung unserer Datenschutzerklärung</h2>
|
||||
<p>
|
||||
Wir behalten uns vor, diese Datenschutzerklärung ohne vorherige Ankündigung an neue
|
||||
rechtliche oder technische Sachverhalte sowie an Änderungen unserer Leistungen oder
|
||||
Prozesse anzupassen. Es gilt die jeweils auf der Onlinepräsenz veröffentlichte Version der
|
||||
Datenschutzerklärung.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<div class="container-normal">
|
||||
<div class="container-normal relative">
|
||||
<div class="text">
|
||||
{{ Safe .record.Text }}
|
||||
{{ if .record.Title }}<h1 class="mb-12">{{ .record.Title }}</h1>{{ end }}
|
||||
<div class="text [&_*]:!indent-0 [&_p]:mt-2">
|
||||
{{ Safe .record.Text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<title>{{ .site.title }} – {{ .record.title }}</title>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="container-normal relative">
|
||||
<div class="text">
|
||||
{{ if .record.Title }}<h1 class="mb-12">{{ .record.Title }}</h1>{{ end }}
|
||||
<div class="text">
|
||||
{{ Safe .record.Text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<title>{{ .site.title }} – {{ .record.Title }}</title>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<title>{{ .site.title }} – {{ .record.Title }}</title>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<div class="container-normal">
|
||||
<div class="container-normal relative">
|
||||
<div class="text">
|
||||
{{ Safe .record.Text }}
|
||||
{{ if .record.Title }}<h1 class="mb-12">{{ .record.Title }}</h1>{{ end }}
|
||||
<div class="text [&_*]:!indent-0 [&_p]:mt-2">
|
||||
{{ Safe .record.Text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<title>{{ .site.title }} – {{ .record.Title }}</title>
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
<div class="container-normal">
|
||||
{{ $toc := TOCFromHTML .record.Text }}
|
||||
|
||||
|
||||
<div class="container-normal relative">
|
||||
<div class="text indented">
|
||||
{{ Safe .record.Text }}
|
||||
{{ if .record.Title }}<h1 class="mb-12">{{ .record.Title }}</h1>{{ end }}
|
||||
<div class="flex flex-row gap-x-6 justify-between">
|
||||
<div class="jumptext grow shrink-0">
|
||||
{{ Safe .record.Text }}
|
||||
</div>
|
||||
<div>
|
||||
{{- if $toc -}}
|
||||
<div class="xl:sticky xl:top-8 grow-0">
|
||||
{{- template "_toc" $toc -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<title>{{ .site.title }} – {{ .record.Title }}</title>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<title>Musenalm - Reihen</title>
|
||||
<title>{{ .site.title }} – Reihen</title>
|
||||
|
||||
@@ -14,6 +14,7 @@ const FILTER_LIST_SEARCHABLE = "filter-list-searchable";
|
||||
const SCROLL_BUTTON_ELEMENT = "scroll-button";
|
||||
const TOOLTIP_ELEMENT = "tool-tip";
|
||||
const ABBREV_TOOLTIPS_ELEMENT = "abbrev-tooltips";
|
||||
const INT_LINK_ELEMENT = "int-link";
|
||||
|
||||
class XSLTParseProcess {
|
||||
#processors;
|
||||
@@ -738,6 +739,35 @@ class AbbreviationTooltips extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
class IntLink extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
// Basic styling to mimic a link.
|
||||
this.style.cursor = "pointer";
|
||||
this.addEventListener("click", this.handleClick);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.removeEventListener("click", this.handleClick);
|
||||
}
|
||||
|
||||
handleClick(event) {
|
||||
const selector = this.getAttribute("data-jump");
|
||||
if (selector) {
|
||||
const target = document.querySelector(selector);
|
||||
if (target) {
|
||||
target.scrollIntoView({ behavior: "smooth" });
|
||||
} else {
|
||||
console.warn(`No element found for selector: ${selector}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(INT_LINK_ELEMENT, IntLink);
|
||||
customElements.define(ABBREV_TOOLTIPS_ELEMENT, AbbreviationTooltips);
|
||||
customElements.define(FILTER_LIST_ELEMENT, FilterList);
|
||||
customElements.define(SCROLL_BUTTON_ELEMENT, ScrollButton);
|
||||
|
||||
@@ -167,11 +167,19 @@
|
||||
}
|
||||
|
||||
.text {
|
||||
@apply font-serif max-w-[48rem];
|
||||
@apply font-serif hyphens-auto;
|
||||
}
|
||||
|
||||
.text p {
|
||||
@apply text-lg hyphens-auto;
|
||||
@apply text-lg hyphens-auto max-w-[70ch];
|
||||
}
|
||||
|
||||
.text ul {
|
||||
@apply list-disc ml-0 max-w-[70ch];
|
||||
}
|
||||
|
||||
.text ol {
|
||||
@apply list-decimal ml-0 max-w-[70ch];
|
||||
}
|
||||
|
||||
.text p:first-of-type {
|
||||
@@ -190,6 +198,18 @@
|
||||
@apply mt-1;
|
||||
}
|
||||
|
||||
.text h1 {
|
||||
@apply text-3xl font-bold mt-6 mb-1.5 hyphens-none leading-normal;
|
||||
}
|
||||
|
||||
.text h2 {
|
||||
@apply text-xl font-bold mt-3 mb-1.5 hyphens-none;
|
||||
}
|
||||
|
||||
.text h3 {
|
||||
@apply text-lg font-bold mt-3 mb-1.5 hyphens-none;
|
||||
}
|
||||
|
||||
.text p + ol {
|
||||
@apply mt-1;
|
||||
}
|
||||
@@ -203,7 +223,7 @@
|
||||
}
|
||||
|
||||
.text p + p:not(.indented p, p:first-of-type) {
|
||||
@apply indent-3.5;
|
||||
@apply indent-6;
|
||||
}
|
||||
|
||||
.indented p {
|
||||
@@ -327,4 +347,8 @@
|
||||
|
||||
#entrydata {
|
||||
}
|
||||
|
||||
int-link {
|
||||
@apply text-slate-700 hover:text-slate-900 underline decoration-dotted hover:decoration-solid;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user