Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// Fetch image author and license info for up to 50 images in one API call
(function() {
    'use strict';

    // Define translations
    const translations = {
        en: {
            unknownAuthor: 'Unknown author',
            unknownLicense: 'Unknown license',
            imageBy: 'Image by: '
        },
        de: {
            unknownAuthor: 'Unbekannter Autor',
            unknownLicense: 'Unbekannte Lizenz',
            imageBy: 'Bild von: '
        },
        sv: {
            unknownAuthor: 'Okänd författare',
            unknownLicense: 'Okänd licens',
            imageBy: 'Bild av: '
        }
        // Add more languages here as needed
    };

    // Function to get the translation based on the user's language
    function translate(key) {
        const userLang = mw.config.get('wgUserLanguage') || 'en'; // Default to English
        console.log('User language:', userLang); // Debugging: Log the user language
        
        // Check if translations exist for the user language, else fallback to 'en'
        if (translations.hasOwnProperty(userLang) && translations[userLang].hasOwnProperty(key)) {
            return translations[userLang][key];
        } else {
            // Fallback to English if translation not found
            return translations['en'][key];
        }
    }

    // Function to fetch image metadata for multiple images (up to 50) using the MediaWiki API
    async function fetchImageMetadataBatch(imageTitles) {
        const titlesParam = imageTitles.map(title => encodeURIComponent(title)).join('|');
        const url = `/w/api.php?action=query&titles=${titlesParam}&prop=imageinfo&iiprop=extmetadata&format=json&origin=*`;
        const response = await fetch(url);
        const data = await response.json();
        const pages = data.query.pages;

        // Extract the metadata for each image
        const metadata = {};
        for (const page of Object.values(pages)) {
            if (page.imageinfo && page.imageinfo[0].extmetadata) {
                const extmetadata = page.imageinfo[0].extmetadata;
                const author = extmetadata.Artist ? extmetadata.Artist.value : translate('unknownAuthor');
                const license = extmetadata.LicenseShortName ? extmetadata.LicenseShortName.value : translate('unknownLicense');
                const licenseUrl = extmetadata.LicenseUrl ? extmetadata.LicenseUrl.value : "#";
                metadata[page.title] = { author, license, licenseUrl };
            } else {
                metadata[page.title] = {
                    author: translate('unknownAuthor'),
                    license: translate('unknownLicense'),
                    licenseUrl: "#"
                };
            }
        }

        return metadata;
    }

    // Function to insert the byline next to the image
    function insertByline(image, author, license, licenseUrl) {
        const byline = document.createElement('div');
        byline.className = 'image-byline';
        // Use <strong> for bold styling around "Image by" translated string
        byline.innerHTML = `<strong>${translate('imageBy')}</strong>${author} | <a href="${licenseUrl}" target="_blank">${license}</a>`;
        image.parentNode.insertBefore(byline, image.nextSibling);
    }

    // Function to batch process images in chunks of 50
    async function processImagesInBatches(imageTitles) {
        const BATCH_SIZE = 50;

        // Process images in batches
        for (let i = 0; i < imageTitles.length; i += BATCH_SIZE) {
            const batch = imageTitles.slice(i, i + BATCH_SIZE); // Get batch of up to 50 images
            const metadata = await fetchImageMetadataBatch(batch); // Fetch metadata for the batch

            for (const imageTitle of batch) {
                const imageName = imageTitle.split(':')[1].trim(); // Extract the actual image name
                const encodedImageName = mw.util.wikiUrlencode(imageName); // Encode the image name safely

                const imageElement = document.querySelector(`img[src*="${encodedImageName}"]`); // Find the image element on the page
                if (imageElement) {
                    const { author, license, licenseUrl } = metadata[imageTitle];
                    insertByline(imageElement, author, license, licenseUrl); // Insert the byline
                }
            }
        }
    }

    // Function to fetch all images on the page from the API
    async function fetchKnownImages(pageTitle) {
        const url = `/w/api.php?action=query&format=json&prop=images&titles=${encodeURIComponent(pageTitle)}&imlimit=max&formatversion=2&origin=*`;
        const response = await fetch(url);
        const data = await response.json();
        const pages = data.query.pages;
        const page = Object.values(pages)[0];
        return page.images ? page.images.map(img => img.title) : [];
    }

    // Function to process images known to the API
    async function processImages() {
        const pageTitle = mw.config.get('wgPageName'); // Get the current page title
        const knownImageTitles = await fetchKnownImages(pageTitle); // Fetch known images

        if (knownImageTitles.length > 0) {
            // Process images in batches of 50
            await processImagesInBatches(knownImageTitles);
        }
    }

    // Function to add click event to the page title immediately
    const pageTitleElement = document.getElementById('firstHeading'); // Get the page title element
    if (pageTitleElement) {
        pageTitleElement.style.cursor = 'pointer'; // Change cursor to pointer to indicate it's clickable
        pageTitleElement.addEventListener('click', processImages); // Add click event to trigger processing images
    }
})();