User:Polygnotus/Scripts/ListGenerator.js
Appearance
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. |
![]() | Documentation for this user script can be added at User:Polygnotus/Scripts/ListGenerator. |
// Universal Wikipedia List Copier - Refactored for simplicity and maintainability
const CONFIG = {
API_DELAY: 500,
MAX_RETRIES: 3,
BASE_URL: 'https://en.wikipedia.org',
API_URL: 'https://en.wikipedia.org/w/api.php'
};
// ===== CORE UTILITIES =====
function addTooltip(element, text) {
element.title = text;
}
function formatItems(items, includeUrls, baseUrl = `${CONFIG.BASE_URL}/wiki/`) {
if (!includeUrls) return items.join('\n');
return items.map(item => `${baseUrl}${encodeURIComponent(item.replace(/ /g, '_'))}`).join('\n');
}
async function copyToClipboardOrDownload(text, filename, statusElement) {
const success = await tryClipboardCopy(text);
if (!success) {
statusElement.innerHTML = `<p>Clipboard access failed. Click the link below to download items:</p>`;
offerTextAsDownload(text, filename, statusElement);
}
return success;
}
function offerTextAsDownload(text, filename, statusElement) {
const blob = new Blob([text], {type: 'text/plain'});
const url = URL.createObjectURL(blob);
const downloadLink = document.createElement('a');
Object.assign(downloadLink, {
href: url,
download: filename || 'wikipedia-items.txt',
textContent: `Download ${filename || 'items'} as text file`,
style: 'display: block; margin-top: 10px;'
});
statusElement.appendChild(downloadLink);
}
async function tryClipboardCopy(text) {
if (navigator.clipboard?.writeText) {
try {
await navigator.clipboard.writeText(text);
return true;
} catch {}
}
// Fallback method
try {
const textarea = document.createElement('textarea');
Object.assign(textarea.style, {
position: 'fixed',
left: '-999999px',
top: '-999999px'
});
textarea.value = text;
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
const success = document.execCommand('copy');
document.body.removeChild(textarea);
return success;
} catch {
return false;
}
}
// ===== API UTILITIES =====
async function makeApiRequest(url, retryCount = 0) {
await new Promise(resolve => setTimeout(resolve, CONFIG.API_DELAY));
try {
const response = await fetch(url);
if (response.status === 429 || response.status >= 500) {
if (retryCount < CONFIG.MAX_RETRIES) {
await new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
return makeApiRequest(url, retryCount + 1);
}
throw new Error(`Request failed after ${CONFIG.MAX_RETRIES} retries: ${response.status}`);
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (data.error?.code === 'maxlag') {
const waitTime = (data.error.lag || 5 + 2) * 1000;
await new Promise(resolve => setTimeout(resolve, waitTime));
return makeApiRequest(url, retryCount);
}
if (data.error) {
throw new Error(`API Error: ${data.error.code} - ${data.error.info}`);
}
return data;
} catch (error) {
if (retryCount < CONFIG.MAX_RETRIES) {
await new Promise(resolve => setTimeout(resolve, 1000));
return makeApiRequest(url, retryCount + 1);
}
throw error;
}
}
// Generic paginated API fetcher
async function fetchAllPages(apiConfig, statusCallback) {
let allItems = [];
let continueToken = null;
let pagesProcessed = 0;
do {
const url = apiConfig.buildUrl(continueToken);
statusCallback(`${apiConfig.progressMessage} (page ${pagesProcessed + 1})...`);
const data = await makeApiRequest(url);
const { items, continueToken: nextToken } = apiConfig.parseResponse(data);
allItems = allItems.concat(items);
continueToken = nextToken;
pagesProcessed++;
statusCallback(`Retrieved ${allItems.length} ${apiConfig.itemType} (page ${pagesProcessed})...`);
} while (continueToken);
return allItems;
}
// ===== CONSOLIDATED FETCH METHODS =====
// Generic paginated fetcher - handles all simple list-based APIs
async function fetchPaginatedList(listType, params, statusCallback = () => {}) {
const configs = {
categoryMembers: {
list: 'categorymembers',
titleParam: 'cmtitle',
continueParam: 'cmcontinue',
limitParam: 'cmlimit',
namespaceParam: 'cmnamespace',
dataPath: 'categorymembers',
defaultNamespaces: '0|1|2|3|4|5|6|7|8|9|10|11|12|13|15'
},
categorySubcategories: {
list: 'categorymembers',
titleParam: 'cmtitle',
continueParam: 'cmcontinue',
limitParam: 'cmlimit',
namespaceParam: 'cmnamespace',
dataPath: 'categorymembers',
defaultNamespaces: '14'
},
backlinks: {
list: 'backlinks',
titleParam: 'bltitle',
continueParam: 'blcontinue',
limitParam: 'bllimit',
namespaceParam: 'blnamespace',
dataPath: 'backlinks'
},
prefixPages: {
list: 'allpages',
titleParam: 'apprefix',
continueParam: 'apcontinue',
limitParam: 'aplimit',
namespaceParam: 'apnamespace',
dataPath: 'allpages'
},
search: {
list: 'search',
titleParam: 'srsearch',
continueParam: 'sroffset',
limitParam: 'srlimit',
dataPath: 'search'
}
};
const config = configs[listType];
if (!config) {
throw new Error(`Unknown list type: ${listType}`);
}
return fetchAllPages({
buildUrl: (continueToken) => {
let url = `${CONFIG.API_URL}?action=query&list=${config.list}&${config.limitParam}=max&maxlag=5&format=json&origin=*`;
// Add title/search parameter
if (config.titleParam && params.title) {
if (listType === 'categoryMembers' || listType === 'categorySubcategories') {
url += `&${config.titleParam}=Category:${encodeURIComponent(params.title)}`;
} else {
url += `&${config.titleParam}=${encodeURIComponent(params.title)}`;
}
}
// Add namespace parameter
if (config.namespaceParam) {
const namespace = params.namespace !== undefined ? params.namespace : config.defaultNamespaces;
if (namespace !== null) {
url += `&${config.namespaceParam}=${namespace}`;
}
}
// Add continuation token
if (continueToken) {
url += `&${config.continueParam}=${continueToken}`;
}
return url;
},
parseResponse: (data) => ({
items: data.query?.[config.dataPath]?.map(item => item.title) || [],
continueToken: data.continue?.[config.continueParam] || null
}),
progressMessage: params.progressMessage || `Fetching ${listType}`,
itemType: params.itemType || 'items'
}, statusCallback);
}
// Individual fetch methods using the consolidated approach
async function fetchCategoryMembers(categoryTitle) {
return fetchPaginatedList('categoryMembers', {
title: categoryTitle,
progressMessage: `Fetching items for: ${categoryTitle}`,
itemType: 'items'
});
}
async function fetchCategorySubcategories(categoryTitle) {
return fetchPaginatedList('categorySubcategories', {
title: categoryTitle,
progressMessage: `Fetching subcategories for: ${categoryTitle}`,
itemType: 'subcategories'
});
}
async function fetchBacklinks(targetTitle, namespaces, statusText) {
return fetchPaginatedList('backlinks', {
title: targetTitle,
namespace: namespaces,
progressMessage: `Fetching backlinks for: ${targetTitle}`,
itemType: 'backlinks'
}, (msg) => statusText.innerHTML = msg);
}
async function fetchPrefixPages(prefix, namespace, statusText) {
return fetchPaginatedList('prefixPages', {
title: prefix,
namespace: namespace,
progressMessage: `Fetching pages with prefix "${prefix}" in namespace ${namespace}`,
itemType: 'pages'
}, (msg) => statusText.innerHTML = msg);
}
async function fetchSearchResults(query, statusText) {
return fetchPaginatedList('search', {
title: query,
progressMessage: `Searching for: "${query}"`,
itemType: 'search results'
}, (msg) => statusText.innerHTML = msg);
}
// Combined category fetch methods
async function fetchCategoryBoth(categoryTitle) {
const [items, subcategories] = await Promise.all([
fetchCategoryMembers(categoryTitle),
fetchCategorySubcategories(categoryTitle)
]);
return [...items, ...subcategories];
}
// Recursive methods
async function fetchCategoryMembersRecursive(categoryTitle, statusText) {
const visited = new Set();
const allItems = [];
const queue = [categoryTitle];
let totalCategories = 0;
while (queue.length > 0) {
const currentCategory = queue.shift();
const categoryKey = `Category:${currentCategory}`;
if (visited.has(categoryKey)) continue;
visited.add(categoryKey);
totalCategories++;
statusText.innerHTML = `Getting items from "${currentCategory}" (processed ${totalCategories} categories, found ${allItems.length} items, queue: ${queue.length})...`;
const currentItems = await fetchCategoryMembers(currentCategory);
allItems.push(...currentItems);
const subcategories = await fetchCategorySubcategories(currentCategory);
for (const subcategory of subcategories) {
if (!visited.has(subcategory)) {
queue.push(subcategory.replace('Category:', ''));
}
}
}
return [...new Set(allItems)];
}
async function fetchCategorySubcategoriesRecursive(categoryTitle, statusText) {
const visited = new Set();
const allSubcategories = [];
const queue = [`Category:${categoryTitle}`];
while (queue.length > 0) {
const currentCategory = queue.shift();
if (visited.has(currentCategory)) continue;
visited.add(currentCategory);
statusText.innerHTML = `Exploring subcategories (found ${allSubcategories.length} categories, queue: ${queue.length})...`;
const categoryNameForApi = currentCategory.replace('Category:', '');
const directSubcategories = await fetchCategorySubcategories(categoryNameForApi);
for (const subcategory of directSubcategories) {
if (!visited.has(subcategory)) {
allSubcategories.push(subcategory);
queue.push(subcategory);
}
}
}
return [...new Set(allSubcategories)];
}
async function fetchCategoryBothRecursive(categoryTitle, statusText) {
const visited = new Set();
const allItems = [];
const allSubcategories = [];
const queue = [categoryTitle];
let totalCategories = 0;
while (queue.length > 0) {
const currentCategory = queue.shift();
const categoryKey = `Category:${currentCategory}`;
if (visited.has(categoryKey)) continue;
visited.add(categoryKey);
totalCategories++;
statusText.innerHTML = `Getting items and subcategories from "${currentCategory}" (processed ${totalCategories} categories, found ${allItems.length} items, ${allSubcategories.length} subcategories, queue: ${queue.length})...`;
const [currentItems, directSubcategories] = await Promise.all([
fetchCategoryMembers(currentCategory),
fetchCategorySubcategories(currentCategory)
]);
allItems.push(...currentItems);
for (const subcategory of directSubcategories) {
if (!visited.has(subcategory)) {
allSubcategories.push(subcategory);
queue.push(subcategory.replace('Category:', ''));
}
}
}
return [...new Set([...allItems, ...allSubcategories])];
}
// Article links fetcher
async function fetchArticleLinks(statusText) {
const articleTitle = document.querySelector('.mw-first-heading').textContent;
const apiUrl = `${CONFIG.API_URL}?action=query&titles=${encodeURIComponent(articleTitle)}&prop=revisions&rvprop=content&format=json&origin=*`;
const data = await makeApiRequest(apiUrl);
if (!data.query?.pages) {
throw new Error('Could not fetch article content.');
}
const pages = Object.values(data.query.pages);
if (pages.length === 0 || !pages[0].revisions) {
throw new Error('No content found for this article.');
}
const wikitext = pages[0].revisions[0]['*'];
const wikilinkRegex = /\[\[([^\]|\[]+)(?:\|[^\]]+)?\]\]/g;
const links = [];
let match;
while ((match = wikilinkRegex.exec(wikitext)) !== null) {
let linkTarget = match[1].trim();
if (linkTarget.includes('#')) {
linkTarget = linkTarget.split('#')[0];
}
if (linkTarget &&
!linkTarget.startsWith('File:') &&
!linkTarget.startsWith('Image:') &&
!linkTarget.startsWith('Category:') &&
!linkTarget.startsWith('Template:') &&
!linkTarget.startsWith(':') &&
!linkTarget.includes('|')) {
links.push(linkTarget);
}
}
// Return links in original order (don't sort)
return [...new Set(links)];
}
// Scrape watchlist items from the current page (no API calls)
async function scrapeWatchlistFromPage() {
const watchlistItems = [];
// Primary method: Extract from data-target-page attributes
const targetPageElements = document.querySelectorAll('[data-target-page]');
console.log(`Found ${targetPageElements.length} data-target-page elements`);
for (const element of targetPageElements) {
const pageName = element.getAttribute('data-target-page');
if (pageName) {
// Remove namespace prefixes to get the actual article name
let articleName = pageName;
// Handle special cases
if (pageName.startsWith('Talk:')) {
// For talk pages, extract the main article name
articleName = pageName.replace('Talk:', '');
} else if (pageName.includes(':') &&
!pageName.startsWith('Category:') &&
!pageName.startsWith('File:') &&
!pageName.startsWith('Template:') &&
!pageName.startsWith('Help:') &&
!pageName.startsWith('Portal:') &&
!pageName.startsWith('MediaWiki:')) {
// Skip other namespace pages like Wikipedia:, User:, etc.
continue;
}
// Only include if it's a mainspace article (no colon in name)
if (articleName && !articleName.includes(':') && !watchlistItems.includes(articleName)) {
watchlistItems.push(articleName);
}
}
}
// Backup method: Look for .mw-changeslist-title links if we didn't get many results
if (watchlistItems.length < 3) {
console.log('Using backup method: .mw-changeslist-title');
const titleLinks = document.querySelectorAll('.mw-changeslist-title');
for (const link of titleLinks) {
const href = link.getAttribute('href');
if (href && href.startsWith('/wiki/')) {
const pageName = decodeURIComponent(href.replace('/wiki/', '').replace(/_/g, ' '));
// Extract mainspace article name
let articleName = pageName;
if (pageName.startsWith('Talk:')) {
articleName = pageName.replace('Talk:', '');
} else if (pageName.includes(':')) {
continue; // Skip other namespaces
}
if (articleName && !watchlistItems.includes(articleName)) {
watchlistItems.push(articleName);
}
}
}
}
console.log(`Final watchlist items found: ${watchlistItems.length}`, watchlistItems);
return [...new Set(watchlistItems)];
}
// Helper function to extract target title
function extractTargetTitle() {
const urlParams = new URLSearchParams(window.location.search);
let targetTitle = urlParams.get('target');
if (!targetTitle) {
const headingElement = document.querySelector('.mw-first-heading');
if (headingElement) {
const match = headingElement.textContent.match(/Pages that link to "(.+)"/);
if (match) targetTitle = match[1];
}
}
if (!targetTitle) {
const linkElement = document.querySelector('.mw-whatlinkshere-target a');
if (linkElement) {
targetTitle = linkElement.getAttribute('title') || linkElement.textContent;
}
}
return targetTitle;
}
// ===== UI UTILITIES =====
function createControlBox(title, insertTarget) {
const container = document.createElement('div');
Object.assign(container.style, {
padding: '10px',
margin: '10px 0',
backgroundColor: '#f8f9fa',
border: '1px solid #a2a9b1',
borderRadius: '3px'
});
const titleElement = document.createElement('h4');
titleElement.textContent = title;
Object.assign(titleElement.style, {
margin: '0 0 10px 0',
fontSize: '14px',
fontWeight: 'bold'
});
container.appendChild(titleElement);
const buttonsDiv = document.createElement('div');
buttonsDiv.style.marginBottom = '10px';
const urlCheckbox = document.createElement('input');
urlCheckbox.type = 'checkbox';
urlCheckbox.id = 'includeUrls';
urlCheckbox.style.marginLeft = '15px';
const urlLabel = document.createElement('label');
urlLabel.htmlFor = 'includeUrls';
urlLabel.textContent = 'Include URLs';
urlLabel.style.marginLeft = '5px';
addTooltip(urlLabel, 'Include full Wikipedia URLs for each item');
buttonsDiv.appendChild(urlCheckbox);
buttonsDiv.appendChild(urlLabel);
container.appendChild(buttonsDiv);
const statusText = document.createElement('div');
Object.assign(statusText.style, {
marginTop: '10px',
color: '#555'
});
container.appendChild(statusText);
const insertLocation = insertTarget || document.querySelector('#content');
if (insertTarget) {
insertTarget.parentNode.insertBefore(container, insertTarget.nextSibling);
} else {
insertLocation.prepend(container);
}
return { container, buttonsDiv, urlCheckbox, statusText };
}
function createButton(text, tooltip, buttonsDiv, urlCheckbox) {
const btn = document.createElement('button');
btn.textContent = text;
Object.assign(btn.style, {
marginRight: '10px',
padding: '8px 12px',
cursor: 'pointer'
});
addTooltip(btn, tooltip);
buttonsDiv.insertBefore(btn, urlCheckbox);
return btn;
}
// Generic button action handler
async function handleButtonAction(config, statusText, urlCheckbox) {
statusText.innerHTML = config.startMessage;
try {
const items = await config.fetchFunction();
if (items.length === 0) {
statusText.innerHTML = config.emptyMessage;
return;
}
const includeUrls = urlCheckbox.checked;
const formattedText = formatItems(items, includeUrls);
const copySuccess = await copyToClipboardOrDownload(formattedText, config.filename, statusText);
if (copySuccess) {
statusText.innerHTML = `Successfully copied ${items.length} ${config.itemType} to clipboard.`;
}
} catch (error) {
statusText.innerHTML = `Error: ${error.message}`;
}
}
// ===== PAGE HANDLERS =====
function initializeCategoryPage() {
const categoryName = decodeURIComponent(window.location.pathname.split('/Category:')[1]);
console.log("Category Name:", categoryName);
const pageTitleHeading = document.querySelector('.mw-first-heading');
const { buttonsDiv, urlCheckbox, statusText } = createControlBox('Category Tools', pageTitleHeading);
const buttons = [
{
text: 'Copy items',
tooltip: 'Copy all items in this category. Not recursive.',
action: () => handleButtonAction({
startMessage: 'Gathering items from this category via API...',
fetchFunction: () => fetchCategoryMembers(categoryName),
emptyMessage: 'No items found in this category.',
filename: categoryName,
itemType: 'items'
}, statusText, urlCheckbox)
},
{
text: 'Copy items recursively',
tooltip: 'Copy all items in this category AND all items in its subcategories.',
action: () => handleButtonAction({
startMessage: 'Gathering items from this category and all subcategories recursively via API...',
fetchFunction: () => fetchCategoryMembersRecursive(categoryName, statusText),
emptyMessage: 'No items found in this category or its subcategories.',
filename: `${categoryName}_all_recursive`,
itemType: 'items'
}, statusText, urlCheckbox)
},
{
text: 'Copy subcats',
tooltip: 'Copy all subcategories of this category. Not recursive.',
action: () => handleButtonAction({
startMessage: 'Gathering direct subcategories from this category via API...',
fetchFunction: () => fetchCategorySubcategories(categoryName),
emptyMessage: 'No direct subcategories found in this category.',
filename: `${categoryName}_direct_subcats`,
itemType: 'subcategories'
}, statusText, urlCheckbox)
},
{
text: 'Copy subcategories recursively',
tooltip: 'Copy all subcategories of this category and its subcategories.',
action: () => handleButtonAction({
startMessage: 'Gathering all subcategories recursively via API...',
fetchFunction: () => fetchCategorySubcategoriesRecursive(categoryName, statusText),
emptyMessage: 'No subcategories found.',
filename: `${categoryName}_subcategories`,
itemType: 'subcategories'
}, statusText, urlCheckbox)
},
{
text: 'Copy both',
tooltip: 'Copy all items and subcategories from this category. Not recursive.',
action: () => handleButtonAction({
startMessage: 'Gathering both items and subcategories from this category via API...',
fetchFunction: () => fetchCategoryBoth(categoryName),
emptyMessage: 'No items or subcategories found in this category.',
filename: `${categoryName}_both`,
itemType: 'items and subcategories'
}, statusText, urlCheckbox)
},
{
text: 'Copy both recursively',
tooltip: 'Copy all items and subcategories from this category and all its subcategories.',
action: () => handleButtonAction({
startMessage: 'Gathering both items and subcategories recursively via API...',
fetchFunction: () => fetchCategoryBothRecursive(categoryName, statusText),
emptyMessage: 'No items or subcategories found in this category or its subcategories.',
filename: `${categoryName}_both_recursive`,
itemType: 'items and subcategories'
}, statusText, urlCheckbox)
}
];
buttons.forEach(({ text, tooltip, action }) => {
const btn = createButton(text, tooltip, buttonsDiv, urlCheckbox);
btn.addEventListener('click', action);
});
}
function initializeWhatLinksHerePage() {
const targetTitle = extractTargetTitle();
console.log("Target Title:", targetTitle);
const targetHeading = document.querySelector('.mw-first-heading');
const { buttonsDiv, urlCheckbox, statusText } = createControlBox('What Links Here Tools', targetHeading);
const buttons = [
{
text: 'Copy all links',
tooltip: 'Copy all pages that link to this page',
namespace: null,
type: 'all backlinks'
},
{
text: 'Copy mainspace links',
tooltip: 'Copy only mainspace (article) pages that link to this page',
namespace: '0',
type: 'mainspace backlinks'
}
];
buttons.forEach(({ text, tooltip, namespace, type }) => {
const btn = createButton(text, tooltip, buttonsDiv, urlCheckbox);
btn.addEventListener('click', () => handleButtonAction({
startMessage: `Gathering ${type} via API...`,
fetchFunction: () => fetchBacklinks(targetTitle, namespace, statusText),
emptyMessage: namespace === '0' ? 'No mainspace pages link to this page.' : 'No pages link to this page.',
filename: namespace === '0' ? 'mainspace_backlinks' : 'all_backlinks',
itemType: 'backlinks'
}, statusText, urlCheckbox));
});
// Non-mainspace button (special case)
const nonMainspaceBtn = createButton('Copy non-mainspace links', 'Copy only non-mainspace pages (talk, user, etc.) that link to this page', buttonsDiv, urlCheckbox);
nonMainspaceBtn.addEventListener('click', () => handleButtonAction({
startMessage: 'Gathering non-mainspace backlinks via API...',
fetchFunction: async () => {
const allBacklinks = await fetchBacklinks(targetTitle, null, statusText);
statusText.innerHTML = 'Filtering out mainspace backlinks...';
const mainspaceBacklinks = await fetchBacklinks(targetTitle, '0', statusText);
const mainspaceSet = new Set(mainspaceBacklinks);
return allBacklinks.filter(link => !mainspaceSet.has(link));
},
emptyMessage: 'No non-mainspace pages link to this page.',
filename: 'non_mainspace_backlinks',
itemType: 'non-mainspace backlinks'
}, statusText, urlCheckbox));
}
function initializePrefixPage() {
const urlParams = new URLSearchParams(window.location.search);
let prefix = urlParams.get('prefix') || urlParams.get('from') || '';
let namespace = urlParams.get('namespace') || '0';
// Extract prefix from URL path for Special:PrefixIndex/PREFIX format
if (!prefix && window.location.pathname.includes('Special:PrefixIndex/')) {
const pathParts = window.location.pathname.split('Special:PrefixIndex/');
if (pathParts.length > 1) {
const fullPrefix = decodeURIComponent(pathParts[1]);
// Handle namespace prefixes like "User:Polygnotus"
if (fullPrefix.includes(':')) {
const [namespaceName, actualPrefix] = fullPrefix.split(':', 2);
const namespaceMap = {
'User': '2',
'Wikipedia': '4',
'File': '6',
'MediaWiki': '8',
'Template': '10',
'Help': '12',
'Category': '14',
'Portal': '100',
'Draft': '118'
};
if (namespaceMap[namespaceName]) {
namespace = namespaceMap[namespaceName];
prefix = actualPrefix;
} else {
prefix = fullPrefix;
}
} else {
prefix = fullPrefix;
}
}
}
console.log("Prefix:", prefix, "Namespace:", namespace);
const targetHeading = document.querySelector('.mw-first-heading');
const { buttonsDiv, urlCheckbox, statusText } = createControlBox('Prefix Search Tools', targetHeading);
const btn = createButton('Copy all pages', 'Copy all pages with this prefix using API', buttonsDiv, urlCheckbox);
btn.addEventListener('click', () => {
if (!prefix) {
statusText.innerHTML = 'Error: No prefix found in URL parameters or path.';
return;
}
handleButtonAction({
startMessage: 'Gathering all pages with this prefix via API...',
fetchFunction: () => fetchPrefixPages(prefix, namespace, statusText),
emptyMessage: `No pages found with prefix "${prefix}" in namespace ${namespace}.`,
filename: `prefix_${prefix.replace(/[^a-zA-Z0-9]/g, '_')}`,
itemType: 'pages'
}, statusText, urlCheckbox);
});
}
function initializeWatchlistPage() {
const targetHeading = document.querySelector('.mw-first-heading');
const { buttonsDiv, urlCheckbox, statusText } = createControlBox('Watchlist Tools', targetHeading);
const btn = createButton('Copy all watchlist items', 'Copy all pages from your watchlist by scraping the page', buttonsDiv, urlCheckbox);
btn.addEventListener('click', () => handleButtonAction({
startMessage: 'Scraping watchlist items from page...',
fetchFunction: () => scrapeWatchlistFromPage(),
emptyMessage: 'No items found in your watchlist.',
filename: 'complete_watchlist',
itemType: 'watchlist items'
}, statusText, urlCheckbox));
}
function initializeSearchPage() {
const urlParams = new URLSearchParams(window.location.search);
const searchQuery = urlParams.get('search') || '';
console.log("Search Query:", searchQuery);
const targetHeading = document.querySelector('.mw-first-heading');
const { buttonsDiv, urlCheckbox, statusText } = createControlBox('Search Results Tools', targetHeading);
const btn = createButton('Copy all search results', 'Copy all search results using API', buttonsDiv, urlCheckbox);
btn.addEventListener('click', () => {
if (!searchQuery) {
statusText.innerHTML = 'Error: No search query found in URL parameters.';
return;
}
handleButtonAction({
startMessage: 'Gathering all search results via API...',
fetchFunction: () => fetchSearchResults(searchQuery, statusText),
emptyMessage: `No search results found for "${searchQuery}".`,
filename: `search_${searchQuery.replace(/[^a-zA-Z0-9]/g, '_')}`,
itemType: 'search results'
}, statusText, urlCheckbox);
});
}
function initializeArticlePage() {
const targetHeading = document.querySelector('.mw-first-heading');
const { buttonsDiv, urlCheckbox, statusText } = createControlBox('Article Links Tools', targetHeading);
const btn = createButton('Copy wikitext links', 'Copy all wikilink targets from this article\'s source', buttonsDiv, urlCheckbox);
btn.addEventListener('click', () => handleButtonAction({
startMessage: 'Fetching article wikitext...',
fetchFunction: () => fetchArticleLinks(statusText),
emptyMessage: 'No wikilinks found in this article.',
filename: 'article_wikilinks',
itemType: 'wikilinks'
}, statusText, urlCheckbox));
}
// ===== INITIALIZATION =====
function initializePageHandler() {
const currentUrl = window.location.href;
const currentPath = window.location.pathname;
if (currentUrl.includes('/wiki/Category:')) {
initializeCategoryPage();
} else if (currentUrl.includes('Special:WhatLinksHere') || currentPath.includes('Special:WhatLinksHere')) {
initializeWhatLinksHerePage();
} else if (currentUrl.includes('Special:PrefixIndex') || currentPath.includes('Special:PrefixIndex') || currentUrl.includes('Special:AllPages')) {
initializePrefixPage();
} else if (currentUrl.includes('Special:Watchlist')) {
initializeWatchlistPage();
} else if (currentUrl.includes('Special:Search') || currentPath.includes('Special:Search') || currentUrl.includes('search=')) {
initializeSearchPage();
} else if (currentPath.startsWith('/wiki/') && !currentPath.includes(':')) {
initializeArticlePage();
}
}
if (!mw.config.get('wgIsMainPage')) {
initializePageHandler();
}
console.log('Universal Wikipedia List Copier script loaded successfully!');