Jump to content

User:Rusalkii/RfDInfo.js

From Wikipedia, the free encyclopedia
This is the current revision of this page, as edited by Rusalkii (talk | contribs) at 20:19, 4 May 2025. The present address (URL) is a permanent link to this version.
(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.
// <nowiki>
(function() {
    'use strict';
    
    const API_BASE = 'https://en.wikipedia.org/w/api.php';

    async function fetchAPI(params) {
        const url = `${API_BASE}?format=json&origin=*&${params}`;
        const response = await fetch(url);
        return response.json();
    }

    async function getPageInfo(title) {
        const data = await fetchAPI(`action=query&prop=info|linkshere|revisions&inprop=&lhlimit=500&rvlimit=1&rvdir=newer&rvprop=timestamp&titles=${encodeURIComponent(title)}`);
        const pageId = Object.keys(data.query.pages)[0];
        return data.query.pages[pageId];
    }

    async function getPageContent(title) {
        const data = await fetchAPI(`action=query&prop=revisions&rvprop=content&titles=${encodeURIComponent(title)}`);
        const page = Object.values(data.query.pages)[0];
        return page.revisions?.[0]?.['*'] || '';
    }

    async function getRedirectTarget(content) {
        const match = content.match(/#REDIRECT\s*\[\[(.*?)\]\]/i);
        return match ? match[1] : null;
    }

    function formatCreationDate(timestamp) {
        const creationDate = new Date(timestamp);
        const ageYears = (new Date() - creationDate) / (365.25 * 24 * 60 * 60 * 1000);
        
        let formatted = ageYears < 1 
            ? creationDate.toLocaleDateString(undefined, { month: 'long', day: 'numeric', year: 'numeric' })
            : creationDate.getFullYear().toString();
        
        if (ageYears > 10) {
            formatted = `<span style="color: red;">${formatted}</span>`;
        }
        
        return formatted;
    }

    async function checkTextMentioned(targetTitle, redirectTitle) {
        const content = await getPageContent(targetTitle);
        const cleanContent = content.replace(/<ref[^>]*>.*?<\/ref>|<ref[^>]*\/>/g, '');
        return cleanContent.includes(redirectTitle);
    }

	async function getRedirectHistory(title) {
	    const data = await fetchAPI(`action=query&prop=revisions&titles=${encodeURIComponent(title)}&rvprop=content|timestamp|ids&rvlimit=max`);
	    const page = Object.values(data.query.pages)[0];
	    
	    if (!page?.revisions) return {};
	    
	    const targets = new Map();
	    let lastNonRedirectRev = null;
	    let firstRedirectYear = null;
	    let firstRedirectRev = null;
	    
	    // Revisions are newest first, so iterate backwards
	    for (let i = page.revisions.length - 1; i >= 0; i--) {
	        const rev = page.revisions[i];
	        const isRedirect = /#REDIRECT/i.test(rev['*']);
	        
	        if (isRedirect) {
	            const redirectMatch = rev['*'].match(/#REDIRECT\s*\[\[(.*?)\]\]/i);
	            if (redirectMatch) {
	                const year = new Date(rev.timestamp).getFullYear();
	                targets.set(redirectMatch[1], year);
	                if (lastNonRedirectRev && !firstRedirectYear) {
	                	firstRedirectRev = rev.revid;
	                    firstRedirectYear = year;
	                }
                }
	        } else {
	            lastNonRedirectRev = rev.revid;
	        }
	    }
	    
	    return {
	        history: targets.size > 1 ? Array.from(targets.entries())
	            .map(([target, year]) => `${target} (${year})`)
	            .join(' → ') : null,
	        significantHistory: firstRedirectRev && firstRedirectYear ? {
	            revid: firstRedirectRev,
	            redirectYear: firstRedirectYear
	        } : null
	    };
	}

    function extractRedirectTemplates(content) {
        const matches = content.match(/\{\{(?:R(?:edir(?:ect)?)?[ _](?!category\s*shell)|Redirect[ _]from)[^}|]*(?:\|[^}]*)?\}\}/gi) || [];
        return matches.map(template => template.replace(/\{\{([^}|]+).*?\}\}/i, '$1').trim());
    }

    async function getTalkPageInfo(title) {
        const talkContent = await getPageContent(`Talk:${title}`);
        if (!talkContent) return {};
        
        const rfdMatches = talkContent.match(/\{\{old ?rfd[^\}]*?\|\s*result\s*=\s*'''(.*?)'''.*}}/gi) || [];
        const afdMatches = talkContent.match(/\{\{old\s*[ax]fd(?:\s+multi|\s*full)?[^\}]*?\|\s*result\s*=\s*'''(.*?)'''.*}}/gi) || [];
        const mergeMatches = talkContent.match(/\{\{merged[-_ ]?(from|to)[^\}]*?\}\}/gi) || [];
        
	    return {
	        rfdHistory: rfdMatches.map(match => {
	            const pageMatch = match.match(/\|\s*page\s*=\s*([^|}\n]+)/i);
	            const resultMatch = match.match(/\|\s*result\s*=\s*'''(.*?)'''/i);
	            const page = pageMatch[1].trim();
	            const result = resultMatch[1].trim();
	            return `<a href="https://en.wikipedia.org/wiki/Wikipedia:Redirects_for_discussion/Log/${page}" target="_blank">${result}</a>`;
	        }),
	        afdHistory: afdMatches.map(match => {
	            const pageMatch = match.match(/\|\s*page\s*=\s*([^|}\n]+)/i);
	            const resultMatch = match.match(/\|\s*result\s*=\s*'''(.*?)'''/i);
	            const page = pageMatch ? pageMatch[1].trim() : title;
	            const result = resultMatch[1].trim();
	            return `<a href="https://en.wikipedia.org/wiki/Wikipedia:Articles_for_deletion/${page}" target="_blank">${result}</a>`;
	        }),
	        mergeProposals: mergeMatches.length
	    };
    }

    async function getMoveHistory(title) {
        const data = await fetchAPI(`action=query&list=logevents&letype=move&letitle=${encodeURIComponent(title)}&leprop=title|details|timestamp`);
        const events = data.query.logevents;
        
        if (!events?.length) return null;
        
        const moves = events.map(event => 
            `${event.params.target_title} (${new Date(event.timestamp).getFullYear()})`
        );
        const firstYear = new Date(events[events.length - 1].timestamp).getFullYear();
        moves.unshift(`${events[events.length - 1].title} (${firstYear})`);
        
        return moves.join(' → ');
    }

    async function getPageViews(title) {
        const end = new Date();
        const start = new Date();
        start.setDate(start.getDate() - 30);
        const formatDate = date => date.toISOString().slice(0, 10).replace(/-/g, '');
        
        const url = `/media/api/rest_v1/metrics/pageviews/per-article/en.wikipedia/all-access/user/${encodeURIComponent(title)}/daily/${formatDate(start)}/${formatDate(end)}`;
        const response = await fetch(url);
        
        if (!response.ok) return 0;
        
        const data = await response.json();
        return data.items?.reduce((sum, item) => sum + item.views, 0) || 0;
    }

    async function fetchRedirectInfo(title) {
        const info = { errors: [] };
        
        try {
            const pageInfo = await getPageInfo(title);
            
            if (pageInfo.missing) {
                info.errors.push('Page not found');
                return info;
            }
            
            // Creation date
            if (pageInfo.revisions?.[0]?.timestamp) {
                info.created = formatCreationDate(pageInfo.revisions[0].timestamp);
            }
            
            // Incoming links
            info.incomingLinks = pageInfo.linkshere 
                ? pageInfo.linkshere.filter(link => [0, 12, 100, 126].includes(link.ns)).length // Main, help, portal, MOS
                : 0;
            
            // Redirect content analysis
            const content = await getPageContent(title);
            const targetTitle = await getRedirectTarget(content);
            
            if (targetTitle) {
                info.targetTitle = targetTitle;
                info.textMentioned = await checkTextMentioned(targetTitle, title);
            }
            
            // Redirect history
            const { history, significantHistory } = await getRedirectHistory(title);
            info.redirectHistory = history;
            info.significantHistory = significantHistory;
            
            // Redirect templates
            info.redirectTemplates = extractRedirectTemplates(content);
            
            // Talk page info (RfDs, AfDs, merge proposals)
            Object.assign(info, await getTalkPageInfo(title));
            
            // Move history
            info.moveHistory = await getMoveHistory(title);
            
            // Page views
            info.views = await getPageViews(title);
            
        } catch (error) {
            info.errors.push(error.message);
        }
        
        return info;
    }

    function createInfoButton(redirectLink) {
        const button = document.createElement('button');
        button.textContent = 'ℹ️';
        button.style.cssText = 'margin-left: 5px; font-size: 12px; padding: 2px 5px; cursor: pointer;';
        
        const title = redirectLink.title;
        
        button.addEventListener('click', async (e) => {
            e.preventDefault();
            e.stopPropagation();
            
            button.disabled = true;
            button.textContent = '⌛';
            
            const info = await fetchRedirectInfo(title);
            
			let infoText = `<strong>${title}</strong>`;
			infoText += ` <small>(<a href="https://en.wikipedia.org/w/index.php?fulltext=1&search=%22${encodeURIComponent(title)}%22&title=Special%3ASearch&ns0=1" target="_blank">Wikipedia</a> | <a href="https://www.google.com/search?q=%22${encodeURIComponent(title)}%22" target="_blank">Google</a>)</small><div style="margin-bottom: 8px;"></div>`;

			infoText += info.created ? `<b>Created:</b> ${info.created}<br>` : '';
			infoText += info.incomingLinks > 0 ? `<b>Incoming links:</b> ${info.incomingLinks}<br>` : '';
			infoText += info.views !== undefined ? `<b>Views (30d):</b> ${info.views}<br>` : '';
			infoText += info.textMentioned !== undefined ? `<b>Mentioned in target:</b> ${info.textMentioned ? `<a href="https://en.wikipedia.org/wiki/${encodeURIComponent(info.targetTitle)}#:~:text=${title}" target="_blank">Yes</a>` : 'No'}<br>` : '';
			infoText += info.redirectTemplates?.length ? `<b>Tags:</b> ${info.redirectTemplates.join(', ')}<br>` : '';
			infoText += info.rfdHistory?.length ? `<b>Past RfDs:</b> ${info.rfdHistory.join(', ')}<br>` : '';
			infoText += info.afdHistory?.length ? `<b>Past AfDs:</b> ${info.afdHistory.join(', ')}<br>` : '';
			infoText += info.mergeProposals ? `<b>Merge proposals:</b> ${info.mergeProposals}<br>` : '';
			infoText += info.moveHistory ? `<b>Move history:</b> ${info.moveHistory}<br>` : '';
			infoText += info.redirectHistory ? `<b>Target history:</b> ${info.redirectHistory}<br>` : '';
			infoText += info.significantHistory ? `<b>BLARed:</b> <a href="https://en.wikipedia.org/w/index.php?diff=${info.significantHistory.revid}" target="_blank">Last non-redirect revision</a> (${info.significantHistory.redirectYear})<br>` : '';

            if (info.errors.length > 0) {
                infoText += `Errors: ${info.errors.join(', ')}`;
            }
            
            const existingPopup = document.querySelector('.rfd-info-popup');
            if (existingPopup) existingPopup.remove();
            
			const popup = document.createElement('div');
			popup.className = 'rfd-info-popup';
			popup.style.cssText = 'position: fixed; background: #fff; border: 1px solid #aaa; padding: 15px; z-index: 999999; box-shadow: 2px 2px 5px rgba(0,0,0,0.2); font-size: 13px; line-height: 1.5; left: 50%; top: 50%; transform: translate(-50%, -50%); max-width: 400px; max-height: 80vh; overflow-y: auto;';            
			popup.innerHTML = infoText;
            
            document.body.appendChild(popup);
            
            const closePopup = (e) => {
                if (!popup.contains(e.target) && e.target !== button) {
                    popup.remove();
                    document.removeEventListener('click', closePopup);
                }
            };
            
            setTimeout(() => {
                document.addEventListener('click', closePopup);
            }, 100);
            
            button.disabled = false;
            button.textContent = 'ℹ️';
        });
        
        return button;
    }

    function addButtons() {
        const redirectLinks = document.querySelectorAll('a.deletion');
        
        redirectLinks.forEach((link) => {
            if (!link.previousElementSibling || !link.previousElementSibling.classList.contains('rfd-info-button')) {
                const infoButton = createInfoButton(link);
                infoButton.classList.add('rfd-info-button');
                link.insertAdjacentElement('beforebegin', infoButton);
            }
        });
    }

	// Do not run anywhere except RfD pages
    if (!mw.config.get('wgPageName').match(/Wikipedia:Redirects_for_discussion/)) {
    	return;
    }

    // Initialize
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', addButtons);
    } else {
        setTimeout(addButtons, 1000);
    }
    
    // Watch for dynamic content
    const observer = new MutationObserver(() => {
        addButtons();
    });
    
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
})();
// </nowiki>