User talk:The Transhumanist/RedlinksRemover.js


 * This script is functional

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).

= Script's workshop =
 * This is the work area for developing the script and its documentation. The talk page portion of this page starts at, below.

Description / instruction manual

 * This script is functional

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).

The redlink remover has two major uses (but it is not limited to these):
 * It can help clean up outlines that have accumulated too many redlinks.
 * It simplifies creation of outlines using standard templates. A problem with outline generation templates is that they include every possible link that a particular type of topic (say, provinces, or cities) might have, which creates outlines with lots of red links. Following up outline creation with this script will solve that problem. Tip: it is best to work on the outline with redlinks for awhile before using the redlink remover, because the script will delink those redlinks that have children, leaving them in as informative branches in the outline. Removing redlinks too early creates extra work as many of the topics may need to be added back in or relinkified.

Calling a function
In JavaScript, a function is a subroutine, essentially, a program within the main program. Functions are usually placed at the end of the program, after its core, but can also be located in a library, like jQuery. You call a function by its name. The function "example" is called like this:

See also: JavaScript Function Invocation.

window.location.href
window.location.href returns the current URL.

The window object represents the current window in the browser, and is at the top of the Browser Object Model hierarchy.

The location object pertains to the URL of the current document, and href is one of its properties.

window.location.href.indexOf
This applies the indexof method upon the URL, to return the index (starting position) of a given string. This can be used to check if the URL contains a specific string.

essentially means "if 'action' is in the URL". That is, its position in the URL is equal or greater than 0 (0 represents the first spot, 1 is the second spot, etc.), telling us that it is in there. If it is not there, it would return a -1.

window.location.href.substr
Gets part of the URL.

The substr method returns the substring from the provided start and end indexes, from within the string the method is applied to. If only a start index is provided, the substring will be from that index to the end of the string. In this case, the string is window.location.href (that is, the URL). Note that 0 represents the first character of the string.

So,  would return the first 7 characters of the URL.

That's not particularly useful, as we probably want to manipulate the string based on what is in it. For example...

What that returns is the beginning of the URL through the # character, which we can in turn use in concatenation. The following line of code concatenates (adds)  to the substring, and then replaces the URL with it:

This jumps to the edit page for the current page, as if we clicked on "Edit".

redlinks = [];
This line assigns the variable  to an empty array (represented by opening and closing square brackets).

Arrays are ordered sets of items.

We created this array to store all the redlinks that are on the page. (See below).

document.getElementsByTagName
The following line of code declares and assigns to the variable "a" all the elements in the document with the tag "", creating an array:

using a for loop to process an array
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration#for_statement

getAttribute('class')
This method returns the value of the attribute specified for an element it is attached to (with a dot, for example ). This allows elements to be processed by a particular attribut, such as their class.

https://www.w3schools.com/jsref/met_element_getattribute.asp

https://www.w3schools.com/jsref/met_element_getattribute.asp

https://www.w3schools.com/html/html_attributes.asp

localStorage
This didn't work:

So I used this, and it worked:

JSON.stringify method
JSON.stringify

Difference between JSON.stringify and JSON.parse

alert
alert is short for "window.alert".

This command makes a message box with a message appear, with an OK button. The script will not continue until the OK button is pushed.

The message is included within the parentheses. It can be a string, a variable, or an object. If it is a variable or an object, its value or contents is displayed in the message.

JSON.parse method
JSON.parse

Difference between JSON.stringify and JSON.parse

RegExp

 * The RegExp Global Object
 * [ ]
 * [ ]
 * [ ]
 * [ ]

