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.
//<source lang=javascript>

/* TODO: test, convert [[Wikipedia:AutoEd/wikilinks.js]], 
 * [[User:GregU/dashes.js]], and [[User:Ash/tidyCitations.js]],
 * take a crack at [[User:Cameltrader/Advisor.js/Description#Further_development]]
 * make some suggestions only apply in article namespace
 */

var aekNumberToMonthArray = ['January', 'February', 'March', 'April', 'May', 'June', 
    'July', 'August', 'September', 'October', 'November', 'December'];

/* General purpose function to convert regexp to suggestions */

function aekSuggestionsFromRe(regex, repl, str, skel) {
    var match, sug;
    var suggestions = [];
    while (match = regex.exec(str)) {
        var substr = str.substring(match.index, match.index + match[0].length);
        var r = substr.replace(regex, repl);
        sug = skel;
        sug.start = match.index;
        sug.end = match.index + match[0].length;
        sug.replacement = r;
        suggestions.push(sug);
        // HACK: regex.exec should get subsequent matches automatically, but it doesn't (?)
        str = str.substring(match.index + match[0].length, str.length);
    }
    return suggestions;
}

/* ported from [[Wikipedia:AutoEd/extrabreaks.js]] */

function aekAdvisorExtraBreaks(s) {
    var skel = {
        name: "extra-br",
        description: "Remove unneeded br tags"
    };
    var res = [{
        re: /[\t ]*<[\s\/\.]*br[\s\/\.]*>[\t ]*([\t\n ]*?)(\]\]|}}|\|)/gim,
        rep: '$1$2'
    },
    {
        re: /[\t ]*<[\s\/\.]*br[\s\/\.]*>[\t ]*([\s]*?[\n]\*)/gim,
        rep: '$1'
    },
    {
        re: /[\t ]*<[\s\/\.]*br[\s\/\.]*>[\t ]*([\n])[\t ]*([\n])/gim,
        rep: '$1$2'
    }];

    var suggestions = res.map(function(p) {
        return aekSuggestionsFromRe(p.re, p.rep, s, skel);
    }).reduce(function (x, y) {
        return x.concat(y);
    });

    return suggestions;
}
ct.rules.push(aekAdvisorExtraBreaks);

/* ported from [[Wikipedia:AutoEd/htmltowikitext.js]] */
function aekAdvisorHTMLToWiki(s) {
    var skel = {
        name: "wikify",
        description: "Convert HTML to wikicode"
    };
    var res = [

  { re: /<(B|STRONG)[ ]*>((?:[^<>]|<[a-z][^<>]*\/>|<([a-z]+)(?:| [^<>]*)>[^<>]*<\/\3>)*?)<\/\1[ ]*>/gi, 
    rep:  "'''$2'''" },
  { re: /<(I|EM)[ ]*>((?:[^<>]|<[a-z][^<>]*\/>|<([a-z]+)(?:| [^<>]*)>[^<>]*<\/\3>)*?)<\/\1[ ]*>/gi, 
    rep:  "''$2''" },
  // </br>, <\br>, <br\>, <BR />, ...
  { re: /<<?[\\\/\s\.]*BR[\\\s\.]*>>?/gim, rep: '<br />' },
  { re: /<BR\/>/gim, rep: '<br />' },
  // <hr>
  { re: /([\r\n])[\t ]*<[\\\/\. ]*HR[\\\/\. ]*>/gi, rep: '$1----' },
  { re: /(.)<[\\\/\. ]*HR[\\\/\. ]*>/gi, rep: '$1\n----' },
  // Not really an HTML-to-wikitext fix, but close enough
  { re: /<[\\\/\s]*REFERENCES[\\\/\s]*>/gim, rep: '<references />' },
  // Repeated references tag
  { re: /(<references \/>)[\s]*\1/gim, rep: '$1' },
  // Make sure <H1>, ..., <H6> is after a newline
  { re: /([^\r\n ])[\t ]*(<H[1-6][^<>]*>)/gim, rep: '$1\n$2' },
  // Make sure </H1>, ..., </H6> is before a newline
  { re: /(<\/H[1-6][^<>]*>)[\t ]*([^\r\n ])/gim, rep: '$1\n$2' },
  // Remove newlines from inside <H1>, ..., <H6>
  // Replace <H1>, ..., <H6> with wikified section headings
  { re: /(^|[\r\n])[\t ]*<H1[^<>]*>([^\r\n]*?)<\/H1[\r\n\t ]*>[\t ]*([\r\n]|$)/gim, rep: '$1=$2=$3' },
  { re: /(^|[\r\n])[\t ]*<H2[^<>]*>([^\r\n]*?)<\/H2[\r\n\t ]*>[\t ]*([\r\n]|$)/gim, rep: '$1==$2==$3' },
  { re: /(^|[\r\n])[\t ]*<H3[^<>]*>([^\r\n]*?)<\/H3[\r\n\t ]*>[\t ]*([\r\n]|$)/gim, rep: '$1===$2===$3' },
  { re: /(^|[\r\n])[\t ]*<H4[^<>]*>([^\r\n]*?)<\/H4[\r\n\t ]*>[\t ]*([\r\n]|$)/gim, rep: '$1====$2====$3' },
  { re: /(^|[\r\n])[\t ]*<H5[^<>]*>([^\r\n]*?)<\/H5[\r\n\t ]*>[\t ]*([\r\n]|$)/gim, rep: '$1=====$2=====$3' },
  { re: /(^|[\r\n])[\t ]*<H6[^<>]*>([^\r\n]*?)<\/H6[\r\n\t ]*>[\t ]*([\r\n]|$)/gim, rep: '$1======$2======$3' },

];

    var suggestions = res.map(function(p) {
        return aekSuggestionsFromRe(p.re, p.rep, s, skel);
    }).reduce(function(x, y) {
        return x.concat(y);
    });

    return suggestions;
}
ct.rules.push(aekAdvisorHTMLToWiki);

