Some improvements with the akteure page

This commit is contained in:
Simon Martens
2025-09-21 11:06:28 +02:00
parent 0f6ffbf63f
commit 3f2811acbc
18 changed files with 1404 additions and 463 deletions

189
CLAUDE.md
View File

@@ -118,9 +118,15 @@ views/
│ └── default/ # Default layout (root.gohtml) │ └── default/ # Default layout (root.gohtml)
├── routes/ # Page-specific templates ├── routes/ # Page-specific templates
│ ├── akteure/ # Agents/People pages (body.gohtml, head.gohtml) │ ├── akteure/ # Agents/People pages (body.gohtml, head.gohtml)
│ ├── autoren/ # Authors-only pages (body.gohtml, head.gohtml)
│ ├── ausgabe/ # Issue pages with components │ ├── ausgabe/ # Issue pages with components
│ │ └── components/ # Issue-specific components (_inhaltsverzeichnis, _bilder, etc.) │ │ └── components/ # Issue-specific components (_inhaltsverzeichnis, _bilder, etc.)
│ ├── components/ # Shared route components (_akteur.gohtml) │ ├── components/ # Shared route components
│ │ ├── _akteur.gohtml # Main agent component (uses sub-components)
│ │ ├── _akteur_header.gohtml # Agent name, dates, professions, links
│ │ ├── _akteur_werke.gohtml # Works section with categorized pieces
│ │ ├── _akteur_beitraege.gohtml # Contributions/pieces with grouping
│ │ └── _piece_summary.gohtml # Individual piece display logic
│ ├── datenschutz/ # Privacy policy │ ├── datenschutz/ # Privacy policy
│ ├── edition/ # Edition pages │ ├── edition/ # Edition pages
│ ├── kategorie/ # Category pages │ ├── kategorie/ # Category pages
@@ -171,6 +177,7 @@ Each route has dedicated `head.gohtml` and `body.gohtml` files following Go temp
- **HTMX**: Core interactivity and AJAX requests - **HTMX**: Core interactivity and AJAX requests
- **Alpine.js**: Lightweight reactivity for UI components - **Alpine.js**: Lightweight reactivity for UI components
- **Custom Extensions**: HTMX plugins for response targets, client-side templates, loading states - **Custom Extensions**: HTMX plugins for response targets, client-side templates, loading states
- **Scrollspy System**: Advanced navigation for agents/authors pages with multi-item highlighting
- **Build Tool**: Vite for module bundling and development server - **Build Tool**: Vite for module bundling and development server
**CSS Stack**: **CSS Stack**:
@@ -217,8 +224,10 @@ The application follows a **logic-in-Go, presentation-in-templates** approach:
### JavaScript Integration ### JavaScript Integration
- **Progressive Enhancement**: HTMX + Alpine.js for interactivity - **Progressive Enhancement**: HTMX + Alpine.js for interactivity
- **Real-time Highlighting**: Intersection Observer API with scroll fallback - **Real-time Highlighting**: Intersection Observer API with scroll fallback (issue view)
- **Scrollspy Navigation**: Multi-item highlighting system for agents/authors pages
- **Page Navigation**: Smooth scrolling with visibility detection - **Page Navigation**: Smooth scrolling with visibility detection
- **HTMX Integration**: Automatic cleanup and re-initialization on page swaps
- **Responsive Design**: Mobile-optimized with proper touch interactions - **Responsive Design**: Mobile-optimized with proper touch interactions
## Development Workflow ## Development Workflow
@@ -439,3 +448,179 @@ const pageUrl = `/${year}/${issue}/${pageNumber}`;
// Old format still used for beilage pages // Old format still used for beilage pages
const beilageUrl = `${window.location.pathname}#beilage-1-page-${pageNumber}`; const beilageUrl = `${window.location.pathname}#beilage-1-page-${pageNumber}`;
``` ```
## Agents/Authors View System (/akteure/ and /autoren/)
The application provides sophisticated person and organization browsing through dual view systems with advanced navigation and filtering capabilities.
### Dual View Architecture
**General Agents View** (`/akteure/`):
- Displays all persons and organizations mentioned in the newspaper
- Supports letter-based navigation (A-Z)
- Individual person pages with detailed information
- Two-column layout with scrollspy navigation on large screens
**Authors-Only View** (`/autoren/`):
- Filtered view showing only people who authored pieces (Beiträge)
- Single-page display of all authors regardless of starting letter
- No alphabet navigation (all authors shown together)
- Same advanced layout and scrollspy functionality
### URL Structure & Navigation
**URL Patterns**:
- `/akteure/` - All persons overview
- `/akteure/a` - Persons starting with letter "A"
- `/akteure/{id}` - Individual person page
- `/akteure/autoren` - Authors-only filtered view
**Toggle Navigation**:
- Checkbox interface: "Nur Autoren anzeigen" switches between views
- HTMX-powered transitions with URL history management
- Unchecking returns to `/akteure/a` (letter A starting point)
### Template Architecture & Components
**Modular Template System** (`views/routes/components/`):
- `_akteur.gohtml` - Main component using sub-components
- `_akteur_header.gohtml` - Name, life dates, professions, external links
- `_akteur_werke.gohtml` - Works section with categorized pieces
- `_akteur_beitraege.gohtml` - Contributions/pieces with grouping
**Component Benefits**:
- Reusable across different view contexts
- Maintainable separation of concerns
- Consistent styling and behavior
- Easy customization for specific views (authors vs. full agents)
### Advanced Scrollspy Navigation
**Full-Height Sidebar** (2XL+ screens only):
- Fixed 320px width with full viewport height
- Sticky positioning that follows scroll
- Complete name list with smooth scrolling navigation
- Automatic cleanup on HTMX page transitions
**Multi-Item Highlighting**:
- Highlights ALL currently visible authors simultaneously
- Red left border indicating visible items (matches issue view pattern)
- Header visibility detection (name, life data, professions must be fully visible)
- Real-time updates during scroll with 50ms debouncing
**Visual Features**:
- Larger text (`text-base`) for better readability
- Closer spacing (`py-1`) for more names visible
- Smooth transitions and hover effects
- Blue background highlighting for active items
### Controller Architecture
**Unified Controller** (`controllers/akteur_controller.go`):
- Handles both general agents and authors-only views
- Special routing for "autoren" parameter
- Template path switching based on view type
- Letter-based filtering and ID lookup
**View Models** (`viewmodels/agent_view.go`):
- `AgentsView()` - General person lookup by letter/ID
- `AuthorsView()` - Filtered view of piece authors only
- `AuthorsListView` struct with sorting and letter availability
- Pre-processed agent data for efficient template rendering
### Template Features & Data Processing
**Enhanced Data Presentation**:
- Grouped pieces by title and work reference
- Category combination with proper German grammar ("und" vs. "mit")
- Inline citation format: DD.MM.YYYY/ISSUENO, PPP[-PPP]
- Works section showing review/commentary pieces
- External link integration (Wikipedia, GND, VIAF)
**Text Sizing & Hierarchy**:
- Large serif names (`text-2xl font-serif font-bold`)
- Readable life dates and professions (`text-xl`)
- Appropriately sized content text (`text-lg`)
- Larger pill text (`text-sm`) matching issue view standards
### JavaScript Integration
**HTMX-Safe Scrollspy** (`views/transform/main.js`):
- Proper event listener cleanup on page navigation
- Memory leak prevention with timeout management
- Auto-initialization detection for `.author-section` elements
- Smooth scroll behavior for sidebar navigation
**Performance Optimizations**:
- Debounced scroll handling (50ms)
- Efficient viewport calculations using `getBoundingClientRect()`
- Minimal DOM queries with cached element references
- Responsive behavior with automatic sidebar hiding
### Responsive Design
**Desktop Experience** (2XL+ screens):
- Two-column layout: 320px sidebar + flexible content area
- Fixed scrollspy navigation with full name list
- Multi-author highlighting system
- Smooth scrolling between authors
**Mobile Experience** (< 2XL screens):
- Single-column layout with full-width content
- Hidden scrollspy navigation (saves space)
- Touch-optimized interactions
- Same content organization and functionality
### Data Categories & Processing
**Comprehensive Category Support**:
- All 29 XML-defined categories supported
- Dynamic category detection and grouping
- Proper German grammar rules for combinations
- Author filtering for non-current-user pieces
**Helper Functions** (`templating/engine.go`):
- `merge`, `append`, `slice` - Data manipulation
- `sortStrings`, `unique` - Array processing
- `joinWithUnd` - German grammar formatting
- Enhanced data processing for complex template logic
### Usage Examples
**Template Integration**:
```gohtml
{{ template "_akteur_header" $agent }}
{{ template "_akteur_werke" $agent }}
{{ template "_akteur_beitraege" $agent }}
```
**Scrollspy Navigation**:
```gohtml
<a href="#author-{{ $id }}"
class="scrollspy-link border-l-4 border-transparent"
data-target="author-{{ $id }}">
{{ index $agent.Names 0 }}
</a>
```
**HTMX Toggle**:
```gohtml
<input type="checkbox"
hx-get="/akteure/autoren"
hx-target="body"
hx-push-url="true">
```
### Error Handling & Edge Cases
**Template Safety**:
- Null checks for GND data and agent information
- Graceful fallback for missing names or professions
- Safe handling of empty work/piece lists
- Error boundaries for external link data
**Navigation Robustness**:
- 404 handling for invalid agent IDs
- Automatic fallback for missing letters
- Smooth transitions between view modes
- Proper state management across HTMX swaps

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

@@ -17,6 +17,21 @@ func GetAgents(kgpz *xmlmodels.Library) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
a := c.Params("letterorid", DEFAULT_AGENT) a := c.Params("letterorid", DEFAULT_AGENT)
a = strings.ToLower(a) a = strings.ToLower(a)
// Handle special "autoren" route
if a == "autoren" {
agents := viewmodels.AuthorsView(kgpz)
if len(agents.Agents) == 0 {
logging.Error(nil, "No authors found")
return c.SendStatus(fiber.StatusNotFound)
}
return c.Render(
"/autoren/",
fiber.Map{"model": agents},
)
}
// Handle normal letter/id lookup
agents := viewmodels.AgentsView(a, kgpz) agents := viewmodels.AgentsView(a, kgpz)
if len(agents.Agents) == 0 { if len(agents.Agents) == 0 {
logging.Error(nil, "No agents found for letter or id: "+a) logging.Error(nil, "No agents found for letter or id: "+a)

View File

@@ -4,6 +4,7 @@ import "github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
// CategoryFlags represents all possible category flags for a piece // CategoryFlags represents all possible category flags for a piece
type CategoryFlags struct { type CategoryFlags struct {
// Categories from kategorien.xml
Rezension bool Rezension bool
Weltnachrichten bool Weltnachrichten bool
EinkommendeFremde bool EinkommendeFremde bool
@@ -15,6 +16,7 @@ type CategoryFlags struct {
Gedicht bool Gedicht bool
Vorladung bool Vorladung bool
Auszug bool Auszug bool
Provinienz bool // Added missing category
Aufsatz bool Aufsatz bool
GelehrteNachrichten bool GelehrteNachrichten bool
Theaterkritik bool Theaterkritik bool
@@ -23,17 +25,18 @@ type CategoryFlags struct {
Nachruf bool Nachruf bool
Replik bool Replik bool
Proklamation bool Proklamation bool
Ineigenersache bool
Brief bool Brief bool
Anzeige bool Anzeige bool
Ineigenersache bool
Desertionsliste bool Desertionsliste bool
Notenblatt bool Notenblatt bool
Vorlesungsverzeichnis bool Vorlesungsverzeichnis bool
Erzaehlung bool Erzaehlung bool
Abbildung bool
// Additional categories that appear in combinations
Nachtrag bool Nachtrag bool
Panegyrik bool Panegyrik bool
Kriminalanzeige bool Kriminalanzeige bool
Abbildung bool
Rezepte bool Rezepte bool
Korrektur bool Korrektur bool
} }
@@ -67,6 +70,8 @@ func GetCategoryFlags(piece xmlmodels.Piece) CategoryFlags {
flags.Vorladung = true flags.Vorladung = true
case "auszug": case "auszug":
flags.Auszug = true flags.Auszug = true
case "provinienz":
flags.Provinienz = true
case "aufsatz": case "aufsatz":
flags.Aufsatz = true flags.Aufsatz = true
case "gelehrte-nachrichten": case "gelehrte-nachrichten":

View File

@@ -225,6 +225,121 @@ func (e *Engine) funcs() error {
return dict, nil return dict, nil
}) })
e.AddFunc("merge", func(dest map[string]interface{}, src map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{})
// Copy from dest first
for k, v := range dest {
result[k] = v
}
// Override with src values
for k, v := range src {
result[k] = v
}
return result
})
e.AddFunc("append", func(slice interface{}, item interface{}) interface{} {
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
return slice
}
newSlice := reflect.Append(v, reflect.ValueOf(item))
return newSlice.Interface()
})
e.AddFunc("slice", func(items ...interface{}) []interface{} {
return items
})
e.AddFunc("keys", func(m map[string]interface{}) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
})
e.AddFunc("has", func(slice interface{}, item interface{}) bool {
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
return false
}
for i := 0; i < v.Len(); i++ {
if reflect.DeepEqual(v.Index(i).Interface(), item) {
return true
}
}
return false
})
e.AddFunc("joinWithUnd", func(items []string) string {
if len(items) == 0 {
return ""
}
if len(items) == 1 {
return items[0]
}
if len(items) == 2 {
return items[0] + " und " + items[1]
}
// For 3+ items: "A, B und C"
result := ""
for i, item := range items {
if i == 0 {
result = item
} else if i == len(items)-1 {
result += " und " + item
} else {
result += ", " + item
}
}
return result
})
e.AddFunc("sortStrings", func(items interface{}) []string {
v := reflect.ValueOf(items)
if v.Kind() != reflect.Slice {
return []string{}
}
// Convert to string slice
result := make([]string, v.Len())
for i := 0; i < v.Len(); i++ {
if str, ok := v.Index(i).Interface().(string); ok {
result[i] = str
}
}
// Simple bubble sort
for i := 0; i < len(result)-1; i++ {
for j := i + 1; j < len(result); j++ {
if result[i] > result[j] {
result[i], result[j] = result[j], result[i]
}
}
}
return result
})
e.AddFunc("unique", func(items interface{}) []string {
v := reflect.ValueOf(items)
if v.Kind() != reflect.Slice {
return []string{}
}
seen := make(map[string]bool)
result := []string{}
for i := 0; i < v.Len(); i++ {
if str, ok := v.Index(i).Interface().(string); ok {
if !seen[str] {
seen[str] = true
result = append(result, str)
}
}
}
return result
})
// Strings // Strings
e.AddFunc("FirstLetter", functions.FirstLetter) e.AddFunc("FirstLetter", functions.FirstLetter)
e.AddFunc("Upper", strings.ToUpper) e.AddFunc("Upper", strings.ToUpper)