Change log

 * 2017-02-09
 * Started script with some pseudocode and feature wish list
 * Added:
 * importScript('User:AlexTheWhovian/script-functions.js');
 * copy/paste User:AlexTheWhovian/script-redlinks.js
 * Removed importScript('User:AlexTheWhovian/script-functions.js')
 * AlexTheWhovian said it wasn't used by script-redlinks.js
 * 2017-02-10 & 2017-02-11
 * In the process of documenting the script with detailed comments.
 * Got down through mw.util.addPortletLink
 * Added "Explanatory notes" section to the talk page, to provide more in-depth scripting support than the comments. Got to mw.util.addPortletLink
 * 2017-02-12
 * Fixed bug that prevented operation and required another version being loaded.
 * It was working weird because of a function invocation being placed out of context, at the start of the script.
 * There was also a function invocation missing from a conditional in the body of the script.
 * This script now runs stand-alone (without the crutch of the other version being run)
 * Changed the menu item to "Remove red links" (it was "remove redlinks")
 * 2017-02-14
 * Add ready function
 * 2017-02-15
 * Wrote pseudocode in script for bullet list item processing.
 * 2017-04-06
 * Worked out incrementing structure for the while/for nested loop pair for processing bullet list items.

Task list

 * Start script ✅
 * Add AlexTheWhovian's redlink script and function library ✅
 * Test it to see if it works ✅ (it does not work = Dec 26 2016 15:08 version)
 * Cycle through various versions to see which work with Firefox ✅
 * Feb 28 2015 version ✅ (it works)
 * May 27 2016 version ✅ (it works)
 * Dec 4 2016 version ✅ (it works)
 * Dec 22 2016 version ✅ (failed - it appeared in menu, but failed to remove redlinks)
 * Dec 26 2016 15:05 version ✅ (it works = chose this one for starting point)
 * Dec 26 2016 15:08 version ✅ (failed - no menu item)
 * Determine which of the functions in User:AlexTheWhovian/script-functions.js are called in redlinks.js ✅ (none)
 * If none, remove it ✅
 * Test local storage feature ✅
 * With alerts ✅
 * Fix red link removal so it works in originally intended fashion ✅
 * Implement nested loop to remove unannotated non-parent bulleted list entries
 * Situate a for loop inside a while loop ✅
 * Work out increment structure ((done))
 * Write the guts of the for loop (the regex for removing end of branch redlinks without annotation)
 * Study the regex objects used in the forked block of code
 * Review how to match a multiple-line string
 * Figure out why the "outline in title" alert in the function redlinks_removal gets activated
 * Clear the local memory and test it
 * Study program flow
 * Wrap local storage in a try catch
 * Needs more comments, and more detailed comments
 * Write explanatory notes, on talk page, explaining the programming in-depth
 * Change the title to indicate the single function (redlink removal). Make a separate multi-function script.

