MediaWiki

Common.js: Difference between revisions

From Illustrations in German Translations of Mark Twain's Works

No edit summary
No edit summary
 
(62 intermediate revisions by 2 users not shown)
Line 1: Line 1:
/* Any JavaScript here will be loaded for all users on every page load. */
/* Any JavaScript here will be loaded for all users on every page load. */
console.log("Common.js läuft");
// ===============================
// Catalog Script
// ===============================
// Canvas prüfen
const canvas = document.getElementById('lineChart');
console.log("Canvas:", canvas);


mw.loader.using('jquery').then(function () {
mw.loader.using('jquery').then(function () {
Line 162: Line 176:
     rows.forEach(row => {
     rows.forEach(row => {
         var imageCell = row.cells[8];  // Die 9. Zelle enthält den Dateinamen als Link
         var imageCell = row.cells[8];  // Die 9. Zelle enthält den Dateinamen als Link
         var link = imageCell.querySelector('a'); // Link innerhalb der Zelle
         var link = imageCell.querySelector('a');
         if (link) {
         if (link) {
             var imageName = link.textContent.trim(); // Der Text des Links ist der Dateiname
             var imageName = link.textContent.trim();
             var imageUrl = '/index.php/Special:Redirect/file/' + imageName + '.jpg';
             var imageUrl = '/index.php/Special:Redirect/file/' + imageName + '.jpg';
             imageLinks.push(imageUrl);
             imageLinks.push(imageUrl);
Line 177: Line 191:
         galleryWindow.document.write('body { font-family: sans-serif; padding: 20px; }');
         galleryWindow.document.write('body { font-family: sans-serif; padding: 20px; }');
         galleryWindow.document.write('img { max-width: 300px; margin: 10px; cursor: pointer; display: inline-block; }');
         galleryWindow.document.write('img { max-width: 300px; margin: 10px; cursor: pointer; display: inline-block; }');
        galleryWindow.document.write('#infoBox { position:absolute; background: rgba(255,255,255,0.95); border:1px solid #ccc; padding:10px; max-width:300px; display:none; z-index:9999; font-size:0.9em; pointer-events:none; }');
         galleryWindow.document.write('</style></head><body>');
         galleryWindow.document.write('</style></head><body>');
         galleryWindow.document.write('<h2>Results</h2>');
         galleryWindow.document.write('<h2>Results</h2>');
Line 183: Line 198:
             galleryWindow.document.write('<img src="' + url + '" onclick="window.open(\'' + url + '\')">');
             galleryWindow.document.write('<img src="' + url + '" onclick="window.open(\'' + url + '\')">');
         });
         });
        // Infobox-Container
        galleryWindow.document.write('<div id="infoBox"></div>');
        // Script für Hover-Infobox mit Raw-Text und Cache
        galleryWindow.document.write('<script>' +
        'const imgs = document.querySelectorAll("img");' +
        'const infoBox = document.getElementById("infoBox");' +
        'const mediaCache = new Map();' +
        'imgs.forEach(img => {' +
        '  img.removeAttribute("title");' + // Browser Tooltip deaktivieren
        '  img.addEventListener("mouseenter", async function(e) {' +
        '    const fileName = img.src.split("/").pop().replace(".jpg","");' +
        '    if(mediaCache.has(fileName)) {' +
        '      infoBox.innerHTML = mediaCache.get(fileName);' +
        '      infoBox.style.display = "block";' +
        '      return;' +
        '    }' +
        '    infoBox.textContent = "Loading ...";' +
        '    infoBox.style.display = "block";' +
        '    try {' +
        '      const resp = await fetch("/index.php?title=File:" + encodeURIComponent(fileName) + ".jpg&action=raw");' +
        '      const text = await resp.text();' +
        '      const match = text.match(/\\{\\{MediaInfo([\\s\\S]*?)\\}\\}/);' +
        '      if(!match) { infoBox.textContent = "No Data found."; mediaCache.set(fileName,"No Data found."); return; }' +
        '      const block = match[1];' +
        '      const fields = ["title","chapter","illustration","illustrator","year","tags"];' +
        '      let html = "<ul style=\'margin:0; padding-left:1em; list-style:none;\'>";' +
        '      fields.forEach(field => {' +
        '        const m = block.match(new RegExp("\\\\|\\\\s*" + field + "\\\\s*=([^\\\\n]*)"));' +
        '        if(m) html += `<li><b>${field.charAt(0).toUpperCase() + field.slice(1)}:</b> ${m[1].trim()}</li>`;' +
        '      });' +
        '      html += "</ul>";' +
        '      mediaCache.set(fileName, html);' +
        '      infoBox.innerHTML = html;' +
        '    } catch(err) {' +
        '      infoBox.textContent = "Error loading info";' +
        '      mediaCache.set(fileName,"Error loading info");' +
        '      console.error(err);' +
        '    }' +
        '  });' +
        '  img.addEventListener("mousemove", function(e){' +
        '    infoBox.style.top = (e.pageY + 15) + "px";' +
        '    infoBox.style.left = (e.pageX + 15) + "px";' +
        '  });' +
        '  img.addEventListener("mouseleave", function(){' +
        '    infoBox.style.display = "none";' +
        '  });' +
        '});' +
        '<\/script>');


         galleryWindow.document.write('</body></html>');
         galleryWindow.document.write('</body></html>');
         galleryWindow.document.close();
         galleryWindow.document.close();
     } else {
     } else {
         alert('Popup wurde blockiert. Bitte erlaube Popups für diese Seite.');
         alert("Popup wurde blockiert. Bitte erlaube Popups für diese Seite.");
     }
     }
}
}


$(document).ready(function () {
// ===============================
    const columnCount = 9;
// Comparison / Slideshow Script
// ===============================


    // Neue Bedingung hinzufügen
// ===============================
    $('#addCondition').on('click', function () {
// DataTable Initialisierung
        const newRow = $('<div class="search-row" style="margin-bottom: 5px;">' +
// ===============================
            '<select class="bool-select">' +
$(document).ready(function() {
                '<option value="AND">AND</option>' +
    if ($('#catalog').length) {
                '<option value="OR">OR</option>' +
         $('#catalog').DataTable();
                '<option value="NOT">NOT</option>' +
     }
                '<option value="XOR">XOR</option>' +
});
            '</select>' +
            '<select class="field-select">' +
                '<option value="8">ID</option>' +
                '<option value="all">All</option>' +
                '<option value="0">Book</option>' +
                '<option value="1">Year</option>' +
                '<option value="2">Illustrator</option>' +
                '<option value="3">Chpt in Orig</option>' +
                '<option value="4">Chpt in this Ed.</option>' +
                '<option value="5">Ill. in Chpt.</option>' +
                '<option value="6">Illustration Title</option>' +
                '<option value="7">Tags</option>' +
            '</select>' +
            '<input type="text" class="search-input" placeholder=" ">' +
            '<button class="remove-condition" style="margin-left: 5px;">🗑️</button>' +
        '</div>');
         $('#searchConditions').append(newRow);
     });


    // Dynamisch hinzugefügte Bedingungen löschen
// ===============================
    $('#searchConditions').on('click', '.remove-condition', function () {
// Globale Slideshow-Objekte
        if ($('#searchConditions .search-row').not('.fixed').length > 1) {
// ===============================
            $(this).closest('.search-row').remove();
const slideshows = {
        }
    A: { images: [], index: 0 },
    });
    B: { images: [], index: 0 },
 
    C: { images: [], index: 0 },
    // Suche ausführen
     D: { images: [], index: 0 },
    $('#runAdvancedSearch').on('click', function () {
};
        const table = $('#catalog').DataTable();
        table.search('').columns().search('');
 
        const filters = [];
 
        // 1. Feste erste Bedingung einfügen
        filters.push({
            column: '8', // ID-Spalte
            term: 'hf',
            boolOp: null
        });
 
        // 2. Alle weiteren Bedingungen
        $('#searchConditions .search-row').not('.fixed').each(function () {
            const column = $(this).find('.field-select').val();
            const term = $(this).find('.search-input').val().trim();
            const boolOp = $(this).find('.bool-select').val();
 
            if (term !== '') {
                filters.push({ column, term, boolOp });
            }
        });
 
        if (filters.length === 0) {
            table.draw();
            return;
        }
 
        // Filterlogik
        $.fn.dataTable.ext.search = [];
        $.fn.dataTable.ext.search.push(function (settings, data, dataIndex) {
            let result = null;
 
            filters.forEach(function (filter, i) {
                const val = filter.column === 'all'
                    ? data.join(' ').toLowerCase()
                    : (data[filter.column] || '').toLowerCase();
 
                const match = val.includes(filter.term.toLowerCase());
 
                if (i === 0) {
                    result = match;
                } else {
                    switch (filter.boolOp) {
                        case 'AND': result = result && match; break;
                        case 'OR': result = result || match; break;
                        case 'NOT': result = result && !match; break;
                        case 'XOR': result = (result && !match) || (!result && match); break;
                    }
                }
            });
 
            return result;
        });
 
        table.draw();
     });
});


// ===============================
// ===============================
// Comparison / Slideshow Script
// Hilfsfunktionen
// ===============================
// ===============================


// Gefilterte Bild-Links aus DataTable holen
// Gefilterte Bild-Links aus DataTable holen
function getFilteredImageLinks() {
function getFilteredImageLinks() {
     var rows = document.querySelectorAll('#catalog tbody tr');
     const rows = document.querySelectorAll('#catalog tbody tr');
     var images = [];
     const images = [];
 
     rows.forEach(row => {
     rows.forEach(row => {
        // Nur sichtbare Zeilen (durch DataTables-Filter!) berücksichtigen
         if (row.style.display !== 'none') {
         if (row.style.display !== 'none') {
             var imageCell = row.cells[8];
             const cell = row.cells[8];
             var link = imageCell ? imageCell.querySelector('a') : null;
             const link = cell ? cell.querySelector('a') : null;
             if (link) {
             if (link) {
                 var imageName = link.textContent.trim();
                 const name = link.textContent.trim();
                 var imageUrl = '/index.php/Special:Redirect/file/' + imageName + '.jpg';
                 const url = '/index.php/Special:Redirect/file/' + name + '.jpg';
 
                 images.push({ url: url, name: name + '.jpg' });
                 images.push({
                    url: imageUrl,               // echte Bilddatei (für Slideshow)
                    name: imageName + '.jpg'     // Dateiname (für File:-Link)
                });
             }
             }
         }
         }
     });
     });
     return images;
     return images;
}
}
// Globale Slideshow-Objekte
const slideshows = {
    A: { images: [], index: 0 },
    B: { images: [], index: 0 },
    C: { images: [], index: 0 },
    D: { images: [], index: 0 },
};


// Counter-Update
// Counter-Update
Line 330: Line 305:
     const data = slideshows[target];
     const data = slideshows[target];
     const total = data.images.length;
     const total = data.images.length;
     const current = total > 0 ? data.index + 1 : 0; // Index ist 0-basiert
     const current = total > 0 ? data.index + 1 : 0;
     const counterEl = document.getElementById("counter" + target);
     const counterEl = document.getElementById("counter" + target);
     if (counterEl) {
     if (counterEl) counterEl.textContent = current + "/" + total;
        counterEl.textContent = current + "/" + total;
}
     }
 
// ===============================
// MediaInfo Funktionen (aus HTML-Text der File-Seite)
// ===============================
function loadMediaInfo(target, filename) {
    const infoBox = document.getElementById('mediainfo' + target);
    if (!infoBox) return;
    infoBox.textContent = "Loading ...";
 
    fetch('/index.php?title=File:' + encodeURIComponent(filename) + '&action=raw')
    .then(resp => resp.text())
    .then(text => {
        const match = text.match(/\{\{MediaInfo([\s\S]*?)\}\}/);
        if (!match) {
            infoBox.textContent = "No Data found.";
            return;
        }
 
        const block = match[1];
 
        // Felder, die angezeigt werden sollen
        const fields = ["title","chapter","illustration","illustrator","year","tags"];
        let htmlList = "<ul style='margin:0; padding-left:1em; list-style:none;'>";
 
        fields.forEach(field => {
            const m = block.match(new RegExp("\\|\\s*" + field + "\\s*=([^\\n]*)"));
            if (m) {
                // Label groß schreiben
                const label = field.charAt(0).toUpperCase() + field.slice(1);
                htmlList += `<li><b>${label}:</b> ${m[1].trim()}</li>`;
            }
        });
 
        htmlList += "</ul>";
        infoBox.innerHTML = htmlList;
 
        // Optional: etwas CSS stylen
        infoBox.style.backgroundColor = "#f9f9f9";
        infoBox.style.border = "1px solid #ccc";
        infoBox.style.padding = "0.5em 1em";
        infoBox.style.marginTop = "0.5em";
        infoBox.style.borderRadius = "4px";
        infoBox.style.fontSize = "0.9em";
     })
    .catch(err => {
        console.error(err);
        infoBox.textContent = "Error";
    });
}
}


