User:Polygnotus/Scripts/CategoryToClipboard.js

This is an old revision of this page, as edited by Polygnotus (talk | contribs) at 14:34, 13 April 2025 (Created page with '// Wikipedia Category Items Copier // This script adds two buttons to Wikipedia category pages: // 1. "Copy Items" - Copies all items in the current category // 2. "Copy All Items" - Copies all items in the current category and its subcategories // Check if we're on a Wikipedia category page if (!window.___location.href.includes('/wiki/Category:')) { alert('This script only works on Wikipedia category pages.'); } else { // Create a container for our b...'). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff)
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// Wikipedia Category Items Copier
// This script adds two buttons to Wikipedia category pages:
// 1. "Copy Items" - Copies all items in the current category
// 2. "Copy All Items" - Copies all items in the current category and its subcategories

// Check if we're on a Wikipedia category page
if (!window.___location.href.includes('/wiki/Category:')) {
    alert('This script only works on Wikipedia category pages.');
} else {
    // Create a container for our buttons
    const container = document.createElement('div');
    container.style.padding = '10px';
    container.style.margin = '10px 0';
    container.style.backgroundColor = '#f8f9fa';
    container.style.border = '1px solid #a2a9b1';
    container.style.borderRadius = '3px';

    // Create the "Copy Items" button
    const copyItemsBtn = document.createElement('button');
    copyItemsBtn.textContent = 'Copy Items from this Category';
    copyItemsBtn.style.marginRight = '10px';
    copyItemsBtn.style.padding = '8px 12px';
    copyItemsBtn.style.cursor = 'pointer';
    
    // Create the "Copy All Items" button
    const copyAllItemsBtn = document.createElement('button');
    copyAllItemsBtn.textContent = 'Copy Items from All Subcategories';
    copyAllItemsBtn.style.padding = '8px 12px';
    copyAllItemsBtn.style.cursor = 'pointer';
    
    // Create status text
    const statusText = document.createElement('div');
    statusText.style.marginTop = '10px';
    statusText.style.color = '#555';
    
    // Add buttons to container
    container.appendChild(copyItemsBtn);
    container.appendChild(copyAllItemsBtn);
    container.appendChild(statusText);
    
    // Insert container after the page title
    const pageTitleHeading = document.querySelector('.mw-first-heading');
    if (pageTitleHeading) {
        pageTitleHeading.parentNode.insertBefore(container, pageTitleHeading.nextSibling);
    } else {
        document.querySelector('#content').prepend(container);
    }

    // Function to get items from the current category page
    function getItemsFromCurrentPage() {
        const items = [];
        
        // Get all links from the category page that aren't subcategories
        const itemLinks = Array.from(document.querySelectorAll('#mw-pages a'));
        
        itemLinks.forEach(link => {
            // Skip unnecessary links like "previous page" or "next page"
            if (link.parentElement.id === 'mw-pages-prev' || 
                link.parentElement.id === 'mw-pages-next' ||
                link.textContent.trim() === 'next page' ||
                link.textContent.trim() === 'previous page') {
                return;
            }
            
            // Skip links from the navigation sections
            if (link.closest('.mw-normal-catlinks') || 
                link.closest('.noprint') ||
                link.closest('.catlinks')) {
                return;
            }
            
            // Add the item to our list
            items.push(link.textContent.trim());
        });
        
        return items;
    }

    // Function to copy text to clipboard (Firefox on Pop OS compatible)
    function copyToClipboard(text) {
        return new Promise((resolve, reject) => {
            // Create a temporary textarea element
            const textarea = document.createElement('textarea');
            textarea.value = text;
            
            // Make the textarea invisible but present in the DOM
            textarea.style.position = 'fixed';
            textarea.style.opacity = '0';
            document.body.appendChild(textarea);
            
            // Select and copy
            textarea.select();
            
            try {
                // Try the modern clipboard API first
                if (navigator.clipboard && navigator.clipboard.writeText) {
                    navigator.clipboard.writeText(text).then(() => {
                        document.body.removeChild(textarea);
                        resolve(true);
                    }).catch(err => {
                        // Fall back to document.execCommand
                        const success = document.execCommand('copy');
                        document.body.removeChild(textarea);
                        if (success) {
                            resolve(true);
                        } else {
                            reject(new Error('Unable to copy to clipboard'));
                        }
                    });
                } else {
                    // Use execCommand as fallback
                    const success = document.execCommand('copy');
                    document.body.removeChild(textarea);
                    if (success) {
                        resolve(true);
                    } else {
                        reject(new Error('Unable to copy to clipboard'));
                    }
                }
            } catch (err) {
                document.body.removeChild(textarea);
                reject(err);
            }
        });
    }

    // Function to get subcategories from the current page
    function getSubcategories() {
        const subcategories = [];
        const subcategoryLinks = document.querySelectorAll('#mw-subcategories a');
        
        subcategoryLinks.forEach(link => {
            // Skip "previous page" or "next page" links
            if (link.parentElement.id === 'mw-subcategories-prev' || 
                link.parentElement.id === 'mw-subcategories-next' ||
                link.textContent.trim() === 'next page' ||
                link.textContent.trim() === 'previous page') {
                return;
            }
            
            // Only include links to category pages
            if (link.href && link.href.includes('/wiki/Category:')) {
                subcategories.push(link.href);
            }
        });
        
        return subcategories;
    }

    // Function to fetch and process a category page
    async function fetchCategoryPage(url) {
        statusText.textContent = `Fetching: ${url}`;
        
        try {
            // Add a small delay to avoid hammering the server
            await new Promise(resolve => setTimeout(resolve, 500));
            
            const response = await fetch(url);
            const html = await response.text();
            
            // Create a temporary element to parse the HTML
            const tempDiv = document.createElement('div');
            tempDiv.innerHTML = html;
            
            // Get items from the page
            const items = [];
            const itemLinks = tempDiv.querySelectorAll('#mw-pages a');
            
            itemLinks.forEach(link => {
                // Skip unnecessary links
                if (link.parentElement.id === 'mw-pages-prev' || 
                    link.parentElement.id === 'mw-pages-next' ||
                    link.textContent.trim() === 'next page' ||
                    link.textContent.trim() === 'previous page') {
                    return;
                }
                
                // Add the item to our list
                items.push(link.textContent.trim());
            });
            
            return items;
        } catch (error) {
            console.error('Error fetching category page:', error);
            statusText.textContent = `Error fetching ${url}: ${error.message}`;
            return [];
        }
    }

    // Handle "Copy Items" button click
    copyItemsBtn.addEventListener('click', async () => {
        statusText.textContent = 'Gathering items from this category...';
        
        const items = getItemsFromCurrentPage();
        
        if (items.length === 0) {
            statusText.textContent = 'No items found in this category.';
            return;
        }
        
        try {
            await copyToClipboard(items.join('\n'));
            statusText.textContent = `Successfully copied ${items.length} items to clipboard.`;
        } catch (error) {
            statusText.textContent = `Error copying to clipboard: ${error.message}`;
            console.error('Copy error:', error);
        }
    });

    // Handle "Copy All Items" button click
    copyAllItemsBtn.addEventListener('click', async () => {
        statusText.textContent = 'Gathering items from all subcategories (this may take a while)...';
        
        // Get items from the current page
        let allItems = getItemsFromCurrentPage();
        
        // Get subcategories
        const subcategories = getSubcategories();
        const processedCategories = new Set(); // To avoid processing the same category twice
        
        // Process each subcategory (limited to avoid hammering the API)
        const MAX_SUBCATEGORIES = 10; // Limit the number of subcategories to process
        
        for (let i = 0; i < Math.min(subcategories.length, MAX_SUBCATEGORIES); i++) {
            const subcategoryUrl = subcategories[i];
            
            if (!processedCategories.has(subcategoryUrl)) {
                processedCategories.add(subcategoryUrl);
                
                const subcategoryItems = await fetchCategoryPage(subcategoryUrl);
                allItems = allItems.concat(subcategoryItems);
                
                statusText.textContent = `Processed ${i + 1}/${Math.min(subcategories.length, MAX_SUBCATEGORIES)} subcategories. Found ${allItems.length} items so far...`;
            }
        }
        
        if (subcategories.length > MAX_SUBCATEGORIES) {
            statusText.textContent += ` (Limited to ${MAX_SUBCATEGORIES} subcategories to avoid overloading. Found ${allItems.length} items.)`;
        }
        
        // Deduplicate items
        const uniqueItems = [...new Set(allItems)];
        
        if (uniqueItems.length === 0) {
            statusText.textContent = 'No items found in this category or its subcategories.';
            return;
        }
        
        try {
            await copyToClipboard(uniqueItems.join('\n'));
            statusText.textContent = `Successfully copied ${uniqueItems.length} unique items to clipboard.`;
        } catch (error) {
            statusText.textContent = `Error copying to clipboard: ${error.message}`;
            console.error('Copy error:', error);
        }
    });

    console.log('Wikipedia Category Copier script has been loaded successfully!');
}