function aekAdvisorLinks(s) {
    var skel = {
        name: "links",
        description: "Fix external links"
    };
    var res = [{
        re: /\]\[/g, 
        rep: "] ["
    }];

    var suggestions = res.map(function(p) {
        return aekSuggestionsFromRe(p.re, p.rep, s, skel);
    }).reduce(function (x, y) {
        return x.concat(y);
    });

    return suggestions;
}
ct.rules.push(aekAdvisorLinks);


function aekAdvisorTemplates(s) {
    var skel = {
        name: "templates",
        description: "Fix template syntax"
    };
    var res = [

    //Remove unneeded Template: text from transclusions
    {
        re: /{{[_ ]*Template:[_ ]*/gi,
        rep: '{{'
    },

    //Replace redirects to Reflist with Reflist
    {
        re: /{{[_ ]*(?:Reference[_ ]+List|References-Small|Reflink)[_ ]*(\||}})/gi,
        rep: '{{Reflist$1'
    },
    {
        re: /{{[_ ]*(?:Refs|Reference|Ref-list|Listaref|FootnotesSmall)[_ ]*(\||}})/gi,
        rep: '{{Reflist$1'
    },

    //Replace a long version of Reflist with Reflist
    {
        re: /]*[ ]+class=['"]*references-small['"]*[^<>]*>[\r\n]*[\r\n]*<\/div>/gim,
        rep: '{{Reflist}}'
    },

    //Replace redirects to about with about
    {
        re: /{{[_ ]*(?:Otheruses4|Four[_ ]+other[_ ]+uses|Otherusesabout|This2)[_ ]*(\||}})/gi,
        rep: '{{about$1'
    }

    ];

    var suggestions = res.map(function(p) {
        return aekSuggestionsFromRe(p.re, p.rep, s, skel);
    }).reduce(function(x, y) {
        return x.concat(y);
    });

    return suggestions;
}
ct.rules.push(aekAdvisorTemplates);

function aekAddArticleTags(s) {
    var match;
    var suggestions = [];
    /* TODO: add length heuristic?; rewrite in terms of templates array */
    var stub_regex = new RegExp("stub}" + "}");
    var is_stub = stub_regex.test(s);
    var templates_re = new RegExp("{" + "{[^|}]+", "g");
    var templates = s.match(templates_re) || [];
    templates = templates.map(function(str) {
        str = str.replace("subst:", "");
        str = str.replace("{" + "{", "");
        str = str.toLowerCase();
        return str;
    });
    var now = new Date();
    var template_date = "|date = " + aekNumberToMonthArray[now.getMonth()] + " " + now.getFullYear();

    /* Trivia sections */
    if ((match = /== ?(Trivia|Miscellany|Miscellaneous) ?==/i.exec(s))) {
        if (!templates.some(function(str) {
            return str == "trivia"
        })) {
            suggestions.push({
                name: "trivia",
                description: "Tag trivia sections",
                start: match.index,
                end: match.index + match[0].length,
                replacement: match[0] + "\n{" + "{trivia" + template_date + "}}"
            });
        }
    }

    /* Excessive links */
    /* Most external links begin with http (?) */
    var s_without_refs = s.replace(/<ref>.*?<\/ref>/, "");
    var ext_links = s_without_refs.match(/[^\[]\[\s*http.*?\]/g) || [];
    if (ext_links.length > 3 && !templates.some(function(str) {
        return str == "excessivelinks" || str == "too many links";
    })) {
        /* for testing, make 20-ish for release */
        suggestions.push({
            name: "too-many-links",
            description: "Tag for profusion of external links",
            start: 0,
            end: 0,
            replacement: "{" + "{Excessivelinks" + template_date + "}}\n"
        });
    }

    /* some things only make sense for full articles */
    if (!is_stub) {

        /* No sections */
        if (!/== ?.+ ?==/.test(s) && !templates.some(function(str) {
            return str == "sections"
        })) {
            suggestions.push({
                name: "no-sections",
                description: "Tag for lack of sections",
                start: 0,
                end: 0,
                replacement: "{" + "{Sections" + template_date + "}}\n"
            });
        }

        /* No references */
        if (!/<ref>/.test(s) && !templates.some(function(str) {
            return str == "unreferenced"
        })) {
            suggestions.push({
                name: "no-references",
                description: "Tag for lack of references",
                start: 0,
                end: 0,
                /* TODO: unreferencedBLP */
                replacement: "{" + "{unreferenced" + template_date + "}}\n"
            });
        }
    } /* is_stub */

    return suggestions;
}
ct.rules.push(aekAddArticleTags);

//</source>