// Bildanzeige
 
 
 
// ===============================
// Slideshow-Funktionen
// ===============================
function updateSlide(target) {
function updateSlide(target) {
     const data = slideshows[target];
     const data = slideshows[target];
     const imgElement = document.getElementById('slide' + target);
     const imgEl = document.getElementById('slide' + target);


     if (data.images.length === 0) {
     if (data.images.length === 0) {
         imgElement.src = '';
         imgEl.src = '';
         imgElement.alt = 'No Images';
         imgEl.alt = 'No Images';
         imgElement.onclick = null;
         imgEl.onclick = null;
         updateCounter(target);
         updateCounter(target);
        // Info leeren
         const infoBox = document.getElementById('mediainfo' + target);
         const infoBox = document.getElementById('mediainfo' + target);
         if (infoBox) infoBox.textContent = "";
         if(infoBox) infoBox.textContent = "";
         return;
         return;
     }
     }
Line 357: Line 382:
     if (data.index >= data.images.length) data.index = 0;
     if (data.index >= data.images.length) data.index = 0;


     const currentImage = data.images[data.index];
     const current = data.images[data.index];
 
     imgEl.src = current.url;
     imgElement.src = currentImage.url;
     imgEl.alt = current.name;
     imgElement.alt = currentImage.name;
     imgEl.style.cursor = "pointer";
     imgElement.style.cursor = "pointer";
     imgEl.onclick = () => window.open('/File:' + current.name, '_blank');
     imgElement.onclick = function () {
        window.open('/File:' + currentImage.name, '_blank');
    };


     updateCounter(target);
     updateCounter(target);
 
     loadMediaInfo(target, current.name);
     // Neue Info laden
    showMediaInfo(target, currentImage.name);
}
}


