MediaWiki

BibTest.js: Difference between revisions

From Illustrations in German Translations of Mark Twain's Works

No edit summary
No edit summary
 
(40 intermediate revisions by the same user not shown)
Line 1: Line 1:
let rawData = [];
/**
let activeTags = new Set();
* Bibliography Tag Filter, Sorting and Search
*/
function updateCounter() {
  const entries = document.querySelectorAll('.bib-entry');
  const visible = Array.from(entries).filter(entry => entry.style.display !== 'none').length;


/* =========================
  const counter = document.getElementById('bibCounter');
  INIT
  if (counter) {
========================= */
    counter.textContent = `${visible} of ${entries.length} sources shown`;
  }
}
(function() {
  'use strict';


document.addEventListener("DOMContentLoaded", () => {
  let ACTIVE_TAGS = new Set();
  let SEARCH_TERM = '';


     if (typeof BibliographyData !== "undefined") {
  function init() {
        loadBibliography();
     const tagBar = document.getElementById('tagBar');
     }
    const bibliography = document.getElementById('listView');
    const searchInput = document.getElementById('bibSearch');
      
    if (!tagBar || !bibliography) return;


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


});
    // Sammle alle Original-Tagnamen aus den Einträgen
    const allTagsMap = new Map();
    document.querySelectorAll('.bib-entry .tag').forEach(tagEl => {
      const originalName = tagEl.textContent.trim();
      const normalized = normalizeTag(originalName);
      allTagsMap.set(normalized, originalName);
    });


/* =========================
    // Lösche alte Tags aus tagBar (aber nur einmal!)
  LOAD + RENDER
    if (tagBar.children.length === 0) {
========================= */
      Array.from(allTagsMap.entries())
        .sort((a, b) => a[1].localeCompare(b[1]))
        .forEach(([normalized, originalName]) => {
  const span = document.createElement('span');
  span.className = 'tag inactive';
  span.textContent = originalName;
  span.dataset.normalized = normalized;


function loadBibliography() {
  // Sonderfarbe für Twain-Werke
const orangeTags = [
  'a connecticut yankee in king arthur\'s court',
  'adventures of huckleberry finn',
  'pudd\'nhead wilson',
  'roughing it',
  'the adventures of tom sawyer',
  'the innocents abroad',
  'a tramp abroad'
];


    rawData = normalizeData(BibliographyData);
if (orangeTags.includes(originalName.toLowerCase())) {
  span.classList.add('twain-work');
}


    renderTags(rawData);
// Sonderfarbe für Illustratoren
    renderList(rawData);
const illustratorTags = [
  'kemble, edward w.',
  'senior, frank m.',
  'warren, calvin h.',
  'williams, true w.',
  'brown, walter f.',
  'beard, daniel carter'
];


if (illustratorTags.includes(originalName.toLowerCase())) {
  span.classList.add('illustrator-tag');
}
}


/* =========================
  span.onclick = () => toggleTag(originalName, span, normalized);
  NORMALIZE ZOTERO DATA
  tagBar.appendChild(span);
========================= */
});
    }
    // Info-Legende für Tag-Farben
    if (!document.getElementById('tagLegend')) {
      const legend = document.createElement('span');
      legend.id = 'tagLegend';


function normalizeData(data) {
      legend.innerHTML = `
        <span class="info-icon">i</span>
        <div class="legend-box">
          <div><span class="legend-color twain"></span> Twain's Works</div>
          <div><span class="legend-color illustrator"></span> Illustrators</div>
          <div><span class="legend-color general"></span> General Tags</div>
        </div>
      `;


     return data.map(item => ({
      tagBar.appendChild(legend);
     }
    // Search Input Listener
    if (searchInput && !searchInput.dataset.listenerSet) {
      searchInput.addEventListener('input', (e) => {
        SEARCH_TERM = e.target.value.toLowerCase();
        filterEntries();
      });
      searchInput.dataset.listenerSet = 'true';
    }


        title: item.title || "Untitled",
    sortEntries();
        date: item.date || "",
        author: item.creators?.[0]?.lastName || "Unknown",


        tags: (item.tags || []).map(t => t.tag),


        abstractNote: item.abstractNote || ""


    }));


}


/* =========================
removeEmptyAbstractButtons();
  GLOBAL TAG BAR
removeEmptyNotesButtons();
========================= */


function renderTags(data) {
collapseAbstractsInitially();
collapseNotesInitially();


    const container = document.getElementById("globalTags");
setupClickHandlers();
  }


    const tagSet = new Set();
function removeEmptyAbstractButtons() {
  document.querySelectorAll('.bib-entry').forEach(entry => {


     data.forEach(item => {
     const abstractDiv = entry.querySelector('.abstract');
        item.tags.forEach(t => tagSet.add(t));
     const button = entry.querySelector('.expand-btn');
     });


     container.innerHTML = "";
     if (!abstractDiv || !button) return;


     [...tagSet].sort().forEach(tag => {
     const text = abstractDiv.textContent.trim();
 
        const el = document.createElement("span");
        el.className = "tag";
        el.dataset.tag = tag;
        el.innerText = tag;
 
        el.addEventListener("click", () => toggleTag(tag, el));
 
        container.appendChild(el);
 
    });


    // Nur prüfen ob leer bzw. Platzhaltertext
    if (
      text === '' ||
      text === 'No abstract available.'
     
    ) {
      button.remove();
      abstractDiv.remove();
    }
  });
}
}
 
function collapseAbstractsInitially() {
/* =========================
  document.querySelectorAll('.abstract').forEach(el => {
  TAG TOGGLE
    el.classList.add('hidden');
========================= */
     el.style.display = ''; // entfernt evtl. inline overrides
 
  });
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();
 
}
}


