_ _    _ _____  ___   __                       
 __      _(_) | _(_)___ / ( _ ) / /_   ___ ___  _ __ ___  
 \ \ /\ / / | |/ / | |_ \ / _ \| '_ \ / __/ _ \| '_ ` _ \ 
  \ V  V /| |   <| |___) | (_) | (_) | (_| (_) | | | | | |
   \_/\_/ |_|_|\_\_|____/ \___/ \___(_)___\___/|_| |_| |_|

User:The Transhumanist/RedlinksRemover.js

In this article we will delve into the fascinating world of User:The Transhumanist/RedlinksRemover.js, exploring its many facets and delving into its importance in modern society. From its origins to its impact today, User:The Transhumanist/RedlinksRemover.js has played a crucial role in different aspects of daily life, influencing people of all ages, cultures and social strata. Along these lines, we will analyze how User:The Transhumanist/RedlinksRemover.js has evolved over time, as well as its significance in various areas, from politics and economics to science and popular culture. Get ready to immerse yourself in an exciting journey through the history and present of User:The Transhumanist/RedlinksRemover.js, discovering its relevance and impact on the contemporary world.
// <syntaxhighlight lang="javascript">

/* 

RedlinksRemover.js: This script processes bulleted lists, removing the redlinked end nodes, 
reiteratively, until none are left. (A redlinked end node is a list item that is comprised 
of nothing more than a redlink, and that has no children.)  After it has done that, this 
script delinks the remaining red links, and deletes red category links.  It doesn't remove 
list item entries that have annotations, or that have children (indented entries beneath it).


    Fork of December 26, 2016 15:05 version of User:AlexTheWhovian/script-redlinks.js
        That script already does the last 2 steps needed for outlines: 
        	remove redlink categories,
        	and delink all remaining unprocessed redlinks.
	Before those functions are executed, the script processes the redlinks that are 
	list item entries.

Brief comments are provided within the sourcecode below. For extensive explanatory 
notes on what the source code does and how it works, see the Script's workshop on 
the talk page. (Partially complete)

*/

// ============== Set up ==============
// Start off with a bodyguard function to reserve mw and $ as "mediawiki" and "jQuery", respectively,
// within the scope of this function. Those terms are specified after the closing bracket of the function, 
// which is at the end of the program. (For more details, see Explanatory notes on talk page).
( function ( mw, $) {

	// ============== Load dependencies ============== 
	// For support of mw.util.addPortletLink
	mw.loader.using( , function () {

//  /////////////////////////// PROCESS READ PAGE ////////////////////////////////////

    // ============== ready() event listener/handler ==============
	// Enclose this program section within a ready() event listener/handler, which defers 
	// the "handler" while the script "listens" for the "ready event" (which indicates
	// that the page/DOM is done loading). The listener ensures that the handler (a function)
	// is called only after all the DOM elements of the page are ready to be used (otherwise,
	// the script may not have anything to process):
	$(document).ready( function () {

		// Create linked menu item
		var portletlink = mw.util.addPortletLink('p-tb', '#', 'Remove red links', 'rm-redlink', 'Remove red links', 'o');
		// Bind click handler
		$(portletlink).click( function(e) {
			e.preventDefault();     // prevents any default action -- we want only the following actions to run:

			// Do some stuff when clicked...
			// Default parameters, and begin script on regular view of article
			var loc = window.location.href;  // the URL
			var redlinks; var i;
		
			// Gather all redlinks with class "new"
			redlinks = ;
			var a = document.getElementsByTagName('a');
			for (i = 0; i < a.length; i++) {
				if (a.getAttribute('class') == "new") {
					redlinks = a.href.replace('https://en.wikipedia.org/w/index.php?title=','').replace('&action=edit&redlink=1','');
					redlinks = redlinks.replace(/_/g,' ');
					redlinks = decodeURIComponent(redlinks);
				}
			}

			// Save all redlinks
			if (redlinks.length > 0) {
				var jsonString = JSON.stringify(redlinks);
				localStorage.OLUtils_redlinks = jsonString;

				// If we are in the edit page, then remove redlinks automatically; if we are on the reading page, then go to the edit page
				if (window.location.href.indexOf('action') >= 0) {
					// (do nothing - it already does it above)
				} else {
					window.location = window.location.href.substr(0, window.location.href.indexOf('#'))+"?action=edit";
				}	
			} else {
				alert('No redlinks!');
			}
		});     // end of function(e) (click handler)
	});		// end of ready() event listener/handler wrapper
	});		// end of dependency loading wrapper

// //////////////////////////// PROCESS EDIT PAGE ////////////////////////////////

	// In case we have to wait for the page to load...
	// Below is jQuery short-hand for $(document).ready(function() { ... });
	// referred to in English as the "ready() event listener/handler".
	// It makes the script wait until the page's DOM is loaded and ready.
	$(function() {

	    // Invoke a function by its name; this one checks the script's localStorage slot
	    // (The function itself is defined down the page a little, using the word "function"). 
	    check_storage();
	});

	function check_storage() {
		// Check to see if anything is in storage, and if there is, invoke redlinks removal 
    	if (localStorage.OLUtils_redlinks !== "") {

		    // Invoke a function by its name (it is defined further down the page). 
		    // This one removes redlinks, if they exist:
		    redlinks_removal();
    	}
	}
	
	// Automatic removal of redlinks when stored.
	function redlinks_removal() {
		// Gather saved redlinks
		try {
			// User generated content so parse issues possible. 
			var redlinks = JSON.parse(localStorage.OLUtils_redlinks);
		} catch (e) {
			return;
		}
	
		// Regular expression to escape special characters
		var totalredlinks = 0;
		RegExp.quote = function(str) { return str.replace(/\\(){}|-]/g, "\\$&"); };
		
		var wpTextbox1 = document.getElementById('wpTextbox1');

		// REMOVE REDLINKED END NODES (PROCESS OUTLINE ENTRIES)
		// initialize incrementer
		var incrementer = 1;

		// while loop with nested for loop
		while (incrementer > 0) {

			// The nested loop (inside the while loop) is a modified version of the 
			// for loop from the "remove embedded redlinks" section below, using the same array
			for (i = 0; i < redlinks.length; i++) {

				// Use nodeScoop1 & 2 to "scoop" up whole entry (the node with the array term), plus the whole next line.
				// I did not use the global parameter, as the while/for loop structure should catch all occurrences
				// Development note: here we need the regular expression for our first scoop target (redlinked entry with pipe, plus next line) 
				var nodeScoop1 = new RegExp('\\n((\\*)+)*?\\))+'\\s*\\|\\s*(]*)\\s*\\]\\].*?\\n(.*?\\n)','i');
				// Here is the regular expression for our second scoop target (redlinked entry with direct (non-piped) link, plus next line) 
				var nodeScoop2 = new RegExp('\\n((\\*)+)*?\\))+'\\s*\\]\\].*?\\n(.*?\\n)','i');

				// Generate match strings
				var matchString1 = wpTextbox1.value.match(nodeScoop1);
				var matchString2 = wpTextbox1.value.match(nodeScoop2);
				// Declare match patterns
				var patt1 = new RegExp(":");
				var patt2 = new RegExp("( – )|( &ndash\\; )");
				var patt3 = new RegExp("\\*{"+(RegExp.$1.length+1)+"} *");

				// Development note: Process nodeScoop1 (which holds a piped redlink entry and the whole line following it)
				// Determine if matchString1 doesn't match our exclusion criteria. If it doesn't match, delete the entry:
			  	if (matchString1 !== null) {

					// If there is no coloned annotation (that is, does not have ":")
					if (patt1.test(matchString1) === false) {

						// If there is no hyphenated annotation (that is, does not have " – " or " &ndash; ")
						if (patt2.test(matchString1) === false) {

							// ...and if the line following it is not a child (that is, does not have more asterisks)
							if (patt3.test(matchString1) === false) {

								// ... then replace nodeScoop1 with the last line in it, thereby removing the end node entry
								wpTextbox1.value = wpTextbox1.value.replace(nodeScoop1,"\n$4");
								incrementer++;
							}
						}
					}
				}		

				// Process nodeScoop2 (which holds a non-piped redlink entry and the whole line following it)
				// Determine if matchString2 doesn't match our exclusion criteria. If it doesn't match, delete the entry:
				// If matchString2 isn't empty
			  	if (matchString2 !== null) {

					// If there is no coloned annotation (that is, does not have ":")
					if (patt1.test(matchString2) === false) {

						// If there is no hyphenated annotation (that is, does not have " – " or " &ndash; ")
						if (patt2.test(matchString2) === false) {

							// ...and if the line following it is not a child (that is, does not have more asterisks)
							if (patt3.test(matchString2) === false) {

								// ... then replace nodeScoop2 with the last line in it, thereby removing the end node entry
								wpTextbox1.value = wpTextbox1.value.replace(nodeScoop2,"\n$3");
								incrementer++;
							}
						}
					}
				}		

			}

			// Adjust the counter
			//  If incrementer is >100, set to 100. This eliminates unnecessary dry runs (iterations with no matches).
			// if (incrementer>100) {
			//   incrementer = 100;
			// Otherwise, the loop would have to continue until the decrementer below slowly whittled the counter down to zero.
			//
			// }

			// After the for loop, still within the while loop, subtract 1 from the incrementer.
			// (If this drops the incrementer down to zero, the while loop should terminate). 
			incrementer--;
		}

		// Mop-up section (removes redlinks not removed above)
		// REMOVE EMBEDDED REDLINKS
		for (i = 0; i < redlinks.length; i++) {
			// Regular expression for piped links and direct links
			var reglink1 = new RegExp('\\)+')\\s*\\|\\s*(]*)\\s*\\]\\]','gi');
			var reglink2 = new RegExp('\\)+')\\s*\\]\\]','gi');
			
			// Remove category rather than simply convert it to unlinked text
			if (redlinks.substr(0,9) == "Category:") {
				var reglink3 = new RegExp('\\)+')\\s*\\]\\]\\n','gi');
				wpTextbox1.value = wpTextbox1.value.replace(reglink3,"");
			}
			
			// Remove redlinks and convert to unlinked text
			wpTextbox1.value = wpTextbox1.value.replace(reglink1,"$2");
			wpTextbox1.value = wpTextbox1.value.replace(reglink2,"$1");
		}

		// summary of removed redlinks
		document.getElementById('wpSummary').value += "Removed redlinks using ].";

		// Remove the template(s)
		wpTextbox1.value = wpTextbox1.value.replace(/\{\{leanup red links*\}\}/g, "");
		wpTextbox1.value = wpTextbox1.value.replace(/\{\{leanup Red Link*\}\}/g, "");
		wpTextbox1.value = wpTextbox1.value.replace(/\{\{leanup redlinks*\}\}/g, "");
		wpTextbox1.value = wpTextbox1.value.replace(/\{\{leanup-redlinks*\}\}/g, "");
		wpTextbox1.value = wpTextbox1.value.replace(/\{\{oo many red links*\}\}/g, "");
		
		// Clear all saved redlinks
		localStorage.OLUtils_redlinks = '';
	}	
}) ( mediaWiki, jQuery );       //the end of bodyguard function
// END OF PROGRAM

// </syntaxhighlight>