// Slideshow initial befüllen
function populateSlideshow(target) {
function populateSlideshow(target) {
     const images = getFilteredImageLinks();
     const images = getFilteredImageLinks();
Line 379: Line 397:
     slideshows[target].index = 0;
     slideshows[target].index = 0;


     const imgElement = document.getElementById('slide' + target);
     const imgEl = document.getElementById('slide' + target);
 
     if(images.length > 0){
     if (images.length > 0) {
         imgEl.src = images[0].url;
         imgElement.src = images[0].url;
         imgEl.alt = images[0].name;
         imgElement.alt = images[0].name;
         imgEl.style.cursor = "pointer";
         imgElement.style.cursor = "pointer";
         imgEl.onclick = () => window.open('/File:' + images[0].name, '_blank');
         imgElement.onclick = function () {
         loadMediaInfo(target, images[0].name);
            window.open('/File:' + images[0].name, '_blank');
         };
     } else {
     } else {
         imgElement.src = '';
         imgEl.src = '';
         imgElement.alt = 'No Images';
         imgEl.alt = 'No Images';
         imgElement.onclick = null;
         imgEl.onclick = null;
        const infoBox = document.getElementById('mediainfo' + target);
        if(infoBox) infoBox.textContent = "";
     }
     }
    // Counter sofort aktualisieren
     updateCounter(target);
     updateCounter(target);
}
}


// Buttons
function nextSlideX(target){
function nextSlideX(target) {
     slideshows[target].index++;
     slideshows[target].index++;
     updateSlide(target);
     updateSlide(target);
}
}


function prevSlideX(target) {
function prevSlideX(target){
     slideshows[target].index--;
     slideshows[target].index--;
     updateSlide(target);
     updateSlide(target);
}
}


// Neue Seite mit allen Bildern öffnen
function openSlideshowInNewTab(target){
function openSlideshowInNewTab(target) {
     const data = slideshows[target];
     const data = slideshows[target];
     if (!data.images || data.images.length === 0) {
     if(!data.images || data.images.length===0){
         alert("No images to display for slideshow " + target);
         alert("No images to display for slideshow " + target);
         return;
         return;
     }
     }
     const newTab = window.open();
     const newTab = window.open();
     if (!newTab) {
     if(!newTab){ alert("Popup blocked! Bitte Popups erlauben."); return; }
        alert("Popup blocked! Bitte Popups für diese Seite erlauben.");
        return;
    }


     let html = "<html><head><title>Slideshow " + target + "</title></head><body style='font-family:sans-serif'>";
     let html = "<html><head><title>Slideshow " + target + "</title>";
    html += "<style>";
    html += "body { font-family:sans-serif; padding:20px; }";
    html += "img { max-width:300px; max-height:400px; margin:10px; cursor:pointer; display:inline-block; object-fit:contain; }";
    html += "#infoBox { position:absolute; background: rgba(255,255,255,0.95); border:1px solid #ccc; padding:10px; max-width:300px; display:none; z-index:9999; font-size:0.9em; pointer-events:none; }";
    html += "</style></head><body>";
     html += "<h2>Slideshow " + target + " (" + data.images.length + " images)</h2>";
     html += "<h2>Slideshow " + target + " (" + data.images.length + " images)</h2>";
     html += "<div style='display:flex; flex-wrap:wrap; gap:10px;'>";
   
    // Galerie wie im Catalog-Script: lockeres Grid ohne Flexbox
     html += "<div>";
    data.images.forEach(img=>{
        html += "<img src='" + img.url + "' alt='" + img.name + "' onclick=\"window.open('/File:" + img.name + "', '_blank')\">";
    });
    html += "</div>";
   
    // Infobox für MediaInfo
    html += "<div id='infoBox'></div>";


     data.images.forEach(img => {
     // Script für MediaInfo-Hover
        html += "<div>";
    html += "<script>";
        html += "<a href='/File:" + img.name + "' target='_blank'>";
    html += "const imgs = document.querySelectorAll('img');";
        html += "<img src='" + img.url + "' style='max-width:300px; height:auto;'>";
    html += "const infoBox = document.getElementById('infoBox');";
        html += "</a></div>";
    html += "const mediaCache = new Map();";
     });
    html += "imgs.forEach(img => {";
    html += " img.removeAttribute('title');";
    html += "  img.addEventListener('mouseenter', async function(e) {";
    html += "    const fileName = img.alt;";
    html += "    if(mediaCache.has(fileName)) { infoBox.innerHTML = mediaCache.get(fileName); infoBox.style.display='block'; return; }";
    html += "    infoBox.textContent = 'Loading ...'; infoBox.style.display='block';";
    html += "    try {";
    html += "     const resp = await fetch('/index.php?title=File:' + encodeURIComponent(fileName) + '&action=raw');";
    html += "      const text = await resp.text();";
    html += "      const match = text.match(/\\{\\{MediaInfo([\\s\\S]*?)\\}\\}/);";
    html += "     if(!match) { infoBox.textContent='No Data found.'; mediaCache.set(fileName,'No Data found.'); return; }";
    html += "      const block = match[1];";
    html += "     const fields = ['title','chapter','illustration','illustrator','year','tags'];";
    html += "     let htmlList = '<ul style=\"margin:0;padding-left:1em;list-style:none;\">';";
    html += "      fields.forEach(field => {";
    html += "        const m = block.match(new RegExp('\\\\|\\\\s*' + field + '\\\\s*=([^\\\\n]*)'));";
    html += "       if(m) htmlList += '<li><b>' + field.charAt(0).toUpperCase() + field.slice(1) + ':</b> ' + m[1].trim() + '</li>';";  
     html += "      });";
    html += "      htmlList += '</ul>'; mediaCache.set(fileName, htmlList); infoBox.innerHTML = htmlList;";
    html += "    } catch(err) { infoBox.textContent='Error loading info'; mediaCache.set(fileName,'Error loading info'); console.error(err); }";
    html += "  });";
    html += "  img.addEventListener('mousemove', function(e){ infoBox.style.top=(e.pageY+15)+'px'; infoBox.style.left=(e.pageX+15)+'px'; });";
    html += "  img.addEventListener('mouseleave', function(){ infoBox.style.display='none'; });";
    html += "});";
    html += "<\/script>";


     html += "</div></body></html>";
     html += "</body></html>";


     newTab.document.open();
     newTab.document.open();
Line 441: Line 489:
}
}


