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.
// [[User:Quarl/afd_vote.js]] - AFD auto-voting and shortcuts

// depends: wikipage.js, wikipageAfd.js, wikiedit.js, util.js, shortcuts.js, datestamp.js
// enhanced by: advanced_sig.js

// originally based on http://en.wikipedia.org/wiki/User:Jnothman/afd_helper/script.js
// [[User:Jnothman/afd_helper/script.js]]

//  - asynchronous updating (never opens new windows)
//  - live updating of log page
//  - huge list of shortcut expansions and other shortcut features
//  - more "vote" buttons such as in original article
//  - escaping bugs ('&&', '+', etc) fixed
//  - refuse to create AFD pages (in case of more escaping bugs)

//<pre><nowiki>

if(typeof window.makeSignature=='undefined')makeSignature=function(){return "~~~~"};

afd_vote = new Object();

afd_vote.summary_prompt = true;

afd_vote.shortcuts = Shortcuts({
            'D'         : 'Delete',
            'DA'        : 'Delete all',
            'K'         : 'Keep',
            'M'         : 'Merge',
            'MV'        : 'Move',
            'R,RD'      : 'Redirect',
            'RW'        : 'Rewrite',
            'T'         : 'Transwiki',
            'WD'        : 'Weak delete',
            'SD'        : 'Strong delete',
            'SP'        : 'Speedy delete',
            'SK'        : 'Strong keep',
            'SM'        : 'Slight merge',
            'WK'        : 'Weak keep',
            //'SPK'     : '[[Wikipedia:Speedy keep|Speedy keep]]',
            'SPK'       : 'Speedy keep',
            'C'         : 'Comment',
            'MC'        : 'Metacomment',
            'BJAODN,BJ' : 'BJAODN'
            });

