Example #1
0
        public void WPBiography()
        {
            string a = @"{{WPBiography|foo=yes|living=yes|listas=Foé}}";

            Assert.AreEqual(a.Replace(@"é", "e"), TalkPageFixes.WPBiography(a), "diacritics removed from WPBiography listas");
            Assert.AreEqual(a.Replace(@"é", "e"), TalkPageFixes.WPBiography(a.Replace(@"é", "e")), "no change when no diacritics in WPBiography listas");
        }
Example #2
0
        public void TalkHeaderDefaultsortChange()
        {
            string start = @"
==Foo==
bar", df = @"{{DEFAULTSORT:Bert}}";

            string articleText = start + "\r\n" + df;

            TalkPageFixes.ProcessTalkPage(ref articleText, DEFAULTSORT.MoveToBottom);
            Assert.AreEqual(start + "\r\n" + "\r\n" + df, articleText);

            articleText = start + df;

            TalkPageFixes.ProcessTalkPage(ref articleText, DEFAULTSORT.MoveToTop);
            Assert.AreEqual(df + "\r\n" + start, articleText);

            articleText = start + df;

            TalkPageFixes.ProcessTalkPage(ref articleText, DEFAULTSORT.NoChange);
            Assert.AreEqual(start + df, articleText);

            string df2 = @"{{DEFAULTSORT:}}";

            articleText = start + df2;

            TalkPageFixes.ProcessTalkPage(ref articleText, DEFAULTSORT.MoveToBottom);
            Assert.AreEqual(start, articleText, "defaultsort with no key removed");

            articleText = start + df2;

            TalkPageFixes.ProcessTalkPage(ref articleText, DEFAULTSORT.MoveToTop);
            Assert.AreEqual(start, articleText, "defaultsort with no key removed");
        }
Example #3
0
        public void AddWikiProjectBannerShell()
        {
            const string a = @"{{WikiProject a|text}}", b = @"{{WikiProject b|text}}", c = @"{{WikiProject c|text}}", d = @"{{WikiProject d|text}}";

            Assert.AreEqual(a, TalkPageFixes.WikiProjectBannerShell(a));
            Assert.AreEqual(a + b, TalkPageFixes.WikiProjectBannerShell(a + b));
            Assert.AreEqual(a + b + c, TalkPageFixes.WikiProjectBannerShell(a + b + c));
            Assert.AreEqual(@"{{WikiProjectBannerShell|1=" + "\r\n" + a + "\r\n" + b + "\r\n" + c + "\r\n" + d + "\r\n" + @"}}", TalkPageFixes.WikiProjectBannerShell(a + b + c + d));

            const string e = @"{{talk header}}
{{WikiProject Biography|listas=Bar, F|living=yes|class=Start|priority=mid|sports-work-group=yes}}
{{WikiProject F|class=Start|importance=mid|italy=y}}
{{WikiProject X|class=Start|importance=Mid}}
{{WikiProject D|class=Start|importance=Low}}", f = @"{{WikiProjectBannerShell|1=
{{WikiProject Biography|listas=Bar, F|living=yes|class=Start|priority=mid|sports-work-group=yes}}
{{WikiProject F|class=Start|importance=mid|italy=y}}
{{WikiProject X|class=Start|importance=Mid}}
{{WikiProject D|class=Start|importance=Low}}
| blp=yes
}}{{talk header}}



";

            Assert.AreEqual(f, TalkPageFixes.WikiProjectBannerShell(e), "adds WPBS, ignores non-wikiproject templates");
        }
Example #4
0
        public void WikiProjectBannerShellBLP()
        {
            const string a = @"{{WikiProjectBannerShell|blp=yes|1={{WPBiography|foo=bar|living=yes}}}}";

            Assert.AreEqual(a, TalkPageFixes.WikiProjectBannerShell(a + "{{Blp}}"));
            Assert.AreEqual(a, TalkPageFixes.WikiProjectBannerShell(a.Replace("blp=yes", "blp=") + "{{Blp}}"));
            Assert.AreEqual("{{Blp}}", TalkPageFixes.WikiProjectBannerShell("{{Blp}}"));
        }