// ===============================
// Chapter Script
// Zusatz: MediaInfo abrufen & anzeigen
importScript('MediaWiki:ChapterSlides.js');
// ===============================
 
// Chapter Script
importScript('MediaWiki:ChapterSlidesAlt.js');
 
// Filter Scripts, character pages
importScript('MediaWiki:CharacterFilter.js');


// MediaInfo aus API laden
// CharacterDistribution
async function fetchMediaInfo(filename) {
// Chart.js laden
    const apiUrl = '/api.php?action=query&prop=revisions&titles=File:' + encodeURIComponent(filename) + '&rvprop=content&format=json';
mw.loader.load('https://cdn.jsdelivr.net/npm/chart.js');


     try {
// Chart initialisieren
         const response = await fetch(apiUrl);
function initChart() {
         const data = await response.json();
     if (typeof Chart === 'undefined') {
         setTimeout(initChart, 50);
         return;
    }


        // Wikitext der Seite extrahieren
    const canvas = document.getElementById('lineChart');
        const pages = data.query.pages;
    if (!canvas) return;
        const page = pages[Object.keys(pages)[0]];
        if (!page.revisions) return null;


        const wikitext = page.revisions[0]['*'] || page.revisions[0]['slots']?.main['*'];
    const ctx = canvas.getContext('2d');
        if (!wikitext) return null;


        // MediaInfo-Block herausziehen
    // HiDPI / Retina: Pixelratio berücksichtigen
        const match = wikitext.match(/\{\{MediaInfo([\s\S]*?)\}\}/);
    const dpr = window.devicePixelRatio || 1;
        if (!match) return null;
    canvas.width = canvas.offsetWidth * dpr;
    canvas.height = canvas.offsetHeight * dpr;
    ctx.scale(dpr, dpr);


        const block = match[1];
    const data = {
        labels: ['Kemble (1885)', 'Schröder (1898)', 'Hirth (1920)', 'Trier (1936)', 'Harder (1938)', 'Kellerer (1938)', 'Busoni (1940)','Bebié (1944)'],
        datasets: [
            { label: 'Huck', data: [47.40,50.98,72.22,54.29,93.10,50.00,54.10,69.81], backgroundColor:'red', borderColor:'red', borderWidth:1, barPercentage:0.8 },
            { label: 'Jim', data: [17.92,29.41,22.22,28.57,44.83,18.18,26.23,17.00], backgroundColor:'blue', borderColor:'blue', borderWidth:1, barPercentage:0.8 },
            { label: 'Pap Finn', data: [5.78,7.84,5.56,5.71,6.90,9.09,4.92,1.88], backgroundColor:'green', borderColor:'green', borderWidth:1, barPercentage:0.8 },
            { label: 'King/Duke', data: [13.29,13.73,16.67,20.00,17.24,9.09,16.39,3.77], backgroundColor:'orange', borderColor:'orange', borderWidth:1, barPercentage:0.8 },
            { label: 'Tom', data: [11.56,7.84,19.44,11.43,24.14,9.09,11.48,13.21], backgroundColor:'black', borderColor:'black', borderWidth:1, barPercentage:0.8 },
            { label: 'Others', data: [38.73,29.41,36.11,31.43,17.24,31.82,22.95,13.21], backgroundColor:'pink', borderColor:'pink', borderWidth:1, barPercentage:0.8 },
            { type: 'scatter', label:'Total # of Illustrations', data:[173,51,36,35,29,22,61,53], borderColor:'purple', borderWidth:3, xAxisID:'x2', pointStyle:'cross', pointRadius:8, pointHoverRadius:10, showLine:false }
        ]
    };


         // Werte in Objekt parsen
    const config = {
        const info = {};
        type:'bar',
        const fields = ["title","chapter","illustration","illustrator","year","tags","publication"];
        data:data,
        fields.forEach(field => {
         options:{
            const regex = new RegExp("\\|\\s*" + field + "\\s*=([^\\n]*)");
            responsive:true,
            const m = block.match(regex);
            maintainAspectRatio:false, // damit Chart die Containerhöhe nutzt
             if (m) info[field] = m[1].trim();
            indexAxis:'y',
         });
            plugins:{ legend:{ display:true }, tooltip:{ mode:'index', intersect:false } },
            interaction:{ mode:'nearest', axis:'y', intersect:false },
            scales:{
                y:{ display:true, title:{ display:true, text:'Illustrator (Year)' }, stacked:false, barThickness:30 },
                x:{ display:true, position:'top', title:{ display:true, text:'% of Illustrations Featuring Character' }, min:0, max:100 },
                x2:{ display:true, position:'bottom', title:{ display:true, text:'Total # of Illustrations' }, min:0, max:180, grid:{ drawOnChartArea:false } }
             }
         }
    };


        return info;
     new Chart(ctx, config);
     } catch (err) {
    console.log('Chart erfolgreich initialisiert!');
        console.error("Fehler beim Laden der MediaInfo:", err);
        return null;
    }
}
}


// MediaInfo im passenden Textfeld anzeigen
$(document).ready(initChart);
async function showMediaInfo(target, filename) {
    const infoBox = document.getElementById('mediainfo' + target);
    if (!infoBox) return;


    infoBox.textContent = "Lade Informationen...";


    const info = await fetchMediaInfo(filename);
// initChart beim Laden starten
$(document).ready(initChart);


    if (!info) {
        infoBox.textContent = "Keine Informationen gefunden.";
        return;
    }


    let html = "<ul style='margin:0; padding-left:1em'>";
    for (const [key, value] of Object.entries(info)) {
        html += "<li><b>" + key + ":</b> " + value + "</li>";
    }
    html += "</ul>";


    infoBox.innerHTML = html;
}


// Media Viewer Adjustments to Display Title and Tags
importScript('MediaWiki:MediaViewerDisplay.js');