afd_vote.comment_shortcuts = Shortcuts( {
            'PN'                          : 'per nomination',
            'NN'                          : '[[WP:N|non-notable]]',
            'V,VAIN,VANITY'               : '[[WP:VAIN|vanity]]',
            'NNBIO'                       : '[[WP:BIO|non-notable biography]]',
            'NNVBIO'                      : '[[WP:BIO|non-notable vanity biography]]',
            'NNWEB'                       : '[[WP:WEB|non-notable website]]',
            'NNWEBCOMIC '                 : '[[WP:WEB|non-notable webcomic]]',
            'NNBLOG,NNWEBLOG'             : '[[WP:WEB|non-notable weblog]] ',
            'NNFORUM,NNWEBFORUM'          : '[[WP:WEB|non-notable web forum]] ',
            'NNSOFTWARE,NNSOFT,NNSW'      : '[[WP:SOFTWARE|non-notable software]] ',
            'NNCORP,NNCOMPANY'            : '[[WP:CORP|non-notable corporation]]',
            'NNMUSIC'                     : '[[WP:MUSIC|non-notable musical group]]',
            'NNBAND'                      : '[[WP:MUSIC|non-notable band]]',
            'NNUNEO'                      : '[[Wikipedia:Avoid_neologisms|non-notable unstable neologism, i.e. protologism]]',
            'NNUUNEO'                     : '[[Wikipedia:Avoid_neologisms|non-notable unverifiable unstable neologism, i.e. protologism]]',
            'NNFICT,NNFICTION'            : '[[WP:FICT|non-notable reference to fictional work]]',
            'NNFICTC,NNFICTCHAR,NNCHAR'   : '[[WP:FICT|non-notable character from fictional work]] ',
            'FANCRUFT,NNFAN'              : '[[Wikipedia:Fancruft|fancruft]]',
            'NNFANFIC'                    : '[[Wikipedia:Fancruft|non-notable fan fiction]]',
            'NNGAME'                      : '[[WP:N|non-notable]] online gaming group',
            'U'                           : '[[WP:V|unverifiable]]',
            'UPH'                         : '[[WP:V|unverifiable]], possible [[WP:HOAX|hoax]]',
            'OR'                          : '[[WP:NOR|original research]]',
            'UOR'                         : '[[WP:V|unverifiable]] and/or [[WP:NOR|original research]]',
            'H'                           : '[[WP:HOAX|hoax]]',
            'HSANC,HSANCT,HSANCTION'      : '[[WP:HOAX|hoax]], and sanction article author',
            'ATTACK'                      : '[[WP:CSD|attack page]]',
            'WISHSP,WISHEXPAND'           : 'I wish for expansion of [[WP:CSD]] so that this kind of article could be speedy-deleted when no notability is asserted ',
            'UNENC,UNENCYCLO,NOTPEDIC'    : '[[WP:NOT|unencyclopedic]]',
            'NOT'                         : '[[WP:NOT|Wikipedia is not]]',
            'NOTADVERT'                   : '[[WP:NOT|Wikipedia is not a vehicle for advertising]] ',
            'NOTBALL,NOTCRYSTAL'          : '[[WP:NOT|Wikipedia is not a crystal ball]] ',
            'NOTCRUFT'                    : '[[WP:NOT|Wikipedia is not an indiscriminate collection of information]] ',
            'NOTDICT,NOTDIC'              : '[[WP:NOT|Wikipedia is not a dictionary]] (but [[Wiktionary]] is) ',
            'NOTMEMORIAL'                 : '[[WP:NOT|Wikipedia is not a memorial]] ',
            'NOTOR,NOTORIGINAL'           : '[[WP:NOT|Wikipedia is not a publisher of original thought]] ',
            'NOTSOAPBOX'                  : '[[WP:NOT|Wikipedia is not a soapbox]] ',
            'NOTSW,NOTSWDIR'              : '[[WP:NOT|Wikipedia is not a software directory]] ',
            'NOTWEBHOST,NOTFREEHOST'      : '[[WP:NOT|Wikipedia is not a free host or webspace provider]] ',
            'NOTWEBDIR'                   : '[[WP:NOT|Wikipedia is not a web directory]] ',
            'NFT,NOTSCHDAY,NOTSCH'        : '[[WP:NFT|Wikipedia is not for things made up in school one day]] ',
            'XBIO,BIOX'                   : 'Recommend the article author see [http://www.wikime.org/ WikiMe] for writing biographies and/or [http://www.wikitree.org WikiTree] for writing genealogies ',
            'XUSERFY,USERFYX'             : 'Article author may want to consider moving the content to his [[Wikipedia:User page|user page]] ',
            'XPROTO,XPROTOLOGISM,PROTOX'  : 'Protologisms may deserve listing at [[Wiktionary:List_of_protologisms]] ',
            'BALLS,BALL'                  : '[[WP:BALLS|Complete bollocks]]'
            });

afd_vote._load = function() {
    if (afdLogP) {
        // log page
        afd_vote.annotateAfd();
    } else if (afdP) {
        // AFD page
        afd_vote.annotateAfd();
        addTab('javascript:afd_vote.doVote()', 'vote', 'ca-vote', "Vote on this AFD");
    } else {
        afd_vote.annotateArticle();
    }
}

afd_vote.annotateArticle = function() {
    // is this a regular article that has an AFD notice?
    var afd = document.getElementById('afd');
    if (!afd) return;

    var href = 'javascript:afd_vote.doVote()';
    var title = 'Vote on deletion of '+wikiPage.page;

    var anchors = copyArray(afd.getElementsByTagName('a'));
    for (i in anchors) {
        if (anchors[i].text == "this article's entry" &&
            anchors[i].href.match(/Wikipedia:Articles_for_deletion\/.*/))
        {
            var span = document.createElement('span');
            span.innerHTML = ' [<a href="'+href+'" title="'+title+'">vote</a>]';
            add_after(anchors[i], span);
            break;
        }
    }

    addTab(href, 'vote', 'ca-vote', title);
}

