mirror of
https://github.com/Theodor-Springmann-Stiftung/kgpz_web.git
synced 2025-10-28 16:45:32 +00:00
Refined orte
This commit is contained in:
361
app/kgpz.go
361
app/kgpz.go
@@ -221,6 +221,178 @@ func (k *KGPZ) Funcs() map[string]interface{} {
|
||||
e["contains"] = func(s, substr string) bool { return strings.Contains(s, substr) }
|
||||
e["lower"] = func(s string) string { return strings.ToLower(s) }
|
||||
|
||||
// Place helper functions
|
||||
e["GetModernCountryName"] = func(geoID string) string {
|
||||
if geoID == "" || k.Geonames == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
geoPlace := k.Geonames.Place(geoID)
|
||||
if geoPlace == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Map country names to German translations
|
||||
switch geoPlace.CountryName {
|
||||
case "France":
|
||||
return "heutiges Frankreich"
|
||||
case "United Kingdom":
|
||||
return "heutiges Großbritannien"
|
||||
case "Russia":
|
||||
return "heutiges Russland"
|
||||
case "Czech Republic", "Czechia":
|
||||
return "heutiges Tschechien"
|
||||
case "Netherlands", "The Netherlands":
|
||||
return "heutige Niederlande"
|
||||
case "Poland":
|
||||
return "heutiges Polen"
|
||||
case "Switzerland":
|
||||
return "heutige Schweiz"
|
||||
case "Latvia":
|
||||
return "heutiges Lettland"
|
||||
case "Sweden":
|
||||
return "heutiges Schweden"
|
||||
case "Austria":
|
||||
return "heutiges Österreich"
|
||||
case "Belgium":
|
||||
return "heutiges Belgien"
|
||||
case "Slovakia":
|
||||
return "heutige Slowakei"
|
||||
case "Finland":
|
||||
return "heutiges Finnland"
|
||||
case "Denmark":
|
||||
return "heutiges Dänemark"
|
||||
default:
|
||||
// Return original country name for unknown countries (excluding Germany)
|
||||
if geoPlace.CountryName != "Germany" && geoPlace.CountryName != "" {
|
||||
return geoPlace.CountryName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
e["GetFullPlaceInfo"] = func(geoID string, originalName string) string {
|
||||
if geoID == "" || k.Geonames == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
geoPlace := k.Geonames.Place(geoID)
|
||||
if geoPlace == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Only show info for places outside Germany
|
||||
if geoPlace.CountryName == "Germany" || geoPlace.CountryName == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Get the modern country name
|
||||
countryName := ""
|
||||
switch geoPlace.CountryName {
|
||||
case "France":
|
||||
countryName = "heutiges Frankreich"
|
||||
case "United Kingdom":
|
||||
countryName = "heutiges Großbritannien"
|
||||
case "Russia":
|
||||
countryName = "heutiges Russland"
|
||||
case "Czech Republic", "Czechia":
|
||||
countryName = "heutiges Tschechien"
|
||||
case "Netherlands", "The Netherlands":
|
||||
countryName = "heutige Niederlande"
|
||||
case "Poland":
|
||||
countryName = "heutiges Polen"
|
||||
case "Switzerland":
|
||||
countryName = "heutige Schweiz"
|
||||
case "Latvia":
|
||||
countryName = "heutiges Lettland"
|
||||
case "Sweden":
|
||||
countryName = "heutiges Schweden"
|
||||
case "Austria":
|
||||
countryName = "heutiges Österreich"
|
||||
case "Belgium":
|
||||
countryName = "heutiges Belgien"
|
||||
case "Slovakia":
|
||||
countryName = "heutige Slowakei"
|
||||
case "Finland":
|
||||
countryName = "heutiges Finnland"
|
||||
case "Denmark":
|
||||
countryName = "heutiges Dänemark"
|
||||
default:
|
||||
countryName = geoPlace.CountryName
|
||||
}
|
||||
|
||||
// Extract German alternate name (same logic as GetModernPlaceName)
|
||||
modernName := ""
|
||||
hasGermanName := false
|
||||
|
||||
for _, altName := range geoPlace.AlternateNames {
|
||||
if altName.Lang == "de" {
|
||||
hasGermanName = true
|
||||
if altName.IsPreferredName {
|
||||
modernName = altName.Name
|
||||
break
|
||||
} else if modernName == "" {
|
||||
modernName = altName.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !hasGermanName {
|
||||
modernName = geoPlace.ToponymName
|
||||
}
|
||||
|
||||
// Combine country and modern place name
|
||||
result := countryName
|
||||
if modernName != "" && strings.ToLower(modernName) != strings.ToLower(originalName) {
|
||||
result += ", " + modernName
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
e["GetModernPlaceName"] = func(geoID string, originalName string) string {
|
||||
if geoID == "" || k.Geonames == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
geoPlace := k.Geonames.Place(geoID)
|
||||
if geoPlace == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Only show modern names for places outside Germany
|
||||
if geoPlace.CountryName == "Germany" || geoPlace.CountryName == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Extract German alternate name
|
||||
modernName := ""
|
||||
hasGermanName := false
|
||||
|
||||
for _, altName := range geoPlace.AlternateNames {
|
||||
if altName.Lang == "de" {
|
||||
hasGermanName = true
|
||||
if altName.IsPreferredName {
|
||||
modernName = altName.Name
|
||||
break
|
||||
} else if modernName == "" {
|
||||
modernName = altName.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !hasGermanName {
|
||||
modernName = geoPlace.ToponymName
|
||||
}
|
||||
|
||||
// Only return if it's different from the original name
|
||||
if modernName != "" && strings.ToLower(modernName) != strings.ToLower(originalName) {
|
||||
return modernName
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
e["LookupPieces"] = k.Library.Pieces.ReverseLookup
|
||||
e["LookupWorks"] = k.Library.Works.ReverseLookup
|
||||
e["LookupIssues"] = k.Library.Issues.ReverseLookup
|
||||
@@ -263,6 +435,126 @@ func (k *KGPZ) Enrich() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnrichAndRebuildIndex ensures enrichment completes before rebuilding search index
|
||||
func (k *KGPZ) EnrichAndRebuildIndex() error {
|
||||
if k.Library == nil || k.Library.Agents == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
go func() {
|
||||
k.fsmu.Lock()
|
||||
defer k.fsmu.Unlock()
|
||||
|
||||
logging.Info("Starting enrichment process...")
|
||||
|
||||
// Fetch GND data for agents
|
||||
data := xmlmodels.AgentsIntoDataset(k.Library.Agents)
|
||||
k.GND.FetchPersons(data)
|
||||
k.GND.WriteCache(filepath.Join(k.Config.BaseDIR, k.Config.GNDPath))
|
||||
|
||||
// Fetch Geonames data for places
|
||||
if k.Library.Places != nil {
|
||||
placeData := xmlmodels.PlacesIntoDataset(k.Library.Places)
|
||||
k.Geonames.FetchPlaces(placeData)
|
||||
k.Geonames.WriteCache(filepath.Join(k.Config.BaseDIR, k.Config.GeoPath))
|
||||
}
|
||||
|
||||
logging.Info("Enrichment complete. Starting search index rebuild...")
|
||||
|
||||
// Clear existing indices before rebuilding
|
||||
k.ClearSearchIndices()
|
||||
|
||||
// Rebuild search index after enrichment is complete
|
||||
k.buildSearchIndexSync()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearSearchIndices removes all existing search indices
|
||||
func (k *KGPZ) ClearSearchIndices() error {
|
||||
if k.Search == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return k.Search.ClearAllIndices()
|
||||
}
|
||||
|
||||
// buildSearchIndexSync builds the search index synchronously (no goroutine)
|
||||
func (k *KGPZ) buildSearchIndexSync() error {
|
||||
if k.Library == nil || k.Library.Agents == nil || k.Search == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(6)
|
||||
|
||||
go func() {
|
||||
for _, agent := range k.Library.Agents.Array {
|
||||
err := k.Search.Index(agent, k.Library)
|
||||
if err != nil {
|
||||
logging.Error(err, "Error indexing agent")
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for _, place := range k.Library.Places.Array {
|
||||
err := k.Search.Index(place, k.Library)
|
||||
if err != nil {
|
||||
logging.Error(err, "Error indexing place")
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for _, cat := range k.Library.Categories.Array {
|
||||
err := k.Search.Index(cat, k.Library)
|
||||
if err != nil {
|
||||
logging.Error(err, "Error indexing category")
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for _, work := range k.Library.Works.Array {
|
||||
err := k.Search.Index(work, k.Library)
|
||||
if err != nil {
|
||||
logging.Error(err, "Error indexing work")
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for _, issue := range k.Library.Issues.Array {
|
||||
err := k.Search.Index(issue, k.Library)
|
||||
if err != nil {
|
||||
logging.Error(err, "Error indexing issue")
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for _, piece := range k.Library.Pieces.Array {
|
||||
err := k.Search.Index(piece, k.Library)
|
||||
if err != nil {
|
||||
logging.Error(err, "Error indexing piece")
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
logging.Info("Search index built.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *KGPZ) BuildSearchIndex() error {
|
||||
if k.Library == nil || k.Library.Agents == nil || k.Search == nil {
|
||||
return nil
|
||||
@@ -271,71 +563,7 @@ func (k *KGPZ) BuildSearchIndex() error {
|
||||
go func() {
|
||||
k.fsmu.Lock()
|
||||
defer k.fsmu.Unlock()
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(6)
|
||||
go func() {
|
||||
for _, agent := range k.Library.Agents.Array {
|
||||
err := k.Search.Index(agent, k.Library)
|
||||
if err != nil {
|
||||
logging.Error(err, "Error indexing agent")
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for _, place := range k.Library.Places.Array {
|
||||
err := k.Search.Index(place, k.Library)
|
||||
if err != nil {
|
||||
logging.Error(err, "Error indexing place")
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for _, cat := range k.Library.Categories.Array {
|
||||
err := k.Search.Index(cat, k.Library)
|
||||
if err != nil {
|
||||
logging.Error(err, "Error indexing category")
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for _, work := range k.Library.Works.Array {
|
||||
err := k.Search.Index(work, k.Library)
|
||||
if err != nil {
|
||||
logging.Error(err, "Error indexing work")
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for _, issue := range k.Library.Issues.Array {
|
||||
err := k.Search.Index(issue, k.Library)
|
||||
if err != nil {
|
||||
logging.Error(err, "Error indexing issue")
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for _, piece := range k.Library.Pieces.Array {
|
||||
err := k.Search.Index(piece, k.Library)
|
||||
if err != nil {
|
||||
logging.Error(err, "Error indexing piece")
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
logging.Info("Search index built.")
|
||||
|
||||
k.buildSearchIndexSync()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
@@ -380,8 +608,7 @@ func (k *KGPZ) Pull() {
|
||||
if changed {
|
||||
logging.ObjDebug(&k.Repo, "Remote changed. Reparsing")
|
||||
k.Serialize()
|
||||
k.Enrich()
|
||||
k.BuildSearchIndex()
|
||||
k.EnrichAndRebuildIndex()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -127,6 +127,7 @@ func GetQuickFilter(kgpz *xmlmodels.Library) fiber.Handler {
|
||||
placeSummary := PlaceSummary{
|
||||
ID: place.ID,
|
||||
Name: name,
|
||||
Geo: place.Geo,
|
||||
}
|
||||
|
||||
places = append(places, placeSummary)
|
||||
@@ -164,6 +165,7 @@ type PersonSummary struct {
|
||||
type PlaceSummary struct {
|
||||
ID string
|
||||
Name string
|
||||
Geo string
|
||||
}
|
||||
|
||||
// IssueSummary represents an issue for the Jahr/Ausgabe filter
|
||||
|
||||
@@ -33,8 +33,17 @@ func GetPlace(kgpz *xmlmodels.Library) fiber.Handler {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
}
|
||||
|
||||
return c.Render("/ort/", fiber.Map{
|
||||
"model": places,
|
||||
})
|
||||
// Render different templates based on whether we're showing list or detail view
|
||||
if places.SelectedPlace != nil {
|
||||
// Individual place detail view
|
||||
return c.Render("/ort/detail/", fiber.Map{
|
||||
"model": places,
|
||||
})
|
||||
} else {
|
||||
// Places overview/list view
|
||||
return c.Render("/ort/overview/", fiber.Map{
|
||||
"model": places,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package searchprovider
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
@@ -161,3 +162,29 @@ func default_mapping() (*mapping.IndexMappingImpl, error) {
|
||||
indexMapping.DefaultAnalyzer = "customNgramAnalyzer"
|
||||
return indexMapping, nil
|
||||
}
|
||||
|
||||
// ClearAllIndices closes and removes all search indices
|
||||
func (sp *SearchProvider) ClearAllIndices() error {
|
||||
// Close all open indices
|
||||
sp.indeces.Range(func(key, value interface{}) bool {
|
||||
if index, ok := value.(bleve.Index); ok {
|
||||
index.Close()
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// Clear the sync.Map
|
||||
sp.indeces = sync.Map{}
|
||||
|
||||
// Remove all .bleve directories from disk
|
||||
files, err := filepath.Glob(filepath.Join(sp.basepath, "*.bleve"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
os.RemoveAll(file)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -497,7 +497,7 @@ func (e *Engine) Render(out io.Writer, path string, data interface{}, layout ...
|
||||
l = lay
|
||||
} else {
|
||||
if layout[0] == "clear" {
|
||||
lay, err := template.New("clear").Parse(CLEAR_LAYOUT)
|
||||
lay, err := template.New("clear").Funcs(e.FuncMap).Parse(CLEAR_LAYOUT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -433,8 +433,8 @@ class O extends HTMLElement {
|
||||
if (!o && !r) {
|
||||
const c = n.querySelector("div:first-child");
|
||||
if (c) {
|
||||
const d = c.getBoundingClientRect(), u = d.top >= 0, g = d.bottom <= window.innerHeight;
|
||||
u && g && (l = !0);
|
||||
const d = c.getBoundingClientRect(), u = d.top >= 0, h = d.bottom <= window.innerHeight;
|
||||
u && h && (l = !0);
|
||||
}
|
||||
}
|
||||
l && e.push(s);
|
||||
@@ -451,8 +451,8 @@ class O extends HTMLElement {
|
||||
const n = document.getElementById("scrollspy-nav"), s = n.getBoundingClientRect();
|
||||
let o = 1 / 0, r = -1 / 0;
|
||||
t.forEach((c) => {
|
||||
const d = c.getBoundingClientRect(), u = d.top - s.top + n.scrollTop, g = u + d.height;
|
||||
o = Math.min(o, u), r = Math.max(r, g);
|
||||
const d = c.getBoundingClientRect(), u = d.top - s.top + n.scrollTop, h = u + d.height;
|
||||
o = Math.min(o, u), r = Math.max(r, h);
|
||||
});
|
||||
let l = r - o;
|
||||
i.style.top = `${o}px`, i.style.height = `${l}px`, i.style.opacity = "1", setTimeout(() => this.ensureMarkerVisibility(), 100);
|
||||
@@ -490,8 +490,8 @@ class O extends HTMLElement {
|
||||
document.documentElement.offsetHeight
|
||||
), s = window.innerHeight, o = n - s, r = o > 0 ? window.scrollY / o : 0, l = t.clientHeight, d = t.scrollHeight - l;
|
||||
if (d > 0) {
|
||||
const u = r * d, g = i.getBoundingClientRect(), p = t.getBoundingClientRect(), f = g.top - p.top + t.scrollTop, m = l / 2, q = f - m, v = 0.7, I = v * u + (1 - v) * q, y = Math.max(0, Math.min(d, I)), T = t.scrollTop;
|
||||
Math.abs(y - T) > 10 && t.scrollTo({
|
||||
const u = r * d, h = i.getBoundingClientRect(), p = t.getBoundingClientRect(), f = h.top - p.top + t.scrollTop, m = l / 2, I = f - m, v = 0.7, T = v * u + (1 - v) * I, y = Math.max(0, Math.min(d, T)), q = t.scrollTop;
|
||||
Math.abs(y - q) > 10 && t.scrollTo({
|
||||
top: y,
|
||||
behavior: "smooth"
|
||||
});
|
||||
@@ -512,6 +512,59 @@ class O extends HTMLElement {
|
||||
}
|
||||
}
|
||||
customElements.define("akteure-scrollspy", O);
|
||||
class $ extends HTMLElement {
|
||||
constructor() {
|
||||
super(), this.searchInput = null, this.placeCards = [], this.countElement = null, this.debounceTimer = null, this.originalCount = 0;
|
||||
}
|
||||
connectedCallback() {
|
||||
this.render(), this.setupEventListeners(), this.initializePlaceCards();
|
||||
}
|
||||
disconnectedCallback() {
|
||||
this.cleanupEventListeners(), this.debounceTimer && clearTimeout(this.debounceTimer);
|
||||
}
|
||||
render() {
|
||||
this.innerHTML = `
|
||||
<div class="mb-6">
|
||||
<input
|
||||
type="text"
|
||||
id="places-search"
|
||||
placeholder="Ortsnamen eingeben..."
|
||||
autocomplete="off"
|
||||
class="w-full px-3 py-2 border border-slate-300 rounded-md text-sm bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 focus:border-blue-400"
|
||||
>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
setupEventListeners() {
|
||||
this.searchInput = this.querySelector("#places-search"), this.searchInput && this.searchInput.addEventListener("input", this.handleSearchInput.bind(this));
|
||||
}
|
||||
cleanupEventListeners() {
|
||||
this.searchInput && this.searchInput.removeEventListener("input", this.handleSearchInput.bind(this));
|
||||
}
|
||||
initializePlaceCards() {
|
||||
const e = this.closest(".bg-white") || document;
|
||||
this.placeCards = Array.from(e.querySelectorAll("[data-place-name]")), this.countElement = e.querySelector("[data-places-count]"), this.countElement && (this.originalCount = this.placeCards.length);
|
||||
}
|
||||
handleSearchInput(e) {
|
||||
this.debounceTimer && clearTimeout(this.debounceTimer), this.debounceTimer = setTimeout(() => {
|
||||
this.filterPlaces(e.target.value.trim());
|
||||
}, 150);
|
||||
}
|
||||
filterPlaces(e) {
|
||||
if (!this.placeCards.length) return;
|
||||
const t = e.toLowerCase();
|
||||
let i = 0;
|
||||
this.placeCards.forEach((n) => {
|
||||
var l, c;
|
||||
const s = ((l = n.getAttribute("data-place-name")) == null ? void 0 : l.toLowerCase()) || "", o = ((c = n.getAttribute("data-modern-name")) == null ? void 0 : c.toLowerCase()) || "";
|
||||
e === "" || s.includes(t) || o.includes(t) ? (n.style.display = "", i++) : n.style.display = "none";
|
||||
}), this.updateCountDisplay(i, e);
|
||||
}
|
||||
updateCountDisplay(e, t) {
|
||||
this.countElement && (t === "" ? this.countElement.textContent = `Alle Orte (${this.originalCount})` : e === 0 ? this.countElement.textContent = `Keine Orte gefunden für "${t}"` : this.countElement.textContent = `${e} von ${this.originalCount} Orten`);
|
||||
}
|
||||
}
|
||||
customElements.define("places-filter", $);
|
||||
class V extends HTMLElement {
|
||||
constructor() {
|
||||
super(), this.resizeObserver = null;
|
||||
@@ -641,14 +694,14 @@ class V extends HTMLElement {
|
||||
show(e, t, i, n = !1, s = 0, o = null, r = null, l = null) {
|
||||
const c = this.querySelector("#single-page-image"), d = this.querySelector("#page-number"), u = this.querySelector("#page-icon");
|
||||
this.querySelector("#page-indicator"), c.src = e, c.alt = t, this.currentPageNumber = i, this.currentIsBeilage = n, this.currentPartNumber = o;
|
||||
let g;
|
||||
let h;
|
||||
if (l)
|
||||
g = l;
|
||||
h = l;
|
||||
else {
|
||||
const f = this.getIssueContext(i);
|
||||
g = f ? `${f}, ${i}` : `${i}`;
|
||||
h = f ? `${f}, ${i}` : `${i}`;
|
||||
}
|
||||
if (d.innerHTML = g, s && i === s) {
|
||||
if (d.innerHTML = h, s && i === s) {
|
||||
d.style.position = "relative";
|
||||
const f = d.querySelector(".target-page-dot");
|
||||
f && f.remove();
|
||||
@@ -843,9 +896,9 @@ class V extends HTMLElement {
|
||||
const d = c.textContent.trim(), u = d.match(/(\d{1,2}\.\d{1,2}\.\d{4}\s+Nr\.\s+\d+)/);
|
||||
if (u)
|
||||
return u[1];
|
||||
const g = d.match(/(\d{4})\s+Nr\.\s+(\d+)/);
|
||||
if (g)
|
||||
return `${g[1]} Nr. ${g[2]}`;
|
||||
const h = d.match(/(\d{4})\s+Nr\.\s+(\d+)/);
|
||||
if (h)
|
||||
return `${h[1]} Nr. ${h[2]}`;
|
||||
}
|
||||
}
|
||||
const l = document.title.match(/(\d{4}).*Nr\.\s*(\d+)/);
|
||||
@@ -874,7 +927,7 @@ window.addEventListener("beforeunload", function() {
|
||||
const a = document.querySelector("single-page-viewer");
|
||||
a && a.close();
|
||||
});
|
||||
class $ extends HTMLElement {
|
||||
class R extends HTMLElement {
|
||||
constructor() {
|
||||
super(), this.isVisible = !1, this.scrollHandler = null, this.htmxAfterSwapHandler = null;
|
||||
}
|
||||
@@ -915,8 +968,8 @@ class $ extends HTMLElement {
|
||||
});
|
||||
}
|
||||
}
|
||||
customElements.define("scroll-to-top-button", $);
|
||||
class R extends HTMLElement {
|
||||
customElements.define("scroll-to-top-button", R);
|
||||
class z extends HTMLElement {
|
||||
constructor() {
|
||||
super(), this.pageObserver = null, this.pageContainers = /* @__PURE__ */ new Map(), this.singlePageViewerActive = !1, this.singlePageViewerCurrentPage = null, this.boundHandleSinglePageViewer = this.handleSinglePageViewer.bind(this);
|
||||
}
|
||||
@@ -1001,7 +1054,7 @@ class R extends HTMLElement {
|
||||
}
|
||||
const o = t.getBoundingClientRect(), r = e.getBoundingClientRect();
|
||||
if (!(r.top >= o.top && r.bottom <= o.bottom)) {
|
||||
const c = t.scrollTop, d = r.top - o.top + c, u = o.height, g = r.height, p = d - (u - g) / 2;
|
||||
const c = t.scrollTop, d = r.top - o.top + c, u = o.height, h = r.height, p = d - (u - h) / 2;
|
||||
t.scrollTo({
|
||||
top: Math.max(0, p),
|
||||
behavior: "smooth"
|
||||
@@ -1035,8 +1088,8 @@ class R extends HTMLElement {
|
||||
this.pageObserver && (this.pageObserver.disconnect(), this.pageObserver = null), document.removeEventListener("singlepageviewer:opened", this.boundHandleSinglePageViewer), document.removeEventListener("singlepageviewer:closed", this.boundHandleSinglePageViewer), document.removeEventListener("singlepageviewer:pagechanged", this.boundHandleSinglePageViewer), this.pageContainers.clear();
|
||||
}
|
||||
}
|
||||
customElements.define("inhaltsverzeichnis-scrollspy", R);
|
||||
class z extends HTMLElement {
|
||||
customElements.define("inhaltsverzeichnis-scrollspy", z);
|
||||
class j extends HTMLElement {
|
||||
constructor() {
|
||||
super(), this.innerHTML = `
|
||||
<div id="error-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50 flex items-center justify-center backdrop-blur-sm">
|
||||
@@ -1084,11 +1137,11 @@ class z extends HTMLElement {
|
||||
window.showErrorModal = (e) => this.show(e), window.closeErrorModal = () => this.close();
|
||||
}
|
||||
}
|
||||
customElements.define("error-modal", z);
|
||||
customElements.define("error-modal", j);
|
||||
window.currentPageContainers = window.currentPageContainers || [];
|
||||
window.currentActiveIndex = window.currentActiveIndex || 0;
|
||||
window.pageObserver = window.pageObserver || null;
|
||||
function j(a, e, t, i = null) {
|
||||
function D(a, e, t, i = null) {
|
||||
let n = document.querySelector("single-page-viewer");
|
||||
n || (n = document.createElement("single-page-viewer"), document.body.appendChild(n));
|
||||
const s = a.closest('[data-beilage="true"]') !== null, o = window.templateData && window.templateData.targetPage ? window.templateData.targetPage : 0, r = a.closest(".newspaper-page-container, .piece-page-container");
|
||||
@@ -1097,8 +1150,8 @@ function j(a, e, t, i = null) {
|
||||
l = r.getAttribute("data-page-icon-type"), r.querySelector(".part-number") && (l = "part-number");
|
||||
const u = r.querySelector(".page-indicator");
|
||||
if (u) {
|
||||
const g = u.cloneNode(!0);
|
||||
g.querySelectorAll("i").forEach((m) => m.remove()), g.querySelectorAll('[class*="target-page-dot"], .target-page-indicator').forEach((m) => m.remove()), c = g.textContent.trim();
|
||||
const h = u.cloneNode(!0);
|
||||
h.querySelectorAll("i").forEach((m) => m.remove()), h.querySelectorAll('[class*="target-page-dot"], .target-page-indicator').forEach((m) => m.remove()), c = h.textContent.trim();
|
||||
}
|
||||
}
|
||||
n.show(a.src, a.alt, e, s, o, i, l, c);
|
||||
@@ -1106,7 +1159,7 @@ function j(a, e, t, i = null) {
|
||||
function E() {
|
||||
document.getElementById("pageModal").classList.add("hidden");
|
||||
}
|
||||
function D() {
|
||||
function F() {
|
||||
if (window.pageObserver && (window.pageObserver.disconnect(), window.pageObserver = null), window.currentPageContainers = Array.from(document.querySelectorAll(".newspaper-page-container")), window.currentActiveIndex = 0, b(), document.querySelector(".newspaper-page-container")) {
|
||||
let e = /* @__PURE__ */ new Set();
|
||||
window.pageObserver = new IntersectionObserver(
|
||||
@@ -1127,7 +1180,7 @@ function D() {
|
||||
});
|
||||
}
|
||||
}
|
||||
function F() {
|
||||
function K() {
|
||||
if (window.currentActiveIndex > 0) {
|
||||
let a = -1;
|
||||
const e = [];
|
||||
@@ -1148,7 +1201,7 @@ function F() {
|
||||
}, 100));
|
||||
}
|
||||
}
|
||||
function K() {
|
||||
function Z() {
|
||||
if (window.currentActiveIndex < window.currentPageContainers.length - 1) {
|
||||
let a = -1;
|
||||
const e = [];
|
||||
@@ -1169,8 +1222,8 @@ function K() {
|
||||
}, 100));
|
||||
}
|
||||
}
|
||||
function Z() {
|
||||
if (P()) {
|
||||
function W() {
|
||||
if (C()) {
|
||||
const e = document.querySelector("#newspaper-content .newspaper-page-container");
|
||||
e && e.scrollIntoView({
|
||||
block: "start"
|
||||
@@ -1189,7 +1242,7 @@ function Z() {
|
||||
}
|
||||
}
|
||||
}
|
||||
function P() {
|
||||
function C() {
|
||||
const a = [];
|
||||
window.currentPageContainers.forEach((e, t) => {
|
||||
const i = e.getBoundingClientRect(), n = window.innerHeight, s = Math.max(i.top, 0), o = Math.min(i.bottom, n), r = Math.max(0, o - s), l = i.height;
|
||||
@@ -1205,11 +1258,11 @@ function P() {
|
||||
function b() {
|
||||
const a = document.getElementById("prevPageBtn"), e = document.getElementById("nextPageBtn"), t = document.getElementById("beilageBtn");
|
||||
if (a && (a.style.display = "flex", window.currentActiveIndex <= 0 ? (a.disabled = !0, a.classList.add("opacity-50", "cursor-not-allowed"), a.classList.remove("hover:bg-gray-200")) : (a.disabled = !1, a.classList.remove("opacity-50", "cursor-not-allowed"), a.classList.add("hover:bg-gray-200"))), e && (e.style.display = "flex", window.currentActiveIndex >= window.currentPageContainers.length - 1 ? (e.disabled = !0, e.classList.add("opacity-50", "cursor-not-allowed"), e.classList.remove("hover:bg-gray-200")) : (e.disabled = !1, e.classList.remove("opacity-50", "cursor-not-allowed"), e.classList.add("hover:bg-gray-200"))), t) {
|
||||
const i = P(), n = t.querySelector("i");
|
||||
const i = C(), n = t.querySelector("i");
|
||||
i ? (t.title = "Zur Hauptausgabe", t.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", n && (n.className = "ri-file-text-line text-lg lg:text-xl")) : (t.title = "Zu Beilage", t.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", n && (n.className = "ri-attachment-line text-lg lg:text-xl"));
|
||||
}
|
||||
}
|
||||
function W() {
|
||||
function J() {
|
||||
const a = document.getElementById("shareLinkBtn");
|
||||
let e = "";
|
||||
if (window.currentActiveIndex !== void 0 && window.currentPageContainers && window.currentPageContainers[window.currentActiveIndex]) {
|
||||
@@ -1227,48 +1280,48 @@ function W() {
|
||||
function x(a, e) {
|
||||
if (navigator.clipboard)
|
||||
navigator.clipboard.writeText(a).then(() => {
|
||||
h(e, "Link kopiert!");
|
||||
g(e, "Link kopiert!");
|
||||
}).catch((t) => {
|
||||
h(e, "Kopieren fehlgeschlagen");
|
||||
g(e, "Kopieren fehlgeschlagen");
|
||||
});
|
||||
else {
|
||||
const t = document.createElement("textarea");
|
||||
t.value = a, document.body.appendChild(t), t.select();
|
||||
try {
|
||||
const i = document.execCommand("copy");
|
||||
h(e, i ? "Link kopiert!" : "Kopieren fehlgeschlagen");
|
||||
g(e, i ? "Link kopiert!" : "Kopieren fehlgeschlagen");
|
||||
} catch {
|
||||
h(e, "Kopieren fehlgeschlagen");
|
||||
g(e, "Kopieren fehlgeschlagen");
|
||||
} finally {
|
||||
document.body.removeChild(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
function J() {
|
||||
function Y() {
|
||||
const a = document.getElementById("citationBtn"), e = document.title || "KGPZ";
|
||||
let t = window.location.origin + window.location.pathname;
|
||||
t.includes("#") && (t = t.split("#")[0]);
|
||||
const i = (/* @__PURE__ */ new Date()).toLocaleDateString("de-DE"), n = `Königsberger Gelehrte und Politische Zeitung (KGPZ). ${e}. Digital verfügbar unter: ${t} (Zugriff: ${i}).`;
|
||||
if (navigator.clipboard)
|
||||
navigator.clipboard.writeText(n).then(() => {
|
||||
h(a, "Zitation kopiert!");
|
||||
g(a, "Zitation kopiert!");
|
||||
}).catch((s) => {
|
||||
h(a, "Kopieren fehlgeschlagen");
|
||||
g(a, "Kopieren fehlgeschlagen");
|
||||
});
|
||||
else {
|
||||
const s = document.createElement("textarea");
|
||||
s.value = n, document.body.appendChild(s), s.select();
|
||||
try {
|
||||
const o = document.execCommand("copy");
|
||||
h(a, o ? "Zitation kopiert!" : "Kopieren fehlgeschlagen");
|
||||
g(a, o ? "Zitation kopiert!" : "Kopieren fehlgeschlagen");
|
||||
} catch {
|
||||
h(a, "Kopieren fehlgeschlagen");
|
||||
g(a, "Kopieren fehlgeschlagen");
|
||||
} finally {
|
||||
document.body.removeChild(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
function h(a, e) {
|
||||
function g(a, e) {
|
||||
const t = document.querySelector(".simple-popup");
|
||||
t && t.remove();
|
||||
const i = document.createElement("div");
|
||||
@@ -1297,7 +1350,7 @@ function h(a, e) {
|
||||
}, 200);
|
||||
}, 2e3);
|
||||
}
|
||||
function Y(a, e, t = !1) {
|
||||
function G(a, e, t = !1) {
|
||||
let i = "";
|
||||
if (t)
|
||||
i = window.location.origin + window.location.pathname + `#beilage-1-page-${a}`;
|
||||
@@ -1312,24 +1365,24 @@ function Y(a, e, t = !1) {
|
||||
const n = i;
|
||||
if (navigator.clipboard)
|
||||
navigator.clipboard.writeText(n).then(() => {
|
||||
h(e, "Link kopiert!");
|
||||
g(e, "Link kopiert!");
|
||||
}).catch((s) => {
|
||||
h(e, "Kopieren fehlgeschlagen");
|
||||
g(e, "Kopieren fehlgeschlagen");
|
||||
});
|
||||
else {
|
||||
const s = document.createElement("textarea");
|
||||
s.value = n, document.body.appendChild(s), s.select();
|
||||
try {
|
||||
const o = document.execCommand("copy");
|
||||
h(e, o ? "Link kopiert!" : "Kopieren fehlgeschlagen");
|
||||
g(e, o ? "Link kopiert!" : "Kopieren fehlgeschlagen");
|
||||
} catch {
|
||||
h(e, "Kopieren fehlgeschlagen");
|
||||
g(e, "Kopieren fehlgeschlagen");
|
||||
} finally {
|
||||
document.body.removeChild(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
function G(a, e) {
|
||||
function U(a, e) {
|
||||
const t = document.title || "KGPZ", i = window.location.pathname.split("/");
|
||||
let n;
|
||||
if (i.length >= 3) {
|
||||
@@ -1340,25 +1393,25 @@ function G(a, e) {
|
||||
const s = n, o = (/* @__PURE__ */ new Date()).toLocaleDateString("de-DE"), r = `Königsberger Gelehrte und Politische Zeitung (KGPZ). ${t}, Seite ${a}. Digital verfügbar unter: ${s} (Zugriff: ${o}).`;
|
||||
if (navigator.clipboard)
|
||||
navigator.clipboard.writeText(r).then(() => {
|
||||
h(e, "Zitation kopiert!");
|
||||
g(e, "Zitation kopiert!");
|
||||
}).catch((l) => {
|
||||
h(e, "Kopieren fehlgeschlagen");
|
||||
g(e, "Kopieren fehlgeschlagen");
|
||||
});
|
||||
else {
|
||||
const l = document.createElement("textarea");
|
||||
l.value = r, document.body.appendChild(l), l.select();
|
||||
try {
|
||||
const c = document.execCommand("copy");
|
||||
h(e, c ? "Zitation kopiert!" : "Kopieren fehlgeschlagen");
|
||||
g(e, c ? "Zitation kopiert!" : "Kopieren fehlgeschlagen");
|
||||
} catch {
|
||||
h(e, "Kopieren fehlgeschlagen");
|
||||
g(e, "Kopieren fehlgeschlagen");
|
||||
} finally {
|
||||
document.body.removeChild(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
function L() {
|
||||
D(), window.addEventListener("scroll", function() {
|
||||
F(), window.addEventListener("scroll", function() {
|
||||
clearTimeout(window.scrollTimeout), window.scrollTimeout = setTimeout(() => {
|
||||
b();
|
||||
}, 50);
|
||||
@@ -1366,7 +1419,7 @@ function L() {
|
||||
a.key === "Escape" && E();
|
||||
});
|
||||
}
|
||||
function k() {
|
||||
function P() {
|
||||
const a = window.location.pathname;
|
||||
document.querySelectorAll(".citation-link[data-citation-url]").forEach((t) => {
|
||||
const i = t.getAttribute("data-citation-url");
|
||||
@@ -1383,7 +1436,7 @@ function k() {
|
||||
n ? (t.classList.add("text-red-700", "pointer-events-none"), t.setAttribute("aria-current", "page")) : (t.classList.remove("text-red-700", "pointer-events-none"), t.removeAttribute("aria-current"));
|
||||
});
|
||||
}
|
||||
function C() {
|
||||
function k() {
|
||||
const a = window.location.pathname, e = document.body;
|
||||
e.classList.remove(
|
||||
"page-akteure",
|
||||
@@ -1395,21 +1448,21 @@ function C() {
|
||||
"page-edition"
|
||||
), a.includes("/akteure/") || a.includes("/autoren") ? e.classList.add("page-akteure") : a.match(/\/\d{4}\/\d+/) ? e.classList.add("page-ausgabe") : a.includes("/search") || a.includes("/suche") ? e.classList.add("page-search") : a.includes("/ort/") ? e.classList.add("page-ort") : a.includes("/kategorie/") ? e.classList.add("page-kategorie") : a.includes("/beitrag/") ? e.classList.add("page-piece") : a.includes("/edition") && e.classList.add("page-edition");
|
||||
}
|
||||
window.enlargePage = j;
|
||||
window.enlargePage = D;
|
||||
window.closeModal = E;
|
||||
window.scrollToPreviousPage = F;
|
||||
window.scrollToNextPage = K;
|
||||
window.scrollToBeilage = Z;
|
||||
window.shareCurrentPage = W;
|
||||
window.generateCitation = J;
|
||||
window.copyPagePermalink = Y;
|
||||
window.generatePageCitation = G;
|
||||
C();
|
||||
window.scrollToPreviousPage = K;
|
||||
window.scrollToNextPage = Z;
|
||||
window.scrollToBeilage = W;
|
||||
window.shareCurrentPage = J;
|
||||
window.generateCitation = Y;
|
||||
window.copyPagePermalink = G;
|
||||
window.generatePageCitation = U;
|
||||
k();
|
||||
P();
|
||||
document.querySelector(".newspaper-page-container") && L();
|
||||
let U = function(a) {
|
||||
C(), k(), S(), setTimeout(() => {
|
||||
let X = function(a) {
|
||||
k(), P(), S(), setTimeout(() => {
|
||||
document.querySelector(".newspaper-page-container") && L();
|
||||
}, 50);
|
||||
};
|
||||
document.body.addEventListener("htmx:afterSettle", U);
|
||||
document.body.addEventListener("htmx:afterSettle", X);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -11,7 +11,7 @@
|
||||
<!-- Year Selection -->
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<label for="year-select" class="text-sm text-slate-600 w-12 hidden">Jahr wählen...</label>
|
||||
<select id="year-select" class="tabular-nums flex-1 px-2 py-1 border border-slate-300 rounded text-sm bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 focus:border-blue-400" style="max-height: 200px; overflow-y: auto;">
|
||||
<select id="year-select" autocomplete="off" class="tabular-nums flex-1 px-2 py-1 border border-slate-300 rounded text-sm bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 focus:border-blue-400" style="max-height: 200px; overflow-y: auto;">
|
||||
<option value="">Jahr wählen</option>
|
||||
{{ range $year := .AvailableYears }}
|
||||
<option value="{{ $year }}">{{ $year }}</option>
|
||||
@@ -21,10 +21,10 @@
|
||||
|
||||
<!-- Ausgabe Selection - Two Selects -->
|
||||
<div class="flex items-center gap-2">
|
||||
<select id="issue-number-select" disabled class="flex-1 px-2 py-1 border border-slate-300 rounded text-sm bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 focus:border-blue-400 disabled:bg-slate-100 disabled:cursor-not-allowed">
|
||||
<select id="issue-number-select" disabled autocomplete="off" class="flex-1 px-2 py-1 border border-slate-300 rounded text-sm bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 focus:border-blue-400 disabled:bg-slate-100 disabled:cursor-not-allowed">
|
||||
<option value="">Nr.</option>
|
||||
</select>
|
||||
<select id="issue-date-select" disabled class="flex-1 px-2 py-1 border border-slate-300 rounded text-sm bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 focus:border-blue-400 disabled:bg-slate-100 disabled:cursor-not-allowed">
|
||||
<select id="issue-date-select" disabled autocomplete="off" class="flex-1 px-2 py-1 border border-slate-300 rounded text-sm bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 focus:border-blue-400 disabled:bg-slate-100 disabled:cursor-not-allowed">
|
||||
<option value="">Datum</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -39,7 +39,7 @@
|
||||
<!-- Page Input -->
|
||||
<div class="flex items-center gap-2">
|
||||
<label for="page-input" class="text-sm text-slate-600 w-12 hidden"> oder Seite eingeben...</label>
|
||||
<input type="number" id="page-input" min="1" placeholder="Seite eingeben" disabled class="flex-1 px-2 py-1 border border-slate-300 rounded text-sm bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 focus:border-blue-400 disabled:bg-slate-100 disabled:cursor-not-allowed disabled:text-slate-500">
|
||||
<input type="number" id="page-input" min="1" placeholder="Seite eingeben" disabled autocomplete="off" class="flex-1 px-2 py-1 border border-slate-300 rounded text-sm bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 focus:border-blue-400 disabled:bg-slate-100 disabled:cursor-not-allowed disabled:text-slate-500">
|
||||
</div>
|
||||
|
||||
<!-- Page Jump Button -->
|
||||
@@ -75,6 +75,7 @@
|
||||
type="text"
|
||||
id="person-search"
|
||||
placeholder="Name oder Lebensdaten eingeben..."
|
||||
autocomplete="off"
|
||||
class="flex-1 px-2 py-1 border border-slate-300 rounded text-sm bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 focus:border-blue-400"
|
||||
>
|
||||
</div>
|
||||
@@ -143,6 +144,7 @@
|
||||
type="text"
|
||||
id="place-search"
|
||||
placeholder="Ortsname eingeben..."
|
||||
autocomplete="off"
|
||||
class="flex-1 px-2 py-1 border border-slate-300 rounded text-sm bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 focus:border-blue-400"
|
||||
>
|
||||
</div>
|
||||
@@ -154,6 +156,10 @@
|
||||
<div class="place-item odd:bg-slate-50 even:bg-white">
|
||||
<a href="/ort/{{ $place.ID }}" class="block px-2 py-1 hover:bg-blue-50 border-b border-slate-100 last:border-b-0">
|
||||
<span class="place-name font-medium text-slate-800">{{ $place.Name }}</span>
|
||||
{{ $modernName := GetModernPlaceName $place.Geo $place.Name }}
|
||||
{{ if ne $modernName "" }}
|
||||
<span class="text-xs text-slate-400 ml-2">{{ $modernName }}</span>
|
||||
{{ end }}
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
@@ -1,722 +0,0 @@
|
||||
{{ if .model.SelectedPlace }}
|
||||
<!-- Single Place Detail View -->
|
||||
<div class="max-w-7xl mx-auto px-8 py-8">
|
||||
<div class="bg-white px-6 py-6 rounded w-full">
|
||||
<!-- Back Navigation -->
|
||||
<div class="mb-6">
|
||||
<a href="/ort/" class="inline-flex items-center hover:text-black text-gray-600 transition-colors text-xl no-underline font-bold">
|
||||
<i class="ri-arrow-left-line mr-1 text-xl font-bold"></i>
|
||||
Orte
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Place Header -->
|
||||
<div class="mb-8">
|
||||
{{ $geonames := GetGeonames .model.SelectedPlace.Place.Geo }}
|
||||
|
||||
<!-- Name and external links - similar to akteure header -->
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="flex-1">
|
||||
<h1 class="text-3xl font-bold text-slate-800 mb-2">
|
||||
{{ if .model.SelectedPlace.Place.Names }}
|
||||
{{ index .model.SelectedPlace.Place.Names 0 }}
|
||||
{{ else }}
|
||||
{{ .model.SelectedPlace.Place.ID }}
|
||||
{{ end }}
|
||||
</h1>
|
||||
|
||||
<!-- Geographic Information from Geonames -->
|
||||
{{ if ne $geonames nil }}
|
||||
<div class="text-lg text-slate-700 mb-2">
|
||||
<!-- Modern Country Info (only if not Germany) -->
|
||||
{{ if and (ne $geonames.CountryName "") (ne $geonames.CountryName "Germany") }}
|
||||
<div class="mb-1">
|
||||
{{ $mainPlaceName := "" }}
|
||||
{{ if .model.SelectedPlace.Place.Names }}
|
||||
{{ $mainPlaceName = index .model.SelectedPlace.Place.Names 0 }}
|
||||
{{ end }}
|
||||
{{ if eq $geonames.CountryName "France" }}
|
||||
heutiges Frankreich{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "United Kingdom" }}
|
||||
heutiges Großbritannien{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Russia" }}
|
||||
heutiges Russland{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if or (eq $geonames.CountryName "Czech Republic") (eq $geonames.CountryName "Czechia") }}
|
||||
heutiges Tschechien{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if or (eq $geonames.CountryName "Netherlands") (eq $geonames.CountryName "The Netherlands") }}
|
||||
heutige Niederlande{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Poland" }}
|
||||
heutiges Polen{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Switzerland" }}
|
||||
heutige Schweiz{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Latvia" }}
|
||||
heutiges Lettland{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Sweden" }}
|
||||
heutiges Schweden{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Austria" }}
|
||||
heutiges Österreich{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Belgium" }}
|
||||
heutiges Belgien{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Slovakia" }}
|
||||
heutige Slowakei{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Finland" }}
|
||||
heutiges Finnland{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Denmark" }}
|
||||
heutiges Dänemark{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else }}
|
||||
{{ $geonames.CountryName }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<!-- Coordinates -->
|
||||
<div class="text-slate-600 text-base space-y-1">
|
||||
{{ if and (ne $geonames.Lat "") (ne $geonames.Lng "") }}
|
||||
<div>
|
||||
<i class="ri-map-pin-line mr-1"></i><a href="https://www.openstreetmap.org/?mlat={{ $geonames.Lat }}&mlon={{ $geonames.Lng }}&zoom=12" target="_blank" rel="noopener noreferrer" class="text-blue-600 hover:text-blue-700 underline">{{ $geonames.Lat }}, {{ $geonames.Lng }}</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
<!-- Fallback when no Geonames data -->
|
||||
{{ if .model.SelectedPlace.Place.Geo }}
|
||||
<p class="text-slate-600 mb-2">
|
||||
<i class="ri-map-pin-line mr-1"></i>
|
||||
<a href="{{ .model.SelectedPlace.Place.Geo }}" target="_blank" rel="noopener noreferrer" class="text-blue-600 hover:text-blue-700 underline">
|
||||
Geonames
|
||||
</a>
|
||||
</p>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<!-- External link symbols on the right - similar to akteure -->
|
||||
<div class="flex gap-3 flex-shrink-0 items-center">
|
||||
{{ if ne $geonames nil }}
|
||||
<!-- Wikipedia link if available -->
|
||||
{{ if ne $geonames.WikipediaURL "" }}
|
||||
<a href="https://{{ $geonames.WikipediaURL }}" target="_blank" class="hover:opacity-80 transition-opacity" title="Wikipedia">
|
||||
<img src="/assets/wikipedia.png" alt="Wikipedia" class="w-6 h-6">
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
<!-- Geonames link -->
|
||||
{{ if .model.SelectedPlace.Place.Geo }}
|
||||
<a href="{{ .model.SelectedPlace.Place.Geo }}" target="_blank" class="hover:opacity-80 transition-opacity no-underline" title="Geonames">
|
||||
<i class="ri-global-line text-xl text-blue-600"></i>
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Associated Pieces -->
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-slate-800 mb-4">
|
||||
<i class="ri-newspaper-line mr-2"></i><u class="decoration underline-offset-3">Verlinkte Beiträge</u> ({{ len .model.SelectedPlace.Pieces }})
|
||||
</h2>
|
||||
|
||||
{{ if .model.SelectedPlace.Pieces }}
|
||||
<div class="space-y-2">
|
||||
{{- /* Group pieces by their own title/incipit */ -}}
|
||||
{{- $groupedPieces := dict -}}
|
||||
{{- range $_, $p := .model.SelectedPlace.Pieces -}}
|
||||
{{- $groupKey := "" -}}
|
||||
{{- if $p.Title -}}
|
||||
{{- $groupKey = index $p.Title 0 -}}
|
||||
{{- else if $p.Incipit -}}
|
||||
{{- $groupKey = index $p.Incipit 0 -}}
|
||||
{{- else -}}
|
||||
{{- $groupKey = printf "untitled-%s" $p.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-1 hyphens-auto">
|
||||
{{- /* Display grouped pieces */ -}}
|
||||
{{- range $groupKey, $groupedItems := $groupedPieces -}}
|
||||
<div class="break-inside-avoid pl-4">
|
||||
<div class="pb-1 indent-4">
|
||||
{{- /* Use first piece for display text with colon format for places */ -}}
|
||||
{{ template "_piece_summary_for_place" (dict "Piece" (index $groupedItems 0) "CurrentActorID" "") }}
|
||||
|
||||
{{- /* Show all citations from all pieces in this group inline with commas */ -}}
|
||||
{{ " " }}{{- range $groupIndex, $groupItem := $groupedItems -}}
|
||||
{{- range $issueIndex, $issue := $groupItem.IssueRefs -}}
|
||||
{{- if or (gt $groupIndex 0) (gt $issueIndex 0) }}, {{ end -}}
|
||||
<span class="text-blue-600 hover:text-blue-700 underline decoration-dotted hover:decoration-solid [&>a]:text-blue-600 [&>a:hover]:text-blue-700">{{- template "_citation" $issue -}}</span>{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- /* Add "Ganzer Beitrag" link if piece spans multiple issues */ -}}
|
||||
{{- $firstGroupItem := index $groupedItems 0 -}}
|
||||
{{- if gt (len $firstGroupItem.IssueRefs) 1 -}}
|
||||
{{ " " }}<div class="inline-flex items-center gap-1 px-2 py-1 bg-blue-50
|
||||
hover:bg-blue-100 text-blue-700 hover:text-blue-800 border border-blue-200
|
||||
hover:border-blue-300 rounded text-xs font-medium transition-colors duration-200
|
||||
indent-0">
|
||||
<i class="ri-file-copy-2-line text-xs"></i>
|
||||
<a href="{{ GetPieceURL $firstGroupItem.ID }}" class="">
|
||||
Ganzer Beitrag
|
||||
</a>
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
<p class="text-slate-500 italic">Keine verlinkten Beiträge für diesen Ort gefunden.</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ else }}
|
||||
<!-- Places Overview -->
|
||||
<div class="max-w-7xl mx-auto px-8 py-8">
|
||||
<div class="bg-white px-6 py-6 rounded w-full">
|
||||
<h1 class="text-3xl font-bold text-slate-800 mb-8">Orte</h1>
|
||||
|
||||
<!-- Available Letters Navigation -->
|
||||
|
||||
<!-- Places List -->
|
||||
{{ if .model.Places }}
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-slate-700 mb-4">
|
||||
Alle Orte ({{ len .model.Places }})
|
||||
</h2>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{{ range $placeID := .model.Sorted }}
|
||||
{{ $place := index $.model.Places $placeID }}
|
||||
{{ $geonames := GetGeonames $place.Geo }}
|
||||
<div class="border border-slate-200 rounded-lg hover:bg-slate-50 transition-colors h-24">
|
||||
<a href="/ort/{{ $place.ID }}" class="block p-4 h-full flex flex-col justify-between">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<div class="flex-1">
|
||||
<h3 class="font-medium text-slate-800 mb-1">
|
||||
{{ if $place.Names }}
|
||||
{{ index $place.Names 0 }}
|
||||
{{ else }}
|
||||
{{ $place.ID }}
|
||||
{{ end }}
|
||||
</h3>
|
||||
<!-- Show geographic info if available (only if not Germany) -->
|
||||
{{ if ne $geonames nil }}
|
||||
{{ if and (ne $geonames.CountryName "") (ne $geonames.CountryName "Germany") }}
|
||||
<p class="text-sm text-slate-600 mb-1">
|
||||
<i class="ri-map-pin-line mr-1"></i>
|
||||
{{ $mainPlaceName := "" }}
|
||||
{{ if $place.Names }}
|
||||
{{ $mainPlaceName = index $place.Names 0 }}
|
||||
{{ end }}
|
||||
{{ if eq $geonames.CountryName "France" }}
|
||||
heutiges Frankreich{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "United Kingdom" }}
|
||||
heutiges Großbritannien{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Russia" }}
|
||||
heutiges Russland{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if or (eq $geonames.CountryName "Czech Republic") (eq $geonames.CountryName "Czechia") }}
|
||||
heutiges Tschechien{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if or (eq $geonames.CountryName "Netherlands") (eq $geonames.CountryName "The Netherlands") }}
|
||||
heutige Niederlande{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Poland" }}
|
||||
heutiges Polen{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Switzerland" }}
|
||||
heutige Schweiz{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Latvia" }}
|
||||
heutiges Lettland{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Sweden" }}
|
||||
heutiges Schweden{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Austria" }}
|
||||
heutiges Österreich{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Belgium" }}
|
||||
heutiges Belgien{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Slovakia" }}
|
||||
heutige Slowakei{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Finland" }}
|
||||
heutiges Finnland{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else if eq $geonames.CountryName "Denmark" }}
|
||||
heutiges Dänemark{{- $modernName := "" -}}
|
||||
{{- $hasGermanName := false -}}
|
||||
{{- range $altName := $geonames.AlternateNames -}}
|
||||
{{- if eq $altName.Lang "de" -}}
|
||||
{{- $hasGermanName = true -}}
|
||||
{{- if $altName.IsPreferredName -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- break -}}
|
||||
{{- else if eq $modernName "" -}}
|
||||
{{- $modernName = $altName.Name -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasGermanName -}}
|
||||
{{- $modernName = $geonames.ToponymName -}}
|
||||
{{- end -}}
|
||||
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}
|
||||
{{ else }}
|
||||
{{ $geonames.CountryName }}
|
||||
{{ end }}
|
||||
</p>
|
||||
{{ end }}
|
||||
{{ else if $place.Geo }}
|
||||
<p class="text-sm text-slate-600">
|
||||
<i class="ri-map-pin-line mr-1"></i>Geonames verfügbar
|
||||
</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
<p class="text-slate-500 italic">Keine Orte gefunden.</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
8
views/routes/ort/components/_back_navigation.gohtml
Normal file
8
views/routes/ort/components/_back_navigation.gohtml
Normal file
@@ -0,0 +1,8 @@
|
||||
{{- /* Back navigation for individual place pages */ -}}
|
||||
<!-- Back Navigation -->
|
||||
<div class="mb-6">
|
||||
<a href="/ort/" class="inline-flex items-center hover:text-black text-gray-600 transition-colors text-xl no-underline font-bold">
|
||||
<i class="ri-arrow-left-line mr-1 text-xl font-bold"></i>
|
||||
Orte
|
||||
</a>
|
||||
</div>
|
||||
42
views/routes/ort/components/_place_card.gohtml
Normal file
42
views/routes/ort/components/_place_card.gohtml
Normal file
@@ -0,0 +1,42 @@
|
||||
{{- /* Individual place card for overview grid */ -}}
|
||||
{{ $geonames := GetGeonames .Geo }}
|
||||
{{ $mainPlaceName := "" }}
|
||||
{{ if .Names }}
|
||||
{{ $mainPlaceName = index .Names 0 }}
|
||||
{{ else }}
|
||||
{{ $mainPlaceName = .ID }}
|
||||
{{ end }}
|
||||
{{ $modernName := GetModernPlaceName .Geo $mainPlaceName }}
|
||||
<div class="border border-slate-200 rounded-lg hover:bg-slate-50 transition-colors h-20" data-place-name="{{ $mainPlaceName }}" data-modern-name="{{ $modernName }}">
|
||||
<a href="/ort/{{ .ID }}" class="block p-4 h-full flex flex-col justify-between">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<div class="flex-1">
|
||||
<h3 class="font-medium text-slate-800 mb-1">
|
||||
{{ if .Names }}
|
||||
{{ index .Names 0 }}
|
||||
{{ else }}
|
||||
{{ .ID }}
|
||||
{{ end }}
|
||||
</h3>
|
||||
<!-- Show geographic info if available (only if not Germany) -->
|
||||
{{ if ne $geonames nil }}
|
||||
{{ $mainPlaceName := "" }}
|
||||
{{ if .Names }}
|
||||
{{ $mainPlaceName = index .Names 0 }}
|
||||
{{ end }}
|
||||
{{ $fullInfo := GetFullPlaceInfo .Geo $mainPlaceName }}
|
||||
{{ if ne $fullInfo "" }}
|
||||
<p class="text-sm text-slate-600 mb-1">
|
||||
<i class="ri-map-pin-line mr-1"></i>{{ $fullInfo }}
|
||||
</p>
|
||||
{{ end }}
|
||||
{{ else if .Geo }}
|
||||
<p class="text-sm text-slate-600">
|
||||
<i class="ri-map-pin-line mr-1"></i>Geonames verfügbar
|
||||
</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
71
views/routes/ort/components/_place_header.gohtml
Normal file
71
views/routes/ort/components/_place_header.gohtml
Normal file
@@ -0,0 +1,71 @@
|
||||
{{- /* Place header with name, geographic info, and external links */ -}}
|
||||
<!-- Place Header -->
|
||||
<div class="mb-8">
|
||||
{{ $geonames := GetGeonames .Place.Geo }}
|
||||
|
||||
<!-- Name and external links - similar to akteure header -->
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="flex-1">
|
||||
<h1 class="text-3xl font-bold text-slate-800 mb-2">
|
||||
{{ if .Place.Names }}
|
||||
{{ index .Place.Names 0 }}
|
||||
{{ else }}
|
||||
{{ .Place.ID }}
|
||||
{{ end }}
|
||||
</h1>
|
||||
|
||||
<!-- Geographic Information from Geonames -->
|
||||
{{ if ne $geonames nil }}
|
||||
<div class="text-lg text-slate-700 mb-2">
|
||||
<!-- Modern Country Info (only if not Germany) -->
|
||||
{{ $mainPlaceName := "" }}
|
||||
{{ if .Place.Names }}
|
||||
{{ $mainPlaceName = index .Place.Names 0 }}
|
||||
{{ end }}
|
||||
{{ $fullInfo := GetFullPlaceInfo .Place.Geo $mainPlaceName }}
|
||||
{{ if ne $fullInfo "" }}
|
||||
<div class="mb-1">{{ $fullInfo }}</div>
|
||||
{{ end }}
|
||||
|
||||
<!-- Coordinates -->
|
||||
<div class="text-slate-600 text-base space-y-1">
|
||||
{{ if and (ne $geonames.Lat "") (ne $geonames.Lng "") }}
|
||||
<div>
|
||||
<i class="ri-map-pin-line mr-1"></i><a href="https://www.openstreetmap.org/?mlat={{ $geonames.Lat }}&mlon={{ $geonames.Lng }}&zoom=12" target="_blank" rel="noopener noreferrer" class="text-blue-600 hover:text-blue-700 underline">{{ $geonames.Lat }}, {{ $geonames.Lng }}</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
<!-- Fallback when no Geonames data -->
|
||||
{{ if .Place.Geo }}
|
||||
<p class="text-slate-600 mb-2">
|
||||
<i class="ri-map-pin-line mr-1"></i>
|
||||
<a href="{{ .Place.Geo }}" target="_blank" rel="noopener noreferrer" class="text-blue-600 hover:text-blue-700 underline">
|
||||
Geonames
|
||||
</a>
|
||||
</p>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<!-- External link symbols on the right - similar to akteure -->
|
||||
<div class="flex gap-3 flex-shrink-0 items-center">
|
||||
{{ if ne $geonames nil }}
|
||||
<!-- Wikipedia link if available -->
|
||||
{{ if ne $geonames.WikipediaURL "" }}
|
||||
<a href="https://{{ $geonames.WikipediaURL }}" target="_blank" class="hover:opacity-80 transition-opacity" title="Wikipedia">
|
||||
<img src="/assets/wikipedia.png" alt="Wikipedia" class="w-6 h-6">
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
<!-- Geonames link -->
|
||||
{{ if .Place.Geo }}
|
||||
<a href="{{ .Place.Geo }}" target="_blank" class="hover:opacity-80 transition-opacity no-underline" title="Geonames">
|
||||
<i class="ri-global-line text-xl text-blue-600"></i>
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
98
views/routes/ort/components/_place_pieces.gohtml
Normal file
98
views/routes/ort/components/_place_pieces.gohtml
Normal file
@@ -0,0 +1,98 @@
|
||||
{{- /* Associated pieces section for place detail view */ -}}
|
||||
<!-- Associated Pieces -->
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-slate-800 mb-4">
|
||||
<i class="ri-newspaper-line mr-2"></i><u class="decoration underline-offset-3">Verlinkte Beiträge</u> ({{ len .Pieces }})
|
||||
</h2>
|
||||
|
||||
{{ if .Pieces }}
|
||||
{{- /* Group pieces by year */ -}}
|
||||
{{- $piecesByYear := dict -}}
|
||||
{{- range $_, $p := .Pieces -}}
|
||||
{{- range $issueRef := $p.IssueRefs -}}
|
||||
{{- $year := printf "%d" $issueRef.When.Year -}}
|
||||
{{- $existing := index $piecesByYear $year -}}
|
||||
{{- if $existing -}}
|
||||
{{- $piecesByYear = merge $piecesByYear (dict $year (append $existing $p)) -}}
|
||||
{{- else -}}
|
||||
{{- $piecesByYear = merge $piecesByYear (dict $year (slice $p)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- /* Get sorted years */ -}}
|
||||
{{- $sortedYears := slice -}}
|
||||
{{- range $year, $pieces := $piecesByYear -}}
|
||||
{{- $sortedYears = append $sortedYears $year -}}
|
||||
{{- end -}}
|
||||
{{- $sortedYears = sortStrings $sortedYears -}}
|
||||
|
||||
<div class="space-y-6 max-w-[85ch]">
|
||||
{{- range $year := $sortedYears -}}
|
||||
{{- $yearPieces := index $piecesByYear $year -}}
|
||||
|
||||
<!-- Year Header -->
|
||||
<div>
|
||||
<h3 class="text-lg font-bold font-serif text-slate-800 mb-3">{{ $year }}</h3>
|
||||
|
||||
<div class="space-y-1">
|
||||
{{- /* Group pieces by title within each year */ -}}
|
||||
{{- $groupedPieces := dict -}}
|
||||
{{- range $_, $p := $yearPieces -}}
|
||||
{{- $groupKey := "" -}}
|
||||
{{- if $p.Title -}}
|
||||
{{- $groupKey = index $p.Title 0 -}}
|
||||
{{- else if $p.Incipit -}}
|
||||
{{- $groupKey = index $p.Incipit 0 -}}
|
||||
{{- else -}}
|
||||
{{- $groupKey = printf "untitled-%s" $p.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 -}}
|
||||
|
||||
{{- range $groupKey, $groupedItems := $groupedPieces -}}
|
||||
<div>
|
||||
<div class="pb-1 text-lg indent-4">
|
||||
{{- /* Use first piece for display text with colon format for places */ -}}
|
||||
{{ template "_piece_summary_for_place" (dict "Piece" (index $groupedItems 0) "CurrentActorID" "") }}
|
||||
|
||||
{{- /* Show all citations from all pieces in this group inline with commas */ -}}
|
||||
{{ " " }}{{- range $groupIndex, $groupItem := $groupedItems -}}
|
||||
{{- range $issueIndex, $issue := $groupItem.IssueRefs -}}
|
||||
{{- /* Only show citations for the current year */ -}}
|
||||
{{- if eq (printf "%d" $issue.When.Year) $year -}}
|
||||
{{- if or (gt $groupIndex 0) (gt $issueIndex 0) }}, {{ end -}}
|
||||
<span class="text-blue-600 hover:text-blue-700 underline decoration-dotted hover:decoration-solid [&>a]:text-blue-600 [&>a:hover]:text-blue-700">{{- template "_citation" $issue -}}</span>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- /* Add "Ganzer Beitrag" link if piece spans multiple issues */ -}}
|
||||
{{- $firstGroupItem := index $groupedItems 0 -}}
|
||||
{{- if gt (len $firstGroupItem.IssueRefs) 1 -}}
|
||||
{{ " " }}<div class="inline-flex items-center gap-1 px-2 py-1 bg-blue-50
|
||||
hover:bg-blue-100 text-blue-700 hover:text-blue-800 border border-blue-200
|
||||
hover:border-blue-300 rounded text-xs font-medium transition-colors duration-200">
|
||||
<i class="ri-file-copy-2-line text-xs"></i>
|
||||
<a href="{{ GetPieceURL $firstGroupItem.ID }}" class="">
|
||||
Ganzer Beitrag
|
||||
</a>
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
{{ else }}
|
||||
<p class="text-slate-500 italic">Keine verlinkten Beiträge für diesen Ort gefunden.</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
8
views/routes/ort/detail/body.gohtml
Normal file
8
views/routes/ort/detail/body.gohtml
Normal file
@@ -0,0 +1,8 @@
|
||||
<!-- Single Place Detail View -->
|
||||
<div class="max-w-7xl mx-auto px-8 py-8">
|
||||
<div class="bg-white px-6 py-6 rounded w-full lg:min-w-[800px] xl:min-w-[900px]">
|
||||
{{ template "_back_navigation" . }}
|
||||
{{ template "_place_header" .model.SelectedPlace }}
|
||||
{{ template "_place_pieces" .model.SelectedPlace }}
|
||||
</div>
|
||||
</div>
|
||||
36
views/routes/ort/detail/head.gohtml
Normal file
36
views/routes/ort/detail/head.gohtml
Normal file
@@ -0,0 +1,36 @@
|
||||
{{ $place := .model.SelectedPlace.Place }}
|
||||
<title>{{ if $place.Names }}{{ index $place.Names 0 }}{{ else }}{{ $place.ID }}{{ end }} - KGPZ</title>
|
||||
<meta name="description" content="Informationen zu {{ if $place.Names }}{{ index $place.Names 0 }}{{ else }}{{ $place.ID }}{{ end }} in der Königsberger Gelehrten und Politischen Zeitung.">
|
||||
<meta name="keywords" content="{{ if $place.Names }}{{ index $place.Names 0 }}{{ else }}{{ $place.ID }}{{ end }}, Ort, KGPZ, Königsberg, Zeitung">
|
||||
|
||||
<!-- Open Graph tags for social media -->
|
||||
<meta property="og:title" content="{{ if $place.Names }}{{ index $place.Names 0 }}{{ else }}{{ $place.ID }}{{ end }} - KGPZ">
|
||||
<meta property="og:description" content="Informationen zu {{ if $place.Names }}{{ index $place.Names 0 }}{{ else }}{{ $place.ID }}{{ end }} in der Königsberger Gelehrten und Politischen Zeitung.">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="/ort/{{ $place.ID }}">
|
||||
|
||||
<!-- JSON-LD structured data -->
|
||||
{{ $geonames := GetGeonames $place.Geo }}
|
||||
{{ if ne $geonames nil }}
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Place",
|
||||
"name": "{{ if $place.Names }}{{ index $place.Names 0 }}{{ else }}{{ $place.ID }}{{ end }}",
|
||||
"description": "Historischer Ort erwähnt in der Königsberger Gelehrten und Politischen Zeitung"{{ if or (and (ne $geonames.Lat "") (ne $geonames.Lng "")) (ne $geonames.CountryName "") (ne $place.Geo "") }},{{ end }}
|
||||
{{ if and (ne $geonames.Lat "") (ne $geonames.Lng "") }}
|
||||
"geo": {
|
||||
"@type": "GeoCoordinates",
|
||||
"latitude": {{ $geonames.Lat }},
|
||||
"longitude": {{ $geonames.Lng }}
|
||||
}{{ if or (ne $geonames.CountryName "") (ne $place.Geo "") }},{{ end }}
|
||||
{{ end }}
|
||||
{{ if ne $geonames.CountryName "" }}
|
||||
"addressCountry": "{{ $geonames.CountryName }}"{{ if ne $place.Geo "" }},{{ end }}
|
||||
{{ end }}
|
||||
{{ if ne $place.Geo "" }}
|
||||
"sameAs": "{{ $place.Geo }}"
|
||||
{{ end }}
|
||||
}
|
||||
</script>
|
||||
{{ end }}
|
||||
@@ -1,42 +0,0 @@
|
||||
{{ if .model.SelectedPlace }}
|
||||
{{ $place := .model.SelectedPlace.Place }}
|
||||
<title>{{ if $place.Names }}{{ index $place.Names 0 }}{{ else }}{{ $place.ID }}{{ end }} - KGPZ</title>
|
||||
<meta name="description" content="Informationen zu {{ if $place.Names }}{{ index $place.Names 0 }}{{ else }}{{ $place.ID }}{{ end }} in der Königsberger Gelehrten und Politischen Zeitung.">
|
||||
<meta name="keywords" content="{{ if $place.Names }}{{ index $place.Names 0 }}{{ else }}{{ $place.ID }}{{ end }}, Ort, KGPZ, Königsberg, Zeitung">
|
||||
|
||||
<!-- Open Graph tags for social media -->
|
||||
<meta property="og:title" content="{{ if $place.Names }}{{ index $place.Names 0 }}{{ else }}{{ $place.ID }}{{ end }} - KGPZ">
|
||||
<meta property="og:description" content="Informationen zu {{ if $place.Names }}{{ index $place.Names 0 }}{{ else }}{{ $place.ID }}{{ end }} in der Königsberger Gelehrten und Politischen Zeitung.">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="/ort/{{ $place.ID }}">
|
||||
|
||||
<!-- JSON-LD structured data -->
|
||||
{{ $geonames := GetGeonames $place.Geo }}
|
||||
{{ if ne $geonames nil }}
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Place",
|
||||
"name": "{{ if $place.Names }}{{ index $place.Names 0 }}{{ else }}{{ $place.ID }}{{ end }}",
|
||||
"description": "Historischer Ort erwähnt in der Königsberger Gelehrten und Politischen Zeitung"{{ if or (and (ne $geonames.Lat "") (ne $geonames.Lng "")) (ne $geonames.CountryName "") (ne $place.Geo "") }},{{ end }}
|
||||
{{ if and (ne $geonames.Lat "") (ne $geonames.Lng "") }}
|
||||
"geo": {
|
||||
"@type": "GeoCoordinates",
|
||||
"latitude": {{ $geonames.Lat }},
|
||||
"longitude": {{ $geonames.Lng }}
|
||||
}{{ if or (ne $geonames.CountryName "") (ne $place.Geo "") }},{{ end }}
|
||||
{{ end }}
|
||||
{{ if ne $geonames.CountryName "" }}
|
||||
"addressCountry": "{{ $geonames.CountryName }}"{{ if ne $place.Geo "" }},{{ end }}
|
||||
{{ end }}
|
||||
{{ if ne $place.Geo "" }}
|
||||
"sameAs": "{{ $place.Geo }}"
|
||||
{{ end }}
|
||||
}
|
||||
</script>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<title>Orte - KGPZ</title>
|
||||
<meta name="description" content="Übersicht aller Orte in der Königsberger Gelehrten und Politischen Zeitung.">
|
||||
<meta name="keywords" content="Orte, Geografie, KGPZ, Königsberg, Zeitung">
|
||||
{{ end }}
|
||||
27
views/routes/ort/overview/body.gohtml
Normal file
27
views/routes/ort/overview/body.gohtml
Normal file
@@ -0,0 +1,27 @@
|
||||
<!-- Places Overview -->
|
||||
<div class="max-w-7xl mx-auto px-8 py-8">
|
||||
<div class="bg-white px-6 py-6 rounded w-full">
|
||||
<h1 class="text-3xl font-bold text-slate-800 mb-8">Orte</h1>
|
||||
|
||||
<!-- Places List -->
|
||||
{{ if .model.Places }}
|
||||
<div>
|
||||
<!-- Search Filter -->
|
||||
<places-filter></places-filter>
|
||||
|
||||
<h2 class="text-lg font-semibold text-slate-700 mb-4" data-places-count>
|
||||
Alle Orte ({{ len .model.Places }})
|
||||
</h2>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 lg:min-w-[768px] xl:min-w-[1024px]">
|
||||
{{ range $placeID := .model.Sorted }}
|
||||
{{ $place := index $.model.Places $placeID }}
|
||||
{{ template "_place_card" $place }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
<p class="text-slate-500 italic">Keine Orte gefunden.</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
3
views/routes/ort/overview/head.gohtml
Normal file
3
views/routes/ort/overview/head.gohtml
Normal file
@@ -0,0 +1,3 @@
|
||||
<title>Orte - KGPZ</title>
|
||||
<meta name="description" content="Übersicht aller Orte in der Königsberger Gelehrten und Politischen Zeitung.">
|
||||
<meta name="keywords" content="Orte, Geografie, KGPZ, Königsberg, Zeitung">
|
||||
@@ -1,6 +1,7 @@
|
||||
import "./site.css";
|
||||
import "./search.js";
|
||||
import "./akteure.js";
|
||||
import "./places.js";
|
||||
import { SinglePageViewer } from "./single-page-viewer.js";
|
||||
import { ScrollToTopButton } from "./scroll-to-top.js";
|
||||
import { InhaltsverzeichnisScrollspy } from "./inhaltsverzeichnis-scrollspy.js";
|
||||
|
||||
122
views/transform/places.js
Normal file
122
views/transform/places.js
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Places Filter Web Component
|
||||
* Provides search functionality for filtering place cards in the overview
|
||||
*/
|
||||
export class PlacesFilter extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.searchInput = null;
|
||||
this.placeCards = [];
|
||||
this.countElement = null;
|
||||
this.debounceTimer = null;
|
||||
this.originalCount = 0;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.render();
|
||||
this.setupEventListeners();
|
||||
this.initializePlaceCards();
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.cleanupEventListeners();
|
||||
if (this.debounceTimer) {
|
||||
clearTimeout(this.debounceTimer);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
this.innerHTML = `
|
||||
<div class="mb-6">
|
||||
<input
|
||||
type="text"
|
||||
id="places-search"
|
||||
placeholder="Ortsnamen eingeben..."
|
||||
autocomplete="off"
|
||||
class="w-full px-3 py-2 border border-slate-300 rounded-md text-sm bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 focus:border-blue-400"
|
||||
>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
this.searchInput = this.querySelector('#places-search');
|
||||
if (this.searchInput) {
|
||||
this.searchInput.addEventListener('input', this.handleSearchInput.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
cleanupEventListeners() {
|
||||
if (this.searchInput) {
|
||||
this.searchInput.removeEventListener('input', this.handleSearchInput.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
initializePlaceCards() {
|
||||
// Find all place cards and the count element
|
||||
const container = this.closest('.bg-white') || document;
|
||||
this.placeCards = Array.from(container.querySelectorAll('[data-place-name]'));
|
||||
this.countElement = container.querySelector('[data-places-count]');
|
||||
|
||||
if (this.countElement) {
|
||||
this.originalCount = this.placeCards.length;
|
||||
}
|
||||
}
|
||||
|
||||
handleSearchInput(event) {
|
||||
// Clear previous debounce timer
|
||||
if (this.debounceTimer) {
|
||||
clearTimeout(this.debounceTimer);
|
||||
}
|
||||
|
||||
// Debounce the search to avoid excessive filtering
|
||||
this.debounceTimer = setTimeout(() => {
|
||||
this.filterPlaces(event.target.value.trim());
|
||||
}, 150);
|
||||
}
|
||||
|
||||
filterPlaces(searchTerm) {
|
||||
if (!this.placeCards.length) return;
|
||||
|
||||
const normalizedSearch = searchTerm.toLowerCase();
|
||||
let visibleCount = 0;
|
||||
|
||||
this.placeCards.forEach(card => {
|
||||
const placeName = card.getAttribute('data-place-name')?.toLowerCase() || '';
|
||||
const modernName = card.getAttribute('data-modern-name')?.toLowerCase() || '';
|
||||
|
||||
// Check if search term matches either the place name or modern name
|
||||
const isMatch = searchTerm === '' ||
|
||||
placeName.includes(normalizedSearch) ||
|
||||
modernName.includes(normalizedSearch);
|
||||
|
||||
if (isMatch) {
|
||||
card.style.display = '';
|
||||
visibleCount++;
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Update the count display
|
||||
this.updateCountDisplay(visibleCount, searchTerm);
|
||||
}
|
||||
|
||||
updateCountDisplay(visibleCount, searchTerm) {
|
||||
if (!this.countElement) return;
|
||||
|
||||
if (searchTerm === '') {
|
||||
// Show original count when no search
|
||||
this.countElement.textContent = `Alle Orte (${this.originalCount})`;
|
||||
} else if (visibleCount === 0) {
|
||||
// Show no results message
|
||||
this.countElement.textContent = `Keine Orte gefunden für "${searchTerm}"`;
|
||||
} else {
|
||||
// Show filtered count
|
||||
this.countElement.textContent = `${visibleCount} von ${this.originalCount} Orten`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register the custom element
|
||||
customElements.define('places-filter', PlacesFilter);
|
||||
Reference in New Issue
Block a user