mw.loader.using(['mediawiki.util'], function () {


// Chapter Script
  function cleanFileLink(a) {
importScript('MediaWiki:ChapterSlides.js');
    // a.href = /index.php?title=Hf_1885_kmb_ch001_ill1.jpg&action=edit&redlink=1
    const title = a.textContent.trim();
    const file = title
      .replace(/\s+/g, '_')
      .replace(/\.(jpg|png|jpeg)$/i, '');


// Chapter Script
    return `<a href="/index.php/Special:Redirect/file/${file}.jpg">${file}</a>`;
importScript('MediaWiki:ChapterSlidesAlt.js');
  }


// Filter Scripts, character pages
  document.addEventListener('click', function (e) {
importScript('MediaWiki:CharacterFilter.js');
    if (e.target.id !== 'generateCatalogHTML') return;


// CharacterDistribution
    const dpl = document.getElementById('DPL');
// Chart.js von CDN laden
    if (!dpl) {
mw.loader.load('https://cdn.jsdelivr.net/npm/chart.js');
      alert('DPL table not found');
      return;
    }


// CharacterDistribution.js laden (klassischer Weg)
    const rows = dpl.querySelectorAll('tbody tr');
importScript('MediaWiki:CharacterDistribution.js');
    if (!rows.length) {
      alert('No data rows found');
      return;
    }


// Media Viewer Adjustments to Display Title and Tags
    let html = '';
importScript('MediaWiki:MediaViewerDisplay.js');
    rows.forEach(row => {
      const cells = row.querySelectorAll('td');
      if (cells.length < 8) return;


      const publication  = cells[0].textContent.trim();
      const year        = cells[1].textContent.trim();
      const illustrator  = cells[2].textContent.trim();
      const chapter      = cells[3].textContent.trim();
      const illustration = cells[4].textContent.trim();
      const title        = cells[5].textContent.trim();
      const tags        = cells[6].textContent.trim();
      const link        = cells[7].querySelector('a');


      if (!link) return;


      html +=
`<tr>
<td align="left"><font face="Liberation Serif">${publication}</font></td>
<td align="right"><font face="Liberation Serif">${year}</font></td>
<td align="left"><font face="Liberation Serif">${illustrator}</font></td>
<td align="center"><font face="Liberation Serif">${chapter}</font></td>
<td align="center"><font face="Liberation Serif">${chapter}</font></td>
<td align="center"><font face="Liberation Serif">${illustration}</font></td>
<td align="left"><font face="Liberation Serif">${title}</font></td>
<td align="left"><font face="Liberation Serif">-${tags.replace(/,\s*/g,' -')}</font></td>
<td align="left">${cleanFileLink(link)}</td>
</tr>\n`;
    });


if (mw.config.get('wgPageName') === 'Wiki/chardist') {
    const output = document.getElementById('catalogOutput');
    // Chart.js von CDN laden
    if (!output) {
    mw.loader.load('https://cdn.jsdelivr.net/npm/chart.js', function() {
      alert('Output textarea not found');
      return;
    }


        document.addEventListener('DOMContentLoaded', () => {
    output.value = html;
            const canvas = document.getElementById('testChart');
    output.focus();
            if (!canvas) return;
    output.select();


            const ctx = canvas.getContext('2d');
    alert('Static catalog HTML generated.');
            new Chart(ctx, {
  });
                type: 'bar',
                data: {
                    labels: ['A','B','C'],
                    datasets: [{
                        label: 'Test',
                        data: [1,2,3],
                        backgroundColor: 'red'
                    }]
                },
                options: {}
            });
        });


    });
});
}

Latest revision as of 22:52, 23 February 2026

/* Any JavaScript here will be loaded for all users on every page load. */
console.log("Common.js läuft");

// ===============================
// Catalog Script
// ===============================



// Canvas prüfen
const canvas = document.getElementById('lineChart');
console.log("Canvas:", canvas);




mw.loader.using('jquery').then(function () {

  console.log('jQuery ist verfügbar:', typeof $);

  $.getScript('https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js')
    .then(() => {
      console.log('DataTables geladen:', $.fn.dataTable);
      return $.getScript('https://cdn.datatables.net/datetime/1.5.1/js/dataTables.dateTime.min.js');
    })
    .then(() => $.getScript('https://cdn.datatables.net/searchbuilder/1.6.0/js/dataTables.searchBuilder.min.js'))
    .then(() => {
      console.log('DataTables + Erweiterungen geladen');
      if ($.fn.dataTable && $.fn.dataTable.SearchBuilder) {
  console.log('SearchBuilder:', $.fn.dataTable.SearchBuilder);
} else {
  console.warn('SearchBuilder nicht verfügbar');
}

      mw.hook('wikipage.content').add(function($content) {
        const $table = $content.find('#catalog');

        if ($table.length && !$.fn.DataTable.isDataTable($table)) {
          $table.DataTable({
            dom: 'Qlfrtip',
            searchBuilder: true,
            paging: true,
            pageLength: 600,
            searching: true,
            ordering: true,
            lengthMenu: [[10, 25, 50, 100, 200, 600], [10, 25, 50, 100, 200, 600]],
            order: [[1, 'asc']],
            language: {
              search: "Search:",
              lengthMenu: "Show _MENU_ Entries",
              zeroRecords: "No Matches",
              info: "Page _PAGE_ of _PAGES_ (showing _TOTAL_ of _MAX_ entries)",
              infoEmpty: "Empty",
              infoFiltered: ""
            },
            initComplete: function () {
              this.api().columns().every(function () {
                var column = this;
                var header = $(column.header());
                var columnTitle = header.text();
                $('<input type="text" placeholder="' + columnTitle + ' ..." style="width: 100%; padding: 5px;"/>')
                  .appendTo(header.empty())
                  .on('keyup change', function () {
                    column.search(this.value).draw();
                  })
                  .on('click', function (e) {
                    e.stopPropagation();
                  });
              });
            }
          });
        }
      });
    })
    .catch((err) => {
      console.error('Fehler beim Laden von DataTables oder Erweiterungen:', err);
    });

});







/*AUSKLAPPMENÜS*/
$(function () {
  $('.ausklapp-button').click(function () {
    var $button = $(this);
    var $content = $button.next('.ausklapp-inhalt');

    $content.slideToggle(200);
    var expanded = $button.attr('aria-expanded') === 'true';
    $button.attr('aria-expanded', !expanded);
  });
});



// Hover-Preview für Bild-Links in DataTables
$(document).ready(function() {
    // Neuen Image-Container einfügen
   $('body').append('<div id="image-hover-preview" style="display:none; position:absolute; z-index:9999;"><img src="" style="max-width:400px; max-height:400px; border:1px solid #ccc; background:white; padding:5px;"></div>');

    // Auf alle Links in der Tabelle achten
    $('#catalog').on('mouseenter', 'a', function(e) {
        var href = $(this).attr('href');
        if (href && href.includes('/Special:Redirect/file/')) {
            var imgUrl = href; // URL direkt verwenden
            $('#image-hover-preview img').attr('src', imgUrl);
            $('#image-hover-preview').show();
        }
    }).on('mousemove', 'a', function(e) {
        $('#image-hover-preview').css({
            top: e.pageY + 20 + 'px',
            left: e.pageX + 20 + 'px'
        });
    }).on('mouseleave', 'a', function() {
        $('#image-hover-preview').hide();
    });
});