afd_vote.annotateAfd = function() {
    var url_re = /(\/w\/index.php\?title=Wikipedia:Articles_for_deletion\/([^&]+))&action=edit&/;
    var url, matches;
    afd_vote.sectionDivs = getElementsByClass('editsection', document.getElementById('bodyContent'), 'div');
    afd_vote.labeledSectionDivs = {};

    for (var i in afd_vote.sectionDivs) {
        var div = afd_vote.sectionDivs[i];
        div.i = i;
        var anchor = div.getElementsByTagName('a')[0];
        if (!( anchor.text == "edit"
               && (matches = anchor.href.match(url_re))
               && (matches[2].substr(0, 4) != 'Log/')) )
            continue;
        var title = ""+unescape(matches[2]).replace(/_/g,' ');

        // setup for easy lookup and traversal later
        afd_vote.labeledSectionDivs[title] = div;

        var closed = Boolean(anchor.parentNode.parentNode.getAttribute('class')=='boilerplate metadata vfd');

        if (!closed) {
            var vote_href = "javascript:afd_vote.doVote("+string_quote_escape(title)+")";
            add_after(anchor, createHref(vote_href, 'Vote on deletion of '+title, 'vote'));
            add_after(anchor, document.createTextNode("] ["));
        }
        var log_href = "/w/index.php?title=Special:Log&page=" + wpaescape(title);
        add_before(anchor, createHref(log_href, title, 'log'));
        add_before(anchor, document.createTextNode("] ["));
        var afd_href = matches[1];
        add_before(anchor, createHref(afd_href, title, 'afd'));
        add_before(anchor, document.createTextNode("] ["));
    }
}

// return true if string ends with period (can also have symbols such as closing paren after period)
afd_vote._ends_with_period = function(str) {
    return Boolean(str.match(/[.?!][^a-zA-Z]*$/));
}

// return true if comment needs to be prefixed by 'as '
afd_vote._comment_needs_as = function(comment) {
    var m = comment.match(/^([a-zA-Z]+)(.*)$/);
    var word1 = m && m[1];
    if (!word1) return false;
    if (word1 == 'or') return false; // special case for lowercase 'or'
    if (word1.toUpperCase() == 'PN') return false; // special case for 'PN'
    return afd_vote.comment_shortcuts.substP(word1);
}

afd_vote.expand_vote = function(vote) {
    vote = afd_vote.shortcuts.substFirstWord(vote);
    vote = afd_vote.shortcuts.substUppercaseWords(vote);
    return vote;
}

afd_vote.expand_comment = function(vote, comment) {
    // if first word is a shortcut other than 'per nomination', prefix with 'as'

    var need_as = afd_vote._comment_needs_as(comment);

    comment = afd_vote.comment_shortcuts.substUppercaseWords(comment);
    if (!comment.match(/^or /)) {
        // "or" is too common as first word... use uppercase "OR" if that's intended.
        comment = afd_vote.comment_shortcuts.substFirstWord(comment);
    }

    if (need_as) {
        comment = 'as ' + comment;
    }

    if (!afd_vote._ends_with_period(comment)) {
        comment += ".";
    }

    // prefix with space if necessary
    if (!comment.match(/^[.,:; ]/)) {
        comment = " " + comment;
        if (vote == 'Comment') comment = ":" + comment;
    }

    // common mistake
    comment = comment.replace(/{{(nn-?bio|nn-?band|db-attack|db-repost)}}/, '{{tl|$1}}');

    return comment;
}

afd_vote._comment_possibly_unexpanded = function(comment) {
    // did user typo one of the shortcuts?
    return comment.match(/[A-Z][A-Z][A-Z]+(?![\]A-Z\|])/);
}

afd_vote.doVote = function(pagename) {
    if (pagename) {
        afd_vote.doVoteWP(new WikiPage(null,pagename));
    } else {
        afd_vote.doVoteWP(wikiPage);
    }
}

