MediaWiki

BibTest.js: Difference between revisions

From Illustrations in German Translations of Mark Twain's Works

No edit summary
No edit summary
Line 21: Line 21:


     // Sammle alle Original-Taggamen aus den Einträgen
     // Sammle alle Original-Taggamen aus den Einträgen
     const allTagsMap = new Map(); // normalized -> original name
     const allTagsMap = new Map();
     document.querySelectorAll('.bib-entry .tag').forEach(tagEl => {
     document.querySelectorAll('.bib-entry .tag').forEach(tagEl => {
       const originalName = tagEl.textContent.trim();
       const originalName = tagEl.textContent.trim();
Line 30: Line 30:
     // Lösche alte Tags aus tagBar (aber nur einmal!)
     // Lösche alte Tags aus tagBar (aber nur einmal!)
     if (tagBar.children.length === 0) {
     if (tagBar.children.length === 0) {
      // Erstelle neue Tags mit Original-Namen
       Array.from(allTagsMap.entries())
       Array.from(allTagsMap.entries())
         .sort((a, b) => a[1].localeCompare(b[1])) // Sortiere nach original name
         .sort((a, b) => a[1].localeCompare(b[1]))
         .forEach(([normalized, originalName]) => {
         .forEach(([normalized, originalName]) => {
           const span = document.createElement('span');
           const span = document.createElement('span');
           span.className = 'tag inactive';
           span.className = 'tag inactive';
           span.textContent = originalName; // Zeige original name
           span.textContent = originalName;
           span.dataset.normalized = normalized; // Speichere normalized für Filterung
           span.dataset.normalized = normalized;
           span.onclick = () => toggleTag(originalName, span, normalized);
           span.onclick = () => toggleTag(originalName, span, normalized);
           tagBar.appendChild(span);
           tagBar.appendChild(span);
Line 43: Line 42:
     }
     }


     // Search Input Listener (nur wenn nicht schon gesetzt)
     // Search Input Listener
     if (searchInput && !searchInput.dataset.listenerSet) {
     if (searchInput && !searchInput.dataset.listenerSet) {
       searchInput.addEventListener('input', (e) => {
       searchInput.addEventListener('input', (e) => {
Line 52: Line 51:
     }
     }


    // Sortiere Einträge alphabetisch
     sortEntries();
     sortEntries();
   }
   }


   function hideEmptyAbstracts() {
   function hideEmptyAbstracts() {
     document.querySelectorAll('.bib-entry .abstract').forEach(abstractDiv => {
     document.querySelectorAll('.bib-entry').forEach(entry => {
       const p = abstractDiv.querySelector('p');
       const abstractDiv = entry.querySelector('.abstract');
       const expandBtn = abstractDiv.previousElementSibling;
       const expandBtn = entry.querySelector('.expand-btn');
        
        
       if (!p || !expandBtn) return;
       if (!abstractDiv || !expandBtn) return;
        
        
       const abstractText = p.textContent.trim();
      const p = abstractDiv.querySelector('p');
       const abstractText = p ? p.textContent.trim() : '';
        
        
      // Verstecke Button wenn Abstract leer ist oder nur "No abstract available"
       if (!abstractText || abstractText === 'No abstract available.') {
       if (!abstractText || abstractText === 'No abstract available.') {
         if (expandBtn.classList.contains('expand-btn')) {
         expandBtn.style.display = 'none';
          expandBtn.style.display = 'none';
        }
         abstractDiv.style.display = 'none';
         abstractDiv.style.display = 'none';
       } else {
       } else {
         if (expandBtn.classList.contains('expand-btn')) {
         expandBtn.style.display = 'block';
          expandBtn.style.display = 'block';
        }
         abstractDiv.style.display = 'block';
         abstractDiv.style.display = 'block';
       }
       }
Line 81: Line 75:


   function hideEmptyNotes() {
   function hideEmptyNotes() {
     document.querySelectorAll('.bib-entry .notes').forEach(notesDiv => {
     document.querySelectorAll('.expand-btn-notes').forEach(notesBtn => {
      const notesDiv = notesBtn.nextElementSibling;
     
      if (!notesDiv || !notesDiv.classList.contains('notes')) {
        console.warn('Notes Div nicht gefunden nach Button');
        return;
      }
     
       const p = notesDiv.querySelector('p');
       const p = notesDiv.querySelector('p');
       const expandBtn = notesDiv.previousElementSibling;
       const notesText = p ? p.textContent.trim() : '';
     
      if (!p || !expandBtn) return;
        
        
       const notesText = p.textContent.trim();
       console.log('Notes Text:', notesText); // Debug
        
        
      // Verstecke Button wenn Notes leer sind
       if (!notesText) {
       if (!notesText) {
         if (expandBtn.classList.contains('expand-btn-notes')) {
         notesBtn.style.display = 'none';
          expandBtn.style.display = 'none';
        }
         notesDiv.style.display = 'none';
         notesDiv.style.display = 'none';
       } else {
       } else {
         if (expandBtn.classList.contains('expand-btn-notes')) {
         notesBtn.style.display = 'block';
          expandBtn.style.display = 'block';
        }
         notesDiv.style.display = 'block';
         notesDiv.style.display = 'block';
       }
       }
Line 141: Line 135:
     const searchText = SEARCH_TERM;
     const searchText = SEARCH_TERM;
      
      
    // Suche im Titel
     const title = entry.querySelector('h3')?.textContent || '';
     const title = entry.querySelector('h3')?.textContent || '';
     if (title.toLowerCase().includes(searchText)) return true;
     if (title.toLowerCase().includes(searchText)) return true;


    // Suche in den Tags
     const tagElements = entry.querySelectorAll('.tag');
     const tagElements = entry.querySelectorAll('.tag');
     for (let tag of tagElements) {
     for (let tag of tagElements) {
Line 151: Line 143:
     }
     }


    // Suche im Abstract
     const abstractDiv = entry.querySelector('.abstract');
     const abstractDiv = entry.querySelector('.abstract');
     if (abstractDiv) {
     if (abstractDiv) {
Line 158: Line 149:
     }
     }


    // Suche in den Notes
     const notesDiv = entry.querySelector('.notes');
     const notesDiv = entry.querySelector('.notes');
     if (notesDiv) {
     if (notesDiv) {
Line 174: Line 164:
       const entryTags = (entry.getAttribute('data-tags') || '').split(' ').filter(t => t);
       const entryTags = (entry.getAttribute('data-tags') || '').split(' ').filter(t => t);
        
        
      // Tag-Filter
       let matchesTags = true;
       let matchesTags = true;
       if (ACTIVE_TAGS.size > 0) {
       if (ACTIVE_TAGS.size > 0) {
Line 182: Line 171:
       }
       }


      // Search-Filter
       const matchesSearchTerm = matchesSearch(entry);
       const matchesSearchTerm = matchesSearch(entry);


      // Beide Bedingungen müssen erfüllt sein (AND-Logik)
       entry.style.display = (matchesTags && matchesSearchTerm) ? 'block' : 'none';
       entry.style.display = (matchesTags && matchesSearchTerm) ? 'block' : 'none';
     });
     });
Line 193: Line 180:
   document.addEventListener('click', function(e) {
   document.addEventListener('click', function(e) {
     // Abstract Button
     // Abstract Button
     if (e.target.classList.contains('expand-btn')) {
     if (e.target.classList.contains('expand-btn') && !e.target.classList.contains('expand-btn-notes')) {
       const abstractDiv = e.target.nextElementSibling;
       const abstractDiv = e.target.nextElementSibling;
       if (abstractDiv && abstractDiv.classList.contains('abstract')) {
       if (abstractDiv && abstractDiv.classList.contains('abstract')) {
Line 203: Line 190:
     }
     }
      
      
     // Notes Button
     // Notes Button - mit besserer Fehlerbehandlung
     if (e.target.classList.contains('expand-btn-notes')) {
     if (e.target.classList.contains('expand-btn-notes')) {
      console.log('Notes Button geklickt');
       const notesDiv = e.target.nextElementSibling;
       const notesDiv = e.target.nextElementSibling;
       if (notesDiv && notesDiv.classList.contains('notes')) {
     
         notesDiv.classList.toggle('hidden');
       if (!notesDiv) {
         e.target.textContent = notesDiv.classList.contains('hidden')
         console.error('Kein Element nach Notes Button gefunden');
          ? 'Show Notes'
         return;
          : 'Hide Notes';
       }
       }
     
      if (!notesDiv.classList.contains('notes')) {
        console.error('Nächstes Element ist kein .notes Div:', notesDiv);
        return;
      }
     
      notesDiv.classList.toggle('hidden');
      e.target.textContent = notesDiv.classList.contains('hidden')
        ? 'Show Notes'
        : 'Hide Notes';
     }
     }
   });
   });
Line 222: Line 219:
   }
   }


  // Für den Fall dass MediaWiki später nachlädt
   document.addEventListener('DOMContentLoaded', init);
   document.addEventListener('DOMContentLoaded', init);
})();
})();

Revision as of 22:42, 21 May 2026

/**
 * Bibliography Tag Filter, Sorting and Search
 */

(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;

    // Verstecke leere Abstract- und Notes-Buttons
    hideEmptyAbstracts();
    hideEmptyNotes();

    // Sammle alle Original-Taggamen 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;
          span.onclick = () => toggleTag(originalName, span, normalized);
          tagBar.appendChild(span);
        });
    }

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

    sortEntries();
  }

  function hideEmptyAbstracts() {
    document.querySelectorAll('.bib-entry').forEach(entry => {
      const abstractDiv = entry.querySelector('.abstract');
      const expandBtn = entry.querySelector('.expand-btn');
      
      if (!abstractDiv || !expandBtn) return;
      
      const p = abstractDiv.querySelector('p');
      const abstractText = p ? p.textContent.trim() : '';
      
      if (!abstractText || abstractText === 'No abstract available.') {
        expandBtn.style.display = 'none';
        abstractDiv.style.display = 'none';
      } else {
        expandBtn.style.display = 'block';
        abstractDiv.style.display = 'block';
      }
    });
  }

  function hideEmptyNotes() {
    document.querySelectorAll('.expand-btn-notes').forEach(notesBtn => {
      const notesDiv = notesBtn.nextElementSibling;
      
      if (!notesDiv || !notesDiv.classList.contains('notes')) {
        console.warn('Notes Div nicht gefunden nach Button');
        return;
      }
      
      const p = notesDiv.querySelector('p');
      const notesText = p ? p.textContent.trim() : '';
      
      console.log('Notes Text:', notesText); // Debug
      
      if (!notesText) {
        notesBtn.style.display = 'none';
        notesDiv.style.display = 'none';
      } else {
        notesBtn.style.display = 'block';
        notesDiv.style.display = 'block';
      }
    });
  }

  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';
    });
  }

  // Abstract- und Notes-Button Toggle
  document.addEventListener('click', function(e) {
    // Abstract Button
    if (e.target.classList.contains('expand-btn') && !e.target.classList.contains('expand-btn-notes')) {
      const abstractDiv = e.target.nextElementSibling;
      if (abstractDiv && abstractDiv.classList.contains('abstract')) {
        abstractDiv.classList.toggle('hidden');
        e.target.textContent = abstractDiv.classList.contains('hidden')
          ? 'Show Abstract'
          : 'Hide Abstract';
      }
    }
    
    // Notes Button - mit besserer Fehlerbehandlung
    if (e.target.classList.contains('expand-btn-notes')) {
      console.log('Notes Button geklickt');
      const notesDiv = e.target.nextElementSibling;
      
      if (!notesDiv) {
        console.error('Kein Element nach Notes Button gefunden');
        return;
      }
      
      if (!notesDiv.classList.contains('notes')) {
        console.error('Nächstes Element ist kein .notes Div:', notesDiv);
        return;
      }
      
      notesDiv.classList.toggle('hidden');
      e.target.textContent = notesDiv.classList.contains('hidden')
        ? 'Show Notes'
        : 'Hide Notes';
    }
  });

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

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