$(document).ready(function () {
  const searchButton = document.getElementById('searchButton');
  const tagInput = document.getElementById('tagInput');

  if (searchButton && tagInput) {
    searchButton.addEventListener('click', function () {
      const tags = tagInput.value.split(',').map(tag => tag.trim());
      searchImagesByTags(tags);
    });
  } else {
    console.warn('searchButton oder tagInput nicht gefunden.');
  }
});

function searchImagesByTags(tags) {
    var url = new URL(window.location.href);
    var apiUrl = url.origin + '/w/api.php';
    
    // Hier wird eine Anfrage an die MediaWiki API gestellt, um nach Bildern zu suchen, die den Tags entsprechen
    fetch(`${apiUrl}?action=query&format=json&list=categorymembers&cmtitle=Category:${tags.join('&cmtitle=Category:')}&cmtype=file`)
        .then(response => response.json())
        .then(data => {
            var images = data.query.categorymembers;
            displayImages(images);
        })
        .catch(error => console.error('Error:', error));
}

function displayImages(images) {
    var galleryContainer = document.getElementById('galleryContainer');
    galleryContainer.innerHTML = ''; // Leere den Container, um Platz für neue Bilder zu schaffen

    // Überprüfe, ob Bilder vorhanden sind
    if (images.length === 0) {
        galleryContainer.innerHTML = '<p>No images found.</p>';
        return;
    }

    // Bilder in die Galerie einfügen
    images.forEach(image => {
        var imgElement = document.createElement('img');
        imgElement.src = '/index.php/Special:Redirect/file/' + image.title.replace('Category:', '');
        imgElement.alt = image.title;
        galleryContainer.appendChild(imgElement);
    });
}
// Funktion zur Extraktion der Dateinamen aus der Tabelle und zur Anzeige in der Galerie
function updateGalleryFromTable() {
    // Tabelle durchsuchen und Dateinamen extrahieren
    var rows = document.querySelectorAll('#catalog tbody tr');
    var imageLinks = [];

    rows.forEach(row => {
        var imageCell = row.cells[8];  // Die 9. Zelle enthält den Dateinamen als Link
        var link = imageCell.querySelector('a');
        if (link) {
            var imageName = link.textContent.trim();
            var imageUrl = '/index.php/Special:Redirect/file/' + imageName + '.jpg';
            imageLinks.push(imageUrl);
        }
    });

    // Neue Seite mit Galerie öffnen
    var galleryWindow = window.open('', '_blank');
    if (galleryWindow) {
        galleryWindow.document.write('<html><head><title>Gallery</title>');
        galleryWindow.document.write('<style>');
        galleryWindow.document.write('body { font-family: sans-serif; padding: 20px; }');
        galleryWindow.document.write('img { max-width: 300px; margin: 10px; cursor: pointer; display: inline-block; }');
        galleryWindow.document.write('#infoBox { position:absolute; background: rgba(255,255,255,0.95); border:1px solid #ccc; padding:10px; max-width:300px; display:none; z-index:9999; font-size:0.9em; pointer-events:none; }');
        galleryWindow.document.write('</style></head><body>');
        galleryWindow.document.write('<h2>Results</h2>');

        imageLinks.forEach(url => {
            galleryWindow.document.write('<img src="' + url + '" onclick="window.open(\'' + url + '\')">');
        });

        // Infobox-Container
        galleryWindow.document.write('<div id="infoBox"></div>');

        // Script für Hover-Infobox mit Raw-Text und Cache
        galleryWindow.document.write('<script>' +
        'const imgs = document.querySelectorAll("img");' +
        'const infoBox = document.getElementById("infoBox");' +
        'const mediaCache = new Map();' +
        'imgs.forEach(img => {' +
        '  img.removeAttribute("title");' + // Browser Tooltip deaktivieren
        '  img.addEventListener("mouseenter", async function(e) {' +
        '    const fileName = img.src.split("/").pop().replace(".jpg","");' +
        '    if(mediaCache.has(fileName)) {' +
        '      infoBox.innerHTML = mediaCache.get(fileName);' +
        '      infoBox.style.display = "block";' +
        '      return;' +
        '    }' +
        '    infoBox.textContent = "Loading ...";' +
        '    infoBox.style.display = "block";' +
        '    try {' +
        '      const resp = await fetch("/index.php?title=File:" + encodeURIComponent(fileName) + ".jpg&action=raw");' +
        '      const text = await resp.text();' +
        '      const match = text.match(/\\{\\{MediaInfo([\\s\\S]*?)\\}\\}/);' +
        '      if(!match) { infoBox.textContent = "No Data found."; mediaCache.set(fileName,"No Data found."); return; }' +
        '      const block = match[1];' +
        '      const fields = ["title","chapter","illustration","illustrator","year","tags"];' +
        '      let html = "<ul style=\'margin:0; padding-left:1em; list-style:none;\'>";' +
        '      fields.forEach(field => {' +
        '        const m = block.match(new RegExp("\\\\|\\\\s*" + field + "\\\\s*=([^\\\\n]*)"));' +
        '        if(m) html += `<li><b>${field.charAt(0).toUpperCase() + field.slice(1)}:</b> ${m[1].trim()}</li>`;' +
        '      });' +
        '      html += "</ul>";' +
        '      mediaCache.set(fileName, html);' +
        '      infoBox.innerHTML = html;' +
        '    } catch(err) {' +
        '      infoBox.textContent = "Error loading info";' +
        '      mediaCache.set(fileName,"Error loading info");' +
        '      console.error(err);' +
        '    }' +
        '  });' +
        '  img.addEventListener("mousemove", function(e){' +
        '    infoBox.style.top = (e.pageY + 15) + "px";' +
        '    infoBox.style.left = (e.pageX + 15) + "px";' +
        '  });' +
        '  img.addEventListener("mouseleave", function(){' +
        '    infoBox.style.display = "none";' +
        '  });' +
        '});' +
        '<\/script>');

        galleryWindow.document.write('</body></html>');
        galleryWindow.document.close();
    } else {
        alert("Popup wurde blockiert. Bitte erlaube Popups für diese Seite.");
    }
}

// ===============================
// Comparison / Slideshow Script
// ===============================

// ===============================
// DataTable Initialisierung
// ===============================
$(document).ready(function() {
    if ($('#catalog').length) {
        $('#catalog').DataTable();
    }
});

// ===============================
// Globale Slideshow-Objekte
// ===============================
const slideshows = {
    A: { images: [], index: 0 },
    B: { images: [], index: 0 },
    C: { images: [], index: 0 },
    D: { images: [], index: 0 },
};

// ===============================
// Hilfsfunktionen
// ===============================

// Gefilterte Bild-Links aus DataTable holen
function getFilteredImageLinks() {
    const rows = document.querySelectorAll('#catalog tbody tr');
    const images = [];
    rows.forEach(row => {
        if (row.style.display !== 'none') {
            const cell = row.cells[8];
            const link = cell ? cell.querySelector('a') : null;
            if (link) {
                const name = link.textContent.trim();
                const url = '/index.php/Special:Redirect/file/' + name + '.jpg';
                images.push({ url: url, name: name + '.jpg' });
            }
        }
    });
    return images;
}

