//https://en.wikipedia.orghttps://demo.azizisearch.com/lite/wikipedia/page/Wikipedia_talk:Twinkle#Twinkle_feature_request_--_look_for_previous_XfDs
// Add AfD search functionality to Wikipedia
$(document).ready(function() {
// Only run on article pages (namespace 0)
if (mw.config.get('wgNamespaceNumber') === 0) {
addAfDSearchButton();
}
});
function addAfDSearchButton() {
// Create the search button
var $button = $('<a>')
.attr('href', '#')
.attr('id', 'afd-search-btn')
.text('Search AfD')
.css({
'margin-left': '10px',
'font-size': '0.9em',
'color': '#0645ad',
'text-decoration': 'none'
})
.on('click', function(e) {
e.preventDefault();
searchAfDDiscussions();
});
// Add button to the page - you can modify this selector based on where you want it
// This adds it after the page title
$('#firstHeading').after($('<div>').append($button));
// Alternative locations (uncomment one if preferred):
// $('.mw-indicators').append($button); // Near other page indicators
// $('#p-cactions ul').append($('<li>').append($button)); // In the page actions menu
}
function searchAfDDiscussions() {
var articleTitle = mw.config.get('wgPageName').replace(/_/g, ' ');
var searchPrefix = 'Wikipedia:Articles for deletion/' + articleTitle;
// Show loading indicator
$('#afd-search-btn').text('Searching...').css('color', '#888');
var api = new mw.Api();
// First try exact prefix match
api.get({
action: 'query',
list: 'allpages',
apprefix: searchPrefix,
apnamespace: 4, // Wikipedia namespace
aplimit: 50,
format: 'json'
}).then(function(data) {
var exactMatches = data.query.allpages || [];
// Then do a broader search using the search API
api.get({
action: 'query',
list: 'search',
srsearch: 'prefix:"Wikipedia:Articles for deletion/' + articleTitle + '"',
srnamespace: 4,
srlimit: 50,
format: 'json'
}).then(function(searchData) {
var searchMatches = searchData.query.search || [];
// Combine and deduplicate results
var allMatches = [];
var seenTitles = new Set();
// Add exact matches first
exactMatches.forEach(function(page) {
if (!seenTitles.has(page.title)) {
allMatches.push({title: page.title});
seenTitles.add(page.title);
}
});
// Add search matches
searchMatches.forEach(function(page) {
if (!seenTitles.has(page.title)) {
allMatches.push({title: page.title});
seenTitles.add(page.title);
}
});
// Get creation dates for all found pages
if (allMatches.length > 0) {
getAfDDates(allMatches, articleTitle);
} else {
displayAfDResults(allMatches, articleTitle);
}
}).catch(function() {
// If search fails, just show exact matches
displayAfDResults(exactMatches, articleTitle);
});
}).catch(function() {
$('#afd-search-btn').text('Search AfD').css('color', '#0645ad');
mw.notify('Error searching for AfD discussions', { type: 'error' });
});
}
function getAfDDates(pages, articleTitle) {
var api = new mw.Api();
var pagesWithDates = [];
var mostRecentDate = null;
var mostRecentTimestamp = 0;
var completedRequests = 0;
console.log('Fetching dates for:', pages.map(function(p) { return p.title; }));
// If no pages, just display empty results
if (pages.length === 0) {
displayAfDResults(pages, articleTitle);
return;
}
// Fetch date for each page individually
pages.forEach(function(page) {
api.get({
action: 'query',
format: 'json',
prop: 'revisions',
titles: page.title,
rvlimit: 1,
rvdir: 'newer',
rvprop: 'timestamp'
}).then(function(data) {
var pageData = null;
var hasDate = false;
if (data.query && data.query.pages) {
var pageId = Object.keys(data.query.pages)[0];
pageData = data.query.pages[pageId];
if (pageData && !pageData.missing && pageData.revisions && pageData.revisions.length > 0) {
var timestamp = pageData.revisions[0].timestamp;
var date = new Date(timestamp);
var timestampMs = date.getTime();
console.log('Page:', pageData.title, 'Date:', date.toDateString());
pagesWithDates.push({
title: pageData.title,
date: date,
timestamp: timestampMs
});
// Track most recent
if (timestampMs > mostRecentTimestamp) {
mostRecentTimestamp = timestampMs;
mostRecentDate = date;
}
hasDate = true;
}
}
// If no date found, add page without date
if (!hasDate) {
console.log('No date found for:', page.title);
pagesWithDates.push({
title: page.title,
date: null,
timestamp: 0
});
}
completedRequests++;
// When all requests are complete, display results
if (completedRequests === pages.length) {
// Sort by date (most recent first, pages without dates last)
pagesWithDates.sort(function(a, b) {
if (!a.timestamp && !b.timestamp) return 0;
if (!a.timestamp) return 1;
if (!b.timestamp) return -1;
return b.timestamp - a.timestamp;
});
// Check if most recent is within 3 months
var threeMonthsAgo = new Date();
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3);
var isRecent = mostRecentDate && mostRecentDate > threeMonthsAgo;
console.log('Most recent date:', mostRecentDate ? mostRecentDate.toDateString() : 'none', 'Is recent:', isRecent);
displayAfDResults(pagesWithDates, articleTitle, mostRecentDate, isRecent);
}
}).catch(function(error) {
console.log('API error for', page.title, ':', error);
// Add page without date on error
pagesWithDates.push({
title: page.title,
date: null,
timestamp: 0
});
completedRequests++;
// When all requests are complete (including errors), display results
if (completedRequests === pages.length) {
displayAfDResults(pagesWithDates, articleTitle, mostRecentDate, false);
}
});
});
}
function displayAfDResults(pages, articleTitle, mostRecentDate, isRecent) {
// Reset button
$('#afd-search-btn').text('Search AfD').css('color', '#0645ad');
// Remove any existing results
$('#afd-results').remove();
var $resultsDiv = $('<div>')
.attr('id', 'afd-results')
.css({
'margin': '10px 0',
'padding': '10px',
'border': '1px solid #ccc',
'background-color': '#f9f9f9',
'border-radius': '3px'
});
if (pages.length === 0) {
$resultsDiv.html('<strong>No previous AfD discussions found for "' + articleTitle + '"</strong>');
} else {
var $title = $('<strong>').text('Previous AfD discussions for "' + articleTitle + '":');
// Add warning if recent AfD
if (isRecent && mostRecentDate) {
var $warning = $('<div>')
.css({
'color': '#d33',
'font-weight': 'bold',
'margin': '5px 0',
'padding': '5px',
'background-color': '#fee',
'border': '1px solid #fcc',
'border-radius': '3px'
})
.html('⚠️ <strong>Warning:</strong> Most recent AfD was on ' +
mostRecentDate.toLocaleDateString() + ' (within last 3 months)');
$resultsDiv.append($warning);
}
var $list = $('<ul>').css('margin', '10px 0');
pages.forEach(function(page) {
var $link = $('<a>')
.attr('href', mw.util.getUrl(page.title))
.text(page.title);
var $listItem = $('<li>').append($link);
// Add date if available
if (page.date) {
var dateStr = page.date.toLocaleDateString() + ' (' + getTimeAgo(page.date) + ')';
$listItem.append($('<span>').css('color', '#666').text(' - ' + dateStr));
}
$list.append($listItem);
});
$resultsDiv.append($title).append($list);
}
// Add close button
var $closeBtn = $('<span>')
.text(' [close]')
.css({
'cursor': 'pointer',
'color': '#0645ad',
'font-size': '0.9em'
})
.on('click', function() {
$('#afd-results').remove();
});
$resultsDiv.append($closeBtn);
// Insert results after the button
$('#afd-search-btn').parent().after($resultsDiv);
}
function getTimeAgo(date) {
var now = new Date();
var diffMs = now - date;
var diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
var diffMonths = Math.floor(diffDays / 30);
var diffYears = Math.floor(diffDays / 365);
if (diffDays < 1) {
return 'today';
} else if (diffDays < 30) {
return diffDays + ' day' + (diffDays === 1 ? '' : 's') + ' ago';
} else if (diffMonths < 12) {
return diffMonths + ' month' + (diffMonths === 1 ? '' : 's') + ' ago';
} else {
return diffYears + ' year' + (diffYears === 1 ? '' : 's') + ' ago';
}
}
// Optional: Add keyboard shortcut (Alt+A)
$(document).on('keydown', function(e) {
if (e.altKey && e.which === 65 && mw.config.get('wgNamespaceNumber') === 0) { // Alt+A
e.preventDefault();
searchAfDDiscussions();
}
});