Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page.
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 (API Version)// This script adds two buttons to Wikipedia category pages:// 1. "Copy Items" - Copies all items in the current category via API// 2. "Copy All Items" - Copies all items in the current category and its subcategories via API// Only run on Wikipedia category pagesif(window.___location.href.includes('/wiki/Category:')){// Extract the category name from the URLconstcategoryName=decodeURIComponent(window.___location.pathname.split('/Category:')[1]);console.log("Category Name:",categoryName);// Create a container for our buttonsconstcontainer=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" buttonconstcopyItemsBtn=document.createElement('button');copyItemsBtn.textContent='Copy Items from this Category (API)';copyItemsBtn.style.marginRight='10px';copyItemsBtn.style.padding='8px 12px';copyItemsBtn.style.cursor='pointer';// Create the "Copy All Items" buttonconstcopyAllItemsBtn=document.createElement('button');copyAllItemsBtn.textContent='Copy Items from All Subcategories (API)';copyAllItemsBtn.style.padding='8px 12px';copyAllItemsBtn.style.cursor='pointer';// Create status textconststatusText=document.createElement('div');statusText.style.marginTop='10px';statusText.style.color='#555';// Add buttons to containercontainer.appendChild(copyItemsBtn);container.appendChild(copyAllItemsBtn);container.appendChild(statusText);// Insert container after the page titleconstpageTitleHeading=document.querySelector('.mw-first-heading');if(pageTitleHeading){pageTitleHeading.parentNode.insertBefore(container,pageTitleHeading.nextSibling);}else{document.querySelector('#content').prepend(container);}// Function to copy text to clipboard (Firefox on Pop OS compatible)functioncopyToClipboard(text){returnnewPromise((resolve,reject)=>{// Create a temporary textarea elementconsttextarea=document.createElement('textarea');textarea.value=text;// Make the textarea visible to ensure it works on Firefox/Pop OStextarea.style.position='fixed';textarea.style.top='10px';textarea.style.left='10px';textarea.style.width='1px';textarea.style.height='1px';textarea.style.padding='0';textarea.style.border='none';textarea.style.outline='none';textarea.style.boxShadow='none';textarea.style.background='transparent';textarea.style.zIndex='999999';document.body.appendChild(textarea);// Focus and select the texttextarea.focus();textarea.select();try{// Use document.execCommand first as it's more reliable on Firefox/Linuxconstsuccess=document.execCommand('copy');if(success){document.body.removeChild(textarea);resolve(true);}elseif(navigator.clipboard&&navigator.clipboard.writeText){// Try the Clipboard API as fallbacknavigator.clipboard.writeText(text).then(()=>{document.body.removeChild(textarea);resolve(true);}).catch(err=>{document.body.removeChild(textarea);// Show the copy text in a visible textarea as last resortconstvisibleTextarea=document.createElement('textarea');visibleTextarea.value=text;visibleTextarea.style.position='fixed';visibleTextarea.style.top='50px';visibleTextarea.style.left='50px';visibleTextarea.style.width='80%';visibleTextarea.style.height='300px';visibleTextarea.style.zIndex='999999';constcloseButton=document.createElement('button');closeButton.textContent='Close';closeButton.style.position='fixed';closeButton.style.top='360px';closeButton.style.left='50px';closeButton.style.zIndex='999999';closeButton.onclick=()=>{document.body.removeChild(visibleTextarea);document.body.removeChild(closeButton);};document.body.appendChild(visibleTextarea);document.body.appendChild(closeButton);visibleTextarea.focus();visibleTextarea.select();resolve(true);});}else{document.body.removeChild(textarea);reject(newError('Unable to copy to clipboard'));}}catch(err){document.body.removeChild(textarea);reject(err);}});}// Function to get all members of a category using Wikipedia APIasyncfunctiongetCategoryMembers(categoryTitle,continueToken=null){try{// Base API URLletapiUrl=`https://en.wikipedia.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:${encodeURIComponent(categoryTitle)}&cmlimit=500&format=json&origin=*`;// Add continue token if providedif(continueToken){apiUrl+=`&cmcontinue=${continueToken}`;}statusText.textContent=`Fetching category members for: ${categoryTitle}...`;// Add a small delay to avoid hammering the serverawaitnewPromise(resolve=>setTimeout(resolve,300));constresponse=awaitfetch(apiUrl);constdata=awaitresponse.json();if(!data.query||!data.query.categorymembers){console.error("Unexpected API response:",data);return{members:[],continueToken:null};}// Extract members and continue tokenconstmembers=data.query.categorymembers.map(member=>member.title);constnextContinueToken=data.continue?data.continue.cmcontinue:null;return{members,continueToken:nextContinueToken};}catch(error){console.error("API request error:",error);statusText.textContent=`Error fetching category members: ${error.message}`;return{members:[],continueToken:null};}}// Function to get all subcategories of a categoryasyncfunctiongetSubcategories(categoryTitle,continueToken=null){try{// Base API URL for subcategories (only get items with namespace 14, which is Category)letapiUrl=`https://en.wikipedia.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:${encodeURIComponent(categoryTitle)}&cmnamespace=14&cmlimit=500&format=json&origin=*`;// Add continue token if providedif(continueToken){apiUrl+=`&cmcontinue=${continueToken}`;}statusText.textContent=`Fetching subcategories for: ${categoryTitle}...`;// Add a small delay to avoid hammering the serverawaitnewPromise(resolve=>setTimeout(resolve,300));constresponse=awaitfetch(apiUrl);constdata=awaitresponse.json();if(!data.query||!data.query.categorymembers){console.error("Unexpected API response:",data);return{subcategories:[],continueToken:null};}// Extract subcategories and continue tokenconstsubcategories=data.query.categorymembers.map(member=>member.title.replace('Category:',''));constnextContinueToken=data.continue?data.continue.cmcontinue:null;return{subcategories,continueToken:nextContinueToken};}catch(error){console.error("API request error:",error);statusText.textContent=`Error fetching subcategories: ${error.message}`;return{subcategories:[],continueToken:null};}}// Function to get all non-category members of a categoryasyncfunctiongetNonCategoryMembers(categoryTitle,continueToken=null){try{// Base API URL for non-category members (exclude namespace 14, which is Category)letapiUrl=`https://en.wikipedia.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:${encodeURIComponent(categoryTitle)}&cmnamespace=0|1|2|3|4|5|6|7|8|9|10|11|12|13|15&cmlimit=500&format=json&origin=*`;// Add continue token if providedif(continueToken){apiUrl+=`&cmcontinue=${continueToken}`;}statusText.textContent=`Fetching items for: ${categoryTitle}...`;// Add a small delay to avoid hammering the serverawaitnewPromise(resolve=>setTimeout(resolve,300));constresponse=awaitfetch(apiUrl);constdata=awaitresponse.json();if(!data.query||!data.query.categorymembers){console.error("Unexpected API response:",data);return{members:[],continueToken:null};}// Extract members and continue tokenconstmembers=data.query.categorymembers.map(member=>member.title);constnextContinueToken=data.continue?data.continue.cmcontinue:null;return{members,continueToken:nextContinueToken};}catch(error){console.error("API request error:",error);statusText.textContent=`Error fetching items: ${error.message}`;return{members:[],continueToken:null};}}// Function to get all members of a category, handling paginationasyncfunctiongetAllCategoryMembers(categoryTitle){letallMembers=[];letcontinueToken=null;letpagesProcessed=0;do{const{members,continueToken:nextToken}=awaitgetNonCategoryMembers(categoryTitle,continueToken);allMembers=allMembers.concat(members);continueToken=nextToken;pagesProcessed++;statusText.textContent=`Retrieved ${allMembers.length} items from "${categoryTitle}" (page ${pagesProcessed})...`;// Safeguard against infinite loopsif(pagesProcessed>20){statusText.textContent+=" (Stopped after 20 pages to avoid overloading)";break;}}while(continueToken);returnallMembers;}// Function to get all subcategories of a category, handling paginationasyncfunctiongetAllSubcategories(categoryTitle){letallSubcategories=[];letcontinueToken=null;letpagesProcessed=0;do{const{subcategories,continueToken:nextToken}=awaitgetSubcategories(categoryTitle,continueToken);allSubcategories=allSubcategories.concat(subcategories);continueToken=nextToken;pagesProcessed++;// Safeguard against infinite loopsif(pagesProcessed>10){break;}}while(continueToken);returnallSubcategories;}// Handle "Copy Items" button clickcopyItemsBtn.addEventListener('click',async()=>{statusText.textContent='Gathering items from this category via API...';try{constitems=awaitgetAllCategoryMembers(categoryName);if(items.length===0){statusText.textContent='No items found in this category.';return;}awaitcopyToClipboard(items.join('\n'));statusText.textContent=`Successfully copied ${items.length} items to clipboard.`;}catch(error){statusText.textContent=`Error: ${error.message}`;console.error('Error:',error);}});// Handle "Copy All Items" button clickcopyAllItemsBtn.addEventListener('click',async()=>{statusText.textContent='Gathering items from all subcategories via API (this may take a while)...';try{// Get items from the current categoryletallItems=awaitgetAllCategoryMembers(categoryName);statusText.textContent=`Found ${allItems.length} items in main category. Checking subcategories...`;// Get all subcategoriesconstsubcategories=awaitgetAllSubcategories(categoryName);statusText.textContent=`Found ${subcategories.length} subcategories. Processing...`;// Process a limited number of subcategories to avoid overloadingconstMAX_SUBCATEGORIES=10;constprocessedCategories=newSet();// To avoid processing the same category twicefor(leti=0;i<Math.min(subcategories.length,MAX_SUBCATEGORIES);i++){constsubcategoryTitle=subcategories[i];if(!processedCategories.has(subcategoryTitle)){processedCategories.add(subcategoryTitle);constsubcategoryItems=awaitgetAllCategoryMembers(subcategoryTitle);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 itemsconstuniqueItems=[...newSet(allItems)];if(uniqueItems.length===0){statusText.textContent='No items found in this category or its subcategories.';return;}awaitcopyToClipboard(uniqueItems.join('\n'));statusText.textContent=`Successfully copied ${uniqueItems.length} unique items to clipboard.`;}catch(error){statusText.textContent=`Error: ${error.message}`;console.error('Error:',error);}});console.log('Wikipedia Category Copier script (API version) has been loaded successfully!');}else{// Do nothing on non-category pagesconsole.log('Wikipedia Category Copier: Not a category page, script inactive.');}