// Counter-Update
function updateCounter(target) {
    const data = slideshows[target];
    const total = data.images.length;
    const current = total > 0 ? data.index + 1 : 0;
    const counterEl = document.getElementById("counter" + target);
    if (counterEl) counterEl.textContent = current + "/" + total;
}

// ===============================
// MediaInfo Funktionen (aus HTML-Text der File-Seite)
// ===============================
function loadMediaInfo(target, filename) {
    const infoBox = document.getElementById('mediainfo' + target);
    if (!infoBox) return;
    infoBox.textContent = "Loading ...";

    fetch('/index.php?title=File:' + encodeURIComponent(filename) + '&action=raw')
    .then(resp => resp.text())
    .then(text => {
        const match = text.match(/\{\{MediaInfo([\s\S]*?)\}\}/);
        if (!match) {
            infoBox.textContent = "No Data found.";
            return;
        }

        const block = match[1];

        // Felder, die angezeigt werden sollen
        const fields = ["title","chapter","illustration","illustrator","year","tags"];
        let htmlList = "<ul style='margin:0; padding-left:1em; list-style:none;'>";

        fields.forEach(field => {
            const m = block.match(new RegExp("\\|\\s*" + field + "\\s*=([^\\n]*)"));
            if (m) {
                // Label groß schreiben
                const label = field.charAt(0).toUpperCase() + field.slice(1);
                htmlList += `<li><b>${label}:</b> ${m[1].trim()}</li>`;
            }
        });

        htmlList += "</ul>";
        infoBox.innerHTML = htmlList;

        // Optional: etwas CSS stylen
        infoBox.style.backgroundColor = "#f9f9f9";
        infoBox.style.border = "1px solid #ccc";
        infoBox.style.padding = "0.5em 1em";
        infoBox.style.marginTop = "0.5em";
        infoBox.style.borderRadius = "4px";
        infoBox.style.fontSize = "0.9em";
    })
    .catch(err => {
        console.error(err);
        infoBox.textContent = "Error";
    });
}




// ===============================
// Slideshow-Funktionen
// ===============================
function updateSlide(target) {
    const data = slideshows[target];
    const imgEl = document.getElementById('slide' + target);

    if (data.images.length === 0) {
        imgEl.src = '';
        imgEl.alt = 'No Images';
        imgEl.onclick = null;
        updateCounter(target);
        const infoBox = document.getElementById('mediainfo' + target);
        if(infoBox) infoBox.textContent = "";
        return;
    }

    if (data.index < 0) data.index = data.images.length - 1;
    if (data.index >= data.images.length) data.index = 0;

    const current = data.images[data.index];
    imgEl.src = current.url;
    imgEl.alt = current.name;
    imgEl.style.cursor = "pointer";
    imgEl.onclick = () => window.open('/File:' + current.name, '_blank');

    updateCounter(target);
    loadMediaInfo(target, current.name);
}

function populateSlideshow(target) {
    const images = getFilteredImageLinks();
    slideshows[target].images = images;
    slideshows[target].index = 0;

    const imgEl = document.getElementById('slide' + target);
    if(images.length > 0){
        imgEl.src = images[0].url;
        imgEl.alt = images[0].name;
        imgEl.style.cursor = "pointer";
        imgEl.onclick = () => window.open('/File:' + images[0].name, '_blank');
        loadMediaInfo(target, images[0].name);
    } else {
        imgEl.src = '';
        imgEl.alt = 'No Images';
        imgEl.onclick = null;
        const infoBox = document.getElementById('mediainfo' + target);
        if(infoBox) infoBox.textContent = "";
    }
    updateCounter(target);
}

function nextSlideX(target){
    slideshows[target].index++;
    updateSlide(target);
}

function prevSlideX(target){
    slideshows[target].index--;
    updateSlide(target);
}

function openSlideshowInNewTab(target){
    const data = slideshows[target];
    if(!data.images || data.images.length===0){
        alert("No images to display for slideshow " + target);
        return;
    }
    const newTab = window.open();
    if(!newTab){ alert("Popup blocked! Bitte Popups erlauben."); return; }

    let html = "<html><head><title>Slideshow " + target + "</title>";
    html += "<style>";
    html += "body { font-family:sans-serif; padding:20px; }";
    html += "img { max-width:300px; max-height:400px; margin:10px; cursor:pointer; display:inline-block; object-fit:contain; }";
    html += "#infoBox { position:absolute; background: rgba(255,255,255,0.95); border:1px solid #ccc; padding:10px; max-width:300px; display:none; z-index:9999; font-size:0.9em; pointer-events:none; }";
    html += "</style></head><body>";
    html += "<h2>Slideshow " + target + " (" + data.images.length + " images)</h2>";
    
    // Galerie wie im Catalog-Script: lockeres Grid ohne Flexbox
    html += "<div>";
    data.images.forEach(img=>{
        html += "<img src='" + img.url + "' alt='" + img.name + "' onclick=\"window.open('/File:" + img.name + "', '_blank')\">";
    });
    html += "</div>";
    
    // Infobox für MediaInfo
    html += "<div id='infoBox'></div>";

    // Script für MediaInfo-Hover
    html += "<script>";
    html += "const imgs = document.querySelectorAll('img');";
    html += "const infoBox = document.getElementById('infoBox');";
    html += "const mediaCache = new Map();";
    html += "imgs.forEach(img => {";
    html += "  img.removeAttribute('title');";
    html += "  img.addEventListener('mouseenter', async function(e) {";
    html += "    const fileName = img.alt;";
    html += "    if(mediaCache.has(fileName)) { infoBox.innerHTML = mediaCache.get(fileName); infoBox.style.display='block'; return; }";
    html += "    infoBox.textContent = 'Loading ...'; infoBox.style.display='block';";
    html += "    try {";
    html += "      const resp = await fetch('/index.php?title=File:' + encodeURIComponent(fileName) + '&action=raw');";
    html += "      const text = await resp.text();";
    html += "      const match = text.match(/\\{\\{MediaInfo([\\s\\S]*?)\\}\\}/);";
    html += "      if(!match) { infoBox.textContent='No Data found.'; mediaCache.set(fileName,'No Data found.'); return; }";
    html += "      const block = match[1];";
    html += "      const fields = ['title','chapter','illustration','illustrator','year','tags'];";
    html += "      let htmlList = '<ul style=\"margin:0;padding-left:1em;list-style:none;\">';";
    html += "      fields.forEach(field => {";
    html += "        const m = block.match(new RegExp('\\\\|\\\\s*' + field + '\\\\s*=([^\\\\n]*)'));";
    html += "        if(m) htmlList += '<li><b>' + field.charAt(0).toUpperCase() + field.slice(1) + ':</b> ' + m[1].trim() + '</li>';"; 
    html += "      });";
    html += "      htmlList += '</ul>'; mediaCache.set(fileName, htmlList); infoBox.innerHTML = htmlList;";
    html += "    } catch(err) { infoBox.textContent='Error loading info'; mediaCache.set(fileName,'Error loading info'); console.error(err); }";
    html += "  });";
    html += "  img.addEventListener('mousemove', function(e){ infoBox.style.top=(e.pageY+15)+'px'; infoBox.style.left=(e.pageX+15)+'px'; });";
    html += "  img.addEventListener('mouseleave', function(){ infoBox.style.display='none'; });";
    html += "});";
    html += "<\/script>";

    html += "</body></html>";

    newTab.document.open();
    newTab.document.write(html);
    newTab.document.close();
}