afd_vote.doVoteWP = function(wp) {
    wp = wp.afdPageX();
    if (!(wp instanceof WikiPage)) { alert("## internal error bfc4b745-0e83-4e9a-9a16-7107c8e046ef: afd_vote: not a WikiPage"); return; }
    var vote0 = window.prompt("Enter your vote.   " + afd_vote.shortcuts.msg());
    if (!vote0) return;
    var vote = afd_vote.expand_vote(vote0);
    var vote_used_shortcut = (vote != vote0);

    var comment0prev;
    var comment0 = '';
    var comment;
    var pr = "Enter your comment.  ";

    while(true) {
        comment0 = window.prompt(pr + afd_vote.comment_shortcuts.msg(), comment0);
        if (typeof comment0 != 'string') return;
        comment = afd_vote.expand_comment(vote, comment0);

        if (comment0 != comment0prev &&
            afd_vote._comment_possibly_unexpanded(comment))
        {
            comment0prev = comment0;
            pr = "Did you really mean '"+RegExp.lastMatch+"'?  Correct if you want.  ";
            continue;
        }
        break;
    }

    var default_summary = "vote '"+vote+"'";
    var summary;

    if (afd_vote.summary_prompt && !vote_used_shortcut) {
        summary = window.prompt("Enter the edit summary:", default_summary);
        if (typeof summary != 'string') return;
    }
    summary = summary || default_summary;

    var newtext = "* '''"+vote+"'''"+comment+" " + makeSignature();
    wp.getEditorAsync(afd_vote._edit, newtext, summary);
}

afd_vote._edit = function(editor, newtext, summary) {
    if (editor.refuseCreate()) return;

    editor.wpTextbox1 = trim_lines(editor.wpTextbox1) + '\n' + newtext;
    editor.wpSummary += summary;

    // are we at a log page?  (Note that 'window.location.href' is not as good as wikiPage because of shortcut redirects such as [[WP:AFD/Today]])

    var title = editor.wp.afdTargetPage().page;
    if (!title) {
        alert("## afd_vote._edit: bad page name (error b2e39d30-fd3d-405e-adf1-5f0c7c034e53)");
    }

    var div, sec_end;
    if (afdP || afdLogP) {
        // show status if we're on an AFD or AFD Log page
        if (afdLogP) {
            div = afd_vote.labeledSectionDivs[title];
            if (!div) { alert("## No labeledSectionDivs['"+title+"']"); return; }
            sec_end = afd_vote.sectionDivs[parseInt(div.i)+1];
            if (!sec_end) {
                // No next entry, must be end of page.  Add an empty div.
                sec_end = document.createElement('div');
                div.parentNode.appendChild(sec_end);
            } else if (sec_end.parentNode.id != 'bodyContent') {
                // if the next entry is a closed discussion then we need to go
                // one node up the tree
                sec_end = sec_end.parentNode;
            }
        } else {
            sec_end = getElementsByClass('printfooter', document, 'div')[0];
        }
        var statusDiv = document.createElement('div');
        statusDiv.innerHTML = "<i>(submitting...)</i>";
        add_before( sec_end, statusDiv );
    }

    if (afdLogP) {
        // We're looking at a log page.  Submit this asynchronously and replace
        // the content of this section of the log page with new content.

        editor.submitAsync(null, afd_vote.log_update, div, sec_end);
    } else {
        // submit and go to changed page
        editor.submit();
    }
}

afd_vote.log_update = function(req, div, sec_end) {
    if (req.status != 200) { alert ("Error submitting vote!"); return; }
    if (!div || !sec_end) { alert ("## afd_vote.log_update error"); return; }

    // Replace nodes between div and next div with new content.
    // Start at the <h3> tag, because the afd page text doesn't have a
    // section number (1.123), but the log text does.
    var newnodes_start = getElementsByClass('editsection', req.responseXML, 'div')[0].nextSibling.nextSibling.nextSibling.nextSibling.nextSibling;
    var newnodes_end = getElementsByClass('printfooter', req.responseXML, 'div')[0];
    var replacement_nodes = getNodesInRange(newnodes_start, newnodes_end);
    var newnode = document.createElement('div');
    for (i in replacement_nodes) { newnode.appendChild(replacement_nodes[i]); }

    var oldnodes_start = div.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling;
    removeNodesInRange(oldnodes_start, sec_end);
    add_after(div.nextSibling.nextSibling.nextSibling.nextSibling, newnode);
}

$(afd_vote._load);

//</nowiki></pre>