Jump to content

User:Ahecht/Scripts/RedirectTagID.js

From Wikipedia, the free encyclopedia
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.
//RedirectTagID v0.4 by [[User:Ahecht]]

$(document).ready( mw.loader.using('mediawiki.api').then( async () => {
	if (!['Recentchanges', 'Contributions', 'Newpages'].includes(mw.config.get('wgCanonicalSpecialPageName'))) return;
	const tags = document.querySelectorAll('.mw-tag-marker-mw-new-redirect, .mw-tag-marker-mw-changed-redirect-target');
	var api = new mw.Api( { userAgent: `RedirectTagID/0.4 (${mw.config.get('wgServerName').split('.')[1]}:${mw.config.get('wgServerName').split('.')[0]}, User:Ahecht)` } );
	
	var titleMap = new Map();
	var oldids = [];
	var tagByOldidMap = new Map();
	
	tags.forEach(tag => {
		const container = tag.closest('td') || tag.closest('li');
		if (!container) {console.warn("RedirectTagID: Parent li/tr not found for ", container); return;}

		var title = container.getAttribute('data-target-page');
		
		if (!title) {
			var titleMatch = null;
			var allLinks = Array.from(
				container.querySelectorAll('a.mw-contributions-title, a.mw-changeslist-title, a.mw-newpages-pagename')
			);
			
			for (const titleLink of allLinks) {
				if (
					titleLink.compareDocumentPosition(tag) &
					Node.DOCUMENT_POSITION_FOLLOWING
				) {
					titleMatch = titleLink;
				} else {
					break;
				}
			}

			if (!titleMatch) {console.warn("RedirectTagID: Title link not found for ", container); return;}

			title = titleMatch.textContent.trim();
		}
		
		if (!title) {
			console.log("RedirectTagID: Title not found for ", container);
			return;
		}

		var oldid = null;
		
		if (mw.config.get('wgCanonicalSpecialPageName') == 'Recentchanges') {
			let histContainer = container.querySelector('a.mw-changeslist-diff, a.mw-changeslist-diff-cur')?.href;
			
			if (histContainer) {
				oldid = histContainer.match(/diff=(\d+)/i)?.[1];
				if (oldid == '0') {
					oldid = histContainer.match(/oldid=(\d+)/i)?.[1];
				}
			} else {
				histContainer = container.querySelector('span.mw-changeslist-links')?.querySelector('span');
				if (histContainer && (histContainer.innerHTML == histContainer.innerText)) {
					oldid = 1;
					titleMap.set(tag, title);
				} else {
					console.log('RedirectTagID: Diff link not found for ', container);
					return;
				}
			}
		} else { // Contributions or Newpages
			oldid = container.getAttribute('data-mw-revid');	
		}

		if (!oldid) {
			console.log('RedirectTagID: Diff ID not found for ', container);
			return;
		}

		if (oldid != 1) {
			oldids.push(oldid);
			tagByOldidMap.set(oldid, tag)
		}
	} );

	var contentMap = new Map();
	var promises = [];
	
	titleMap.forEach( (title, tag) => { // grab first revision of each page without an oldid
		promises.push( api.get( {
			"action": "query",
			"format": "json",
			"prop": "revisions",
			"titles": title,
			"formatversion": "2",
			"rvprop": "content",
			"rvdir": "newer",
			"rvlimit": "1"
		} ).fail( 
			(e, f) => console.warn("RedirectTagID: ", f.error.info)
		).done( data => {
			let page = data?.query?.pages?.[0];
			if (typeof page !== 'object') {
				console.warn("RedirectTagID: No page found in API data for ", title, data);
				return;
			}
		
			if (typeof page?.revisions?.[0] !== 'object') {
				if (page.missing === true) {
					contentMap.set(tag, false);
				} else {
					console.warn("RedirectTagID: No revisions found for ", title, data);
				}
				return;
			}

			contentMap.set(tag, page?.revisions?.[0]?.content || '');
		} ) );
	} );

	while (oldids.length) { // grab contents of oldids in batches of 50
		let revids = oldids.splice(0, 50).join('|');

		promises.push( api.get( {
			"action": "query",
			"format": "json",
			"prop": "revisions",
			"revids": revids,
			"formatversion": "2",
			"rvprop": "content|ids",
		} ).fail( 
			(e, f) => console.warn("RedirectTagID: ", f.error.info)
		).done( data => {
			let pages = data?.query?.pages;
			if (typeof pages !== 'object') {
				console.warn("RedirectTagID: No pages found in API data for ", revids, data);
				return;
			}

			pages.forEach( (page) => {
				let content = null;
				let revision = page?.revisions?.[0];
				
				if (typeof revision === 'object') {
					content = revision?.content || '';
				} else {
					if (page.missing === true) {
						content = false;
					} else {
						console.warn("RedirectTagID: No revisions found for ", page.title, data);
						return;
					}
				}

				let tag = tagByOldidMap.get(String(revision?.revid));

				if (tag) {
					contentMap.set(tag, content);
				} else {
					console.warn("RedirectTagID: Invalid revid ", revision.revid, data, tagByOldidMap);
				}
			} );
		} ) );
	}

	await Promise.allSettled(promises); // Wait for all API calls to finish
	
	contentMap.forEach( (content, tag) => {
		if (tag.dataset.redirectTargetAppended === '1') {
			//console.warn("RedirectTagID: Redirect already appended to", tag);
			return;
		}
		
		if (content === false) {
			tag.append( ' (deleted)');
			tag.dataset.redirectTargetAppended = '1';
			return;
		}
		
		var target = content.match(/(?:#REDIRECT:?|return require *\(?) *(?:\[\[|") *(.*?) *(?:\]\]|")/i);
		if (!target?.[1]) {
			console.warn("RedirectTagID: No redirect found in ", content, tag);
			return;
		}
	
		var toLink = mw.config.get('wgArticlePath').replace('$1', mw.util.rawurlencode(target[1]));
		var toText = target[1].replace("#", " § ");
	
		tag.append(	' to ',	Object.assign(document.createElement('a'), {href: toLink, textContent: toText}));
		tag.dataset.redirectTargetAppended = '1';
	} );

} ) );