View File

@@ -46,3 +46,34 @@ func AgentsView(letterorid string, lib *xmlmodels.Library) *AgentsListView {
return &res return &res
} }
// AuthorsView returns only agents who have authored pieces (have written Beiträge)
func AuthorsView(lib *xmlmodels.Library) *AgentsListView {
res := AgentsListView{Search: "autoren", Agents: make(map[string]xmlmodels.Agent)}
av := make(map[string]bool)
// Find all agents who have pieces
authorIDs := make(map[string]bool)
for _, piece := range lib.Pieces.Array {
for _, agentRef := range piece.AgentRefs {
if agentRef.Category == "" || agentRef.Category == "autor" {
authorIDs[agentRef.Ref] = true
}
}
}
// Add all agents who are authors
for _, a := range lib.Agents.Array {
av[strings.ToUpper(a.ID[:1])] = true
if authorIDs[a.ID] {
res.Sorted = append(res.Sorted, a.ID)
res.Agents[a.ID] = a
}
}
res.AvailableLetters = slices.Collect(maps.Keys(av))
slices.Sort(res.AvailableLetters)
slices.Sort(res.Sorted)
return &res
}

View File

@@ -1,25 +1,25 @@
const A = "script[xslt-onload]", w = "xslt-template", E = "xslt-transformed", y = /* @__PURE__ */ new Map(); const D = "script[xslt-onload]", S = "xslt-template", V = "xslt-transformed", P = /* @__PURE__ */ new Map();
function v() { function C() {
let i = htmx.findAll(A); let o = htmx.findAll(D);
for (let t of i) for (let t of o)
L(t); K(t);
} }
function L(i) { function K(o) {
if (i.getAttribute(E) === "true" || !i.hasAttribute(w)) if (o.getAttribute(V) === "true" || !o.hasAttribute(S))
return; return;
let t = "#" + i.getAttribute(w), e = y.get(t); let t = "#" + o.getAttribute(S), e = P.get(t);
if (!e) { if (!e) {
let s = htmx.find(t); let s = htmx.find(t);
if (s) { if (s) {
let a = s.innerHTML ? new DOMParser().parseFromString(s.innerHTML, "application/xml") : s.contentDocument; let a = s.innerHTML ? new DOMParser().parseFromString(s.innerHTML, "application/xml") : s.contentDocument;
e = new XSLTProcessor(), e.importStylesheet(a), y.set(t, e); e = new XSLTProcessor(), e.importStylesheet(a), P.set(t, e);
} else } else
throw new Error("Unknown XSLT template: " + t); throw new Error("Unknown XSLT template: " + t);
} }
let n = new DOMParser().parseFromString(i.innerHTML, "application/xml"), o = e.transformToFragment(n, document), r = new XMLSerializer().serializeToString(o); let n = new DOMParser().parseFromString(o.innerHTML, "application/xml"), i = e.transformToFragment(n, document), r = new XMLSerializer().serializeToString(i);
i.outerHTML = r; o.outerHTML = r;
} }
function q() { function j() {
document.querySelectorAll("template[simple]").forEach((t) => { document.querySelectorAll("template[simple]").forEach((t) => {
let e = t.getAttribute("id"), n = t.content; let e = t.getAttribute("id"), n = t.content;
customElements.define( customElements.define(
@@ -29,11 +29,11 @@ function q() {
super(), this.appendChild(n.cloneNode(!0)), this.slots = this.querySelectorAll("slot"); super(), this.appendChild(n.cloneNode(!0)), this.slots = this.querySelectorAll("slot");
} }
connectedCallback() { connectedCallback() {
let o = []; let i = [];
this.slots.forEach((r) => { this.slots.forEach((r) => {
let s = r.getAttribute("name"), a = this.querySelector(`[slot="${s}"]`); let s = r.getAttribute("name"), a = this.querySelector(`[slot="${s}"]`);
a && (r.replaceWith(a.cloneNode(!0)), o.push(a)); a && (r.replaceWith(a.cloneNode(!0)), i.push(a));
}), o.forEach((r) => { }), i.forEach((r) => {
r.remove(); r.remove();
}); });
} }
@@ -46,63 +46,63 @@ window.currentPageContainers = window.currentPageContainers || [];
window.currentActiveIndex = window.currentActiveIndex || 0; window.currentActiveIndex = window.currentActiveIndex || 0;
window.pageObserver = window.pageObserver || null; window.pageObserver = window.pageObserver || null;
window.scrollTimeout = window.scrollTimeout || null; window.scrollTimeout = window.scrollTimeout || null;
function $() { function W() {
window.highlightObserver && (window.highlightObserver.disconnect(), window.highlightObserver = null); window.highlightObserver && (window.highlightObserver.disconnect(), window.highlightObserver = null);
const i = document.querySelectorAll(".newspaper-page-container"); const o = document.querySelectorAll(".newspaper-page-container");
window.highlightObserver = new IntersectionObserver( window.highlightObserver = new IntersectionObserver(
(t) => { (t) => {
P(); I();
}, },
{ {
rootMargin: "-20% 0px -70% 0px" rootMargin: "-20% 0px -70% 0px"
} }
), i.forEach((t) => { ), o.forEach((t) => {
window.highlightObserver.observe(t); window.highlightObserver.observe(t);
}); });
} }
function P() { function I() {
const i = []; const o = [];
document.querySelectorAll(".newspaper-page-container").forEach((e) => { document.querySelectorAll(".newspaper-page-container").forEach((e) => {
const n = e.getBoundingClientRect(), o = window.innerHeight, r = Math.max(n.top, 0), s = Math.min(n.bottom, o), a = Math.max(0, s - r), l = n.height, g = a / l >= 0.5, u = e.querySelector("img[data-page]"), p = u ? u.getAttribute("data-page") : "unknown"; const n = e.getBoundingClientRect(), i = window.innerHeight, r = Math.max(n.top, 0), s = Math.min(n.bottom, i), a = Math.max(0, s - r), l = n.height, g = a / l >= 0.5, d = e.querySelector("img[data-page]"), p = d ? d.getAttribute("data-page") : "unknown";
g && u && p && !i.includes(p) && i.push(p); g && d && p && !o.includes(p) && o.push(p);
}), k(i), i.length > 0 && C(i); }), Z(o), o.length > 0 && A(o);
} }
function k(i) { function Z(o) {
document.querySelectorAll(".continuation-entry").forEach((t) => { document.querySelectorAll(".continuation-entry").forEach((t) => {
t.style.display = "none"; t.style.display = "none";
}), i.forEach((t) => { }), o.forEach((t) => {
const e = document.querySelector(`[data-page-container="${t}"]`); const e = document.querySelector(`[data-page-container="${t}"]`);
e && e.querySelectorAll(".continuation-entry").forEach((o) => { e && e.querySelectorAll(".continuation-entry").forEach((i) => {
o.style.display = ""; i.style.display = "";
}); });
}), B(i), M(); }), _(o), F();
} }
function B(i) { function _(o) {
document.querySelectorAll(".work-title").forEach((t) => { document.querySelectorAll(".work-title").forEach((t) => {
const e = t.getAttribute("data-short-title"); const e = t.getAttribute("data-short-title");
e && (t.textContent = e); e && (t.textContent = e);
}), i.forEach((t) => { }), o.forEach((t) => {
const e = document.querySelector(`[data-page-container="${t}"]`); const e = document.querySelector(`[data-page-container="${t}"]`);
e && e.querySelectorAll(".work-title").forEach((o) => { e && e.querySelectorAll(".work-title").forEach((i) => {
const r = o.getAttribute("data-full-title"); const r = i.getAttribute("data-full-title");
r && r !== o.getAttribute("data-short-title") && (o.textContent = r); r && r !== i.getAttribute("data-short-title") && (i.textContent = r);
}); });
}); });
} }
function M() { function F() {
document.querySelectorAll(".page-entry").forEach((i) => { document.querySelectorAll(".page-entry").forEach((o) => {
const t = i.querySelectorAll(".inhalts-entry"); const t = o.querySelectorAll(".inhalts-entry");
let e = !1; let e = !1;
t.forEach((n) => { t.forEach((n) => {
window.getComputedStyle(n).display !== "none" && (e = !0); window.getComputedStyle(n).display !== "none" && (e = !0);
}), e ? i.style.display = "" : i.style.display = "none"; }), e ? o.style.display = "" : o.style.display = "none";
}); });
} }
function S(i) { function E(o) {
C([i]); A([o]);
} }
function C(i) { function A(o) {
console.log("markCurrentPagesInInhaltsverzeichnis called with:", i), document.querySelectorAll("[data-page-container]").forEach((e) => { console.log("markCurrentPagesInInhaltsverzeichnis called with:", o), document.querySelectorAll("[data-page-container]").forEach((e) => {
e.hasAttribute("data-beilage") ? (e.classList.remove("border-red-500"), e.classList.add("border-amber-400")) : (e.classList.remove("border-red-500"), e.classList.add("border-slate-300")); e.hasAttribute("data-beilage") ? (e.classList.remove("border-red-500"), e.classList.add("border-amber-400")) : (e.classList.remove("border-red-500"), e.classList.add("border-slate-300"));
}), document.querySelectorAll(".page-number-inhalts").forEach((e) => { }), document.querySelectorAll(".page-number-inhalts").forEach((e) => {
e.classList.remove("text-red-600", "font-bold"), e.classList.add("text-slate-700", "font-semibold"), e.style.textDecoration = "", e.style.pointerEvents = "", e.classList.contains("bg-blue-50") ? e.classList.add("hover:bg-blue-100") : e.classList.contains("bg-amber-50") && e.classList.add("hover:bg-amber-100"), !e.classList.contains("bg-amber-50") && !e.classList.contains("bg-blue-50") && e.classList.add("bg-blue-50"); e.classList.remove("text-red-600", "font-bold"), e.classList.add("text-slate-700", "font-semibold"), e.style.textDecoration = "", e.style.pointerEvents = "", e.classList.contains("bg-blue-50") ? e.classList.add("hover:bg-blue-100") : e.classList.contains("bg-amber-50") && e.classList.add("hover:bg-amber-100"), !e.classList.contains("bg-amber-50") && !e.classList.contains("bg-blue-50") && e.classList.add("bg-blue-50");
@@ -112,14 +112,14 @@ function C(i) {
e.classList.remove("no-underline"), e.classList.contains("bg-blue-50") && e.classList.add("hover:bg-blue-100"); e.classList.remove("no-underline"), e.classList.contains("bg-blue-50") && e.classList.add("hover:bg-blue-100");
}); });
const t = []; const t = [];
i.forEach((e) => { o.forEach((e) => {
const n = document.querySelector( const n = document.querySelector(
`.page-number-inhalts[data-page-number="${e}"]` `.page-number-inhalts[data-page-number="${e}"]`
); );
if (n) { if (n) {
n.classList.remove("text-slate-700", "hover:bg-blue-100", "hover:bg-amber-100"), n.classList.add("text-red-600", "font-bold"), n.style.textDecoration = "none", n.style.pointerEvents = "none", t.push(n); n.classList.remove("text-slate-700", "hover:bg-blue-100", "hover:bg-amber-100"), n.classList.add("text-red-600", "font-bold"), n.style.textDecoration = "none", n.style.pointerEvents = "none", t.push(n);
const o = document.querySelector(`[data-page-container="${e}"]`); const i = document.querySelector(`[data-page-container="${e}"]`);
o && (o.classList.remove("border-slate-300", "border-amber-400"), o.classList.add("border-red-500")); i && (i.classList.remove("border-slate-300", "border-amber-400"), i.classList.add("border-red-500"));
const r = n.closest(".page-entry"); const r = n.closest(".page-entry");
r && (r.querySelectorAll(".inhalts-entry").forEach((a) => { r && (r.querySelectorAll(".inhalts-entry").forEach((a) => {
a.classList.remove("hover:bg-slate-100"), a.style.cursor = "default"; a.classList.remove("hover:bg-slate-100"), a.style.cursor = "default";
@@ -127,43 +127,43 @@ function C(i) {
a.getAttribute("aria-current") === "page" && (a.style.textDecoration = "none", a.style.pointerEvents = "none", a.classList.add("no-underline"), a.classList.remove("hover:bg-blue-100")); a.getAttribute("aria-current") === "page" && (a.style.textDecoration = "none", a.style.pointerEvents = "none", a.classList.add("no-underline"), a.classList.remove("hover:bg-blue-100"));
})); }));
} }
}), t.length > 0 && N(t[0]), document.querySelectorAll(".page-indicator").forEach((e) => { }), t.length > 0 && X(t[0]), document.querySelectorAll(".page-indicator").forEach((e) => {
e.classList.remove("text-red-600", "font-bold"), e.classList.add("text-slate-600", "font-semibold"), e.classList.contains("bg-amber-50") || e.classList.add("bg-blue-50"); e.classList.remove("text-red-600", "font-bold"), e.classList.add("text-slate-600", "font-semibold"), e.classList.contains("bg-amber-50") || e.classList.add("bg-blue-50");
}), i.forEach((e) => { }), o.forEach((e) => {
const n = document.querySelector(`.page-indicator[data-page="${e}"]`); const n = document.querySelector(`.page-indicator[data-page="${e}"]`);
n && (n.classList.remove("text-slate-600"), n.classList.add("text-red-600", "font-bold")); n && (n.classList.remove("text-slate-600"), n.classList.add("text-red-600", "font-bold"));
}); });
} }
function N(i) { function X(o) {
const t = i.closest(".lg\\:overflow-y-auto"); const t = o.closest(".lg\\:overflow-y-auto");
if (t) { if (t) {
const e = t.getBoundingClientRect(), n = i.getBoundingClientRect(), o = n.top < e.top, r = n.bottom > e.bottom; const e = t.getBoundingClientRect(), n = o.getBoundingClientRect(), i = n.top < e.top, r = n.bottom > e.bottom;
(o || r) && i.scrollIntoView({ (i || r) && o.scrollIntoView({
behavior: "smooth", behavior: "smooth",
block: "center" block: "center"
}); });
} }
} }
function H(i, t, e, n = null) { function U(o, t, e, n = null) {
let o = document.querySelector("single-page-viewer"); let i = document.querySelector("single-page-viewer");
o || (o = document.createElement("single-page-viewer"), document.body.appendChild(o)); i || (i = document.createElement("single-page-viewer"), document.body.appendChild(i));
const r = i.closest('[data-beilage="true"]') !== null, s = window.templateData && window.templateData.targetPage ? window.templateData.targetPage : 0; const r = o.closest('[data-beilage="true"]') !== null, s = window.templateData && window.templateData.targetPage ? window.templateData.targetPage : 0;
o.show(i.src, i.alt, t, r, s, n); i.show(o.src, o.alt, t, r, s, n);
} }
function I() { function L() {
document.getElementById("pageModal").classList.add("hidden"); document.getElementById("pageModal").classList.add("hidden");
} }
function O() { function G() {
if (window.pageObserver && (window.pageObserver.disconnect(), window.pageObserver = null), window.currentPageContainers = Array.from(document.querySelectorAll(".newspaper-page-container")), window.currentActiveIndex = 0, h(), document.querySelector(".newspaper-page-container")) { if (window.pageObserver && (window.pageObserver.disconnect(), window.pageObserver = null), window.currentPageContainers = Array.from(document.querySelectorAll(".newspaper-page-container")), window.currentActiveIndex = 0, h(), document.querySelector(".newspaper-page-container")) {
let t = /* @__PURE__ */ new Set(); let t = /* @__PURE__ */ new Set();
window.pageObserver = new IntersectionObserver( window.pageObserver = new IntersectionObserver(
(e) => { (e) => {
if (e.forEach((n) => { if (e.forEach((n) => {
const o = window.currentPageContainers.indexOf(n.target); const i = window.currentPageContainers.indexOf(n.target);
o !== -1 && (n.isIntersecting ? t.add(o) : t.delete(o)); i !== -1 && (n.isIntersecting ? t.add(i) : t.delete(i));
}), t.size > 0) { }), t.size > 0) {
const o = Array.from(t).sort((r, s) => r - s)[0]; const i = Array.from(t).sort((r, s) => r - s)[0];
o !== window.currentActiveIndex && (window.currentActiveIndex = o, h()); i !== window.currentActiveIndex && (window.currentActiveIndex = i, h());
} }
}, },
{ {
@@ -174,21 +174,21 @@ function O() {
}); });
} }
} }
function R() { function Y() {
if (window.currentActiveIndex > 0) { if (window.currentActiveIndex > 0) {
let i = -1; let o = -1;
const t = []; const t = [];
window.currentPageContainers.forEach((n, o) => { window.currentPageContainers.forEach((n, i) => {
const r = n.getBoundingClientRect(), s = window.innerHeight, a = Math.max(r.top, 0), l = Math.min(r.bottom, s), c = Math.max(0, l - a), g = r.height; const r = n.getBoundingClientRect(), s = window.innerHeight, a = Math.max(r.top, 0), l = Math.min(r.bottom, s), c = Math.max(0, l - a), g = r.height;
c / g >= 0.3 && t.push(o); c / g >= 0.3 && t.push(i);
}); });
const e = Math.min(...t); const e = Math.min(...t);
for (let n = e - 1; n >= 0; n--) for (let n = e - 1; n >= 0; n--)
if (!t.includes(n)) { if (!t.includes(n)) {
i = n; o = n;
break; break;
} }
i === -1 && e > 0 && (i = e - 1), i >= 0 && (window.currentActiveIndex = i, window.currentPageContainers[window.currentActiveIndex].scrollIntoView({ o === -1 && e > 0 && (o = e - 1), o >= 0 && (window.currentActiveIndex = o, window.currentPageContainers[window.currentActiveIndex].scrollIntoView({
behavior: "smooth", behavior: "smooth",
block: "start" block: "start"
}), setTimeout(() => { }), setTimeout(() => {
@@ -196,21 +196,21 @@ function R() {
}, 100)); }, 100));
} }
} }
function z() { function J() {
if (window.currentActiveIndex < window.currentPageContainers.length - 1) { if (window.currentActiveIndex < window.currentPageContainers.length - 1) {
let i = -1; let o = -1;
const t = []; const t = [];
window.currentPageContainers.forEach((n, o) => { window.currentPageContainers.forEach((n, i) => {
const r = n.getBoundingClientRect(), s = window.innerHeight, a = Math.max(r.top, 0), l = Math.min(r.bottom, s), c = Math.max(0, l - a), g = r.height; const r = n.getBoundingClientRect(), s = window.innerHeight, a = Math.max(r.top, 0), l = Math.min(r.bottom, s), c = Math.max(0, l - a), g = r.height;
c / g >= 0.3 && t.push(o); c / g >= 0.3 && t.push(i);
}); });
const e = Math.max(...t); const e = Math.max(...t);
for (let n = e + 1; n < window.currentPageContainers.length; n++) for (let n = e + 1; n < window.currentPageContainers.length; n++)
if (!t.includes(n)) { if (!t.includes(n)) {
i = n; o = n;
break; break;
} }
i === -1 && e < window.currentPageContainers.length - 1 && (i = e + 1), i >= 0 && i < window.currentPageContainers.length && (window.currentActiveIndex = i, window.currentPageContainers[window.currentActiveIndex].scrollIntoView({ o === -1 && e < window.currentPageContainers.length - 1 && (o = e + 1), o >= 0 && o < window.currentPageContainers.length && (window.currentActiveIndex = o, window.currentPageContainers[window.currentActiveIndex].scrollIntoView({
behavior: "smooth", behavior: "smooth",
block: "start" block: "start"
}), setTimeout(() => { }), setTimeout(() => {
@@ -218,8 +218,8 @@ function z() {
}, 100)); }, 100));
} }
} }
function D() { function Q() {
if (T()) { if (q()) {
const t = document.querySelector("#newspaper-content .newspaper-page-container"); const t = document.querySelector("#newspaper-content .newspaper-page-container");
t && t.scrollIntoView({ t && t.scrollIntoView({
behavior: "smooth", behavior: "smooth",
@@ -233,13 +233,13 @@ function D() {
}); });
} }
} }
function T() { function q() {
const i = []; const o = [];
window.currentPageContainers.forEach((t, e) => { window.currentPageContainers.forEach((t, e) => {
const n = t.getBoundingClientRect(), o = window.innerHeight, r = Math.max(n.top, 0), s = Math.min(n.bottom, o), a = Math.max(0, s - r), l = n.height; const n = t.getBoundingClientRect(), i = window.innerHeight, r = Math.max(n.top, 0), s = Math.min(n.bottom, i), a = Math.max(0, s - r), l = n.height;
a / l >= 0.3 && i.push(e); a / l >= 0.3 && o.push(e);
}); });
for (const t of i) { for (const t of o) {
const e = window.currentPageContainers[t]; const e = window.currentPageContainers[t];
if (e && e.id && e.id.includes("beilage-")) if (e && e.id && e.id.includes("beilage-"))
return !0; return !0;
@@ -247,72 +247,72 @@ function T() {
return !1; return !1;
} }
function h() { function h() {
const i = document.getElementById("prevPageBtn"), t = document.getElementById("nextPageBtn"), e = document.getElementById("beilageBtn"); const o = document.getElementById("prevPageBtn"), t = document.getElementById("nextPageBtn"), e = document.getElementById("beilageBtn");
if (i && (window.currentActiveIndex <= 0 ? i.style.display = "none" : i.style.display = "flex"), t && (window.currentActiveIndex >= window.currentPageContainers.length - 1 ? t.style.display = "none" : t.style.display = "flex"), e) { if (o && (window.currentActiveIndex <= 0 ? o.style.display = "none" : o.style.display = "flex"), t && (window.currentActiveIndex >= window.currentPageContainers.length - 1 ? t.style.display = "none" : t.style.display = "flex"), e) {
const n = T(), o = e.querySelector("i"); const n = q(), i = e.querySelector("i");
n ? (e.title = "Zur Hauptausgabe", e.className = "w-14 h-10 lg:w-16 lg:h-12 px-2 py-1 bg-gray-100 hover:bg-gray-200 text-gray-700 hover:text-gray-800 border border-gray-300 transition-colors duration-200 flex items-center justify-center cursor-pointer", o && (o.className = "ri-file-text-line text-lg lg:text-xl")) : (e.title = "Zu Beilage", e.className = "w-14 h-10 lg:w-16 lg:h-12 px-2 py-1 bg-amber-100 hover:bg-amber-200 text-amber-700 hover:text-amber-800 border border-amber-300 transition-colors duration-200 flex items-center justify-center cursor-pointer", o && (o.className = "ri-attachment-line text-lg lg:text-xl")); n ? (e.title = "Zur Hauptausgabe", e.className = "w-14 h-10 lg:w-16 lg:h-12 px-2 py-1 bg-gray-100 hover:bg-gray-200 text-gray-700 hover:text-gray-800 border border-gray-300 transition-colors duration-200 flex items-center justify-center cursor-pointer", i && (i.className = "ri-file-text-line text-lg lg:text-xl")) : (e.title = "Zu Beilage", e.className = "w-14 h-10 lg:w-16 lg:h-12 px-2 py-1 bg-amber-100 hover:bg-amber-200 text-amber-700 hover:text-amber-800 border border-amber-300 transition-colors duration-200 flex items-center justify-center cursor-pointer", i && (i.className = "ri-attachment-line text-lg lg:text-xl"));
} }
} }
function K() { function ee() {
const i = document.getElementById("shareLinkBtn"); const o = document.getElementById("shareLinkBtn");
let t = ""; let t = "";
if (window.currentActiveIndex !== void 0 && window.currentPageContainers && window.currentPageContainers[window.currentActiveIndex]) { if (window.currentActiveIndex !== void 0 && window.currentPageContainers && window.currentPageContainers[window.currentActiveIndex]) {
const o = window.currentPageContainers[window.currentActiveIndex].querySelector("[data-page]"); const i = window.currentPageContainers[window.currentActiveIndex].querySelector("[data-page]");
o && (t = `/${o.getAttribute("data-page")}`); i && (t = `/${i.getAttribute("data-page")}`);
} }
const e = window.location.origin + window.location.pathname + t; const e = window.location.origin + window.location.pathname + t;
navigator.share ? navigator.share({ navigator.share ? navigator.share({
title: document.title, title: document.title,
url: e url: e
}).catch((n) => { }).catch((n) => {
x(e, i); T(e, o);
}) : x(e, i); }) : T(e, o);
} }
function x(i, t) { function T(o, t) {
if (navigator.clipboard) if (navigator.clipboard)
navigator.clipboard.writeText(i).then(() => { navigator.clipboard.writeText(o).then(() => {
d(t, "Link kopiert!"); u(t, "Link kopiert!");
}).catch((e) => { }).catch((e) => {
d(t, "Kopieren fehlgeschlagen"); u(t, "Kopieren fehlgeschlagen");
}); });
else { else {
const e = document.createElement("textarea"); const e = document.createElement("textarea");
e.value = i, document.body.appendChild(e), e.select(); e.value = o, document.body.appendChild(e), e.select();
try { try {
const n = document.execCommand("copy"); const n = document.execCommand("copy");
d(t, n ? "Link kopiert!" : "Kopieren fehlgeschlagen"); u(t, n ? "Link kopiert!" : "Kopieren fehlgeschlagen");
} catch { } catch {
d(t, "Kopieren fehlgeschlagen"); u(t, "Kopieren fehlgeschlagen");
} finally { } finally {
document.body.removeChild(e); document.body.removeChild(e);
} }
} }
} }
function V() { function te() {
const i = document.getElementById("citationBtn"), t = document.title || "KGPZ"; const o = document.getElementById("citationBtn"), t = document.title || "KGPZ";
let e = window.location.origin + window.location.pathname; let e = window.location.origin + window.location.pathname;
e.includes("#") && (e = e.split("#")[0]); e.includes("#") && (e = e.split("#")[0]);
const n = (/* @__PURE__ */ new Date()).toLocaleDateString("de-DE"), o = `Königsberger Gelehrte und Politische Zeitung (KGPZ). ${t}. Digital verfügbar unter: ${e} (Zugriff: ${n}).`; const n = (/* @__PURE__ */ new Date()).toLocaleDateString("de-DE"), i = `Königsberger Gelehrte und Politische Zeitung (KGPZ). ${t}. Digital verfügbar unter: ${e} (Zugriff: ${n}).`;
if (navigator.clipboard) if (navigator.clipboard)
navigator.clipboard.writeText(o).then(() => { navigator.clipboard.writeText(i).then(() => {
d(i, "Zitation kopiert!"); u(o, "Zitation kopiert!");
}).catch((r) => { }).catch((r) => {
d(i, "Kopieren fehlgeschlagen"); u(o, "Kopieren fehlgeschlagen");
}); });
else { else {
const r = document.createElement("textarea"); const r = document.createElement("textarea");
r.value = o, document.body.appendChild(r), r.select(); r.value = i, document.body.appendChild(r), r.select();
try { try {
const s = document.execCommand("copy"); const s = document.execCommand("copy");
d(i, s ? "Zitation kopiert!" : "Kopieren fehlgeschlagen"); u(o, s ? "Zitation kopiert!" : "Kopieren fehlgeschlagen");
} catch { } catch {
d(i, "Kopieren fehlgeschlagen"); u(o, "Kopieren fehlgeschlagen");
} finally { } finally {
document.body.removeChild(r); document.body.removeChild(r);
} }
} }
} }
function d(i, t) { function u(o, t) {
const e = document.querySelector(".simple-popup"); const e = document.querySelector(".simple-popup");
e && e.remove(); e && e.remove();
const n = document.createElement("div"); const n = document.createElement("div");
@@ -330,10 +330,10 @@ function d(i, t) {
transition: opacity 0.2s ease; transition: opacity 0.2s ease;
white-space: nowrap; white-space: nowrap;
`; `;
const o = i.getBoundingClientRect(), r = window.innerHeight, s = window.innerWidth; const i = o.getBoundingClientRect(), r = window.innerHeight, s = window.innerWidth;
let a = o.left - 10, l = o.bottom + 8; let a = i.left - 10, l = i.bottom + 8;
const c = 120, g = 32; const c = 120, g = 32;
a + c > s && (a = o.right - c + 10), l + g > r && (l = o.top - g - 8), n.style.left = Math.max(5, a) + "px", n.style.top = Math.max(5, l) + "px", document.body.appendChild(n), setTimeout(() => { a + c > s && (a = i.right - c + 10), l + g > r && (l = i.top - g - 8), n.style.left = Math.max(5, a) + "px", n.style.top = Math.max(5, l) + "px", document.body.appendChild(n), setTimeout(() => {
n.style.opacity = "1"; n.style.opacity = "1";
}, 10), setTimeout(() => { }, 10), setTimeout(() => {
n.style.opacity = "0", setTimeout(() => { n.style.opacity = "0", setTimeout(() => {
@@ -341,125 +341,189 @@ function d(i, t) {
}, 200); }, 200);
}, 2e3); }, 2e3);
} }
function j() { function ne() {
let i = "", t = null; let o = "", t = null;
const e = window.location.pathname.split("/"); const e = window.location.pathname.split("/");
if (e.length >= 4 && !isNaN(e[e.length - 1])) { if (e.length >= 4 && !isNaN(e[e.length - 1])) {
if (i = e[e.length - 1], t = document.getElementById(`page-${i}`), !t) { if (o = e[e.length - 1], t = document.getElementById(`page-${o}`), !t) {
const n = document.querySelectorAll(".newspaper-page-container[data-pages]"); const n = document.querySelectorAll(".newspaper-page-container[data-pages]");
for (const o of n) { for (const i of n) {
const r = o.getAttribute("data-pages"); const r = i.getAttribute("data-pages");
if (r && r.split(",").includes(i)) { if (r && r.split(",").includes(o)) {
t = o; t = i;
break; break;
} }
} }
} }
t || (t = document.getElementById(`beilage-1-page-${i}`) || document.getElementById(`beilage-2-page-${i}`) || document.querySelector(`[id*="beilage"][id*="page-${i}"]`)); t || (t = document.getElementById(`beilage-1-page-${o}`) || document.getElementById(`beilage-2-page-${o}`) || document.querySelector(`[id*="beilage"][id*="page-${o}"]`));
} }
t && i && setTimeout(() => { t && o && setTimeout(() => {
t.scrollIntoView({ t.scrollIntoView({
behavior: "smooth", behavior: "smooth",
block: "start" block: "start"
}), S(i); }), E(o);
}, 300); }, 300);
} }
function b(i, t, e = !1) { function m(o, t, e = !1) {
let n = ""; let n = "";
if (e) if (e)
n = window.location.origin + window.location.pathname + `#beilage-1-page-${i}`; n = window.location.origin + window.location.pathname + `#beilage-1-page-${o}`;
else { else {
const r = window.location.pathname.split("/"); const r = window.location.pathname.split("/");
if (r.length >= 3) { if (r.length >= 3) {
const s = r[1], a = r[2]; const s = r[1], a = r[2];
n = `${window.location.origin}/${s}/${a}/${i}`; n = `${window.location.origin}/${s}/${a}/${o}`;
} else } else
n = window.location.origin + window.location.pathname + `/${i}`; n = window.location.origin + window.location.pathname + `/${o}`;
} }
const o = n; const i = n;
if (navigator.clipboard) if (navigator.clipboard)
navigator.clipboard.writeText(o).then(() => { navigator.clipboard.writeText(i).then(() => {
d(t, "Link kopiert!"); u(t, "Link kopiert!");
}).catch((r) => { }).catch((r) => {
d(t, "Kopieren fehlgeschlagen"); u(t, "Kopieren fehlgeschlagen");
}); });
else { else {
const r = document.createElement("textarea"); const r = document.createElement("textarea");
r.value = o, document.body.appendChild(r), r.select(); r.value = i, document.body.appendChild(r), r.select();
try { try {
const s = document.execCommand("copy"); const s = document.execCommand("copy");
d(t, s ? "Link kopiert!" : "Kopieren fehlgeschlagen"); u(t, s ? "Link kopiert!" : "Kopieren fehlgeschlagen");
} catch { } catch {
d(t, "Kopieren fehlgeschlagen"); u(t, "Kopieren fehlgeschlagen");
} finally { } finally {
document.body.removeChild(r); document.body.removeChild(r);
} }
} }
} }
function m(i, t) { function y(o, t) {
const e = document.title || "KGPZ", n = window.location.pathname.split("/"); const e = document.title || "KGPZ", n = window.location.pathname.split("/");
let o; let i;
if (n.length >= 3) { if (n.length >= 3) {
const l = n[1], c = n[2]; const l = n[1], c = n[2];
o = `${window.location.origin}/${l}/${c}/${i}`; i = `${window.location.origin}/${l}/${c}/${o}`;
} else } else
o = `${window.location.origin}${window.location.pathname}/${i}`; i = `${window.location.origin}${window.location.pathname}/${o}`;
const r = o, s = (/* @__PURE__ */ new Date()).toLocaleDateString("de-DE"), a = `Königsberger Gelehrte und Politische Zeitung (KGPZ). ${e}, Seite ${i}. Digital verfügbar unter: ${r} (Zugriff: ${s}).`; const r = i, s = (/* @__PURE__ */ new Date()).toLocaleDateString("de-DE"), a = `Königsberger Gelehrte und Politische Zeitung (KGPZ). ${e}, Seite ${o}. Digital verfügbar unter: ${r} (Zugriff: ${s}).`;
if (navigator.clipboard) if (navigator.clipboard)
navigator.clipboard.writeText(a).then(() => { navigator.clipboard.writeText(a).then(() => {
d(t, "Zitation kopiert!"); u(t, "Zitation kopiert!");
}).catch((l) => { }).catch((l) => {
d(t, "Kopieren fehlgeschlagen"); u(t, "Kopieren fehlgeschlagen");
}); });
else { else {
const l = document.createElement("textarea"); const l = document.createElement("textarea");
l.value = a, document.body.appendChild(l), l.select(); l.value = a, document.body.appendChild(l), l.select();
try { try {
const c = document.execCommand("copy"); const c = document.execCommand("copy");
d(t, c ? "Zitation kopiert!" : "Kopieren fehlgeschlagen"); u(t, c ? "Zitation kopiert!" : "Kopieren fehlgeschlagen");
} catch { } catch {
d(t, "Kopieren fehlgeschlagen"); u(t, "Kopieren fehlgeschlagen");
} finally { } finally {
document.body.removeChild(l); document.body.removeChild(l);
} }
} }
} }
function f() { function f() {
$(), O(), window.addEventListener("scroll", function() { k();
const o = document.querySelectorAll(".author-section"), t = document.querySelectorAll(".scrollspy-link");
if (o.length === 0 || t.length === 0)
return;
function e() {
const i = [];
o.forEach((s) => {
s.getBoundingClientRect().top + window.scrollY;
const l = s.querySelector("div:first-child");
if (l) {
const c = l.getBoundingClientRect();
c.top + window.scrollY + c.height;
const d = c.top >= 0, p = c.bottom <= window.innerHeight;
d && p && i.push(s.getAttribute("id"));
}
});
const r = [];
t.forEach((s) => {
s.classList.remove("bg-blue-100", "text-blue-700", "font-medium", "border-red-500"), s.classList.add("text-gray-600", "border-transparent");
const a = s.getAttribute("data-target");
i.includes(a) && (s.classList.remove("text-gray-600", "border-transparent"), s.classList.add("bg-blue-100", "text-blue-700", "font-medium", "border-red-500"), r.push(s));
}), r.length > 0 && n(r);
}
function n(i) {
if (window.scrollspyManualNavigation) return;
const r = document.getElementById("scrollspy-nav");
if (!r) return;
const s = i[0], a = Math.max(
document.body.scrollHeight,
document.body.offsetHeight,
document.documentElement.clientHeight,
document.documentElement.scrollHeight,
document.documentElement.offsetHeight
), l = window.innerHeight, c = a - l, g = c > 0 ? window.scrollY / c : 0, d = r.clientHeight, w = r.scrollHeight - d;
if (w > 0) {
const H = g * w, B = s.getBoundingClientRect(), $ = r.getBoundingClientRect(), M = B.top - $.top + r.scrollTop, N = d / 2, R = M - N, v = 0.7, O = v * H + (1 - v) * R, x = Math.max(0, Math.min(w, O)), z = r.scrollTop;
Math.abs(x - z) > 10 && r.scrollTo({
top: x,
behavior: "smooth"
});
}
}
window.scrollspyScrollHandler = function() {
clearTimeout(window.scrollspyTimeout), window.scrollspyTimeout = setTimeout(e, 50);
}, window.addEventListener("scroll", window.scrollspyScrollHandler), window.scrollspyClickHandlers = [], t.forEach((i) => {
const r = function(s) {
s.preventDefault();
const a = document.getElementById(this.getAttribute("data-target"));
a && (window.scrollspyManualNavigation = !0, a.scrollIntoView({
behavior: "smooth",
block: "start"
}), setTimeout(() => {
window.scrollspyManualNavigation = !1;
}, 1e3));
};
window.scrollspyClickHandlers.push({ link: i, handler: r }), i.addEventListener("click", r);
}), e();
}
function k() {
window.scrollspyScrollHandler && (window.removeEventListener("scroll", window.scrollspyScrollHandler), window.scrollspyScrollHandler = null), window.scrollspyTimeout && (clearTimeout(window.scrollspyTimeout), window.scrollspyTimeout = null), window.scrollspyClickHandlers && (window.scrollspyClickHandlers.forEach(({ link: o, handler: t }) => {
o.removeEventListener("click", t);
}), window.scrollspyClickHandlers = null), window.scrollspyManualNavigation = !1;
}
function b() {
W(), G(), window.addEventListener("scroll", function() {
clearTimeout(window.scrollTimeout), window.scrollTimeout = setTimeout(() => { clearTimeout(window.scrollTimeout), window.scrollTimeout = setTimeout(() => {
P(), h(); I(), h();
}, 50); }, 50);
}), j(), document.addEventListener("keydown", function(i) { }), ne(), document.addEventListener("keydown", function(o) {
i.key === "Escape" && I(); o.key === "Escape" && L();
}); });
} }
window.enlargePage = H; window.enlargePage = U;
window.closeModal = I; window.closeModal = L;
window.scrollToPreviousPage = R; window.scrollToPreviousPage = Y;
window.scrollToNextPage = z; window.scrollToNextPage = J;
window.scrollToBeilage = D; window.scrollToBeilage = Q;
window.shareCurrentPage = K; window.shareCurrentPage = ee;
window.generateCitation = V; window.generateCitation = te;
window.copyPagePermalink = b; window.copyPagePermalink = m;
window.generatePageCitation = m; window.generatePageCitation = y;
function Z() { function re() {
v(), q(), document.querySelector(".newspaper-page-container") && f(), htmx.on("htmx:load", function(i) { C(), j(), document.querySelector(".newspaper-page-container") && b(), document.querySelector(".author-section") && f(), htmx.on("htmx:load", function(o) {
v(); C();
}), document.body.addEventListener("htmx:afterSwap", function(i) { }), document.body.addEventListener("htmx:afterSwap", function(o) {
setTimeout(() => { setTimeout(() => {
document.querySelector(".newspaper-page-container") && f(); document.querySelector(".newspaper-page-container") && b(), document.querySelector(".author-section") && f();
}, 100); }, 100);
}), document.body.addEventListener("htmx:afterSettle", function(i) { }), document.body.addEventListener("htmx:afterSettle", function(o) {
setTimeout(() => { setTimeout(() => {
document.querySelector(".newspaper-page-container") && f(); document.querySelector(".newspaper-page-container") && b(), document.querySelector(".author-section") && f();
}, 200); }, 200);
}), document.body.addEventListener("htmx:load", function(i) { }), document.body.addEventListener("htmx:load", function(o) {
setTimeout(() => { setTimeout(() => {
document.querySelector(".newspaper-page-container") && f(); document.querySelector(".newspaper-page-container") && b(), document.querySelector(".author-section") && f();
}, 100); }, 100);
}); });
} }
class W extends HTMLElement { class oe extends HTMLElement {
constructor() { constructor() {
super(), this.resizeObserver = null; super(), this.resizeObserver = null;
} }
@@ -467,8 +531,8 @@ class W extends HTMLElement {
detectSidebarWidth() { detectSidebarWidth() {
const t = document.querySelector('.lg\\:w-1\\/4, .lg\\:w-1\\/3, [class*="lg:w-1/"]'); const t = document.querySelector('.lg\\:w-1\\/4, .lg\\:w-1\\/3, [class*="lg:w-1/"]');
if (t) { if (t) {
const o = t.getBoundingClientRect().width; const i = t.getBoundingClientRect().width;
return console.log("Detected sidebar width:", o, "px"), `${o}px`; return console.log("Detected sidebar width:", i, "px"), `${i}px`;
} }
const e = window.innerWidth; const e = window.innerWidth;
return e < 1024 ? "0px" : e < 1280 ? `${Math.floor(e * 0.25)}px` : `${Math.floor(e * 0.2)}px`; return e < 1024 ? "0px" : e < 1280 ? `${Math.floor(e * 0.25)}px` : `${Math.floor(e * 0.2)}px`;
@@ -584,24 +648,24 @@ class W extends HTMLElement {
t.style.width = e, console.log("Updated sidebar width to:", e); t.style.width = e, console.log("Updated sidebar width to:", e);
} }
} }
show(t, e, n, o = !1, r = 0, s = null) { show(t, e, n, i = !1, r = 0, s = null) {
const a = this.querySelector("#single-page-image"), l = this.querySelector("#page-number"), c = this.querySelector("#page-icon"); const a = this.querySelector("#single-page-image"), l = this.querySelector("#page-number"), c = this.querySelector("#page-icon");
this.querySelector("#page-indicator"), a.src = t, a.alt = e, this.currentPageNumber = n, this.currentIsBeilage = o, this.currentPartNumber = s; this.querySelector("#page-indicator"), a.src = t, a.alt = e, this.currentPageNumber = n, this.currentIsBeilage = i, this.currentPartNumber = s;
const g = this.getIssueContext(n); const g = this.getIssueContext(n);
if (l.innerHTML = g ? `${g}, ${n}` : `${n}`, r && n === r) { if (l.innerHTML = g ? `${g}, ${n}` : `${n}`, r && n === r) {
l.style.position = "relative"; l.style.position = "relative";
const u = l.querySelector(".target-page-dot"); const d = l.querySelector(".target-page-dot");
u && u.remove(); d && d.remove();
const p = document.createElement("span"); const p = document.createElement("span");
p.className = "target-page-dot absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full z-10", p.title = "verlinkte Seite", l.appendChild(p); p.className = "target-page-dot absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full z-10", p.title = "verlinkte Seite", l.appendChild(p);
} }
if (s !== null) if (s !== null)
c.innerHTML = `<span class="part-number bg-slate-100 text-slate-800 font-bold px-1.5 py-0.5 rounded border border-slate-400 flex items-center justify-center">${s}. Teil</span>`; c.innerHTML = `<span class="part-number bg-slate-100 text-slate-800 font-bold px-1.5 py-0.5 rounded border border-slate-400 flex items-center justify-center">${s}. Teil</span>`;
else { else {
const u = this.determinePageIconType(n, o); const d = this.determinePageIconType(n, i);
c.innerHTML = this.getPageIconHTML(u); c.innerHTML = this.getPageIconHTML(d);
} }
this.updateNavigationButtons(), this.style.display = "block", document.body.style.overflow = "hidden", S(n); this.updateNavigationButtons(), this.style.display = "block", document.body.style.overflow = "hidden", E(n);
} }
close() { close() {
this.style.display = "none", document.body.style.overflow = ""; this.style.display = "none", document.body.style.overflow = "";
@@ -639,28 +703,28 @@ class W extends HTMLElement {
} }
// Share current page // Share current page
shareCurrentPage() { shareCurrentPage() {
if (typeof b == "function") { if (typeof m == "function") {
const t = this.querySelector("#share-btn"); const t = this.querySelector("#share-btn");
b(this.currentPageNumber, t, this.currentIsBeilage); m(this.currentPageNumber, t, this.currentIsBeilage);
} }
} }
// Generate citation for current page // Generate citation for current page
generatePageCitation() { generatePageCitation() {
if (typeof m == "function") { if (typeof y == "function") {
const t = this.querySelector("#cite-btn"); const t = this.querySelector("#cite-btn");
m(this.currentPageNumber, t); y(this.currentPageNumber, t);
} }
} }
// Update navigation button visibility based on available pages // Update navigation button visibility based on available pages
updateNavigationButtons() { updateNavigationButtons() {
const t = this.querySelector("#prev-page-btn"), e = this.querySelector("#next-page-btn"), { prevPage: n, nextPage: o } = this.getAdjacentPages(); const t = this.querySelector("#prev-page-btn"), e = this.querySelector("#next-page-btn"), { prevPage: n, nextPage: i } = this.getAdjacentPages();
n !== null ? (t.disabled = !1, t.className = t.className.replace("opacity-50 cursor-not-allowed", ""), t.className = t.className.replace( n !== null ? (t.disabled = !1, t.className = t.className.replace("opacity-50 cursor-not-allowed", ""), t.className = t.className.replace(
"bg-gray-50 text-gray-400", "bg-gray-50 text-gray-400",
"bg-gray-100 text-gray-700" "bg-gray-100 text-gray-700"
)) : (t.disabled = !0, t.className.includes("opacity-50") || (t.className += " opacity-50 cursor-not-allowed"), t.className = t.className.replace( )) : (t.disabled = !0, t.className.includes("opacity-50") || (t.className += " opacity-50 cursor-not-allowed"), t.className = t.className.replace(
"bg-gray-100 text-gray-700", "bg-gray-100 text-gray-700",
"bg-gray-50 text-gray-400" "bg-gray-50 text-gray-400"
)), o !== null ? (e.disabled = !1, e.className = e.className.replace("opacity-50 cursor-not-allowed", ""), e.className = e.className.replace( )), i !== null ? (e.disabled = !1, e.className = e.className.replace("opacity-50 cursor-not-allowed", ""), e.className = e.className.replace(
"bg-gray-50 text-gray-400", "bg-gray-50 text-gray-400",
"bg-gray-100 text-gray-700" "bg-gray-100 text-gray-700"
)) : (e.disabled = !0, e.className.includes("opacity-50") || (e.className += " opacity-50 cursor-not-allowed"), e.className = e.className.replace( )) : (e.disabled = !0, e.className.includes("opacity-50") || (e.className += " opacity-50 cursor-not-allowed"), e.className = e.className.replace(
@@ -684,10 +748,10 @@ class W extends HTMLElement {
return console.log("Container page:", l, "parsed:", c), c; return console.log("Container page:", l, "parsed:", c), c;
}).filter((a) => a !== null).sort((a, l) => a - l); }).filter((a) => a !== null).sort((a, l) => a - l);
console.log("All pages found:", n), console.log("Current page:", this.currentPageNumber); console.log("All pages found:", n), console.log("Current page:", this.currentPageNumber);
const o = n.indexOf(this.currentPageNumber); const i = n.indexOf(this.currentPageNumber);
console.log("Current index:", o); console.log("Current index:", i);
let r = null, s = null; let r = null, s = null;
return o > 0 && (r = n[o - 1]), o < n.length - 1 && (s = n[o + 1]), console.log("Adjacent pages - prev:", r, "next:", s), { prevPage: r, nextPage: s }; return i > 0 && (r = n[i - 1]), i < n.length - 1 && (s = n[i + 1]), console.log("Adjacent pages - prev:", r, "next:", s), { prevPage: r, nextPage: s };
} }
// Navigate to previous page // Navigate to previous page
goToPreviousPage() { goToPreviousPage() {
@@ -705,12 +769,12 @@ class W extends HTMLElement {
`${e}[data-page-container="${t}"]` `${e}[data-page-container="${t}"]`
); );
if (n) { if (n) {
const o = n.querySelector(".newspaper-page-image"); const i = n.querySelector(".newspaper-page-image");
if (o) { if (i) {
let r = null; let r = null;
this.currentPartNumber !== null && (r = this.getPartNumberForPage(t)), this.show( this.currentPartNumber !== null && (r = this.getPartNumberForPage(t)), this.show(
o.src, i.src,
o.alt, i.alt,
t, t,
this.currentIsBeilage, this.currentIsBeilage,
0, 0,
@@ -725,17 +789,17 @@ class W extends HTMLElement {
if (e) { if (e) {
const n = e.querySelector(".part-number"); const n = e.querySelector(".part-number");
if (n) { if (n) {
const o = n.textContent.match(/(\d+)\./); const i = n.textContent.match(/(\d+)\./);
if (o) if (i)
return parseInt(o[1]); return parseInt(i[1]);
} }
} }
return null; return null;
} }
// Toggle sidebar visibility // Toggle sidebar visibility
toggleSidebar() { toggleSidebar() {
const t = this.querySelector("#sidebar-spacer"), e = this.querySelector("#sidebar-toggle-btn"), n = e.querySelector("i"), o = t.style.width, r = o === "0px" || o === "0"; const t = this.querySelector("#sidebar-spacer"), e = this.querySelector("#sidebar-toggle-btn"), n = e.querySelector("i"), i = t.style.width, r = i === "0px" || i === "0";
if (console.log("Current state - isCollapsed:", r), console.log("Current width:", o), r) { if (console.log("Current state - isCollapsed:", r), console.log("Current width:", i), r) {
const s = this.detectSidebarWidth(); const s = this.detectSidebarWidth();
t.style.width = s, e.className = "w-10 h-10 bg-slate-100 hover:bg-slate-200 text-slate-700 border border-slate-300 rounded flex items-center justify-center transition-colors duration-200 cursor-pointer", n.className = "ri-sidebar-fold-line text-lg font-bold", e.title = "Inhaltsverzeichnis ausblenden", console.log("Expanding sidebar to:", s); t.style.width = s, e.className = "w-10 h-10 bg-slate-100 hover:bg-slate-200 text-slate-700 border border-slate-300 rounded flex items-center justify-center transition-colors duration-200 cursor-pointer", n.className = "ri-sidebar-fold-line text-lg font-bold", e.title = "Inhaltsverzeichnis ausblenden", console.log("Expanding sidebar to:", s);
} else } else
@@ -750,9 +814,9 @@ class W extends HTMLElement {
if (s) { if (s) {
const c = s.querySelector(".page-indicator"); const c = s.querySelector(".page-indicator");
if (c) { if (c) {
const g = c.textContent.trim(), u = g.match(/(\d{1,2}\.\d{1,2}\.\d{4}\s+Nr\.\s+\d+)/); const g = c.textContent.trim(), d = g.match(/(\d{1,2}\.\d{1,2}\.\d{4}\s+Nr\.\s+\d+)/);
if (u) if (d)
return u[1]; return d[1];
const p = g.match(/(\d{4})\s+Nr\.\s+(\d+)/); const p = g.match(/(\d{4})\s+Nr\.\s+(\d+)/);
if (p) if (p)
return `${p[1]} Nr. ${p[2]}`; return `${p[1]} Nr. ${p[2]}`;
@@ -763,9 +827,9 @@ class W extends HTMLElement {
return `${l[1]} Nr. ${l[2]}`; return `${l[1]} Nr. ${l[2]}`;
} else } else
return ""; return "";
const o = e.match(/\/(\d{4})\/(\d+)/); const i = e.match(/\/(\d{4})\/(\d+)/);
if (o) if (i)
return n ? `${o[1]} Nr. ${o[2]}` : ""; return n ? `${i[1]} Nr. ${i[2]}` : "";
const r = document.querySelector(".page-indicator"); const r = document.querySelector(".page-indicator");
if (r) { if (r) {
const a = r.textContent.trim().match(/(\d{4})\s+Nr\.\s+(\d+)/); const a = r.textContent.trim().match(/(\d{4})\s+Nr\.\s+(\d+)/);
@@ -775,15 +839,15 @@ class W extends HTMLElement {
return "KGPZ"; return "KGPZ";
} }
} }
customElements.define("single-page-viewer", W); customElements.define("single-page-viewer", oe);
document.body.addEventListener("htmx:beforeRequest", function(i) { document.body.addEventListener("htmx:beforeRequest", function(o) {
const t = document.querySelector("single-page-viewer"); const t = document.querySelector("single-page-viewer");
t && t.style.display !== "none" && (console.log("Cleaning up single page viewer before HTMX navigation"), t.destroy()); t && t.style.display !== "none" && (console.log("Cleaning up single page viewer before HTMX navigation"), t.destroy()), k();
}); });
window.addEventListener("beforeunload", function() { window.addEventListener("beforeunload", function() {
const i = document.querySelector("single-page-viewer"); const o = document.querySelector("single-page-viewer");
i && i.destroy(); o && o.destroy();
}); });
export { export {
Z as setup re as setup
}; };

File diff suppressed because one or more lines are too long

View File

@@ -1,21 +1,22 @@
{{ if ne (len .model.Search) 1 }} {{ if ne (len .model.Search) 1 }}
{{ $agent := index $.model.Agents .model.Search }} {{ $agent := index $.model.Agents .model.Search }}
{{ if not $agent }} {{ if not $agent }}
<div class="max-w-4xl mx-auto px-4 py-8"> <div class="max-w-6xl mx-auto px-8 py-8">
<div class="bg-red-50 border border-red-200 rounded-lg p-4"> <div class="bg-red-50 border border-red-200 rounded-lg p-6">
<div class="flex items-center"> <div class="flex items-center">
<i class="ri-error-warning-line text-red-600 text-xl mr-2"></i> <i class="ri-error-warning-line text-red-600 text-2xl mr-3"></i>
<span class="text-red-800">Person nicht gefunden: <strong>{{ .model.Search }}</strong></span> <span class="text-red-800 text-lg">Person nicht gefunden: <strong>{{ .model.Search }}</strong></span>
</div> </div>
</div> </div>
</div> </div>
{{ else }} {{ else }}
<div class="max-w-4xl mx-auto px-4 py-8"> <div class="max-w-full mx-auto px-8 py-8">
<div class="mb-6"> <div class="mb-8">
{{ $letter := Upper (FirstLetter $agent.ID) }} {{ $letter := Upper (FirstLetter $agent.ID) }}
<a href="/akteure/{{ $letter }}" class="inline-flex items-center text-blue-600 hover:text-blue-800 transition-colors"> <a href="/akteure/{{ $letter }}" class="inline-flex items-center text-blue-600
<i class="ri-arrow-left-line mr-2"></i> hover:text-blue-800 transition-colors text-xl">
Zurück zu Buchstabe {{ $letter }} <i class="ri-arrow-left-line mr-3 text-xl"></i>
{{ $letter }}
</a> </a>
</div> </div>
<div>{{ template "_akteur" $agent }}</div> <div>{{ template "_akteur" $agent }}</div>
@@ -23,31 +24,71 @@
{{ end }} {{ end }}
{{ else }} {{ else }}
<div class="max-w-7xl mx-auto px-4 py-8"> <div class="max-w-full mx-auto px-8 py-8">
<div class="mb-8"> <div class="mb-10">
<h1 class="text-3xl font-bold text-gray-900 mb-4">Personen & Körperschaften</h1> <div class="bg-slate-100 px-6 py-4 rounded-lg mb-6">
<p class="text-gray-600">Verzeichnis aller in der Zeitung erwähnten Personen und Institutionen</p> {{ if eq .model.Search "autoren" }}
<h1 class="text-4xl font-bold text-gray-900 mb-2">Autoren</h1>
<p class="text-gray-700 text-lg">Personen, die Beiträge in der Zeitung verfasst haben</p>
{{ else }}
<h1 class="text-4xl font-bold text-gray-900 mb-2">Personen & Körperschaften</h1>
<p class="text-gray-700 text-lg">Verzeichnis aller in der Zeitung erwähnten Personen und Institutionen</p>
{{ end }}
</div>
<div class="flex items-center gap-4 mb-6">
<label class="inline-flex items-center">
<input type="checkbox"
class="form-checkbox h-5 w-5 text-blue-600 rounded"
{{ if eq .model.Search "autoren" }}checked{{ end }}
hx-get="{{ if eq .model.Search "autoren" }}/akteure/a{{ else }}/akteure/autoren{{ end }}"
hx-target="body"
hx-push-url="true">
<span class="ml-2 text-lg text-gray-700">Nur Autoren anzeigen</span>
</label>
</div>
</div> </div>
<!-- Alphabet Navigation --> <!-- Alphabet Navigation -->
<div class="mb-8 p-4 bg-gray-50 rounded-lg"> <div class="mb-10 p-6 bg-gray-50 rounded-lg">
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-3">
{{ range $_, $l := .model.AvailableLetters }} {{ range $_, $l := .model.AvailableLetters }}
<a href="/akteure/{{ $l }}" class="inline-flex items-center justify-center w-8 h-8 bg-white border border-gray-300 rounded hover:bg-blue-50 hover:border-blue-300 font-medium text-gray-700 hover:text-blue-700 transition-colors"> <a href="/akteure/{{ $l }}" class="inline-flex items-center justify-center w-10 h-10 bg-white border border-gray-300 rounded hover:bg-blue-50 hover:border-blue-300 font-medium text-gray-700 hover:text-blue-700 transition-colors text-lg">
{{ $l }} {{ $l }}
</a> </a>
{{ end }} {{ end }}
</div> </div>
</div> </div>
<!-- People List - Dictionary Column Layout --> <!-- Two Column Layout: Scrollspy + Content -->
<div class="columns-1 lg:columns-2 gap-8 space-y-0"> <div class="flex gap-8">
{{ range $_, $id := .model.Sorted }} <!-- Scrollspy Navigation - Hidden on smaller screens, shown on 2xl+ -->
{{ $a := index $.model.Agents $id }} <div class="hidden 2xl:block w-80 flex-shrink-0">
<div class="break-inside-avoid mb-4 bg-stone-100 rounded-lg p-4 border border-stone-200"> <div class="sticky top-8 h-screen">
{{ template "_akteur" $a }} <div class="bg-white border border-gray-200 rounded-lg p-4 h-full flex flex-col">
<h3 class="text-lg font-bold text-gray-900 mb-4">Auf dieser Seite</h3>
<nav class="flex-1 overflow-y-auto" id="scrollspy-nav">
{{ range $_, $id := .model.Sorted }}
{{ $a := index $.model.Agents $id }}
<a href="#author-{{ $id }}"
class="block px-3 py-1 text-base text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded scrollspy-link transition-colors border-l-4 border-transparent"
data-target="author-{{ $id }}">
{{ index $a.Names 0 }}
</a>
{{ end }}
</nav>
</div>
</div> </div>
{{ end }} </div>
<!-- People List - Main Content -->
<div class="flex-1 space-y-6">
{{ range $_, $id := .model.Sorted }}
{{ $a := index $.model.Agents $id }}
<div id="author-{{ $id }}" class="bg-stone-100 rounded-lg p-6 border border-stone-200 scroll-mt-8 author-section">
{{ template "_akteur" $a }}
</div>
{{ end }}
</div>
</div> </div>
</div> </div>
{{ end }} {{ end }}

View File

@@ -1,7 +1,14 @@
<title> <title>
KGPZ &ndash; KGPZ &ndash;
{{ if ne (len .model.Search) 1 }} {{ if eq .model.Search "autoren" }}
{{ index (index .model.Agents .model.Search).Names 0 }} Autoren
{{ else if ne (len .model.Search) 1 }}
{{ $agent := index .model.Agents .model.Search }}
{{ if $agent }}
{{ index $agent.Names 0 }}
{{ else }}
Person nicht gefunden
{{ end }}
{{ else }} {{ else }}
Personen &amp; Körperschaften: Personen &amp; Körperschaften:
{{ Upper .model.Search }} {{ Upper .model.Search }}

View File

@@ -0,0 +1,54 @@
<div class="max-w-full mx-auto px-8 py-8">
<div class="mb-10">
<div class="bg-slate-100 px-6 py-4 rounded-lg mb-6">
<h1 class="text-4xl font-bold text-gray-900 mb-2">Autoren</h1>
<p class="text-gray-700 text-lg">Personen, die Beiträge in der Zeitung verfasst haben</p>
</div>
<div class="flex items-center gap-4 mb-6">
<label class="inline-flex items-center">
<input type="checkbox"
class="form-checkbox h-5 w-5 text-blue-600 rounded"
checked
hx-get="/akteure/a"
hx-target="body"
hx-push-url="true">
<span class="ml-2 text-lg text-gray-700">Nur Autoren anzeigen</span>
</label>
</div>
</div>
<!-- Two Column Layout: Scrollspy + Content -->
<div class="flex gap-8">
<!-- Scrollspy Navigation - Hidden on smaller screens, shown on 2xl+ -->
<div class="hidden 2xl:block w-80 flex-shrink-0">
<div class="sticky top-8 h-screen">
<div class="bg-white border border-gray-200 rounded-lg p-4 h-full flex flex-col">
<h3 class="text-lg font-bold text-gray-900 mb-4">Auf dieser Seite</h3>
<nav class="flex-1 overflow-y-auto" id="scrollspy-nav">
{{ range $_, $id := .model.Sorted }}
{{ $a := index $.model.Agents $id }}
<a href="#author-{{ $id }}"
class="block px-3 py-1 text-base text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded scrollspy-link transition-colors border-l-4 border-transparent"
data-target="author-{{ $id }}">
{{ index $a.Names 0 }}
</a>
{{ end }}
</nav>
</div>
</div>
</div>
<!-- Authors List - Main Content -->
<div class="flex-1 space-y-6">
{{ range $_, $id := .model.Sorted }}
{{ $a := index $.model.Agents $id }}
<div id="author-{{ $id }}" class="bg-stone-100 rounded-lg p-6 border border-stone-200 scroll-mt-8 author-section">
{{ template "_akteur_header" $a }}
{{ template "_akteur_beitraege" $a }}
</div>
{{ end }}
</div>
</div>
</div>

View File

@@ -0,0 +1 @@
<title>KGPZ &ndash; Autoren</title>

View File

@@ -1,144 +1,8 @@
{{ $a := . }} {{ $a := . }}
{{ if and $a (ne (len $a.Names) 0) }} {{ if and $a (ne (len $a.Names) 0) }}
{{ $gnd := GetGND $a.GND }}
{{ $works := LookupWorks $a }}
{{ $pieces := LookupPieces $a }}
<div> <div>
{{ template "_akteur_header" $a }}
<!-- Name and external links --> {{ template "_akteur_werke" $a }}
<div class="flex items-start justify-between gap-4"> {{ template "_akteur_beitraege" $a }}
<div class="flex-1">
<!-- Large serif name - bold -->
<div class="text-xl font-serif font-bold text-gray-900 leading-tight">
<a href="/akteure/{{ $a.ID }}" class="hover:text-blue-600 transition-colors">
{{ index $a.Names 0 }}
</a>
</div>
<!-- Years only below name -->
{{ if ne $gnd nil }}
{{- if or (ne (len $gnd.DateOfBirth) 0) (ne (len $gnd.DateOfDeath) 0) -}}
<div class="text-gray-600 text-sm mt-1">
{{- if ne (len $gnd.DateOfBirth) 0 -}}
{{ HRDateYear (index $gnd.DateOfBirth 0) }}
{{- end -}}
{{- if ne (len $gnd.DateOfDeath) 0 -}}
{{ HRDateYear (index $gnd.DateOfDeath 0) }}
{{- end -}}
</div>
{{- end -}}
{{ end }}
<!-- First three professions -->
{{ if ne $gnd nil }}
{{- if ne (len $gnd.ProfessionOrOccupation) 0 -}}
<div class="text-gray-600 text-sm mt-1">
{{ range $i, $prof := $gnd.ProfessionOrOccupation }}
{{ if lt $i 3 }}
{{ if gt $i 0 }} · {{ end }}{{ $prof.Label }}
{{ end }}
{{ end }}
</div>
{{- end -}}
{{ end }}
</div>
<!-- External link symbols on the right -->
<div class="flex gap-2 flex-shrink-0">
{{- if ne $gnd nil -}}
{{- /* Wikipedia link if available */ -}}
{{- if ne (len $gnd.Wikipedia) 0 -}}
<a href="{{ (index $gnd.Wikipedia 0).ID }}" target="_blank" class="text-gray-500 hover:text-blue-600 transition-colors text-lg" title="Wikipedia">
<i class="ri-wikipedia-line"></i>
</a>
{{- end -}}
{{- /* GND link if available */ -}}
{{- if ne $a.GND "" -}}
<a href="{{ $a.GND }}" target="_blank" class="text-gray-500 hover:text-blue-600 transition-colors text-lg" title="Gemeinsame Normdatei">
<i class="ri-links-line"></i>
</a>
{{- else -}}
{{- /* VIAF link if no GND available */ -}}
{{- if ne (len $gnd.SameAs) 0 -}}
{{ range $_, $ref := $gnd.SameAs }}
{{- if ne $ref.ID "" -}}
<a href="{{ $ref.ID }}" target="_blank" class="text-gray-500 hover:text-blue-600 transition-colors text-lg" title="External Link">
<i class="ri-global-line"></i>
</a>
{{- end -}}
{{ end }}
{{- end -}}
{{- end -}}
{{- end -}}
</div>
</div>
{{- if ne (len $works) 0 -}}
<div class="mt-3">
<div class="text-sm font-medium text-gray-700 mb-2">
<i class="ri-book-line mr-1"></i>Werke
</div>
{{ range $_, $w := $works }}
<div class="mb-2">
{{- if ne (len $w.Item.Citation.InnerXML ) 0 -}}
<div class="text-sm">
<span class="italic">
<script type="application/xml" xslt-template="transform-citation" xslt-onload>
<xml>
{{- Safe $w.Item.Citation.InnerXML -}}
</xml>
</script>
</span>
{{- range $_, $url := $w.Item.URLs -}}
<span class="ml-1">
<a href="{{ $url.Address }}" target="_blank" class="text-blue-600 hover:text-blue-800 text-sm">
{{ $url.Chardata }} <i class="ri-external-link-line text-xs"></i>
</a>
</span>
{{- end -}}
</div>
{{- end -}}
{{ $workPieces := LookupPieces $w.Item }}
{{ if len $workPieces }}
<div class="flex flex-wrap gap-1 mt-1">
{{ range $_, $p := $workPieces }}
{{ range $_, $issue := $p.Item.IssueRefs }}
<span class="inline-block bg-green-50 hover:bg-green-100 text-green-700 hover:text-green-800 px-2 py-1 rounded text-xs transition-colors">
{{ template "_citation" $issue }}
</span>
{{ end }}
{{ end }}
</div>
{{ end }}
</div>
{{ end }}
</div>
{{ end }}
{{ $pieces := LookupPieces $a }}
{{ if ne (len $pieces) 0 }}
<div class="mt-3">
<div class="text-sm font-medium text-gray-700 mb-2">
<i class="ri-newspaper-line mr-1"></i>Beiträge
</div>
<div class="space-y-2">
{{ range $_, $p := SortPiecesByDate $pieces }}
<div class="border-l-2 border-gray-200 pl-3">
<div class="text-sm">
{{ template "_piece_summary" $p.Item }}
</div>
<div class="mt-1 flex flex-wrap gap-1">
{{ range $_, $issue := $p.Item.IssueRefs }}
<span class="inline-block bg-green-50 hover:bg-green-100 text-green-700 hover:text-green-800 px-2 py-1 rounded text-xs transition-colors">
{{ template "_citation" $issue }}
</span>
{{ end }}
</div>
</div>
{{ end }}
</div>
</div>
{{ end }}
</div> </div>
{{ end }} {{ end }}

View File

@@ -0,0 +1,74 @@
{{ $a := . }}
{{ $pieces := LookupPieces $a }}
{{ if ne (len $pieces) 0 }}
<div class="mt-8">
<div class="px-4 py-3 rounded-lg mb-4">
<h2 class="text-lg font-bold text-gray-900">
<i class="ri-newspaper-line mr-2"></i>Beiträge
</h2>
</div>
<div class="space-y-2">
{{- /* Group pieces by title and work reference */ -}}
{{- $groupedPieces := dict -}}
{{- range $_, $p := SortPiecesByDate $pieces -}}
{{- $groupKey := "" -}}
{{- if $p.Item.Title -}}
{{- $groupKey = index $p.Item.Title 0 -}}
{{- else if $p.Item.WorkRefs -}}
{{- $work := GetWork (index $p.Item.WorkRefs 0).Ref -}}
{{- if $work.PreferredTitle -}}
{{- $groupKey = $work.PreferredTitle -}}
{{- else if $work.Citation.Title -}}
{{- $groupKey = $work.Citation.Title -}}
{{- end -}}
{{- else if $p.Item.Incipit -}}
{{- $groupKey = index $p.Item.Incipit 0 -}}
{{- else -}}
{{- $groupKey = printf "untitled-%s" $p.Item.ID -}}
{{- end -}}
{{- $existing := index $groupedPieces $groupKey -}}
{{- if $existing -}}
{{- $groupedPieces = merge $groupedPieces (dict $groupKey (append $existing $p)) -}}
{{- else -}}
{{- $groupedPieces = merge $groupedPieces (dict $groupKey (slice $p)) -}}
{{- end -}}
{{- end -}}
<div class="columns-2 gap-6">
{{- /* Display grouped pieces */ -}}
{{- range $groupKey, $groupedItems := $groupedPieces -}}
<div class="border-l-2 border-gray-200 pl-4 break-inside-avoid">
<div class="text-lg">
{{- /* Use first piece for display text */ -}}
{{ template "_piece_summary" (dict "Piece" (index $groupedItems 0).Item "CurrentActorID" $a.ID) }}
{{- /* Show all citations from all pieces in this group */ -}}
{{- range $_, $groupItem := $groupedItems -}}
{{- range $i, $issue := $groupItem.Item.IssueRefs -}}
{{ $issueData := GetIssue (printf "%d-%d" $issue.When.Year $issue.Nr) }}
{{- $url := printf "/%s/%d" $issue.When $issue.Nr -}}
{{- if $issue.Von -}}
{{- if $issue.Beilage -}}
{{- $url = printf "%s#beilage-%d-page-%d" $url $issue.Beilage $issue.Von -}}
{{- else -}}
{{- $url = printf "%s/%d" $url $issue.Von -}}
{{- end -}}
{{- end -}}
<a href="{{ $url }}" class="inline-block text-sm bg-green-50 text-green-700 hover:bg-green-100 hover:text-green-800 px-3 py-2 rounded ml-2 no-underline">
{{- if $issueData -}}
{{ $issueData.Datum.When.Day }}.{{ $issueData.Datum.When.Month }}.{{ $issueData.Datum.When.Year }}/{{ $issue.Nr }}, S. {{ $issue.Von }}{{- if and $issue.Bis (ne $issue.Von $issue.Bis) }}-{{ $issue.Bis }}{{ end }}
{{- else -}}
{{ $issue.When.Day }}.{{ $issue.When.Month }}.{{ $issue.When.Year }}/{{ $issue.Nr }}, S. {{ $issue.Von }}{{- if and $issue.Bis (ne $issue.Von $issue.Bis) }}-{{ $issue.Bis }}{{ end }}
{{- end -}}
</a>
{{- end -}}
{{- end -}}
</div>
</div>
{{- end -}}
</div>
</div>
</div>
{{ end }}

View File

@@ -0,0 +1,73 @@
{{ $a := . }}
{{ if and $a (ne (len $a.Names) 0) }}
{{ $gnd := GetGND $a.GND }}
<!-- Name and external links -->
<div class="flex items-start justify-between gap-4">
<div class="flex-1">
<!-- Large serif name - bold -->
<div class="text-2xl font-serif font-bold text-gray-900 leading-tight">
<a href="/akteure/{{ $a.ID }}" class="hover:text-blue-600 transition-colors">
{{ index $a.Names 0 }}
</a>
</div>
<!-- Years only below name -->
{{ if ne $gnd nil }}
{{- if or (ne (len $gnd.DateOfBirth) 0) (ne (len $gnd.DateOfDeath) 0) -}}
<div class="text-gray-600 text-xl mt-2">
{{- if ne (len $gnd.DateOfBirth) 0 -}}
{{ HRDateYear (index $gnd.DateOfBirth 0) }}
{{- end -}}
{{- if ne (len $gnd.DateOfDeath) 0 -}}
{{ HRDateYear (index $gnd.DateOfDeath 0) }}
{{- end -}}
</div>
{{- end -}}
{{ end }}
<!-- First three professions -->
{{ if ne $gnd nil }}
{{- if ne (len $gnd.ProfessionOrOccupation) 0 -}}
<div class="text-gray-600 text-xl mt-2">
{{ range $i, $prof := $gnd.ProfessionOrOccupation }}
{{ if lt $i 3 }}
{{ if gt $i 0 }} · {{ end }}{{ $prof.Label }}
{{ end }}
{{ end }}
</div>
{{- end -}}
{{ end }}
</div>
<!-- External link symbols on the right -->
<div class="flex gap-2 flex-shrink-0">
{{- if ne $gnd nil -}}
{{- /* Wikipedia link if available */ -}}
{{- if ne (len $gnd.Wikipedia) 0 -}}
<a href="{{ (index $gnd.Wikipedia 0).ID }}" target="_blank" class="text-gray-500 hover:text-blue-600 transition-colors text-lg" title="Wikipedia">
<i class="ri-wikipedia-line"></i>
</a>
{{- end -}}
{{- /* GND link if available */ -}}
{{- if ne $a.GND "" -}}
<a href="{{ $a.GND }}" target="_blank" class="text-gray-500 hover:text-blue-600 transition-colors text-lg" title="Gemeinsame Normdatei">
<i class="ri-links-line"></i>
</a>
{{- else -}}
{{- /* VIAF link if no GND available */ -}}
{{- if ne (len $gnd.SameAs) 0 -}}
{{ range $_, $ref := $gnd.SameAs }}
{{- if ne $ref.ID "" -}}
<a href="{{ $ref.ID }}" target="_blank" class="text-gray-500 hover:text-blue-600 transition-colors text-lg" title="External Link">
<i class="ri-global-line"></i>
</a>
{{- end -}}
{{ end }}
{{- end -}}
{{- end -}}
{{- end -}}
</div>
</div>
{{ end }}

View File

@@ -0,0 +1,188 @@
{{ $a := . }}
{{ $works := LookupWorks $a }}
{{- if ne (len $works) 0 -}}
<div class="mt-8">
<div class="px-4 py-3 rounded-lg mb-4">
<h2 class="text-lg font-bold text-gray-900">
<i class="ri-book-line mr-2"></i>Werke
</h2>
</div>
<div class="columns-2 gap-6">
{{ range $_, $w := $works }}
<div class="mb-2 break-inside-avoid pl-4">
{{- if ne (len $w.Item.Citation.InnerXML ) 0 -}}
<div class="text-lg">
<span class="italic">
<script type="application/xml" xslt-template="transform-citation" xslt-onload>
<xml>
{{- Safe $w.Item.Citation.InnerXML -}}
</xml>
</script>
</span>
{{- range $_, $url := $w.Item.URLs -}}
<span class="ml-1">
<a href="{{ $url.Address }}" target="_blank" class="text-blue-600 hover:text-blue-800 text-sm">
{{ $url.Chardata }} <i class="ri-external-link-line text-xs"></i>
</a>
</span>
{{- end -}}
</div>
{{- end -}}
{{ $workPieces := LookupPieces $w.Item }}
{{ if len $workPieces }}
<div class="mt-1 text-lg">
{{- /* Group pieces by category and display inline */ -}}
{{- $groupedByCategory := dict -}}
{{- range $_, $p := $workPieces -}}
{{- $categoryFlags := GetCategoryFlags $p.Item -}}
{{- $categories := slice -}}
{{- if $categoryFlags.Rezension -}}
{{- $categories = append $categories "Rezension" -}}
{{- end -}}
{{- if $categoryFlags.Gedicht -}}
{{- $categories = append $categories "Gedicht" -}}
{{- end -}}
{{- if $categoryFlags.Aufsatz -}}
{{- $categories = append $categories "Aufsatz" -}}
{{- end -}}
{{- if $categoryFlags.Theaterkritik -}}
{{- $categories = append $categories "Theaterkritik" -}}
{{- end -}}
{{- if $categoryFlags.Brief -}}
{{- $categories = append $categories "Brief" -}}
{{- end -}}
{{- if $categoryFlags.Erzaehlung -}}
{{- $categories = append $categories "Erzählung" -}}
{{- end -}}
{{- if $categoryFlags.Kommentar -}}
{{- $categories = append $categories "Kommentar" -}}
{{- end -}}
{{- if $categoryFlags.Uebersetzung -}}
{{- $categories = append $categories "Übersetzung" -}}
{{- end -}}
{{- if $categoryFlags.Auszug -}}
{{- $categories = append $categories "Auszug" -}}
{{- end -}}
{{- if $categoryFlags.Replik -}}
{{- $categories = append $categories "Replik" -}}
{{- end -}}
{{- if $categoryFlags.Lokalnachrichten -}}
{{- $categories = append $categories "Lokalnachrichten" -}}
{{- end -}}
{{- if $categoryFlags.Lotterie -}}
{{- $categories = append $categories "Lotterie" -}}
{{- end -}}
{{- if $categoryFlags.Nachruf -}}
{{- $categories = append $categories "Nachruf" -}}
{{- end -}}
{{- if $categoryFlags.Weltnachrichten -}}
{{- $categories = append $categories "Weltnachrichten" -}}
{{- end -}}
{{- if $categoryFlags.EinkommendeFremde -}}
{{- $categories = append $categories "Einkommende Fremde" -}}
{{- end -}}
{{- if $categoryFlags.Wechselkurse -}}
{{- $categories = append $categories "Wechselkurse" -}}
{{- end -}}
{{- if $categoryFlags.Buecher -}}
{{- $categories = append $categories "Bücher" -}}
{{- end -}}
{{- if $categoryFlags.Lokalanzeigen -}}
{{- $categories = append $categories "Lokalanzeigen" -}}
{{- end -}}
{{- if $categoryFlags.Vorladung -}}
{{- $categories = append $categories "Vorladung" -}}
{{- end -}}
{{- if $categoryFlags.GelehrteNachrichten -}}
{{- $categories = append $categories "Gelehrte Nachrichten" -}}
{{- end -}}
{{- if $categoryFlags.Anzeige -}}
{{- $categories = append $categories "Anzeige" -}}
{{- end -}}
{{- if $categoryFlags.Proklamation -}}
{{- $categories = append $categories "Proklamation" -}}
{{- end -}}
{{- if $categoryFlags.Desertionsliste -}}
{{- $categories = append $categories "Desertionsliste" -}}
{{- end -}}
{{- if $categoryFlags.Notenblatt -}}
{{- $categories = append $categories "Notenblatt" -}}
{{- end -}}
{{- if $categoryFlags.Vorlesungsverzeichnis -}}
{{- $categories = append $categories "Vorlesungsverzeichnis" -}}
{{- end -}}
{{- if $categoryFlags.Abbildung -}}
{{- $categories = append $categories "Abbildung" -}}
{{- end -}}
{{- if $categoryFlags.Ineigenersache -}}
{{- $categories = append $categories "In eigener Sache" -}}
{{- end -}}
{{- if $categoryFlags.Provinienz -}}
{{- $categories = append $categories "Provinienz" -}}
{{- end -}}
{{- if eq (len $categories) 0 -}}
{{- $categories = append $categories "Beitrag" -}}
{{- end -}}
{{- $categoryName := "" -}}
{{- if eq (len $categories) 0 -}}
{{- $categoryName = "Beitrag" -}}
{{- else -}}
{{- $sortedCategories := sortStrings $categories -}}
{{- $categoryName = joinWithUnd $sortedCategories -}}
{{- end -}}
{{- $existing := index $groupedByCategory $categoryName -}}
{{- if $existing -}}
{{- $groupedByCategory = merge $groupedByCategory (dict $categoryName (append $existing $p)) -}}
{{- else -}}
{{- $groupedByCategory = merge $groupedByCategory (dict $categoryName (slice $p)) -}}
{{- end -}}
{{- end -}}
{{- /* Display each category group */ -}}
{{- range $categoryName, $categoryPieces := $groupedByCategory -}}
<span class="inline-block text-sm bg-green-50 text-green-700 px-3 py-2 rounded ml-2">
{{ $categoryName }}
{{- /* Check for additional authors (non-current actor) */ -}}
{{- $additionalAuthorIDs := slice -}}
{{- range $_, $p := $categoryPieces -}}
{{- range $agentref := $p.Item.AgentRefs -}}
{{- if and (or (eq $agentref.Category "") (eq $agentref.Category "autor")) (ne $agentref.Ref $a.ID) -}}
{{- $additionalAuthorIDs = append $additionalAuthorIDs $agentref.Ref -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $uniqueAuthorIDs := unique $additionalAuthorIDs -}}
{{- if $uniqueAuthorIDs -}}
{{ " " }}von {{ range $i, $authorID := $uniqueAuthorIDs }}{{- if gt $i 0 }} und {{ end }}{{- $agent := GetAgent $authorID -}}{{- if and $agent (gt (len $agent.Names) 0) -}}<a href="/akteure/{{ $authorID }}" class="text-green-700 hover:text-green-900 underline text-sm font-bold">{{ index $agent.Names 0 }}</a>{{- end -}}{{ end }}
{{- end -}}{{ ":" }}
</span>
{{- /* Show all citations for this category */ -}}
{{- range $_, $p := $categoryPieces -}}
{{- range $_, $issue := $p.Item.IssueRefs -}}
{{ $issueData := GetIssue (printf "%d-%d" $issue.When.Year $issue.Nr) }}
{{- $url := printf "/%s/%d" $issue.When $issue.Nr -}}
{{- if $issue.Von -}}
{{- if $issue.Beilage -}}
{{- $url = printf "%s#beilage-%d-page-%d" $url $issue.Beilage $issue.Von -}}
{{- else -}}
{{- $url = printf "%s/%d" $url $issue.Von -}}
{{- end -}}
{{- end -}}
<a href="{{ $url }}" class="inline-block text-sm bg-blue-50 text-blue-700 hover:bg-blue-100 hover:text-blue-800 px-3 py-2 rounded ml-2 no-underline">
{{- if $issueData -}}
{{ $issueData.Datum.When.Day }}.{{ $issueData.Datum.When.Month }}.{{ $issueData.Datum.When.Year }}/{{ $issue.Nr }}, S. {{ $issue.Von }}{{- if and $issue.Bis (ne $issue.Von $issue.Bis) }}-{{ $issue.Bis }}{{ end }}
{{- else -}}
{{ $issue.When.Day }}.{{ $issue.When.Month }}.{{ $issue.When.Year }}/{{ $issue.Nr }}, S. {{ $issue.Von }}{{- if and $issue.Bis (ne $issue.Von $issue.Bis) }}-{{ $issue.Bis }}{{ end }}
{{- end -}}
</a>
{{- end -}}
{{- end -}}
{{- end -}}
</div>
{{ end }}
</div>
{{ end }}
</div>
</div>
{{ end }}

View File

@@ -1,4 +1,5 @@
{{- $piece := . -}} {{- $piece := .Piece -}}
{{- $currentActorID := .CurrentActorID -}}
<div class="leading-snug"> <div class="leading-snug">
{{- $categoryFlags := GetCategoryFlags $piece -}} {{- $categoryFlags := GetCategoryFlags $piece -}}
@@ -42,14 +43,113 @@
{{- end -}} {{- end -}}
{{- end -}} {{- end -}}
{{- /* Generate piece descriptions */ -}} {{- /* Build category list */ -}}
{{- $categories := slice -}}
{{- if $categoryFlags.Rezension -}} {{- if $categoryFlags.Rezension -}}
{{- $categories = append $categories "Rezension" -}}
{{- end -}}
{{- if $categoryFlags.Gedicht -}}
{{- $categories = append $categories "Gedicht" -}}
{{- end -}}
{{- if $categoryFlags.Aufsatz -}}
{{- $categories = append $categories "Aufsatz" -}}
{{- end -}}
{{- if $categoryFlags.Theaterkritik -}}
{{- $categories = append $categories "Theaterkritik" -}}
{{- end -}}
{{- if $categoryFlags.Brief -}}
{{- $categories = append $categories "Brief" -}}
{{- end -}}
{{- if $categoryFlags.Erzaehlung -}}
{{- $categories = append $categories "Erzählung" -}}
{{- end -}}
{{- if $categoryFlags.Kommentar -}}
{{- $categories = append $categories "Kommentar" -}}
{{- end -}}
{{- if $categoryFlags.Uebersetzung -}}
{{- $categories = append $categories "Übersetzung" -}}
{{- end -}}
{{- if $categoryFlags.Auszug -}}
{{- $categories = append $categories "Auszug" -}}
{{- end -}}
{{- if $categoryFlags.Replik -}}
{{- $categories = append $categories "Replik" -}}
{{- end -}}
{{- if $categoryFlags.Lokalnachrichten -}}
{{- $categories = append $categories "Lokalnachrichten" -}}
{{- end -}}
{{- if $categoryFlags.Lotterie -}}
{{- $categories = append $categories "Lotterie" -}}
{{- end -}}
{{- if $categoryFlags.Nachruf -}}
{{- $categories = append $categories "Nachruf" -}}
{{- end -}}
{{- if $categoryFlags.Weltnachrichten -}}
{{- $categories = append $categories "Weltnachrichten" -}}
{{- end -}}
{{- if $categoryFlags.EinkommendeFremde -}}
{{- $categories = append $categories "Einkommende Fremde" -}}
{{- end -}}
{{- if $categoryFlags.Wechselkurse -}}
{{- $categories = append $categories "Wechselkurse" -}}
{{- end -}}
{{- if $categoryFlags.Buecher -}}
{{- $categories = append $categories "Bücher" -}}
{{- end -}}
{{- if $categoryFlags.Lokalanzeigen -}}
{{- $categories = append $categories "Lokalanzeigen" -}}
{{- end -}}
{{- if $categoryFlags.Vorladung -}}
{{- $categories = append $categories "Vorladung" -}}
{{- end -}}
{{- if $categoryFlags.GelehrteNachrichten -}}
{{- $categories = append $categories "Gelehrte Nachrichten" -}}
{{- end -}}
{{- if $categoryFlags.Anzeige -}}
{{- $categories = append $categories "Anzeige" -}}
{{- end -}}
{{- if $categoryFlags.Proklamation -}}
{{- $categories = append $categories "Proklamation" -}}
{{- end -}}
{{- if $categoryFlags.Desertionsliste -}}
{{- $categories = append $categories "Desertionsliste" -}}
{{- end -}}
{{- if $categoryFlags.Notenblatt -}}
{{- $categories = append $categories "Notenblatt" -}}
{{- end -}}
{{- if $categoryFlags.Vorlesungsverzeichnis -}}
{{- $categories = append $categories "Vorlesungsverzeichnis" -}}
{{- end -}}
{{- if $categoryFlags.Abbildung -}}
{{- $categories = append $categories "Abbildung" -}}
{{- end -}}
{{- if $categoryFlags.Ineigenersache -}}
{{- $categories = append $categories "In eigener Sache" -}}
{{- end -}}
{{- if $categoryFlags.Provinienz -}}
{{- $categories = append $categories "Provinienz" -}}
{{- end -}}
{{- /* Display category combination */ -}}
{{- $categoryName := "" -}}
{{- if eq (len $categories) 0 -}}
{{- $categoryName = "Beitrag" -}}
{{- else -}}
{{- $sortedCategories := sortStrings $categories -}}
{{- $categoryName = joinWithUnd $sortedCategories -}}
{{- end -}}
{{- /* Generate piece descriptions */ -}}
{{- if has $categories "Rezension" -}}
{{- $authorFound := false -}} {{- $authorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}} {{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}} {{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}} {{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}} {{- if and $agent (gt (len $agent.Names) 0) -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>, Rezension von: {{- if ne $agentref.Ref $currentActorID -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>,
{{- end -}}
{{ $categoryName }} von:
{{ if $workAuthorName }} {{ if $workAuthorName }}
<a href="/akteure/{{ $workAuthorID }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ $workAuthorName }}</a>, <a href="/akteure/{{ $workAuthorID }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ $workAuthorName }}</a>,
{{ end }} {{ end }}
@@ -66,7 +166,7 @@
{{- end -}} {{- end -}}
{{- end -}} {{- end -}}
{{- if not $authorFound -}} {{- if not $authorFound -}}
Rezension von: {{ $categoryName }} von:
{{ if $workAuthorName }} {{ if $workAuthorName }}
<a href="/akteure/{{ $workAuthorID }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ $workAuthorName }}</a>, <a href="/akteure/{{ $workAuthorID }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ $workAuthorName }}</a>,
{{ end }} {{ end }}
@@ -79,104 +179,24 @@
{{ end }} {{ end }}
{{- end -}} {{- end -}}
{{- else if $categoryFlags.Gedicht -}}
{{- $authorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>, Gedicht{{ if $title }}: <em>„{{ $title }}"</em>{{ end }}
{{- $authorFound = true -}}
{{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $authorFound -}}
Gedicht{{ if $title }}: <em>„{{ $title }}"</em>{{ end }}
{{- end -}}
{{- else if $categoryFlags.Aufsatz -}}
{{- $authorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>, Aufsatz{{ if $title }}: <em>„{{ $title }}"</em>{{ end }}
{{- $authorFound = true -}}
{{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $authorFound -}}
Aufsatz{{ if $title }}: <em>„{{ $title }}"</em>{{ end }}
{{- end -}}
{{- else if $categoryFlags.Theaterkritik -}}
{{- $authorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>, Theaterkritik{{ if $workTitle }} zu <em>{{ $workTitle }}</em>{{ else if $title }} zu <em>{{ $title }}</em>{{ end }}
{{- $authorFound = true -}}
{{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $authorFound -}}
Theaterkritik{{ if $workTitle }} zu <em>{{ $workTitle }}</em>{{ else if $title }} zu <em>{{ $title }}</em>{{ end }}
{{- end -}}
{{- else if $categoryFlags.Brief -}}
{{ if $categoryFlags.Nachruf }}Kondolenzbrief{{ else }}Leserbrief{{ end }}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
von <a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>
{{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- else if $categoryFlags.Erzaehlung -}}
{{- $authorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>, Erzählung{{ if $title }}: <em>„{{ $title }}"</em>{{ end }}
{{- $authorFound = true -}}
{{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $authorFound -}}
Erzählung{{ if $title }}: <em>„{{ $title }}"</em>{{ end }}
{{- end -}}
{{- else if $categoryFlags.Lokalnachrichten -}}
{{ if $categoryFlags.Lotterie }}Lotterienachrichten{{ else if $categoryFlags.Nachruf }}Nachrufe{{ else if $categoryFlags.Theaterkritik }}Theaternachrichten{{ else }}Lokalnachrichten{{ end }}
{{- else if $categoryFlags.Weltnachrichten -}}
Politische Nachrichten aus aller Welt
{{- else -}} {{- else -}}
{{- $authorFound := false -}} {{- $authorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}} {{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}} {{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}} {{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}} {{- if and $agent (gt (len $agent.Names) 0) -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>{{ if $title }}: <em>{{ $title }}</em>{{ end }} {{- if ne $agentref.Ref $currentActorID -}}
<a href="/akteure/{{ $agentref.Ref }}" class="text-slate-700 hover:text-slate-900 underline decoration-slate-400 hover:decoration-slate-600">{{ index $agent.Names 0 }}</a>,
{{- end -}}
{{ $categoryName }}{{ if $title }}: <em>„{{ $title }}"</em>{{ end }}
{{- $authorFound = true -}} {{- $authorFound = true -}}
{{- break -}} {{- break -}}
{{- end -}} {{- end -}}
{{- end -}} {{- end -}}
{{- end -}} {{- end -}}
{{- if not $authorFound -}} {{- if not $authorFound -}}
{{ if $title }}<em>{{ $title }}</em>{{ else }}Beitrag ohne Titel{{ end }} {{ $categoryName }}{{ if $title }}: <em>{{ $title }}"</em>{{ else if eq $categoryName "Beitrag" }} ohne Titel{{ end }}
{{- end -}} {{- end -}}
{{- end -}} {{- end -}}
{{- if $place }} <span class="inline-block bg-slate-200 text-slate-700 text-xs px-2 py-0.5 rounded-md ml-1 whitespace-nowrap">{{ $place }}</span>{{ end }}
</div> </div>

View File

@@ -977,6 +977,192 @@ function generatePageCitation(pageNumber, button) {
} }
} }
// Initialize scrollspy functionality for author/agent pages
function initializeScrollspy() {
// Clean up any existing scrollspy
cleanupScrollspy();
const sections = document.querySelectorAll('.author-section');
const navLinks = document.querySelectorAll('.scrollspy-link');
if (sections.length === 0 || navLinks.length === 0) {
return;
}
function updateActiveLink() {
const visibleSections = [];
const viewportTop = window.scrollY;
const viewportBottom = viewportTop + window.innerHeight;
// Check which sections are fully visible (header must be completely visible)
sections.forEach(section => {
const sectionRect = section.getBoundingClientRect();
const sectionTop = sectionRect.top + window.scrollY;
// Find the header element (name, life data, professions)
const headerElement = section.querySelector('div:first-child');
if (headerElement) {
const headerRect = headerElement.getBoundingClientRect();
const headerTop = headerRect.top + window.scrollY;
const headerBottom = headerTop + headerRect.height;
// Check if the entire header is visible in the viewport
const headerTopVisible = headerRect.top >= 0;
const headerBottomVisible = headerRect.bottom <= window.innerHeight;
if (headerTopVisible && headerBottomVisible) {
visibleSections.push(section.getAttribute('id'));
}
}
});
// Update highlighting for all visible sections
const activeLinks = [];
navLinks.forEach(link => {
link.classList.remove('bg-blue-100', 'text-blue-700', 'font-medium', 'border-red-500');
link.classList.add('text-gray-600', 'border-transparent');
const targetId = link.getAttribute('data-target');
if (visibleSections.includes(targetId)) {
link.classList.remove('text-gray-600', 'border-transparent');
link.classList.add('bg-blue-100', 'text-blue-700', 'font-medium', 'border-red-500');
activeLinks.push(link);
}
});
// Implement proportional scrolling to keep active links visible
if (activeLinks.length > 0) {
updateSidebarScroll(activeLinks);
}
}
function updateSidebarScroll(activeLinks) {
// Skip automatic scrolling during manual navigation
if (window.scrollspyManualNavigation) return;
const sidebar = document.getElementById('scrollspy-nav');
if (!sidebar) return;
// Get the first active link as reference
const firstActiveLink = activeLinks[0];
// Calculate the main content scroll progress
const documentHeight = Math.max(
document.body.scrollHeight,
document.body.offsetHeight,
document.documentElement.clientHeight,
document.documentElement.scrollHeight,
document.documentElement.offsetHeight
);
const viewportHeight = window.innerHeight;
const maxScroll = documentHeight - viewportHeight;
const scrollProgress = maxScroll > 0 ? window.scrollY / maxScroll : 0;
// Calculate sidebar scroll dimensions
const sidebarHeight = sidebar.clientHeight;
const sidebarScrollHeight = sidebar.scrollHeight;
const maxSidebarScroll = sidebarScrollHeight - sidebarHeight;
if (maxSidebarScroll > 0) {
// Calculate proportional scroll position
const targetSidebarScroll = scrollProgress * maxSidebarScroll;
// Get the position of the first active link within the sidebar
const linkRect = firstActiveLink.getBoundingClientRect();
const sidebarRect = sidebar.getBoundingClientRect();
const linkOffsetInSidebar = linkRect.top - sidebarRect.top + sidebar.scrollTop;
// Calculate the desired position (center the active link in the sidebar viewport)
const sidebarCenter = sidebarHeight / 2;
const centeredPosition = linkOffsetInSidebar - sidebarCenter;
// Use a blend of proportional scrolling and centering for smooth behavior
const blendFactor = 0.7; // 70% proportional, 30% centering
const finalScrollPosition = (blendFactor * targetSidebarScroll) + ((1 - blendFactor) * centeredPosition);
// Clamp to valid scroll range
const clampedPosition = Math.max(0, Math.min(maxSidebarScroll, finalScrollPosition));
// Apply smooth scrolling, but only if the difference is significant
const currentScrollTop = sidebar.scrollTop;
const scrollDifference = Math.abs(clampedPosition - currentScrollTop);
if (scrollDifference > 10) { // Only scroll if more than 10px difference
sidebar.scrollTo({
top: clampedPosition,
behavior: 'smooth'
});
}
}
}
// Store scroll handler reference for cleanup
window.scrollspyScrollHandler = function() {
clearTimeout(window.scrollspyTimeout);
window.scrollspyTimeout = setTimeout(updateActiveLink, 50);
};
// Add scroll listener
window.addEventListener('scroll', window.scrollspyScrollHandler);
// Store click handlers for cleanup
window.scrollspyClickHandlers = [];
// Add smooth scroll on link click
navLinks.forEach(link => {
const clickHandler = function(e) {
e.preventDefault();
const target = document.getElementById(this.getAttribute('data-target'));
if (target) {
// Temporarily disable automatic sidebar scrolling during manual navigation
window.scrollspyManualNavigation = true;
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
// Re-enable automatic scrolling after navigation completes
setTimeout(() => {
window.scrollspyManualNavigation = false;
}, 1000);
}
};
window.scrollspyClickHandlers.push({ link, handler: clickHandler });
link.addEventListener('click', clickHandler);
});
// Initial active link update
updateActiveLink();
}
// Cleanup scrollspy functionality
function cleanupScrollspy() {
// Remove scroll listener
if (window.scrollspyScrollHandler) {
window.removeEventListener('scroll', window.scrollspyScrollHandler);
window.scrollspyScrollHandler = null;
}
// Clear timeout
if (window.scrollspyTimeout) {
clearTimeout(window.scrollspyTimeout);
window.scrollspyTimeout = null;
}
// Remove click handlers
if (window.scrollspyClickHandlers) {
window.scrollspyClickHandlers.forEach(({ link, handler }) => {
link.removeEventListener('click', handler);
});
window.scrollspyClickHandlers = null;
}
// Reset manual navigation flag
window.scrollspyManualNavigation = false;
}
// Initialize newspaper layout functionality // Initialize newspaper layout functionality
function initializeNewspaperLayout() { function initializeNewspaperLayout() {
// Initialize page highlighting // Initialize page highlighting
@@ -1026,6 +1212,11 @@ function setup() {
initializeNewspaperLayout(); initializeNewspaperLayout();
} }
// Initialize scrollspy if author/agent sections are present
if (document.querySelector(".author-section")) {
initializeScrollspy();
}
// Set up HTMX event handlers // Set up HTMX event handlers
htmx.on("htmx:load", function (_) { htmx.on("htmx:load", function (_) {
// INFO: We can instead use afterSettle; and also clear the map with // INFO: We can instead use afterSettle; and also clear the map with
@@ -1033,12 +1224,16 @@ function setup() {
setup_xslt(); setup_xslt();
}); });
// HTMX event handling for newspaper layout
// HTMX event handling for newspaper layout and scrollspy
document.body.addEventListener("htmx:afterSwap", function (event) { document.body.addEventListener("htmx:afterSwap", function (event) {
setTimeout(() => { setTimeout(() => {
if (document.querySelector(".newspaper-page-container")) { if (document.querySelector(".newspaper-page-container")) {
initializeNewspaperLayout(); initializeNewspaperLayout();
} }
if (document.querySelector(".author-section")) {
initializeScrollspy();
}
}, 100); }, 100);
}); });
@@ -1047,6 +1242,9 @@ function setup() {
if (document.querySelector(".newspaper-page-container")) { if (document.querySelector(".newspaper-page-container")) {
initializeNewspaperLayout(); initializeNewspaperLayout();
} }
if (document.querySelector(".author-section")) {
initializeScrollspy();
}
}, 200); }, 200);
}); });
@@ -1055,6 +1253,9 @@ function setup() {
if (document.querySelector(".newspaper-page-container")) { if (document.querySelector(".newspaper-page-container")) {
initializeNewspaperLayout(); initializeNewspaperLayout();
} }
if (document.querySelector(".author-section")) {
initializeScrollspy();
}
}, 100); }, 100);
}); });
} }
@@ -1664,6 +1865,9 @@ document.body.addEventListener("htmx:beforeRequest", function (event) {
console.log("Cleaning up single page viewer before HTMX navigation"); console.log("Cleaning up single page viewer before HTMX navigation");
viewer.destroy(); viewer.destroy();
} }
// Clean up scrollspy before navigating
cleanupScrollspy();
}); });
// Also clean up on page unload as fallback // Also clean up on page unload as fallback