// Chapter Script
importScript('MediaWiki:ChapterSlides.js');

// Chapter Script
importScript('MediaWiki:ChapterSlidesAlt.js');

// Filter Scripts, character pages 
importScript('MediaWiki:CharacterFilter.js');

// CharacterDistribution
// Chart.js laden
mw.loader.load('https://cdn.jsdelivr.net/npm/chart.js');

// Chart initialisieren
function initChart() {
    if (typeof Chart === 'undefined') {
        setTimeout(initChart, 50);
        return;
    }

    const canvas = document.getElementById('lineChart');
    if (!canvas) return;

    const ctx = canvas.getContext('2d');

    // HiDPI / Retina: Pixelratio berücksichtigen
    const dpr = window.devicePixelRatio || 1;
    canvas.width = canvas.offsetWidth * dpr;
    canvas.height = canvas.offsetHeight * dpr;
    ctx.scale(dpr, dpr);

    const data = {
        labels: ['Kemble (1885)', 'Schröder (1898)', 'Hirth (1920)', 'Trier (1936)', 'Harder (1938)', 'Kellerer (1938)', 'Busoni (1940)','Bebié (1944)'],
        datasets: [
            { label: 'Huck', data: [47.40,50.98,72.22,54.29,93.10,50.00,54.10,69.81], backgroundColor:'red', borderColor:'red', borderWidth:1, barPercentage:0.8 },
            { label: 'Jim', data: [17.92,29.41,22.22,28.57,44.83,18.18,26.23,17.00], backgroundColor:'blue', borderColor:'blue', borderWidth:1, barPercentage:0.8 },
            { label: 'Pap Finn', data: [5.78,7.84,5.56,5.71,6.90,9.09,4.92,1.88], backgroundColor:'green', borderColor:'green', borderWidth:1, barPercentage:0.8 },
            { label: 'King/Duke', data: [13.29,13.73,16.67,20.00,17.24,9.09,16.39,3.77], backgroundColor:'orange', borderColor:'orange', borderWidth:1, barPercentage:0.8 },
            { label: 'Tom', data: [11.56,7.84,19.44,11.43,24.14,9.09,11.48,13.21], backgroundColor:'black', borderColor:'black', borderWidth:1, barPercentage:0.8 },
            { label: 'Others', data: [38.73,29.41,36.11,31.43,17.24,31.82,22.95,13.21], backgroundColor:'pink', borderColor:'pink', borderWidth:1, barPercentage:0.8 },
            { type: 'scatter', label:'Total # of Illustrations', data:[173,51,36,35,29,22,61,53], borderColor:'purple', borderWidth:3, xAxisID:'x2', pointStyle:'cross', pointRadius:8, pointHoverRadius:10, showLine:false }
        ]
    };

    const config = {
        type:'bar',
        data:data,
        options:{
            responsive:true,
            maintainAspectRatio:false, // damit Chart die Containerhöhe nutzt
            indexAxis:'y',
            plugins:{ legend:{ display:true }, tooltip:{ mode:'index', intersect:false } },
            interaction:{ mode:'nearest', axis:'y', intersect:false },
            scales:{
                y:{ display:true, title:{ display:true, text:'Illustrator (Year)' }, stacked:false, barThickness:30 },
                x:{ display:true, position:'top', title:{ display:true, text:'% of Illustrations Featuring Character' }, min:0, max:100 },
                x2:{ display:true, position:'bottom', title:{ display:true, text:'Total # of Illustrations' }, min:0, max:180, grid:{ drawOnChartArea:false } }
            }
        }
    };

    new Chart(ctx, config);
    console.log('Chart erfolgreich initialisiert!');
}

$(document).ready(initChart);


// initChart beim Laden starten
$(document).ready(initChart);




// Media Viewer Adjustments to Display Title and Tags
importScript('MediaWiki:MediaViewerDisplay.js');

mw.loader.using(['mediawiki.util'], function () {

  function cleanFileLink(a) {
    // a.href = /index.php?title=Hf_1885_kmb_ch001_ill1.jpg&action=edit&redlink=1
    const title = a.textContent.trim();
    const file = title
      .replace(/\s+/g, '_')
      .replace(/\.(jpg|png|jpeg)$/i, '');

    return `<a href="/index.php/Special:Redirect/file/${file}.jpg">${file}</a>`;
  }

  document.addEventListener('click', function (e) {
    if (e.target.id !== 'generateCatalogHTML') return;

    const dpl = document.getElementById('DPL');
    if (!dpl) {
      alert('DPL table not found');
      return;
    }

    const rows = dpl.querySelectorAll('tbody tr');
    if (!rows.length) {
      alert('No data rows found');
      return;
    }

    let html = '';
    rows.forEach(row => {
      const cells = row.querySelectorAll('td');
      if (cells.length < 8) return;

      const publication  = cells[0].textContent.trim();
      const year         = cells[1].textContent.trim();
      const illustrator  = cells[2].textContent.trim();
      const chapter      = cells[3].textContent.trim();
      const illustration = cells[4].textContent.trim();
      const title        = cells[5].textContent.trim();
      const tags         = cells[6].textContent.trim();
      const link         = cells[7].querySelector('a');

      if (!link) return;

      html +=
`<tr>
<td align="left"><font face="Liberation Serif">${publication}</font></td>
<td align="right"><font face="Liberation Serif">${year}</font></td>
<td align="left"><font face="Liberation Serif">${illustrator}</font></td>
<td align="center"><font face="Liberation Serif">${chapter}</font></td>
<td align="center"><font face="Liberation Serif">${chapter}</font></td>
<td align="center"><font face="Liberation Serif">${illustration}</font></td>
<td align="left"><font face="Liberation Serif">${title}</font></td>
<td align="left"><font face="Liberation Serif">-${tags.replace(/,\s*/g,' -')}</font></td>
<td align="left">${cleanFileLink(link)}</td>
</tr>\n`;
    });

    const output = document.getElementById('catalogOutput');
    if (!output) {
      alert('Output textarea not found');
      return;
    }

    output.value = html;
    output.focus();
    output.select();

    alert('Static catalog HTML generated.');
  });

});