/* =========================
function removeEmptyNotesButtons() {
  FILTER ENTRIES
  document.querySelectorAll('.bib-entry').forEach(entry => {
========================= */


function applyFilter() {
    const notesDiv = entry.querySelector('.notes');
    const button = entry.querySelector('.expand-btn-notes');


     document.querySelectorAll(".bib-entry").forEach(entry => {
     if (!notesDiv || !button) return;


        const tags = (entry.dataset.tags || "").split(" ");
    const text = notesDiv.textContent.trim();
 
        const match =
            activeTags.size === 0 ||
            [...activeTags].every(t => tags.includes(t));
 
        entry.style.display = match ? "block" : "none";
 
    });


    if (text === '') {
      button.remove();
      notesDiv.remove();
    }
  });
}
}
 
function collapseNotesInitially() {
/* =========================
  document.querySelectorAll('.notes').forEach(notesDiv => {
  SYNC TAG VISUAL STATE
    notesDiv.classList.add('hidden');
========================= */
  });
 
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");
        }
 
    });
 
}
}
  function setupClickHandlers() {
    document.removeEventListener('click', handleClicks);
    document.addEventListener('click', handleClicks);
  }


/* =========================
  function handleClicks(e) {
  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 =>
  // ABSTRACT TOGGLE
                    `<span class="tag" data-tag="${t}">${t}</span>`
  // =========================
                ).join(" ")}
  if (e.target.classList.contains('expand-btn') &&
            </div>
      !e.target.classList.contains('expand-btn-notes')) {


            <p class="short-abstract">
    const entry = e.target.closest('.bib-entry');
                ${item.abstractNote}
    const abstractDiv = entry?.querySelector('.abstract');
            </p>


            <button class="expand-btn">Show Abstract</button>
    if (!abstractDiv) return;


            <div class="abstract hidden">
    const isHidden = abstractDiv.classList.toggle('hidden');
                <p>${item.abstractNote}</p>
            </div>
        `;


        container.appendChild(el);
    e.target.textContent = isHidden
      ? 'Show Abstract'
      : 'Hide Abstract';
  }


    });
  // =========================
  // NOTES TOGGLE
  // =========================
  if (e.target.classList.contains('expand-btn-notes')) {


     rebindUI();
     const entry = e.target.closest('.bib-entry');
    const notesDiv = entry?.querySelector('.notes');


     syncEntryTags();
     if (!notesDiv) return;


     applyFilter();
     const isHidden = notesDiv.classList.toggle('hidden');


    e.target.textContent = isHidden
      ? 'Show Notes'
      : 'Hide Notes';
  }
}
}


/* =========================
  function toggleTag(tagName, element, normalized) {
  UI REBIND
    if (ACTIVE_TAGS.has(normalized)) {
========================= */
      ACTIVE_TAGS.delete(normalized);
      element.classList.remove('active');
      element.classList.add('inactive');
    } else {
      ACTIVE_TAGS.add(normalized);
      element.classList.add('active');
      element.classList.remove('inactive');
    }
    filterEntries();
  }


