MediaWiki

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();
let rawData = [];


/* =========================
/* =========================
   INITIAL LOAD
   INIT
========================= */
========================= */


document.addEventListener("DOMContentLoaded", () => {
document.addEventListener("DOMContentLoaded", () => {


     loadBibliography();
     if (typeof BibliographyData !== "undefined") {
        loadBibliography();
    }


     bindViewButtons();
     document.getElementById("updateBib").addEventListener("click", reloadBibliography);


});
});


/* =========================
/* =========================
   LOAD DATA FROM WIKI PAGE
   LOAD + RENDER
  (BibliographyData must be JS/JSON)
========================= */
========================= */


function loadBibliography() {
function loadBibliography() {


     // BibliographyData muss als globales JSON vorliegen
     rawData = normalizeData(BibliographyData);
    if (typeof BibliographyData === "undefined") {
        console.error("BibliographyData not found!");
        return;
    }
 
    rawData = BibliographyData;


     renderTags(rawData);
     renderTags(rawData);
Line 35: Line 30:


/* =========================
/* =========================
   TAG RENDER (GLOBAL FILTER BAR)
   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));
         item.tags.forEach(t => tagSet.add(t));
     });
     });


Line 52: Line 67:
     [...tagSet].sort().forEach(tag => {
     [...tagSet].sort().forEach(tag => {


         const span = document.createElement("span");
         const el = document.createElement("span");
         span.className = "tag";
         el.className = "tag";
         span.dataset.tag = tag;
         el.dataset.tag = tag;
         span.innerText = tag;
         el.innerText = tag;


         span.addEventListener("click", () => toggleTag(tag, span));
         el.addEventListener("click", () => toggleTag(tag, el));


         container.appendChild(span);
         container.appendChild(el);


     });
     });
Line 80: Line 95:


     applyFilter();
     applyFilter();
    syncEntryTags();


}
}


/* =========================
/* =========================
   FILTER LOGIC
   FILTER ENTRIES
========================= */
========================= */


function applyFilter() {
function applyFilter() {


     const entries = document.querySelectorAll(".bib-entry");
     document.querySelectorAll(".bib-entry").forEach(entry => {
 
    entries.forEach(entry => {


         const tags = (entry.dataset.tags || "").split(" ");
         const tags = (entry.dataset.tags || "").split(" ");
Line 106: Line 120:


/* =========================
/* =========================
   RENDER LIST VIEW
  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 (Titel)
     // alphabetisch sortieren
     data.sort((a, b) =>
     data.sort((a, b) =>
         (a.title || "").localeCompare(b.title || "")
         a.author.localeCompare(b.author)
     );
     );


     data.forEach(item => {
     data.forEach(item => {
        const tags = (item.tags || []).join(" ");


         const el = document.createElement("div");
         const el = document.createElement("div");
         el.className = "bib-entry";
         el.className = "bib-entry";
         el.dataset.tags = tags;
         el.dataset.tags = item.tags.join(" ");
 
        const author = item.author?.[0]?.family || "Unknown";


         el.innerHTML = `
         el.innerHTML = `
             <h3>${author}. <i>${item.title || "No Title"}</i>. ${item.date || ""}</h3>
             <h3>${item.author}. <i>${item.title}</i>. ${item.date}</h3>


             <div class="bib-tags">
             <div class="bib-tags">
                 ${(item.tags || []).map(t =>
                 ${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>
                 <p>${item.abstractNote}</p>
             </div>
             </div>
         `;
         `;
Line 154: Line 184:


     rebindUI();
     rebindUI();
    syncEntryTags();
    applyFilter();


}
}


/* =========================
/* =========================
   UI REBIND (after render)
   UI REBIND
========================= */
========================= */


function rebindUI() {
function rebindUI() {


    // expand abstracts
     document.querySelectorAll(".expand-btn").forEach(btn => {
     document.querySelectorAll(".expand-btn").forEach(btn => {


Line 180: Line 213:
     });
     });


     // entry-level tag clicks (optional: mirror global filter)
     // 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 globalTag = document.querySelector(
             const global = document.querySelector(
                 `.bib-tags#globalTags .tag[data-tag="${value}"]`
                 `.bib-tags#globalTags .tag[data-tag="${value}"]`
             );
             );


             if (globalTag) globalTag.click();
             if (global) global.click();


         };
         };
Line 200: Line 233:


/* =========================
/* =========================
   VIEW SWITCH
   UPDATE BUTTON
========================= */
========================= */


function bindViewButtons() {
function reloadBibliography() {


     document.querySelectorAll(".view-btn").forEach(btn => {
     console.log("🔄 Reloading BibliographyData...");


         btn.addEventListener("click", () => {
    if (typeof BibliographyData === "undefined") {
         console.error("BibliographyData not found");
        return;
    }


            document.querySelectorAll(".view-btn")
    rawData = normalizeData(BibliographyData);
                .forEach(b => b.classList.remove("active"));


            btn.classList.add("active");
    activeTags.clear();


            const view = btn.dataset.view;
    document.getElementById("globalTags").innerHTML = "";


            document.getElementById("listView").style.display =
    loadBibliography();
                view === "list" ? "block" : "none";


            document.getElementById("networkView").classList.toggle(
    console.log("✅ Updated");
                "hidden",
                view !== "network"
            );
 
        });
 
    });


}
}

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");

}