Example #5
0
        public void RenameTalkHeader()
        {
            string talkheader = @"{{talkheader|noarchive=no}}", talkrest = @"==hello==
hello talk";
            string articleText = talkrest + "\r\n" + talkheader;

            TalkPageFixes.ProcessTalkPage(ref articleText, DEFAULTSORT.NoChange);

            Assert.AreEqual(@"{{Talk header|noarchive=no}}" + "\r\n" + talkrest + "\r\n", articleText, "renamed to upper case with space");
        }
Example #6
0
        public void WikiProjectBannerShellUnnamedParam()
        {
            Assert.AreEqual(@"{{WikiProjectBannerShell|1={{WPBiography|foo=bar}}}}", TalkPageFixes.WikiProjectBannerShell(@"{{WikiProjectBannerShell|{{WPBiography|foo=bar}}}}"), "1= added when missing");
            Assert.AreEqual(@"{{WikiProjectBannerShell|1=
{{WPBiography|foo=bar}}}}", TalkPageFixes.WikiProjectBannerShell(@"{{WikiProjectBannerShell|
{{WPBiography|foo=bar}}}}"));

            const string otherUnnamed = @"{{WikiProjectBannerShell|random}}";

            Assert.AreEqual(otherUnnamed, TalkPageFixes.WikiProjectBannerShell(otherUnnamed), "other unknown parameter not named 1=");
        }
Example #7
0
        public void WikiProjectBannerShellWPBiography()
        {
            Assert.AreEqual(@"{{WikiProjectBannerShell|1={{WPBiography|foo=bar}}}}", TalkPageFixes.WikiProjectBannerShell(@"{{WikiProjectBannerShell|1={{WPBiography|foo=bar}}}}"));

            Assert.AreEqual(@"{{WikiProjectBannerShell|blp=yes|1={{WPBiography|foo=bar|living=yes}}}}", TalkPageFixes.WikiProjectBannerShell(@"{{WikiProjectBannerShell|blp=|1={{WPBiography|foo=bar|living=yes}}}}"));
            Assert.AreEqual(@"{{WikiProjectBannerShell|blp=yes|1={{WikiProject Biography|foo=bar|living=yes}}}}", TalkPageFixes.WikiProjectBannerShell(@"{{WikiProjectBannerShell|blp=|1={{WikiProject Biography|foo=bar|living=yes}}}}"));
            Assert.AreEqual(@"{{WikiProjectBannerShell|activepol=yes|1={{WPBiography|foo=bar|activepol=yes}}}}", TalkPageFixes.WikiProjectBannerShell(@"{{WikiProjectBannerShell|activepol=abc|1={{WPBiography|foo=bar|activepol=yes}}}}"));
            Assert.AreEqual(@"{{WikiProjectBannerShell|blpo=yes|1={{WPBiography|foo=bar|blpo=yes}}}}", TalkPageFixes.WikiProjectBannerShell(@"{{WikiProjectBannerShell|blpo=|1={{WPBiography|foo=bar|blpo=yes}}}}"));
            Assert.AreEqual(@"{{WikiProjectBannerShell|blpo=|1={{WPBiography|foo=bar|blpo=no}}}}", TalkPageFixes.WikiProjectBannerShell(@"{{WikiProjectBannerShell|blpo=|1={{WPBiography|foo=bar|blpo=no}}}}"));

            Assert.AreEqual(@"{{WikiProjectBannerShell|1={{WPBiography|foo=bar|living=no}}}}", TalkPageFixes.WikiProjectBannerShell(@"{{WikiProjectBannerShell|blp=yes|1={{WPBiography|foo=bar|living=no}}}}"));
        }
Example #8
0
        public void SkipToTalk()
        {
            string articleText = @"{{Skiptotoc}}", STT = @"{{Skip to talk}}";

            TalkPageFixes.ProcessTalkPage(ref articleText, DEFAULTSORT.NoChange);
            Assert.AreEqual(STT + "\r\n", articleText);

            articleText = @"{{skiptotoctalk}}";

            TalkPageFixes.ProcessTalkPage(ref articleText, DEFAULTSORT.NoChange);
            Assert.AreEqual(STT + "\r\n", articleText);
        }