function rebindUI() {
  function sortEntries() {
 
     const container = document.getElementById('listView');
     document.querySelectorAll(".expand-btn").forEach(btn => {
    if (!container) return;
 
        btn.onclick = () => {
 
            const abs = btn.nextElementSibling;
            abs.classList.toggle("hidden");
 
            btn.textContent =
                abs.classList.contains("hidden")
                    ? "Show Abstract"
                    : "Hide Abstract";
 
        };


    const entries = Array.from(container.querySelectorAll('.bib-entry'));
   
    entries.sort((a, b) => {
      const titleA = a.querySelector('h3')?.textContent || '';
      const titleB = b.querySelector('h3')?.textContent || '';
      return titleA.localeCompare(titleB);
     });
     });


     // entry tags → trigger global filter
     entries.forEach(entry => container.appendChild(entry));
    document.querySelectorAll(".bib-entry .tag").forEach(tag => {
  }


        tag.onclick = () => {
  function normalizeTag(tag) {
    return tag.toLowerCase().replace(/\s+/g, '_').replace(/,/g, '').replace(/\./g, '').replace(/'/g, '').replace(/"/g, '');
  }


            const value = tag.dataset.tag;
  function matchesSearch(entry) {
    if (!SEARCH_TERM) return true;


            const global = document.querySelector(
    const searchText = SEARCH_TERM;
                `.bib-tags#globalTags .tag[data-tag="${value}"]`
   
            );
    const title = entry.querySelector('h3')?.textContent || '';
    if (title.toLowerCase().includes(searchText)) return true;


            if (global) global.click();
    const tagElements = entry.querySelectorAll('.tag');
    for (let tag of tagElements) {
      if (tag.textContent.toLowerCase().includes(searchText)) return true;
    }


        };
    const abstractDiv = entry.querySelector('.abstract');
 
    if (abstractDiv) {
     });
      const abstractText = abstractDiv.textContent || '';
      if (abstractText.toLowerCase().includes(searchText)) return true;
     }


}
    const notesDiv = entry.querySelector('.notes');
 
     if (notesDiv) {
/* =========================
      const notesText = notesDiv.textContent || '';
  UPDATE BUTTON
      if (notesText.toLowerCase().includes(searchText)) return true;
========================= */
 
function reloadBibliography() {
 
    console.log("🔄 Reloading BibliographyData...");
 
     if (typeof BibliographyData === "undefined") {
        console.error("BibliographyData not found");
        return;
     }
     }


     rawData = normalizeData(BibliographyData);
     return false;
  }


     activeTags.clear();
  function filterEntries() {
     const entries = document.querySelectorAll('.bib-entry');
   
    entries.forEach(entry => {
      const entryTags = (entry.getAttribute('data-tags') || '').split(' ').filter(t => t);
     
      let matchesTags = true;
      if (ACTIVE_TAGS.size > 0) {
        matchesTags = Array.from(ACTIVE_TAGS).every(activeTag => {
          return entryTags.includes(activeTag);
        });
      }


    document.getElementById("globalTags").innerHTML = "";
      const matchesSearchTerm = matchesSearch(entry);


     loadBibliography();
      entry.style.display = (matchesTags && matchesSearchTerm) ? 'block' : 'none';
    });
     updateCounter();
  }


     console.log("✅ Updated");
  // Starte wenn DOM bereit
  if (document.readyState === 'loading') {
     document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }


}
  document.addEventListener('DOMContentLoaded', init);
})();
updateCounter();

Latest revision as of 00:12, 17 July 2026

/**
 * Bibliography Tag Filter, Sorting and Search
 */