Bug reports

 * Missing dependencies? (fixed 2017-02-12 ✅ - it wasn't dependencies, it was misplaced and missing function calls.
 * The script won't work. But there's a weird work-around:
 * Run the Feb 28 2016 version at the same time
 * Use it on a page with redlinks
 * Go to your RedlinksRemover.js and bypass the cache – don't need work-around any more
 * 2017-02-13 Red link removal stopped working
 * The script puts the target page into edit mode, but then doesn't edit anything
 * 2017-04-06 The script runs the functions at the end of the script, when the "Remove red links" menu has not been clicked, and I don't know why

Desired/completed features

 * Completed features are marked with ✅


 * Remove redlinked entries in outlines
 * Remove redlinked bullet entries that both have no annotation and have no children. (If one has an annotation, or a child, don't remove it.) Because this could create new candidates, this function needs to be looped.
 * Check for annotation
 * To check for children, see if any bullet entries that follow it have more bullets than it does
 * If no changes are made during a complete loop, stop. (How do you check for changes?)
 * To prevent infinite looping, stop after 10 iterations (it can always be run again)
 * When no more candidates are to be are to be found, remove redcats, and delink the remaining redlinks.
 * Save title to variable. ✅ don't have to. Can check title directly.
 * Some features will work only on outlines, and will check the title variable for "Outline of" first. ✅ used
 * (if match "Outline of" in title, then do....) ✅ used
 * Integrate anno.js (the annotation toggler).
 * get it working right first
 * For stream editing commands, the script will have an optional interactive mode.
 * For Macro compatibility, all toggles will have an on-"button" and an off-"button".
 * Entry linker (checks unlinked entry names for the existence of non-disambiguation page article titles. If one exists, linkify it.)
 * Entry inserter (checks template for entries missing in the current outline, then checks each title for existence.
 * If one exists, insert it, but not if it is a disambiguation page.)
 * Display a random outline, but not if currently in edit mode.
 * Display next outline in the main list of outlines, but not if currently in edit mode.

Trycatch needed, and more

 * The Transhumanist, where you use local storage.getItem or setItem you should always wrap that in try catch, as it can fail at any moment (even if you checked previously). This can be due to the browser running out of storage space for the domain, or because the browser is running in privacy mode or with an ad blocker extensions or something. Also, your new RegExp calls should be lifted outside of the for loops, so that they aren't continuously recreated. For wpTextbox1.value, realise that sometimes the content might be managed by an editor (The syntaxhighlighting beta does this for instance). We use the jquery.textSelection plugin to abstract way from these differences. Don't check document.title, check mw.config.get( 'wgTitle' ) or mw.config.get( 'wgPageName' ). And when you use mw.util.addPortlink, you have to ensure that the mediawiki.util plugin is loaded already, which you can do by using mw.loader.using. —Th e DJ (talk • contribs) 14:47, 27 October 2017 (UTC)

Script dependencies

 * (Copy of Village pump (technical))

Let's say a script works for one person, but not another. Or it's working on two machines, but after one is cold booted, it doesn't work on that one.

How would one find the dependencies required by the script? The Transhumanist 12:16, 12 February 2017 (UTC)
 * I am guessing that your problems are not caused by a lack of dependencies, but rather by the way you are using the localStorage object. According to the docs, you should be using, not  . If you use the API in a non-standard way I wouldn't be surprised if there were differences between the way the various browsers handle it. — Mr. Stradivarius  ♪ talk ♪ 13:18, 12 February 2017 (UTC)
 * Actually, after some more reading, it seems that the  syntax is fine (although the   syntax is preferred). That link does give some other suggestions as to things that could be wrong, though - localStorage might not be implemented on old browsers, it might be disabled by users, or it might be full. — Mr. Stradivarius  ♪ talk ♪ 15:01, 12 February 2017 (UTC)
 * Also, I would use a unique prefix for your localStorage keys, maybe  (so the current key would be  ), to reduce the chance of clashes between your data and other localStorage data saved by MediaWiki or by other gadgets. — Mr. Stradivarius  ♪ talk ♪ 13:24, 12 February 2017 (UTC)
 * It had little to do with memory, but your suggestion provided the essential clue. Since I had 2 versions of the script running simultaneously, the second one worked because of data stored locally by the first one. Without that storage there, the second script failed, which became apparent when I customized the localstorage key per your suggestion.  Which led me to a bug. I fixed the bug, and the now the second script works on its own. Though there are still some bugs (the menu item has to be clicked again after getting a preview, twice, for it to work, but it does work). Thank you! The Transhumanist 00:59, 13 February 2017 (UTC)
 * Also, all calls to LocalStorage should always be wrapped in a try catch. Localstorage can easily fail due to being full, or due to being in a privacy mode or some other restriction that the browser is placing. —Th e DJ (talk • contribs) 07:32, 13 February 2017 (UTC)
 * Thanks, I'll look that up. The Transhumanist 20:01, 13 February 2017 (UTC)

-- End of copy

= Discussions =

Nested RegExp
I'm working on a script (User:The Transhumanist/OLUtils.js) to remove redlinks from outlines, and I've run into a problem with regular expressions:

The above returns two matches, when I was expecting one. The second one is coming from the nested RegExp constructor.

Is there another way to specify a variable within a regular expression? If so, what?

Also, I can't find any documentation on the plus signs as used here. Can you explain them, or point me to an explanation?

What would the RegExp look like in literal notation?

Thank you. The Transhumanist 11:07, 5 May 2017 (UTC)
 * This is the way Twinkle specifies variables in a regular expression; to my knowledge it's the only way to do it. The plus signs are acting as string concatenation operators (string + string = concatenation). And you couldn't express this in literal notation, because literal notation can't accept variables (it is literal after all).
 * As an example of using, this regexp in literal notation:   is entirely equivalent to  . Note the double escaping! This is because character escapes in regular expression are processed separately from character escapes in strings.
 * As to why it is returning two matches instead of one, I really couldn't tell you. Could you provide a simplified test case or example? — This, that and the other (talk) 12:40, 5 May 2017 (UTC)
 * Thank you for the explanation. In answer to your question, "yes". Run the script User:The Transhumanist/OLUtils.js on any article with "Outline of" in the title, and that has red links in it, and the alerts will show you. The Transhumanist 15:35, 5 May 2017 (UTC)


 * It's difficult to quickly assess exactly what's going on without seeing the data it's being run against and the matches you are seeing. Is it possible that there's actually multiple matches in the input text?  E.g. if you look for "apple" in "apple, orange, pineapple", two matches is the expected result.  You would need to look for "\bapple\b" to restrict both ends to word boundaries, but that would still give multiple matches against "red apple, green apple, orange".  There is nothing about that code snippet which suggests that multiple matches should be unexpected behaviour.
 * I think your problem here is that you need to deal with the text before and after the thing the regexp is supposed to match. Looking at Alex's original script, I believe you need to use something like his original regular expressions, as it looks like they already deal with the beginning and end of the string.  I don't see why you appear to be reinventing the wheel here, as it looks like Alex's script already deals with that issue.
 * As for "plus signs as used here", do you mean the string concatenation operators? If you don't recognise basic JS operators and string concatenation, I suggest that you may need to learn fundamental JS programming before continuing.  Try the tutorials and guides at https://developer.mozilla.org/en-US/docs/Web/JavaScript.
 * Literal notation? If you feed "apple" into the above snipped, via the "redlinks" array, you'd get the equivalent of  .  That's very basic stuff, so you should probably be doing some reading on Mozilla's MDN site (or some other JS learning resource).
 * Murph 9000 (talk) 12:55, 5 May 2017 (UTC)
 * Thank you for the input. I've been having much difficulty with this script. The answer is "no" on the multiple matches.  The original statement was
 * which for example returns Geography of France, Geography of France
 * So I figure it's the nested RegExp that is the second match. The Transhumanist 15:33, 5 May 2017 (UTC)
 * Ok, now it's clearer exactly what you are talking about. This is expected behaviour, it's standard regexp group stuff as Syockit explained below.  Don't use the term "nested RegExp" like that, as that's not what it is and that term just adds to the confusion here.   Murph 9000  (talk) 20:50, 5 May 2017 (UTC)
 * Ok, now it's clearer exactly what you are talking about. This is expected behaviour, it's standard regexp group stuff as Syockit explained below.  Don't use the term "nested RegExp" like that, as that's not what it is and that term just adds to the confusion here.   Murph 9000  (talk) 20:50, 5 May 2017 (UTC)


 * The parentheses creates a capturing group. The first match is the whole matched string, while the second one is the captured group. Try with  and see if it works. Syockit (talk) 12:57, 5 May 2017 (UTC)

Wow. It's been many moons since anyone has asked me for JS help- I thought I'd become just a mostly-faded memory for a few editors. With that being said, Syockit is right as far as I can tell in that the parentheses create a capturing group. I'm not entirely sure why they're there at all- I'd use the same nodeScoop2 you currently have without the parentheses around the RegExp.quote; i.e. try:

Best,  Kangaroo  powah  20:09, 5 May 2017 (UTC)


 * I tried what you suggested in User:The Transhumanist/redlinkstest.js, and it doesn't seem to work. I'll keep at it, thgouh. The Transhumanist 20:34, 5 May 2017 (UTC)


 * I forgot the quotes. So I put those back, and adjusted the replace strings to account for the removal of the control group delimiters, and it worked. Now to try it on the current script... The Transhumanist 02:29, 6 May 2017 (UTC)


 * Glad I could help. Best, --03:34, 6 May 2017 (UTC)


 * Perhaps you are looking for String.indexOf. Oftentimes people discover regular expressions and somehow convince themselves that everything must be expressed in terms of regexes.  If regex is not working for you, it is ok not to use it.  91.155.195.247 (talk) 20:07, 5 May 2017 (UTC)


 * According to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf, that returns a number. I'm looking for specific strings, not the position (index) of a string. Thank you, as I was unaware of what this method does. The Transhumanist 11:00, 6 May 2017 (UTC)


 * You will have the starting position of the string and its length (= the length of the substring you are looking for). String.substring will extract you the matching string - which will be the same as the string you were looking for, except possibly for case.  This is how a programmer would do it, not with regexes.  91.155.195.247 (talk) 15:55, 7 May 2017 (UTC)


 * I cannot clear see what do you want to achieve, but I find these codes overkill. Mediawiki add titles of actual destinations as attribute   to links and class   for red links.


 * This jQuery one-liner simply unlinks all red links. This snippet actually inserts linked texts before links and then remove these links.


 * The function in  returns what to remain after link removal.  The   refers to the currently iterated element due to jQuery's design.  If we want to completely remove a link, make the function return nothing then.  The following example completely removes red category links and treat other red links as usual.


 * Chen-Pang He (talk) —Preceding undated comment added 07:40, 6 May 2017 (UTC)
 * Chen-Pang He (talk) —Preceding undated comment added 07:40, 6 May 2017 (UTC)

Sorry, but I don't understand what you are trying to achieve. If you want to remove red links from the DOM (in the generated code of the view), then you can use Javascript (faster) or jQuery (slower) to remove or replace all of them eventually at once, or do more things on each of them in a loop. With Javascript you need to use one of "getElementsByClassName" (for example applied to ) or "getElementsByTagName" for all   elements, and then you can apply styles ('_color_', '_cursor_', &hellip;) or replace them with your own content such as their "innerHTML" values. With jQuery >= 1.2 you can use something like  or , while with jQuery >= 1.4 you can use the   function like this:. jQuery seems to be shorter, but this is because you do not see the whole code that is behind the execution of it, and it is much slower than doing it in native Javascript (when it is well written, of course). All of them, Javascript and jQuery, should be wrapped into a document ready function (via Javascript or jQuery), a setTimeout functions or both. If you need to store their values, then you can create a  or a   loop for each of them and the do whatever you want to. Of course, if you are working on the source code, then the above does not apply at all. About the regex, I need more about the data, plus tests and examples. The reason for its multiple matches has been well explained above. Just a note, if you are sending and parsin a huge quantity of data, for example the whole content of an article, then something like PERL is always the faster and the better solution possible because it was conceived for reporting of the big log files such as those generated by a server. AWK and sed are also good with this. Unfortunately, I do not think that they are available here. –p joe f (talk • contribs) 12:18, 6 May 2017 (UTC)


 * The script is User:The Transhumanist/OLUtils.js, and the section we are working on here is for processing outlines, and starts with this:




 * For outlines, the script is supposed to remove list item entries (including bullet and carriage return) that are comprised entirely of redlinks, but only if they have no children. Red end nodes. It goes through several iterations, just in case the removal of a red end node renders other red entries into end nodes.  After all those have been removed, then the script deletes any red category links, and finally delinks the remaining embedded red links. I've provided a more in-depth explanation below under . For non-outlines, it just deletes red cats and delinks the rest of the redlinks. The Transhumanist 04:09, 7 May 2017 (UTC)

The whole regex
The sample I posted at the beginning of this thread was simplified to show the problem that it was returning 2 matches instead of the expected 1. So, I thought the script might do unexpected replacements, but that has not happened (yet). But I've run into other problems...

The regex from the script is more involved than the sample, and is for matching the line the key topic (redlinks[i]) is included on plus the whole next line:



The reason the whole next line is included is because I'd like to delete entries based upon the type of line that follows (or more accurately, does not follow). If the entry is not followed by a child, then it gets deleted, but should be kept if it does have a child. The weird thing is, that the part matching the whole next line is in the 4th set of parentheses, so you would expect $4 to back reference that. In practice, it is $3 that accesses that capturing group. And I don't know why. Though the solution (ignoring the parentheses around the embedded RegExp, when counting the capturing groups) seems to be working. But, I've run into a worse problem...



The problem is patt3. I'm trying to check for the asterisks at the beginning of the second line. If there is one more asterisk on that line than in the line before it, it means it is a child. In which case I do not want to delete the parent. But, the above code deletes the parents anyways.

In the example below, $1 should match the asterisk at the beginning of the parent line, and $1\* (patt3) should match the asterisks at the beginning of the child line. But it doesn't seem to be working. And when I add an alert to test for the value of patt3 or $1, the script crashes!

* Parent ** Child

If $1 includes asterisks in it, does it return those asterisks escaped?

Any ideas on how to solve my patt3 problem? The Transhumanist 12:14, 6 May 2017 (UTC)


 * Try to double-escape the aterisk  in a   constructor or in this way  . –p joe f (talk • contribs) 12:26, 6 May 2017 (UTC)


 * I did. See the RegExp below. Notice that the double escaped asterisk is inside a capturing group. When you use $1 to refer to that capturing group, will the asterisks in there still be escaped? When I try to use alert to test for $1, it crashes the script.




 * I look forward to your reply. The Transhumanist 13:58, 6 May 2017 (UTC)


 * "*" is a quantifier (a special character) and, as well as all other special characters, it needs to be escaped when it is part of the pattern of characters that you want to find or replace. See: w3schools.com/jsref/jsref_obj_regexp.asp. About the use of the alert for debugging purpose I suggest you to use console.log method to display data directly within the debugger of the browser. More @: w3schools.com/js/js_debugging.asp. The debugger itself should be also able to show you which and where is the error within your code. About the editing of the article and the DOM manipulation, it doesn't save the changes, but if an user is in the editor window/view and it presses the save button all changes that have been made to the content will be saved. –p joe f (talk • contribs) 09:26, 7 May 2017 (UTC)
 * P.S.: I haven't tested it out but probably $1 is "undefined". In this case you need to check for this before you use it: . –p joe f (talk • contribs) 09:34, 7 May 2017 (UTC)


 * Running the code in generated document seems to be easier because we can make use of HTML structure. A leaf link safe to remove is the only child of.




 * Cheers, Chen-Pang He (talk) —Preceding undated comment added 15:19, 6 May 2017 (UTC)


 * Hi. Thanks for the suggestions. I have some questions for you: Would the code you provided edit the article, or just affect the view? I'm looking for editing solutions. How could a script remove children list items in the edit window? The Transhumanist 03:57, 7 May 2017 (UTC)

I got your message. It looks like you may have gotten the help you need. When working with RegExp, I like to try them on some sample strings to see what each one is actually matching, and what it's returning. There's a great website for doing that: regex101. Nathanm mn (talk) 16:12, 6 May 2017 (UTC)


 * We still haven't figured it out. The problem I'm trying to solve is how to identify when a list item has a child. A child list item will have one more asterisk at the beginning than the parent. So, I set up a capturing group for the asterisks at the beginning of the parent (so $1 would be the back reference), and then try to match that number of asterisks plus one more in the child (using $1\*).  But it isn't working. I am stuck.  There are other criteria which the entries to be removed must fail, otherwise I wish to keep them.  So simply getting rid of all children isn't what I'm after. We already know they are red linked entries, because the first half of the program puts all redlinks into an array, which we process in the second half of the program. Then the nested if structure checks first for whether the current redlink in the array has no entry.  If it doesn't, then we check to see if it has no colon annotation. If it doesn't have a colon separator, then we check to see if it doesn't have a hyphenated annotation. If it doesn't have an en dash separator, then we check to see if it has no children.  If it doesn't have a child, then we delete it from the wiki source, modifying the actual article itself.
 * Once all redlinked entries that fail our tests are removed, then the rest of the program mops up, deleting red category links, and delinking all redlinks that still remain after that. We know, due to the extensive filtering we just subjected them to, that they are all embedded redlinks, the content of which we want to keep. I'll make a sample below that presents examples of the data instances to be processed. The Transhumanist 22:12, 6 May 2017 (UTC)

What the script is supposed to do
Here is a sample item list:


 * Geography
 * Geology – this text is an annotation. And here is an embedded redlink 1. After all the end node (dead end) redlinked entries are removed, this redlink will be delinked.
 * Redlink 2
 * Redlink 3
 * Redlink 4
 * Psychology
 * Redlink 5: this text is also an annotation. So I want to keep this entry.
 * Redlink 6
 * Redlink 7
 * Redlink 8
 * Redlink 9 – this annotation will prevent this entry and all its parents from being deleted. They will however be delinked after list item removal and relinked category removal are completed.
 * Redlink 10
 * Redlink 11
 * Redlink 12
 * Redlink 13
 * Redlink 14
 * Redlink 15

What we want to do is remove the list entries for which the topic is a redlink, but which do not have annotations, and which do not have children. Then we delete redlinked categories, and delink whatever redlinks are leftover &mdash; those will be by definition embedded, such as redlink 1 and redlink 3. Redlink 3 is embedded by virtue of having children.

Redlink 2 is a dead end. It is an end node in the tree structure that contains only a redlink. It gets deleted.

The script goes through the list multiple times, until it no longer finds dead end redlinks. This is because when it removes a redlinked end node, that may cause its redlinked parent to become a dead end node (such as when it has no other children). Multiple iterations catch these. So the entire branch starting with Redlink 10 will be deleted.

Here is the problem I've run into: the script currently and erroneously deletes the Redlink 3 list item. Because $1\* or $1\\* do not seem to be identifying the Redlink 4 list item as having more asterisks in the wikisource than the Redlink 3 list item. I do not know why. What should happen is that Redlink 3 would be retained because of Redlink 4, and after Redlink 4 is removed, then Redlink 3 is checked again and is kept by virtue of having Psychology as a child. But, when Redlink 3 is deleted in error, it makes Psychology a child of Geology, thus ruining the tree structure.

All this processing is to be done in the editor, so that the redlinked entries are actually removed from the article.

I'm stuck! I look forward to your replies. The Transhumanist 23:00, 6 May 2017 (UTC)
 * Your patt3 is off for a couple of reasons. First, with the $n regex matches, in general you access them using  (which will be a string containing the match), not just   – except for within String.replace function, when just   is used in the replacement string . Secondly, with regex literals, what you type is literally what you get as the regex string. So   will literally be interpreted as   (where   asserts position at the end of the string;   matches the character  ;   matches the character  ).
 * What you could use instead is  which, for example, will give you the regex   when the   match is "**" - Evad37 &#91;talk] 04:59, 7 May 2017 (UTCt)
 * I'll try it and will let you know how it works. By the way, what about  . Why won't that work? (That was the first thing I tried, before going literal). The Transhumanist 23:14, 7 May 2017 (UTC)
 * as part of a string doesn't have any special meaning, except within the string .replace function. So  would give you the regex  . To use the actual match instead of $1, you would use   which would e.g. give you the regex   for a match "**". To actually get valid regex, the match would have to be escaped (note also that the single slash in   doesn't get preserved unless it is double-escaped as  ) . - Evad37 &#91;talk] 23:55, 7 May 2017 (UTC)
 * Thank you Evad. Using your code, the script now works, matching about 90% of what it is supposed to. So far, I've cleaned up the all the country outlines for Africa. Now working on Asia.  I'm not sure why it is skipping some entries that it shouldn't, but I'm sure I'll figure it out by observing as I use it. The Transhumanist 22:39, 11 May 2017 (UTC)