BibTest.js: Difference between revisions
From Illustrations in German Translations of Mark Twain's Works
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
let rawData = []; | |||
let activeTags = new Set(); | let activeTags = new Set(); | ||
/* ========================= | /* ========================= | ||
INIT | |||
========================= */ | ========================= */ | ||
document.addEventListener("DOMContentLoaded", () => { | document.addEventListener("DOMContentLoaded", () => { | ||
loadBibliography(); | if (typeof BibliographyData !== "undefined") { | ||
loadBibliography(); | |||
} | |||
document.getElementById("updateBib").addEventListener("click", reloadBibliography); | |||
}); | }); | ||
/* ========================= | /* ========================= | ||
LOAD | LOAD + RENDER | ||
========================= */ | ========================= */ | ||
function loadBibliography() { | function loadBibliography() { | ||
rawData = normalizeData(BibliographyData); | |||
renderTags(rawData); | renderTags(rawData); | ||
| Line 35: | Line 30: | ||
/* ========================= | /* ========================= | ||
NORMALIZE ZOTERO DATA | |||
========================= */ | |||
function normalizeData(data) { | |||
return data.map(item => ({ | |||
title: item.title || "Untitled", | |||
date: item.date || "", | |||
author: item.creators?.[0]?.lastName || "Unknown", | |||
tags: (item.tags || []).map(t => t.tag), | |||
abstractNote: item.abstractNote || "" | |||
})); | |||
} | |||
/* ========================= | |||
GLOBAL TAG BAR | |||
========================= */ | ========================= */ | ||
| Line 45: | Line 60: | ||
data.forEach(item => { | data.forEach(item => { | ||
item.tags.forEach(t => tagSet.add(t)); | |||
}); | }); | ||
| Line 52: | Line 67: | ||
[...tagSet].sort().forEach(tag => { | [...tagSet].sort().forEach(tag => { | ||
const | const el = document.createElement("span"); | ||
el.className = "tag"; | |||
el.dataset.tag = tag; | |||
el.innerText = tag; | |||
el.addEventListener("click", () => toggleTag(tag, el)); | |||
container.appendChild( | container.appendChild(el); | ||
}); | }); | ||
| Line 80: | Line 95: | ||
applyFilter(); | applyFilter(); | ||
syncEntryTags(); | |||
} | } | ||
/* ========================= | /* ========================= | ||
FILTER | FILTER ENTRIES | ||
========================= */ | ========================= */ | ||
function applyFilter() { | function applyFilter() { | ||
document.querySelectorAll(".bib-entry").forEach(entry => { | |||
const tags = (entry.dataset.tags || "").split(" "); | const tags = (entry.dataset.tags || "").split(" "); | ||
| Line 106: | Line 120: | ||
/* ========================= | /* ========================= | ||
RENDER LIST | SYNC TAG VISUAL STATE | ||
========================= */ | |||
function syncEntryTags() { | |||
document.querySelectorAll(".bib-entry .tag").forEach(tag => { | |||
const value = tag.dataset.tag; | |||
if (activeTags.has(value)) { | |||
tag.classList.add("active"); | |||
} else { | |||
tag.classList.remove("active"); | |||
} | |||
}); | |||
} | |||
/* ========================= | |||
RENDER LIST | |||
========================= */ | ========================= */ | ||
| Line 114: | Line 148: | ||
container.innerHTML = ""; | container.innerHTML = ""; | ||
// alphabetisch sortieren | // alphabetisch sortieren | ||
data.sort((a, b) => | data.sort((a, b) => | ||
a.author.localeCompare(b.author) | |||
); | ); | ||
data.forEach(item => { | data.forEach(item => { | ||
const el = document.createElement("div"); | const el = document.createElement("div"); | ||
el.className = "bib-entry"; | el.className = "bib-entry"; | ||
el.dataset.tags | el.dataset.tags = item.tags.join(" "); | ||
el.innerHTML = ` | el.innerHTML = ` | ||
<h3>${author}. <i>${item.title | <h3>${item.author}. <i>${item.title}</i>. ${item.date}</h3> | ||
<div class="bib-tags"> | <div class="bib-tags"> | ||
${ | ${item.tags.map(t => | ||
`<span class="tag" data-tag="${t}">${t}</span>` | `<span class="tag" data-tag="${t}">${t}</span>` | ||
).join(" ")} | ).join(" ")} | ||
| Line 139: | Line 169: | ||
<p class="short-abstract"> | <p class="short-abstract"> | ||
${item.abstractNote | ${item.abstractNote} | ||
</p> | </p> | ||
| Line 145: | Line 175: | ||
<div class="abstract hidden"> | <div class="abstract hidden"> | ||
<p>${item.abstractNote | <p>${item.abstractNote}</p> | ||
</div> | </div> | ||
`; | `; | ||
| Line 154: | Line 184: | ||
rebindUI(); | rebindUI(); | ||
syncEntryTags(); | |||
applyFilter(); | |||
} | } | ||
/* ========================= | /* ========================= | ||
UI REBIND | UI REBIND | ||
========================= */ | ========================= */ | ||
function rebindUI() { | function rebindUI() { | ||
document.querySelectorAll(".expand-btn").forEach(btn => { | document.querySelectorAll(".expand-btn").forEach(btn => { | ||
| Line 180: | Line 213: | ||
}); | }); | ||
// entry | // entry tags → trigger global filter | ||
document.querySelectorAll(".bib-entry .tag").forEach(tag => { | document.querySelectorAll(".bib-entry .tag").forEach(tag => { | ||
| Line 187: | Line 220: | ||
const value = tag.dataset.tag; | const value = tag.dataset.tag; | ||
const | const global = document.querySelector( | ||
`.bib-tags#globalTags .tag[data-tag="${value}"]` | `.bib-tags#globalTags .tag[data-tag="${value}"]` | ||
); | ); | ||
if ( | if (global) global.click(); | ||
}; | }; | ||
| Line 200: | Line 233: | ||
/* ========================= | /* ========================= | ||
UPDATE BUTTON | |||
========================= */ | ========================= */ | ||
function | function reloadBibliography() { | ||
console.log("🔄 Reloading BibliographyData..."); | |||
if (typeof BibliographyData === "undefined") { | |||
console.error("BibliographyData not found"); | |||
return; | |||
} | |||
rawData = normalizeData(BibliographyData); | |||
activeTags.clear(); | |||
document.getElementById("globalTags").innerHTML = ""; | |||
loadBibliography(); | |||
console.log("✅ Updated"); | |||
} | } | ||
Revision as of 23:01, 11 May 2026
let rawData = [];
let activeTags = new Set();
/* =========================
INIT
========================= */
document.addEventListener("DOMContentLoaded", () => {
if (typeof BibliographyData !== "undefined") {
loadBibliography();
}
document.getElementById("updateBib").addEventListener("click", reloadBibliography);
});
/* =========================
LOAD + RENDER
========================= */
function loadBibliography() {
rawData = normalizeData(BibliographyData);
renderTags(rawData);
renderList(rawData);
}
/* =========================
NORMALIZE ZOTERO DATA
========================= */
function normalizeData(data) {
return data.map(item => ({
title: item.title || "Untitled",
date: item.date || "",
author: item.creators?.[0]?.lastName || "Unknown",
tags: (item.tags || []).map(t => t.tag),
abstractNote: item.abstractNote || ""
}));
}
/* =========================
GLOBAL TAG BAR
========================= */
function renderTags(data) {
const container = document.getElementById("globalTags");
const tagSet = new Set();
data.forEach(item => {
item.tags.forEach(t => tagSet.add(t));
});
container.innerHTML = "";
[...tagSet].sort().forEach(tag => {
const el = document.createElement("span");
el.className = "tag";
el.dataset.tag = tag;
el.innerText = tag;
el.addEventListener("click", () => toggleTag(tag, el));
container.appendChild(el);
});
}
/* =========================
TAG TOGGLE
========================= */
function toggleTag(tag, el) {
if (activeTags.has(tag)) {
activeTags.delete(tag);
el.classList.remove("active");
} else {
activeTags.add(tag);
el.classList.add("active");
}
applyFilter();
syncEntryTags();
}
/* =========================
FILTER ENTRIES
========================= */
function applyFilter() {
document.querySelectorAll(".bib-entry").forEach(entry => {
const tags = (entry.dataset.tags || "").split(" ");
const match =
activeTags.size === 0 ||
[...activeTags].every(t => tags.includes(t));
entry.style.display = match ? "block" : "none";
});
}
/* =========================
SYNC TAG VISUAL STATE
========================= */
function syncEntryTags() {
document.querySelectorAll(".bib-entry .tag").forEach(tag => {
const value = tag.dataset.tag;
if (activeTags.has(value)) {
tag.classList.add("active");
} else {
tag.classList.remove("active");
}
});
}
/* =========================
RENDER LIST
========================= */
function renderList(data) {
const container = document.getElementById("listView");
container.innerHTML = "";
// alphabetisch sortieren
data.sort((a, b) =>
a.author.localeCompare(b.author)
);
data.forEach(item => {
const el = document.createElement("div");
el.className = "bib-entry";
el.dataset.tags = item.tags.join(" ");
el.innerHTML = `
<h3>${item.author}. <i>${item.title}</i>. ${item.date}</h3>
<div class="bib-tags">
${item.tags.map(t =>
`<span class="tag" data-tag="${t}">${t}</span>`
).join(" ")}
</div>
<p class="short-abstract">
${item.abstractNote}
</p>
<button class="expand-btn">Show Abstract</button>
<div class="abstract hidden">
<p>${item.abstractNote}</p>
</div>
`;
container.appendChild(el);
});
rebindUI();
syncEntryTags();
applyFilter();
}
/* =========================
UI REBIND
========================= */
function rebindUI() {
document.querySelectorAll(".expand-btn").forEach(btn => {
btn.onclick = () => {
const abs = btn.nextElementSibling;
abs.classList.toggle("hidden");
btn.textContent =
abs.classList.contains("hidden")
? "Show Abstract"
: "Hide Abstract";
};
});
// entry tags → trigger global filter
document.querySelectorAll(".bib-entry .tag").forEach(tag => {
tag.onclick = () => {
const value = tag.dataset.tag;
const global = document.querySelector(
`.bib-tags#globalTags .tag[data-tag="${value}"]`
);
if (global) global.click();
};
});
}
/* =========================
UPDATE BUTTON
========================= */
function reloadBibliography() {
console.log("🔄 Reloading BibliographyData...");
if (typeof BibliographyData === "undefined") {
console.error("BibliographyData not found");
return;
}
rawData = normalizeData(BibliographyData);
activeTags.clear();
document.getElementById("globalTags").innerHTML = "";
loadBibliography();
console.log("✅ Updated");
}