Example #9
0
        public void AddMissingFirstCommentHeader()
        {
            const string comment       = @"
Hello world comment.";
            string       articleTextIn = articleTextHeader + comment;

            // plain comment
            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.AreEqual(articleTextIn, articleTextHeader + "\r\n" + @"
==Untitled==
Hello world comment.");

            // idented comment2
            articleTextIn = articleTextHeader + @"
*Hello world comment2.";

            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.AreEqual(articleTextIn, articleTextHeader + "\r\n" + @"
==Untitled==
*Hello world comment2.");

            // idented comment3
            articleTextIn = articleTextHeader + @"
:Hello world comment3.";

            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.AreEqual(articleTextIn, articleTextHeader + "\r\n" + @"
==Untitled==
:Hello world comment3.");

            // quoted comment
            articleTextIn = articleTextHeader + @"
""Hello world comment4"".";

            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.AreEqual(articleTextIn, articleTextHeader + "\r\n" + @"
==Untitled==
""Hello world comment4"".");

            // heading level 3 changed to level 2
            articleTextIn = articleTextHeader + "\r\n" + @"===Foo bar===
*Hello world comment2.";

            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.IsTrue(articleTextIn.Contains(@"==Foo bar=="));
            Assert.IsFalse(articleTextIn.Contains(@"==Untitled=="));
        }
Example #10
0
        public void WikiProjectBannerShellAddingWikiProjects()
        {
            Assert.AreEqual(@"{{WikiProjectBannerShell|1={{WPBiography|foo=bar}}
{{WikiProject foo}}}}
", TalkPageFixes.WikiProjectBannerShell(@"{{WikiProjectBannerShell|1={{WPBiography|foo=bar}}}}
{{WikiProject foo}}"), "WikiProjects pulled into WPBS");
            Assert.AreEqual(@"{{WikiProjectBannerShell|1={{WPBiography|foo=bar}}
{{WikiProject foo}}
{{WikiProject bar}}}}

", TalkPageFixes.WikiProjectBannerShell(@"{{WikiProjectBannerShell|1={{WPBiography|foo=bar}}}}
{{WikiProject foo}}
{{WikiProject bar}}"), "WikiProjects pulled into WPBS");
        }
Example #11
0
        public void RemoveTemplateNamespace()
        {
            string T1 = @"{{Template:Foo}}", T2 = @"{{Template:Foo}}
==Section==
{{Template:Bar}}";

            Assert.IsFalse(TalkPageFixes.ProcessTalkPage(ref T1, DEFAULTSORT.NoChange));
            Assert.AreEqual("{{Foo}}", T1, "template namespace removed");

            Assert.IsFalse(TalkPageFixes.ProcessTalkPage(ref T2, DEFAULTSORT.NoChange));
            Assert.AreEqual(@"{{Foo}}
==Section==
{{Template:Bar}}", T2, "changes only made in zeroth section");
        }
Example #12
0
        public void WikiProjectBannerShellMisc()
        {
            const string a = @"{{wpbs|1=|banner collapsed=no|
{{WPBiography|living=yes|class=Start|priority=|listas=Hill, A}}
{{WikiProject Gender Studies}}
{{WikiProject Oklahoma}}
}}", b = @"{{WikiProjectBannerShell|banner collapsed=no|1=
{{WPBiography|living=yes|class=Start|priority=|listas=Hill, A}}
{{WikiProject Gender Studies}}
{{WikiProject Oklahoma}}
| blp=yes
}}";

            Assert.AreEqual(b, TalkPageFixes.WikiProjectBannerShell(a));
        }
Example #13
0
        public void WPBiographyTopBilling()
        {
            string a = @"{{WPBiography|foo=yes|living=yes}}
{{WikiProject London}}
", b = @"{{WikiProjectBannerShell|banner collapsed=no|1=
{{WPBiography|living=yes|class=Start|priority=|listas=Hill, A}}
{{WikiProject Gender Studies}}
{{WikiProject Oklahoma}}
| blp=yes
}}", c = @"{{WPBiography|foo=yes|living=no}}
{{WikiProject London}}";

            Assert.AreEqual(a, TalkPageFixes.WPBiography(@"{{WikiProject London}}
{{WPBiography|foo=yes|living=yes}}"), "WPBiography moved above WikiProjects");
            Assert.AreEqual(a, TalkPageFixes.WPBiography(a), "no change when WPBiography ahead of WikiProjects");
            Assert.AreEqual(b, TalkPageFixes.WPBiography(b), "no change when WPBS present");
            Assert.AreEqual(c, TalkPageFixes.WPBiography(c), "no change when not living");
            Assert.AreEqual(c + @"{{blp}}", TalkPageFixes.WPBiography(c + @"{{blp}}"), "no change when not living");

            Assert.AreEqual(a, TalkPageFixes.WPBiography(a + @"{{blp}}"), "blp template removed when living=y");
        }
Example #14
0
        public void MoveTalkHeader()
        {
            string talkheader = @"{{talk header|noarchive=no|search=no|arpol=no|wp=no|disclaimer=no|shortcut1|shortcut2}}", talkrest = @"==hello==
hello talk";
            string articleText = talkrest + "\r\n" + talkheader;

            TalkPageFixes.ProcessTalkPage(ref articleText, DEFAULTSORT.NoChange);

            Assert.AreEqual(talkheader.Replace("{{talk", @"{{Talk") + "\r\n" + talkrest + "\r\n", articleText);

            // handles {{talk header}} on same line as other template
            string WPBS = @"{{WikiProjectBannerShell|blp=yes|1=
{{OH-Project|class=B|importance=Low|nested=yes}}
{{WPBiography|living=yes|class=B|priority=Low|filmbio-work-group=yes|nested=yes|listas=Parker, Sarah Jessica}}
{{WikiProject Cincinnati|class=B|importance=mid|nested=yes}}
}}", rest = "\r\n" + @"==Song Jessie by Joshua Kadison==
In the article it says that above mentioned";

            articleText = WPBS + @"{{Talkheader}}" + rest;

            TalkPageFixes.ProcessTalkPage(ref articleText, DEFAULTSORT.NoChange);

            Assert.AreEqual(@"{{Talk header}}" + "\r\n" + WPBS + rest, articleText);

            // no change if already at top
            articleText = talkheader + "\r\n" + talkrest;

            TalkPageFixes.ProcessTalkPage(ref articleText, DEFAULTSORT.NoChange);
            Assert.AreEqual(talkheader + "\r\n" + talkrest, articleText);

            // no change if no talk header
            articleText = talkrest;

            TalkPageFixes.ProcessTalkPage(ref articleText, DEFAULTSORT.NoChange);
            Assert.AreEqual(talkrest, articleText);
        }
Example #15
0
 public void WikiProjectBannerShellUnneededParams()
 {
     Assert.AreEqual(@"{{WikiProjectBannerShell}}", TalkPageFixes.WikiProjectBannerShell(@"{{WikiProjectBannerShell|blp=no|activepol=no|collapsed=no}}"));
 }
Example #16
0
 public void WikiProjectBannerShellDupeParameters()
 {
     Assert.AreEqual(@"{{WikiProjectBannerShell|blp=yes}}", TalkPageFixes.WikiProjectBannerShell(@"{{WikiProjectBannerShell|blp=yes|blp=yes}}"));
 }
        /// <summary>
        /// Extracts DEFAULTSORT + categories from the article text; removes duplicate categories, cleans whitespace and underscores
        /// </summary>
        /// <param name="articleText">The wiki text of the article.</param>
        /// <param name="articleTitle">Title of the article</param>
        /// <returns>The cleaned page categories in a single string</returns>
        public string RemoveCats(ref string articleText, string articleTitle)
        {
            List <string> categoryList        = new List <string>();
            string        originalArticleText = articleText;

            // allow comments between categories, and keep them in the same place, but don't grab any comment just after the last category
            Regex r = new Regex(@"<!-- [^<>]*?\[\[\s*" + Variables.NamespacesCaseInsensitive[Namespace.Category]
                                + @".*?(\]\]|\|.*?\]\]).*?-->|\[\["
                                + Variables.NamespacesCaseInsensitive[Namespace.Category]
                                + @".*?(\]\]|\|.*?\]\])(\s*⌊⌊⌊⌊\d{1,4}⌋⌋⌋⌋|\s*<!--.*?-->(?=\r\n\[\[\s*" + Variables.NamespacesCaseInsensitive[Namespace.Category]
                                + @"))?", RegexOptions.Singleline);

            MatchCollection matches = r.Matches(articleText);

            foreach (Match m in matches)
            {
                if (!Regex.IsMatch(m.Value, @"\[\[Category:(Pages|Categories|Articles) for deletion\]\]"))
                {
                    categoryList.Add(m.Value);
                }
            }

            articleText = Tools.RemoveMatches(articleText, matches);

            // if category tidying has changed comments/nowikis return with no changes – we've pulled a cat from a comment
            if (!UnformattedTextNotChanged(originalArticleText, articleText))
            {
                articleText = originalArticleText;
                return("");
            }

            if (AddCatKey)
            {
                categoryList = CatKeyer(categoryList, articleTitle);
            }

            if (CatCommentRegex.IsMatch(articleText))
            {
                string catComment = CatCommentRegex.Match(articleText).Value;
                articleText = articleText.Replace(catComment, "");
                categoryList.Insert(0, catComment);
            }

            MatchCollection mc = WikiRegexes.Defaultsort.Matches(articleText);

            if (mc.Count > 1)
            {
                throw new ArgumentException("Page contains multiple {{DEFAULTSORTS}} tags. Metadata sorting cancelled");
            }

            string defaultSort = "";

            if (Variables.LangCode.Equals("sl") && LifeTime.IsMatch(articleText))
            {
                defaultSort = LifeTime.Match(articleText).Value;
            }
            else
            {
                // ignore commented out DEFAULTSORT – http://en.wikipedia.org/wiki/Wikipedia_talk:AutoWikiBrowser/Bugs#Moving_DEFAULTSORT_in_HTML_comments
                if (mc.Count > 0 && WikiRegexes.Defaultsort.Matches(WikiRegexes.Comments.Replace(articleText, "")).Count == mc.Count)
                {
                    defaultSort = mc[0].Value;
                }
            }

            if (!string.IsNullOrEmpty(defaultSort))
            {
                articleText = articleText.Replace(defaultSort, "");
            }

            if (!string.IsNullOrEmpty(defaultSort) && defaultSort.ToUpper().Contains("DEFAULTSORT"))
            {
                defaultSort = TalkPageFixes.FormatDefaultSort(defaultSort);
            }

            if (!string.IsNullOrEmpty(defaultSort))
            {
                defaultSort += "\r\n";
            }

            return(defaultSort + ListToString(categoryList));
        }
Example #18
0
        public void AddMissingFirstCommentHeaderNoChanges()
        {
            // no change – header already
            string articleTextIn = articleTextHeader + @"
==Question==
:Hello world comment3.";


            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.AreEqual(articleTextIn, articleTextHeader + @"
==Question==
:Hello world comment3.");

            // no change – already header at top
            articleTextIn = @"
{{Some template}}
==Question==
:Hello world comment3.";


            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.AreEqual(@"
{{Some template}}
==Question==
:Hello world comment3.", articleTextIn);

            // no change – already header at top 2
            articleTextIn = @"
==Question==
{{Some template}}
:Hello world comment3.";

            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.AreEqual(@"
==Question==
{{Some template}}
:Hello world comment3.", articleTextIn);

            // no change – no comments
            articleTextIn = @"
==Question==
{{Some template}}";

            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.AreEqual(@"
==Question==
{{Some template}}", articleTextIn);

            // no change – only text in template
            articleTextIn = @"
{{foo|
bar|
end}}";

            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.AreEqual(@"
{{foo|
bar|
end}}", articleTextIn);

            // no change – only comments
            articleTextIn = @"
<!--
foo
-->";

            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.AreEqual(@"
<!--
foo
-->", articleTextIn);

            // no change – only TOC
            articleTextIn = @"
__TOC__";

            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.AreEqual(@"
__TOC__", articleTextIn);

            // no change -- only in template
            const string allInTemplate = @"{{archive box|
*[[/Archive: GA review|Good Article review]]}}

== Explanation of Wright's work in ''Certaine Errors'' ==";

            articleTextIn = allInTemplate;

            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.AreEqual(allInTemplate, articleTextIn);

            // no change -- only after template on same line
            // http://en.wikipedia.org/wiki/Wikipedia_talk:AutoWikiBrowser/Bugs#Section_header_added_in_wrong_position
            const string allAfterTemplate = @"{{archive box|words}} extra foo

{{another one}}";

            articleTextIn = allAfterTemplate;

            TalkPageFixes.ProcessTalkPage(ref articleTextIn, DEFAULTSORT.NoChange);

            Assert.AreEqual(allAfterTemplate, articleTextIn);
        }
Example #19
0
        public void WikiProjectBannerShellRedirects()
        {
            string red1 = @"{{WPBS}}", WikiProjectBannerShell = @"{{WikiProjectBannerShell}}";

            Assert.AreEqual(WikiProjectBannerShell, TalkPageFixes.WikiProjectBannerShell(red1));
        }
Example #20
0
        /// <summary>
        /// Extracts DEFAULTSORT + categories from the article text; removes duplicate categories, cleans whitespace and underscores
        /// </summary>
        /// <param name="articleText">The wiki text of the article.</param>
        /// <param name="articleTitle">Title of the article</param>
        /// <returns>The cleaned page categories in a single string</returns>
        public string RemoveCats(ref string articleText, string articleTitle)
        {
            // don't pull category from redirects to a category e.g. page Hello is #REDIRECT[[Category:Hello]]
            string rt = Tools.RedirectTarget(articleText);

            if (rt.Length > 0 && WikiRegexes.Category.IsMatch(@"[[" + rt + @"]]"))
            {
                return("");
            }

            // don't operate on pages with (incorrectly) multiple defaultsorts
            MatchCollection mc = WikiRegexes.Defaultsort.Matches(articleText);

            if (mc.Count > 1)
            {
                return("");
            }

            List <string> categoryList          = new List <string>();
            string        originalArticleText   = articleText;
            string        articleTextNoComments = WikiRegexes.Comments.Replace(articleText, "");

            // allow comments between categories, and keep them in the same place, only grab any comment after the last category if on same line
            // whitespace: remove all whitespace after, but leave a blank newline before a heading (rare case where category not in last section)
            Regex r = new Regex(@"<!-- [^<>]*?\[\[\s*" + Variables.NamespacesCaseInsensitive[Namespace.Category]
                                + @".*?(\]\]|\|.*?\]\]).*?-->|\[\["
                                + Variables.NamespacesCaseInsensitive[Namespace.Category]
                                + @".*?(\]\]|\|.*?\]\])(\s*⌊⌊⌊⌊\d{1,4}⌋⌋⌋⌋| *<!--.*?-->|\s*<!--.*?-->(?=\r\n\[\[\s*" + Variables.NamespacesCaseInsensitive[Namespace.Category]
                                + @")|\s*(?=\r\n==)|\s*)?", RegexOptions.Singleline);

            // performance: apply regex on portion of article containing category links rather than whole text
            Match cq = WikiRegexes.CategoryQuick.Match(articleText);

            if (cq.Success)
            {
                int cutoff = Math.Max(0, cq.Index - 500);
                articleText = articleText.Substring(0, cutoff) + r.Replace(articleText.Substring(cutoff), m =>
                {
                    if (!CatsForDeletion.IsMatch(m.Value))
                    {
                        categoryList.Add(m.Value.Trim());
                    }
                    return("");
                });
            }

            // if category tidying has changed comments/nowikis return with no changes – we've pulled a cat from a comment
            if (!Tools.UnformattedTextNotChanged(originalArticleText, articleText))
            {
                articleText = originalArticleText;
                return("");
            }

            if (AddCatKey)
            {
                categoryList = CatKeyer(categoryList, articleTitle);
            }

            articleText = CatCommentRegex.Replace(articleText, m =>
            {
                categoryList.Insert(0, m.Value);
                return("");
            }, 1);

            string defaultSort = "";

            if (Variables.LangCode.Equals("sl") && LifeTime.IsMatch(articleText))
            {
                defaultSort = LifeTime.Match(articleText).Value;
            }
            else
            {
                // ignore commented out DEFAULTSORT – https://en.wikipedia.org/wiki/Wikipedia_talk:AutoWikiBrowser/Bugs#Moving_DEFAULTSORT_in_HTML_comments
                if (mc.Count > 0 && WikiRegexes.Defaultsort.Matches(articleTextNoComments).Count == mc.Count)
                {
                    defaultSort = mc[0].Value;
                }
            }

            if (!string.IsNullOrEmpty(defaultSort))
            {
                articleText = articleText.Replace(defaultSort, "");

                if (defaultSort.ToUpper().Contains("DEFAULTSORT"))
                {
                    defaultSort = TalkPageFixes.FormatDefaultSort(defaultSort);
                }

                defaultSort += "\r\n";
            }

            // Extract any {{uncategorized}} template, but not uncat stub templates
            string uncat = "";

            if (WikiRegexes.Uncat.IsMatch(articleTextNoComments))
            {
                Match uncatm = WikiRegexes.Uncat.Match(articleText);

                if (uncatm.Success && !WikiRegexes.PossiblyCommentedStub.IsMatch(uncatm.Value))
                {
                    articleText = articleText.Replace(uncatm.Value, "");
                    uncat       = uncatm.Value + "\r\n";
                }
            }

            return(uncat + defaultSort + ListToString(categoryList));
        }
        /// <summary>
        /// Extracts DEFAULTSORT + categories from the article text; removes duplicate categories, cleans whitespace and underscores
        /// </summary>
        /// <param name="articleText">The wiki text of the article.</param>
        /// <param name="articleTitle">Title of the article</param>
        /// <returns>The cleaned page categories in a single string</returns>
        public string RemoveCats(ref string articleText, string articleTitle)
        {
            // mainspace only. In category space it may ruin category tree
            if (!Namespace.IsMainSpace(articleTitle))
            {
                return("");
            }

            // don't pull category from redirects to a cagegory e.g. page Hello is #REDIRECT[[Category:Hello]]
            if (WikiRegexes.Category.IsMatch(@"[[" + Tools.RedirectTarget(articleText) + @"]]"))
            {
                return("");
            }

            // don't operate on pages with (incorrectly) multiple defaultsorts
            MatchCollection mc = WikiRegexes.Defaultsort.Matches(articleText);

            if (mc.Count > 1)
            {
                return("");
            }

            List <string> categoryList        = new List <string>();
            string        originalArticleText = articleText;

            // allow comments between categories, and keep them in the same place, only grab any comment after the last category if on same line
            Regex r = new Regex(@"<!-- [^<>]*?\[\[\s*" + Variables.NamespacesCaseInsensitive[Namespace.Category]
                                + @".*?(\]\]|\|.*?\]\]).*?-->|\[\["
                                + Variables.NamespacesCaseInsensitive[Namespace.Category]
                                + @".*?(\]\]|\|.*?\]\])(\s*⌊⌊⌊⌊\d{1,4}⌋⌋⌋⌋| *<!--.*?-->|\s*<!--.*?-->(?=\r\n\[\[\s*" + Variables.NamespacesCaseInsensitive[Namespace.Category]
                                + @")|\s*)?", RegexOptions.Singleline);

            MatchCollection matches = r.Matches(articleText);

            foreach (Match m in matches)
            {
                if (!CatsForDeletion.IsMatch(m.Value))
                {
                    categoryList.Add(m.Value.Trim());
                }
            }

            articleText = Tools.RemoveMatches(articleText, matches);

            // if category tidying has changed comments/nowikis return with no changes – we've pulled a cat from a comment
            if (!UnformattedTextNotChanged(originalArticleText, articleText))
            {
                articleText = originalArticleText;
                return("");
            }

            if (AddCatKey)
            {
                categoryList = CatKeyer(categoryList, articleTitle);
            }

            if (CatCommentRegex.IsMatch(articleText))
            {
                string catComment = CatCommentRegex.Match(articleText).Value;
                articleText = articleText.Replace(catComment, "");
                categoryList.Insert(0, catComment);
            }

            string defaultSort = "";

            if (Variables.LangCode.Equals("sl") && LifeTime.IsMatch(articleText))
            {
                defaultSort = LifeTime.Match(articleText).Value;
            }
            else
            {
                // ignore commented out DEFAULTSORT – https://en.wikipedia.org/wiki/Wikipedia_talk:AutoWikiBrowser/Bugs#Moving_DEFAULTSORT_in_HTML_comments
                if (mc.Count > 0 && WikiRegexes.Defaultsort.Matches(WikiRegexes.Comments.Replace(articleText, "")).Count == mc.Count)
                {
                    defaultSort = mc[0].Value;
                }
            }

            if (!string.IsNullOrEmpty(defaultSort))
            {
                articleText = articleText.Replace(defaultSort, "");

                if (defaultSort.ToUpper().Contains("DEFAULTSORT"))
                {
                    defaultSort = TalkPageFixes.FormatDefaultSort(defaultSort);
                }

                defaultSort += "\r\n";
            }

            return(defaultSort + ListToString(categoryList));
        }