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/rollback.js]] - rollback button for non-admins

// requires util.js, md5.js, wikipage.js, wikiedit.js

// based on http://sam.zoy.org/wikipedia/godmode-light.js
//   - more robust (works with Popups, etc)
//   - factored code

// <pre><nowiki>

// -----------------------------------------------------------------------------
// God-like Monobook skin
// (c) 2005 Sam Hocevar <sam@zoy.org>
// Forked from: Id: godmode-light.js 980 2005-11-12 01:51:51Z sam 
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// Language support, taken from phase3/languages/*
// -----------------------------------------------------------------------------
var rollbacklink = 'rollback';
var cantrollback = 'Cannot revert edit; last contributor is only author of this page.';
var alreadyrolled = 'Cannot rollback last edit of [[$1]] by [[User:$2|$2]] ([[User talk:$2|Talk]]); someone else has edited or rolled back the page already. Last edit was by [[User:$3|$3]] ([[User talk:$3|Talk]]). ';
var revertpage = 'Reverted edits by [[User:$2|$2]] ([[User talk:$2|t]]) ([[Special:Contributions/$2|c]]) to last version by $1';

// -----------------------------------------------------------------------------
// Our nice Revert functions
// -----------------------------------------------------------------------------
var gml_vandal, gml_editor;

function rollback_Revert() {
    if(typeof queryVars === 'undefined') return;
    var token = queryVars['token'];
    gml_vandal = queryVars['vandal'];

    document.cont = document.getElementById('bodyContent');

    document.cont.innerHTML = 'Please wait, reverting edits by ' + gml_vandal + '...';
    // Avoid XSS kiddies by using a special token
    if (!token || token != rollback_mkToken(wikiPage, gml_vandal)) {
        document.cont.innerHTML += 'ERROR: Bad authentication token!';
        return;
    }

    document.getElementById('bodyContent').innerHTML += '<br />Getting article history...';
    asyncDownloadXML(wikiPage.qurl + '&action=history&limit=50', rollback_Revert2);
}

// Get the vandal and new editor names
function rollback_parseHistory(doc) {
    var l = doc.getElementById('pagehistory').getElementsByTagName('li');
    for (i = 0; i < l.length; i++) {
        var name = l[i].getElementsByTagName('span')[0].getElementsByTagName('a')[0].innerHTML.replace(/_/g,' ');
        if (!name) continue;
        if (i == 0) {
            if (name != gml_vandal) {
                document.cont.innerHTML += '<br />Error: Last editor is ' + name + ', not ' + gml_vandal + '!';
                return {};
            }
        } else {
            if (name != gml_vandal) {
                var oldid = l[i].getElementsByTagName('input')[0].value;
                var editor = name;
                return {'oldid':oldid, 'editor':editor};
            }
        }
    }
    return {};
}

function rollback_Revert2(xmlhttp) {
    doc = xmlhttp.responseXML;

    var r = rollback_parseHistory(doc);
    var oldid = r.oldid;
    gml_editor = r.editor;
    if (!gml_editor) {
        document.cont.innerHTML += '<br />Error: ' + gml_vandal + ' is the only editor!';
        return;
    }

    var url = wikiPage.qurl + '&action=edit&oldid=' + oldid;
    document.cont.innerHTML += '<br />Getting article edit form...';
    asyncDownloadXML(url, rollback_Revert3);
}

function rollback_Revert3(xmlhttp) {
    summary_text = revertpage.replace(/\$1/g, gml_editor).replace(/\$2/g, gml_vandal);

    document.cont.innerHTML += '<br />Submitting form...';

    var wd = new WikiDocument(xmlhttp.responseXML, wikiPage);
    var editor = new WikiEditor(wd);
    editor.wpSummary = summary_text;
    editor.submit();
}

function rollback_mkToken(wp, vandal) {
    return hex_md5(wp.page + '%' + vandal + '%' + document.cookie);
}

function rollback_mkUrl(wp, vandal) {
    return url_getpath(wp.qurl + '&fakeaction=rollback&vandal=' + 
                       escape(vandal) + '&token=' + escape(rollback_mkToken(wp, vandal)));
}

// -----------------------------------------------------------------------------
// Add revert buttons to the page
// -----------------------------------------------------------------------------
function rollback_AddButtonDiff() {
    if (wikiDoc.editingP) return; // preview diff pages will also be edit pages and shouldn't have rollback buttons

    var difftag = getElementsByClass('diff-ntitle',document.getElementById('bodyContent'),'td')[0];
    if (!difftag) return;

    // if toplink has an oldid then this is not a diff against current revision.
    var toplink = difftag.getElementsByTagName('a')[0].href;
    if (toplink.match(/oldid=/)) return;

    var vandal = getUsernameFromLink(difftag.getElementsByTagName('a')[1]);
    if (!vandal) { alert("Couldn't parse username in diff page!"); return; }

    var url = rollback_mkUrl(wikiPage,vandal);
    var newtext = ' &nbsp;&nbsp;&nbsp;<strong>[<a href="' + url + '">' + rollbacklink + '</a>]</strong> ';

    difftag.innerHTML = difftag.innerHTML.replace(/(<\/a>\))( *<br)/i, '$1'+newtext+'$2');
}

function rollback_AddButtonContributions() {
    if (wikiPage.page != 'Special:Contributions') return;

    var vandal = relevantUser; // defined in wikipage.js
    if (!vandal) return;

    var l = document.getElementById('bodyContent').getElementsByTagName('li');
    for (i = 0; i < l.length; i++) {
        var t = l[i].innerHTML;
        // If we are already a sysop on this wiki, abort
        if (t.match(/>rollback<\/a>]/)) return;

        if (t.match(/<strong> \(/)) {
            var wp = new WikiPage(l[i].getElementsByTagName('a')[0].href);
            var url = rollback_mkUrl(wp,vandal);
            l[i].innerHTML += ' [<a href="' + url + '">' + rollbacklink + '</a>]';
        }
    }
}

function rollback_Load() {
    if(typeof queryVars === 'undefined') return;
    if (queryVars['fakeaction'] == 'rollback') {
        rollback_Revert();
    } else {
        rollback_AddButtonDiff();
        rollback_AddButtonContributions();
    }
}

$(rollback_Load);

// </nowiki></pre>