Content deleted Content added
Polygnotus (talk | contribs) No edit summary |
Polygnotus (talk | contribs) No edit summary |
||
(7 intermediate revisions by the same user not shown) | |||
Line 9:
// Set up the page
$('#firstHeading').text('Wikipedia List Generator');
document.title = 'Wikipedia List Generator';
setupListGeneratorInterface();
}
Line 18 ⟶ 19:
BASE_URL: 'https://en.wikipedia.org',
API_URL: 'https://en.wikipedia.org/w/api.php'
};
// Global state for pause/stop functionality
const OPERATION_STATE = {
isPaused: false,
shouldStop: false,
currentOperation: null
};
Line 83 ⟶ 91:
async function makeApiRequest(url, retryCount = 0) {
// Check for stop signal
if (OPERATION_STATE.shouldStop) {
throw new Error('Operation stopped by user');
}
// Handle pause
while (OPERATION_STATE.isPaused && !OPERATION_STATE.shouldStop) {
await new Promise(resolve => setTimeout(resolve, 100));
}
// Use dynamic delay from UI
const delay = parseInt($('#delay-input').val()) || CONFIG.API_DELAY;
await new Promise(resolve => setTimeout(resolve, delay));
try {
Line 114 ⟶ 134:
return data;
} catch (error) {
if (retryCount < CONFIG.MAX_RETRIES && !OPERATION_STATE.shouldStop) {
await new Promise(resolve => setTimeout(resolve, 1000));
return makeApiRequest(url, retryCount + 1);
Line 129 ⟶ 149:
do {
// Check for stop signal
if (OPERATION_STATE.shouldStop) {
throw new Error('Operation stopped by user');
}
const url = apiConfig.buildUrl(continueToken);
statusCallback(`${apiConfig.progressMessage} (page ${pagesProcessed + 1})...`);
Line 140 ⟶ 165:
statusCallback(`Retrieved ${allItems.length} ${apiConfig.itemType} (page ${pagesProcessed})...`);
} while (continueToken && !OPERATION_STATE.shouldStop);
return allItems;
Line 285 ⟶ 310:
while (queue.length > 0) {
if (OPERATION_STATE.shouldStop) {
statusCallback('Operation stopped by user.');
break;
}
const currentCategory = queue.shift();
const categoryKey = `Category:${currentCategory}`;
Line 294 ⟶ 324:
statusCallback(`Getting items from "${currentCategory}" (processed ${totalCategories} categories, found ${allItems.length} items, queue: ${queue.length})...`);
if (OPERATION_STATE.shouldStop) break;
const currentItems = await fetchCategoryMembers(currentCategory, statusCallback);
if (OPERATION_STATE.shouldStop) break;
allItems.push(...currentItems);
if (OPERATION_STATE.shouldStop) break;
const subcategories = await fetchCategorySubcategories(currentCategory, statusCallback);
if (OPERATION_STATE.shouldStop) break;
for (const subcategory of subcategories) {
if (OPERATION_STATE.shouldStop) break;
if (!visited.has(subcategory)) {
queue.push(subcategory.replace('Category:', ''));
Line 307 ⟶ 343:
return [...new Set(allItems)];
}
async function fetchCategorySubcategoriesRecursive(categoryTitle, statusCallback) {
const visited = new Set();
const allSubcategories = [];
const queue = [`Category:${categoryTitle}`];
while (queue.length > 0) {
if (OPERATION_STATE.shouldStop) {
statusCallback('Operation stopped by user.');
break;
}
const currentCategory = queue.shift();
if (visited.has(currentCategory)) continue;
visited.add(currentCategory);
statusCallback(`Exploring subcategories (found ${allSubcategories.length} categories, queue: ${queue.length})...`);
if (OPERATION_STATE.shouldStop) break;
const categoryNameForApi = currentCategory.replace('Category:', '');
const directSubcategories = await fetchCategorySubcategories(categoryNameForApi, statusCallback);
if (OPERATION_STATE.shouldStop) break;
for (const subcategory of directSubcategories) {
if (OPERATION_STATE.shouldStop) break;
if (!visited.has(subcategory)) {
allSubcategories.push(subcategory);
queue.push(subcategory);
}
}
}
return [...new Set(allSubcategories)];
}
async function fetchCategoryBothRecursive(categoryTitle, statusCallback) {
const visited = new Set();
const allItems = [];
const allSubcategories = [];
const queue = [categoryTitle];
let totalCategories = 0;
while (queue.length > 0) {
if (OPERATION_STATE.shouldStop) {
statusCallback('Operation stopped by user.');
break;
}
const currentCategory = queue.shift();
const categoryKey = `Category:${currentCategory}`;
if (visited.has(categoryKey)) continue;
visited.add(categoryKey);
totalCategories++;
statusCallback(`Getting items and subcategories from "${currentCategory}" (processed ${totalCategories} categories, found ${allItems.length} items, ${allSubcategories.length} subcategories, queue: ${queue.length})...`);
if (OPERATION_STATE.shouldStop) break;
const [currentItems, directSubcategories] = await Promise.all([
fetchCategoryMembers(currentCategory, statusCallback),
fetchCategorySubcategories(currentCategory, statusCallback)
]);
if (OPERATION_STATE.shouldStop) break;
allItems.push(...currentItems);
for (const subcategory of directSubcategories) {
if (OPERATION_STATE.shouldStop) break;
if (!visited.has(subcategory)) {
allSubcategories.push(subcategory);
queue.push(subcategory.replace('Category:', ''));
}
}
}
return [...new Set([...allItems, ...allSubcategories])];
}
// ===== UTILITY FUNCTIONS =====
const updateStatus = function(message) {
$('#status-text').html(message);
};
// ===== UI SETUP =====
const content = $('#mw-content-text');
content.html(`
Line 319 ⟶ 440:
</div>
<div id="listgen-tabs" style="margin-bottom:
<button class="listgen-tab active" data-tab="category">Categories</button>
<button class="listgen-tab" data-tab="backlinks">Whatlinkshere</button>
Line 331 ⟶ 452:
<div class="input-group">
<label for="category-input">Category name (without "Category:" prefix):</label>
<input type="text" id="category-input" placeholder="e.g., American novelists" style="width:
</div>
<div class="button-group">
Line 349 ⟶ 470:
<div class="input-group">
<label for="backlinks-input">Page title:</label>
<input type="text" id="backlinks-input" placeholder="e.g., United States" style="width:
</div>
<div class="button-group">
Line 363 ⟶ 484:
<h3>Prefix Search Tools</h3>
<div class="input-group">
<label for="prefix-input">
<input type="text" id="prefix-input" placeholder="e.g., List of, User:Jimbo, Template:Infobox" style="width:
<
Examples: "List of" (mainspace), "User:Jimbo" (user namespace), "Template:Infobox" (template namespace)
</div>
<div class="button-group">
Line 389 ⟶ 501:
<div class="input-group">
<label for="search-input">Search query:</label>
<input type="text" id="search-input" placeholder="e.g., American authors" style="width:
</div>
<div class="button-group">
Line 398 ⟶ 510:
<div class="options-section" style="margin: 20px 0; padding: 15px; background: #f8f9fa; border: 1px solid #a2a9b1; border-radius: 3px;">
<
<label>
<input type="checkbox" id="include-urls"> Include URLs </label>
<span style="margin-left: 10px; color: #666; font-size: 12px;">Check to include full Wikipedia URLs for each item</span>
</div>
<div style="margin-bottom: 15px;">
<label for="delay-input" style="display: inline-block; width: 200px;">API Request Delay (ms):</label>
<input type="number" id="delay-input" value="500" min="100" max="5000" step="100" style="width: 100px; padding: 3px;">
<span style="margin-left: 10px; color: #666; font-size: 12px;">Time to wait between API requests (100-5000ms)</span>
</div>
<div class="control-buttons">
<button id="pause-btn" style="background: #ff9500; color: white; border: none; padding: 8px 15px; border-radius: 3px; margin-right: 10px;" disabled>⏸️ Pause</button>
<button id="stop-btn" style="background: #d33; color: white; border: none; padding: 8px 15px; border-radius: 3px;" disabled>⏹️ Stop</button>
</div>
</div>
Line 466 ⟶ 591:
setupEventHandlers();
};
// Tab switching
$('.listgen-tab').on('click', function() {
Line 492 ⟶ 617:
$('#prefix-search').on('click', handlePrefixAction);
$('#search-results').on('click', handleSearchAction);
// Control button handlers
$('#pause-btn').on('click', handlePauseResume);
$('#stop-btn').on('click', handleStop);
};
// ===== CONTROL FUNCTIONS =====
const setOperationState = function(isRunning) {
if (isRunning) {
OPERATION_STATE.shouldStop = false;
OPERATION_STATE.isPaused = false;
$('#pause-btn').prop('disabled', false).text('⏸️ Pause');
$('#stop-btn').prop('disabled', false);
$('.button-group button').prop('disabled', true);
} else {
OPERATION_STATE.shouldStop = false;
OPERATION_STATE.isPaused = false;
OPERATION_STATE.currentOperation = null;
$('#pause-btn').prop('disabled', true).text('⏸️ Pause');
$('#stop-btn').prop('disabled', true);
$('.button-group button').prop('disabled', false);
}
};
const handlePauseResume = function() {
if (OPERATION_STATE.isPaused) {
OPERATION_STATE.isPaused = false;
$('#pause-btn').text('⏸️ Pause');
updateStatus('Operation resumed...');
} else {
OPERATION_STATE.isPaused = true;
$('#pause-btn').text('▶️ Resume');
updateStatus('Operation paused. Click Resume to continue.');
}
};
const handleStop = function() {
OPERATION_STATE.shouldStop = true;
OPERATION_STATE.isPaused = false;
updateStatus('Stop requested - operation will halt after current request...');
};
// ===== ACTION HANDLERS =====
const handleCategoryAction = async function
const categoryName = $('#category-input').val().trim();
if (!categoryName) {
Line 502 ⟶ 668:
return;
}
setOperationState(true);
OPERATION_STATE.currentOperation = `category-${action}`;
const includeUrls = $('#include-urls').is(':checked');
Line 539 ⟶ 708:
filename = `${categoryName}_both_recursive`;
break;
}
if (OPERATION_STATE.shouldStop) {
updateStatus('Operation stopped by user.');
return;
}
Line 553 ⟶ 727:
}
} catch (error) {
updateStatus('Operation stopped by user.');
} else {
updateStatus(`Error: ${error.message}`);
}
} finally {
setOperationState(false);
}
};
const handleBacklinksAction = async function
const targetTitle = $('#backlinks-input').val().trim();
if (!targetTitle) {
Line 563 ⟶ 743:
return;
}
setOperationState(true);
OPERATION_STATE.currentOperation = `backlinks-${type}`;
const includeUrls = $('#include-urls').is(':checked');
Line 582 ⟶ 765:
case 'non-mainspace':
const allBacklinks = await fetchBacklinks(targetTitle, null, statusCallback);
if (OPERATION_STATE.shouldStop) return;
updateStatus('Filtering out mainspace backlinks...');
const mainspaceBacklinks = await fetchBacklinks(targetTitle, '0', statusCallback);
Line 588 ⟶ 772:
filename = 'non_mainspace_backlinks';
break;
}
if (OPERATION_STATE.shouldStop) {
updateStatus('Operation stopped by user.');
return;
}
Line 602 ⟶ 791:
}
} catch (error) {
updateStatus('Operation stopped by user.');
} else {
updateStatus(`Error: ${error.message}`);
}
} finally {
setOperationState(false);
}
};
const handlePrefixAction = async function
const
if (!
updateStatus('Please enter a prefix or page title.');
return;
}
setOperationState(true);
OPERATION_STATE.currentOperation = 'prefix-search';
// Parse namespace and prefix from input
let namespace = '0'; // Default to mainspace
let prefix = fullInput;
// Check if input contains namespace prefix
if (fullInput.includes(':')) {
const [namespaceName, actualPrefix] = fullInput.split(':', 2);
const namespaceMap = {
'User': '2',
'Wikipedia': '4',
'File': '6',
'Image': '6', // Alias for File
'MediaWiki': '8',
'Template': '10',
'Help': '12',
'Category': '14',
'Portal': '100',
'Draft': '118',
'Talk': '1',
'User talk': '3',
'Wikipedia talk': '5',
'File talk': '7',
'MediaWiki talk': '9',
'Template talk': '11',
'Help talk': '13',
'Category talk': '15'
};
if (namespaceMap[namespaceName]) {
namespace = namespaceMap[namespaceName];
prefix = actualPrefix || ''; // Handle cases like "User:" with no prefix after
}
// If not a recognized namespace, treat the whole input as prefix in mainspace
}
Line 620 ⟶ 852:
try {
const items = await fetchPrefixPages(prefix, namespace, statusCallback);
if (OPERATION_STATE.shouldStop) {
updateStatus('Operation stopped by user.');
return;
}
if (items.length === 0) {
updateStatus(`No pages found with prefix "${
return;
}
const filename = `prefix_${
const formattedText = formatItems(items, includeUrls);
const copySuccess = await copyToClipboardOrDownload(formattedText, filename, $('#status-section'));
Line 634 ⟶ 871:
}
} catch (error) {
updateStatus('Operation stopped by user.');
} else {
updateStatus(`Error: ${error.message}`);
}
} finally {
setOperationState(false);
}
};
const handleSearchAction = async function
const query = $('#search-input').val().trim();
if (!query) {
Line 644 ⟶ 887:
return;
}
setOperationState(true);
OPERATION_STATE.currentOperation = 'search';
const includeUrls = $('#include-urls').is(':checked');
Line 650 ⟶ 896:
try {
const items = await fetchSearchResults(query, statusCallback);
if (OPERATION_STATE.shouldStop) {
updateStatus('Operation stopped by user.');
return;
}
if (items.length === 0) {
Line 664 ⟶ 915:
}
} catch (error) {
updateStatus('Operation stopped by user.');
} else {
updateStatus(`Error: ${error.message}`);
}
} finally {
setOperationState(false);
}
};
console.log('Wikipedia List Generator loaded successfully!');
|