User:Phlsph7/HighlightUnreferencedPassages.js

/*** Highlight unreferenced passages ***/ (function{	// finds passages that lack references and marks them	function markUnreferencedPassages{		// stylesheet to color passages lacking references		function addStylesheet{			const stylesheet = document.createElement('style');			stylesheet.innerHTML = `			.has-no-references, .Template-Fact {				background: LightPink;			}			`;			document.head.appendChild(stylesheet);		}		// check whether references are relevant to the element 		function isEligible(element, excludedSections){			// exclude elements that are part of navboxes, sidebars, and the like			// references do not matter for them			if(hasParentClass(element, 'navbox') || hasParentClass(element, 'sidebar') || hasParentClass(element, 'infobox') || hasParentClass(element, 'side-box-flex') || hasParentClass(element, 'noprint') || hasParentClass(element, 'refbegin') || hasParentClass(element, 'gallery') || hasParentClass(element, 'toc') || hasParentClass(element, 'reflist')){				return false;			}			// exclude elements that belong to certain sections where references do not matter			const sectionName = getSectionName(element);			if(excludedSections.indexOf(sectionName) != -1){				return false;			}			return true;		}		// utility function to check whether the elements parents and grand parents have a certain class		function hasParentClass(element, className){			return element.closest('.' + className) != null;		}		// utility function to get the section name to which an element belongs		function getSectionName(element){			var mainContainerChildElement = getGrandchildOfMainContainer(element);			var sectionName = ''; // default section name, this corresponds to the lead			var previousElement = mainContainerChildElement.previousElementSibling;			// sections always start with an h2 element			// the script loops back from an element to the previous element until an h2 element is discovered while(previousElement != null){ // check whether it is an h2 element if(previousElement.classList.contains('mw-heading2')){ // extract the section name sectionName = previousElement.innerText.split('[edit]').join(''); break; }				previousElement = previousElement.previousElementSibling; }			return sectionName; }		// utility function: for any element, return the parent that is a grandchild of the main container function getGrandchildOfMainContainer(element){ const mainContainer = document.getElementById('mw-content-text'); if(element.parentElement.parentElement == mainContainer){ return element; }			else{ return getGrandchildOfMainContainer(element.parentElement); }		}		// utility function to check whether the lead section of the article should be marked function shouldMarkLead{ // if it is a draft then the lead should be highlighted var pageTitleNamespace = document.getElementsByClassName('mw-page-title-namespace')[0]; if(pageTitleNamespace != null){ if(pageTitleNamespace.innerText === 'User' || pageTitleNamespace.innerText === 'Draft'){ return true; }			}			// if it is a stub then the lead should be highlighted else if(document.getElementsByClassName('stub').length > 0){ return true; }			// otherwise not else { return false; }		}		// marks elements that lack references function mark(element){ // mark elements without any reference elements if(element.getElementsByClassName('reference').length == 0){ element.classList.add("has-no-references"); }			// mark elements with some reference elements else{ // if the last element is not a reference then start marking it				markUntilPreviousReference(element.lastChild); // starts from each "citation needed" tag, goes backwards and marks until it reaches a reference var citationNeededTags = element.getElementsByClassName('Template-Fact'); for(var citationNeededTag of citationNeededTags){ markUntilPreviousReference(citationNeededTag); }			}		}		// Function to mark unreferenced passages. It starts with one node and loops back to previous nodes until it hits a reference function markUntilPreviousReference(childNode){ var currentNode = childNode; while(currentNode != null){ // handle nodes that are not HTML elements if(currentNode.classList == null){ // create a span element and classify it					var span = document.createElement('span'); span.classList.add("has-no-references"); // copy the node's text into the span element and replace the node with the span element span.innerHTML = currentNode.data; currentNode.parentElement.replaceChild(span, currentNode); currentNode = span; }				// handle nodes that are HTML elements // if the node is a reference else if(currentNode.classList.contains('reference')){ // check whether the node is an actual reference: they contain numbers if(currentNode != null && 						currentNode.innerText != null && 						/[0-9]/.test(currentNode.innerText) &&						!currentNode.innerText.toLowerCase.includes('note') &&						!currentNode.innerText.toLowerCase.includes('nb')){ break; }					// otherwise it is an explanatory footnote and not a reference else{ currentNode.classList.add('has-no-references'); }				}				// if the node is an element but not a reference then classify it				else { currentNode.classList.add('has-no-references'); }				// set the current node to the previous one to continue the loop currentNode = currentNode.previousSibling; }		}		// removes the red background from elements that were falsely highlighted function excludeFalsePositives{ // exclude references used in nested lists var unreferencedElements = document.getElementsByClassName('has-no-references'); for(let unreferencedElement of unreferencedElements){ // if the element contains a reference inside then it is not unreferenced, so remove the class if(unreferencedElement.getElementsByClassName('reference').length > 0){ unreferencedElement.classList.remove('has-no-references'); }			}			// exclude quoteboxes unreferencedElements = document.getElementsByClassName('has-no-references'); for(let unreferencedElement of unreferencedElements){ // see if the the element is part of a quotebox that has a citation var quoteboxParent = unreferencedElement.closest('.quotebox'); if(quoteboxParent != null && quoteboxParent.getElementsByTagName('cite').length > 0){ unreferencedElement.classList.remove('has-no-references'); }			}			// do not mark empty elements unreferencedElements = document.getElementsByClassName('has-no-references'); for(let unreferencedElement of unreferencedElements){ if(unreferencedElement.innerHTML == "\n" || unreferencedElement.innerHTML == " \n"){ unreferencedElement.classList.remove('has-no-references'); }			}			// exclude the template var referenceElements = document.getElementsByClassName('reference'); for(let referenceElement of referenceElements){ if(referenceElement.classList.contains('has-no-references')){ referenceElement.classList.remove('has-no-references'); }			}			// blockquotes often use a different reference style, so false positives need to be excluded separately const unreferencedParagraphsInsideBlockquotes = document.querySelectorAll('blockquote > p.has-no-references'); for(var unreferencedParagraphInsideBlockquotes of unreferencedParagraphsInsideBlockquotes){ const parent = unreferencedParagraphInsideBlockquotes.parentElement; // check whether the parent blockquote contains a citation element if(parent.getElementsByClassName('templatequotecite').length > 0){ // if it does then the paragraph is not unreferenced unreferencedParagraphInsideBlockquotes.classList.remove('has-no-references'); }			}			// ignore elements in the template "ombox" let unreferencedElementsInOmboxes = document.querySelectorAll(".ombox .has-no-references"); for (let element of unreferencedElementsInOmboxes) { element.classList.remove("has-no-references"); }			// for drafts: exclude comments var pageTitleNamespace = document.getElementsByClassName('mw-page-title-namespace')[0]; if(pageTitleNamespace != null){ if(pageTitleNamespace.innerText === 'User' || pageTitleNamespace.innerText === 'Draft'){ let unreferencedComments = document.querySelectorAll(".has-no-references:has(.localcomments)"); for (let unreferencedComment of unreferencedComments) { unreferencedComment.classList.remove("has-no-references"); }				}			}		}		addStylesheet; // all paragraphs and list entries should have references const paragraphs = document.getElementById('mw-content-text').getElementsByTagName('p'); const listEntries = document.getElementById('mw-content-text').getElementsByTagName('li'); const elements = Array.from(paragraphs).concat(Array.from(listEntries)); // these sections are not checked for references var excludedSections = ['Plot', 'Plots', 'Plot summary', 'Plot synopsis', 'Synopsis', 'Storylines', 'Further reading', 'See also', 'External links', 'References', 'Bibliography', 'Notes', 'Selected publications', 'Selected works', 'Cited sources', 'Sources', 'Footnotes']; // dedice whether the lead should be checked for references if(!shouldMarkLead){ excludedSections.push(''); }		for(var element of elements){ // check whether the element should be excluded if(isEligible(element, excludedSections)){ // mark the element if it lacks references mark(element); }		}		excludeFalsePositives; }	// restrict script to mainspace, userspace, and draftspace var namespaceNumber = mw.config.get('wgNamespaceNumber'); if (namespaceNumber === 0 || namespaceNumber === 2 || namespaceNumber === 118) { // add a link to the toolbox $.when(mw.loader.using('mediawiki.util'), $.ready).then(function {			var portletlink = mw.util.addPortletLink('p-tb', '#', 'Highlight unreferenced passages');			// run the main function when the link is clicked			portletlink.onclick = function(e) {				e.preventDefault;				markUnreferencedPassages;				const unreferencedElements = document.getElementsByClassName('has-no-references');				//mw.notify(`${unreferencedElements.length} elements were highlighted`);				mw.notify('Highlighting finished.');			};		}); }	if (namespaceNumber === 0 || namespaceNumber === 118) { if(typeof highlightUnreferencedPassagesAutomatic != 'undefined' && highlightUnreferencedPassagesAutomatic == true){ markUnreferencedPassages; }	} });