function updateCounter() {
  const entries = document.querySelectorAll('.bib-entry');
  const visible = Array.from(entries).filter(entry => entry.style.display !== 'none').length;

  const counter = document.getElementById('bibCounter');
  if (counter) {
    counter.textContent = `${visible} of ${entries.length} sources shown`;
  }
}
(function() {
  'use strict';

  let ACTIVE_TAGS = new Set();
  let SEARCH_TERM = '';

  function init() {
    const tagBar = document.getElementById('tagBar');
    const bibliography = document.getElementById('listView');
    const searchInput = document.getElementById('bibSearch');
    
    if (!tagBar || !bibliography) return;

    

    // Sammle alle Original-Tagnamen aus den Einträgen
    const allTagsMap = new Map();
    document.querySelectorAll('.bib-entry .tag').forEach(tagEl => {
      const originalName = tagEl.textContent.trim();
      const normalized = normalizeTag(originalName);
      allTagsMap.set(normalized, originalName);
    });

    // Lösche alte Tags aus tagBar (aber nur einmal!)
    if (tagBar.children.length === 0) {
      Array.from(allTagsMap.entries())
        .sort((a, b) => a[1].localeCompare(b[1]))
        .forEach(([normalized, originalName]) => {
  const span = document.createElement('span');
  span.className = 'tag inactive';
  span.textContent = originalName;
  span.dataset.normalized = normalized;

  // Sonderfarbe für Twain-Werke
const orangeTags = [
  'a connecticut yankee in king arthur\'s court',
  'adventures of huckleberry finn',
  'pudd\'nhead wilson',
  'roughing it',
  'the adventures of tom sawyer',
  'the innocents abroad',
  'a tramp abroad'
];

if (orangeTags.includes(originalName.toLowerCase())) {
  span.classList.add('twain-work');
}

// Sonderfarbe für Illustratoren
const illustratorTags = [
  'kemble, edward w.',
  'senior, frank m.',
  'warren, calvin h.',
  'williams, true w.',
  'brown, walter f.',
  'beard, daniel carter'
];

if (illustratorTags.includes(originalName.toLowerCase())) {
  span.classList.add('illustrator-tag');
}

  span.onclick = () => toggleTag(originalName, span, normalized);
  tagBar.appendChild(span);
});
    }
    // Info-Legende für Tag-Farben
    if (!document.getElementById('tagLegend')) {
      const legend = document.createElement('span');
      legend.id = 'tagLegend';

      legend.innerHTML = `
        <span class="info-icon">i</span>
        <div class="legend-box">
          <div><span class="legend-color twain"></span> Twain's Works</div>
          <div><span class="legend-color illustrator"></span> Illustrators</div>
          <div><span class="legend-color general"></span> General Tags</div>
        </div>
      `;

      tagBar.appendChild(legend);
    }
    // Search Input Listener
    if (searchInput && !searchInput.dataset.listenerSet) {
      searchInput.addEventListener('input', (e) => {
        SEARCH_TERM = e.target.value.toLowerCase();
        filterEntries();
      });
      searchInput.dataset.listenerSet = 'true';
    }

    sortEntries();





removeEmptyAbstractButtons();
removeEmptyNotesButtons();

collapseAbstractsInitially();
collapseNotesInitially();

setupClickHandlers();
  }

 function removeEmptyAbstractButtons() {
  document.querySelectorAll('.bib-entry').forEach(entry => {

    const abstractDiv = entry.querySelector('.abstract');
    const button = entry.querySelector('.expand-btn');

    if (!abstractDiv || !button) return;

    const text = abstractDiv.textContent.trim();

    // Nur prüfen ob leer bzw. Platzhaltertext
    if (
      text === '' ||
      text === 'No abstract available.'
      
    ) {
      button.remove();
      abstractDiv.remove();
    }
  });
}
function collapseAbstractsInitially() {
  document.querySelectorAll('.abstract').forEach(el => {
    el.classList.add('hidden');
    el.style.display = ''; // entfernt evtl. inline overrides
  });
}

function removeEmptyNotesButtons() {
  document.querySelectorAll('.bib-entry').forEach(entry => {

    const notesDiv = entry.querySelector('.notes');
    const button = entry.querySelector('.expand-btn-notes');

    if (!notesDiv || !button) return;

    const text = notesDiv.textContent.trim();

    if (text === '') {
      button.remove();
      notesDiv.remove();
    }
  });
}
function collapseNotesInitially() {
  document.querySelectorAll('.notes').forEach(notesDiv => {
    notesDiv.classList.add('hidden');
  });
}
  function setupClickHandlers() {
    document.removeEventListener('click', handleClicks);
    document.addEventListener('click', handleClicks);
  }

  function handleClicks(e) {

  // =========================
  // ABSTRACT TOGGLE
  // =========================
  if (e.target.classList.contains('expand-btn') &&
      !e.target.classList.contains('expand-btn-notes')) {

    const entry = e.target.closest('.bib-entry');
    const abstractDiv = entry?.querySelector('.abstract');

    if (!abstractDiv) return;

    const isHidden = abstractDiv.classList.toggle('hidden');

    e.target.textContent = isHidden
      ? 'Show Abstract'
      : 'Hide Abstract';
  }

  // =========================
  // NOTES TOGGLE
  // =========================
  if (e.target.classList.contains('expand-btn-notes')) {

    const entry = e.target.closest('.bib-entry');
    const notesDiv = entry?.querySelector('.notes');

    if (!notesDiv) return;

    const isHidden = notesDiv.classList.toggle('hidden');

    e.target.textContent = isHidden
      ? 'Show Notes'
      : 'Hide Notes';
  }
}

  function toggleTag(tagName, element, normalized) {
    if (ACTIVE_TAGS.has(normalized)) {
      ACTIVE_TAGS.delete(normalized);
      element.classList.remove('active');
      element.classList.add('inactive');
    } else {
      ACTIVE_TAGS.add(normalized);
      element.classList.add('active');
      element.classList.remove('inactive');
    }
    filterEntries();
  }

  function sortEntries() {
    const container = document.getElementById('listView');
    if (!container) return;

    const entries = Array.from(container.querySelectorAll('.bib-entry'));
    
    entries.sort((a, b) => {
      const titleA = a.querySelector('h3')?.textContent || '';
      const titleB = b.querySelector('h3')?.textContent || '';
      return titleA.localeCompare(titleB);
    });

    entries.forEach(entry => container.appendChild(entry));
  }

  function normalizeTag(tag) {
    return tag.toLowerCase().replace(/\s+/g, '_').replace(/,/g, '').replace(/\./g, '').replace(/'/g, '').replace(/"/g, '');
  }

  function matchesSearch(entry) {
    if (!SEARCH_TERM) return true;

    const searchText = SEARCH_TERM;
    
    const title = entry.querySelector('h3')?.textContent || '';
    if (title.toLowerCase().includes(searchText)) return true;

    const tagElements = entry.querySelectorAll('.tag');
    for (let tag of tagElements) {
      if (tag.textContent.toLowerCase().includes(searchText)) return true;
    }

    const abstractDiv = entry.querySelector('.abstract');
    if (abstractDiv) {
      const abstractText = abstractDiv.textContent || '';
      if (abstractText.toLowerCase().includes(searchText)) return true;
    }

    const notesDiv = entry.querySelector('.notes');
    if (notesDiv) {
      const notesText = notesDiv.textContent || '';
      if (notesText.toLowerCase().includes(searchText)) return true;
    }

    return false;
  }

  function filterEntries() {
    const entries = document.querySelectorAll('.bib-entry');
    
    entries.forEach(entry => {
      const entryTags = (entry.getAttribute('data-tags') || '').split(' ').filter(t => t);
      
      let matchesTags = true;
      if (ACTIVE_TAGS.size > 0) {
        matchesTags = Array.from(ACTIVE_TAGS).every(activeTag => {
          return entryTags.includes(activeTag);
        });
      }

      const matchesSearchTerm = matchesSearch(entry);

      entry.style.display = (matchesTags && matchesSearchTerm) ? 'block' : 'none';
    });
    updateCounter();
  }

  // Starte wenn DOM bereit
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }

  document.addEventListener('DOMContentLoaded', init);
})();
updateCounter();