User:Greenpickle/rcnst.js

if (window.RCNSTDefaultToVisible === undefined) var RCNSTDefaultToVisible = true; if (window.RCNSTStartHidden === undefined) var RCNSTStartHidden = []; if (window.RCNSTStartVisible === undefined) var RCNSTStartVisible = []; if (window.RCNSTControlsID === undefined) var RCNSTControlsID = 'rcnst'; if (window.RCNSTClass === undefined) var RCNSTClass = 'rcnst'; if (window.RCNSTMain === undefined) var RCNSTMain = '(Main)'; if (window.RCNSTToggleAllLabel === undefined) var RCNSTToggleAllLabel = 'toggle all'; if (window.RCNSTInvertLabel === undefined) var RCNSTInvertLabel = 'invert selection'; if (window.RCNSTRestoreLabel === undefined) var RCNSTRestoreLabel = 'restore default';

if (window.hasClass === undefined) { var hasClass = (function {        var reCache = {};        return function (element, className) {            return (reCache[className] ? reCache[className] : (reCache[className] =               new RegExp("(?:\\s|^)" + className + "(?:\\s|$)")))                .test(element.className);        };    }); }

function nextElement (e) { do e = e.nextSibling; while (e !== null && e.nodeType != 1); return e; }

function previousElement (e) { do e = e.previousSibling; while (e !== null && e.nodeType != 1); return e; }

if (!Array.indexOf) { Array.prototype.indexOf = function (o) { var i = this.length; while (i--) if (this[i] === o) return i;       return -1; }; }

var RCNST = {};

RCNST.getDefault = function (changeType) { if (RCNSTDefaultToVisible) return RCNSTStartHidden.indexOf(changeType) == -1; else return RCNSTStartVisible.indexOf(changeType) != -1; };

RCNST.classRegex = new RegExp('\\b' + RCNSTClass + '\\b', 'g');

RCNST.getChangeType = function (change) { var result; var lnks = change.getElementsByTagName('a'); var lnk = lnks[0].firstChild.nodeValue; var isEdit = false; if (RCNST.enh) { if (lnks[0].parentNode.firstChild.nodeValue.indexOf('(') == -1)           isEdit = true;    } else {        if (lnk == 'diff' || lnk == 'hist') {            var isEdit = true;            if (lnk == 'diff') lnk = 2;            else lnk = 1; // new page            lnk = lnks[lnk].firstChild.nodeValue;        }    }    if (isEdit) {        // work out namespace        var colon = lnk.indexOf(':');        if (colon == -1) result = RCNSTMain;        else result = lnk.substring(0, colon);    } else result = lnk; // log    return result; };

RCNST.toggle = function (changeType) { // show/hide changes of given type according to corresponding checkbox var show = RCNST.checkboxes[changeType].checked; var changes = RCNST.changes[changeType]; if (show) var reg = RCNST.classRegex; for (var i = 0; i < changes.length; i++) { if (RCNST.enh) { var div = nextElement(changes[i]); if (div !== null && div.tagName.toLowerCase == 'div') { // also do the same for the subchanges if (show) // remove class div.className = div.className.replace(reg, ' '); else div.className += ' ' + RCNSTClass; // add class }       }        if (show) // remove class changes[i].className = changes[i].className.replace(reg, ' '); else changes[i].className += ' ' + RCNSTClass; // add class } };

RCNST.smartToggle = function (changeType, show) { // set changes of given type to given visibility if not already var c = RCNST.checkboxes[changeType]; var shown = c.checked; if (shown != show) { c.checked = show; RCNST.toggle(changeType); } };

RCNST.toggleAll = function { // if any types are hidden, show them all, else hide them all var show = false; for (var changeType in RCNST.checkboxes) { if (!RCNST.checkboxes[changeType].checked) { show = true; break; }   }    for (var changeType in RCNST.checkboxes) RCNST.smartToggle(changeType, show); RCNST.updateHeadings; };

RCNST.invert = function { for (var changeType in RCNST.checkboxes) RCNST.smartToggle(changeType, !RCNST.checkboxes[changeType].checked); RCNST.updateHeadings; };

RCNST.restore = function { for (var changeType in RCNST.checkboxes) RCNST.smartToggle(changeType, RCNST.getDefault(changeType)); RCNST.updateHeadings; };

RCNST.updateHeadings = function { // hide headings if all changes in them are hidden, else show them var enh = RCNST.enh; var reg = RCNST.classRegex; var h4s = document.getElementsByTagName('h4'); for (var i = 0; i < h4s.length; i++) { var h4 = h4s[i]; // check it's a valid header and get change list var section = nextElement(h4); if (section === null || section.tagName === undefined) continue; var sectionTag = section.tagName.toLowerCase; if (enh) { if (sectionTag != 'div') continue; var changes = getElementsByClassName(section, 'table',                                                'mw-enhanced-rc'); } else { if (sectionTag != 'ul' || !hasClass(section, 'special')) continue; var changes = section.getElementsByTagName('li'); }       if (changes.length == 0) continue; // check changes under this heading var show = false; for (var j = 0; j < changes.length; j++) { if (changes[j].parentNode !== section) continue; if (!hasClass(changes[j], RCNSTClass)) { show = true; break; }       }        if (show) h4.className = h4.className.replace(reg, ' '); else if (!hasClass(h4, RCNSTClass)) h4.className += ' ' + RCNSTClass; } };

RCNST.init = function { if (!hasClass(document.body, 'page-Special_RecentChanges')       && !hasClass(document.body, 'page-Special_Recentchanges')        && !hasClass(document.body, 'page-Special_Watchlist')) return;

// compile per-type lists of RC changes var changes = {}; RCNST.changes = changes; var uls = getElementsByClassName(document, 'ul', 'special'); if (uls.length) { // normal RC       RCNST.enh = false; for (var i = 0; i < uls.length; i++) { var lis = uls[i].getElementsByTagName('li'); for (var j = 0; j < lis.length; j++) { var change = lis[j]; var changeType = RCNST.getChangeType(change); if (changes[changeType] === undefined) changes[changeType] = [change]; else changes[changeType].push(change); }       }    } else { var tbls = getElementsByClassName(document, 'table', 'mw-enhanced-rc'); if (tbls.length) { // enhanced RC           RCNST.enh = true; for (var i = 0; i < tbls.length; i++) { var tbl = tbls[i]; var tds = tbl.getElementsByTagName('td'); if (!hasClass(tds[0], 'mw-enhanced-rc')) continue; var changeType = RCNST.getChangeType(tds[1]); if (changes[changeType] === undefined) changes[changeType] = [tbl]; else changes[changeType].push(tbl); }       } else return; // no changes }

// add controls to page var rcoptions = getElementsByClassName(document, '*', 'rcoptions'); if (rcoptions.length) { // put with the other controls rcoptions[0].appendChild(document.createElement('hr')); var controls = document.createElement('span'); rcoptions[0].appendChild(controls); } else { // put before the first day heading var h4s = document.getElementsByTagName('h4'); for (var i = 0; i < h4s.length; i++) { var e = nextElement(h4s[i]); if (e !== null && e.tagName.toLowerCase == 'ul'               && hasClass(e, 'special')) { var controls = document.createElement('div'); h4s[i].parentNode.insertBefore(controls, h4s[i]); break; }       }        // if there are no day headings, I have no idea what's going on, so        // just don't show them }   controls.id = RCNSTControlsID;

// create ordered list of change types var changeTypes = []; // namespaces first var nsSelect = document.getElementById('namespace'); if (nsSelect !== null) { var optionList = nsSelect.getElementsByTagName('option'); for (var i = 0; i < optionList.length; i++) { if (optionList[i].value == '0') var ns = RCNSTMain; else var ns = optionList[i].firstChild.nodeValue; if (changes[ns] !== undefined) changeTypes.push(ns); }   }    // then logs var toSort = []; for (changeType in changes) if (changeTypes.indexOf(changeType) == -1) toSort.push(changeType); toSort.sort; changeTypes = changeTypes.concat(toSort);

// then create checkboxes in this order var checkboxes = {}; RCNST.checkboxes = checkboxes; for (var i = 0; i < changeTypes.length; i++) { var c = document.createElement('input'); checkboxes[changeTypes[i]] = c;       c.type = 'checkbox'; c.onclick = function { RCNST.toggle(this.nextSibling.firstChild.nodeValue); RCNST.updateHeadings; };       c.checked = true; controls.appendChild(c); var label = document.createElement('label'); label.appendChild(document.createTextNode(changeTypes[i])); controls.appendChild(label); }   RCNST.restore; // add buttons to controls function addButton (label, onclick) { var button = document.createElement('input'); button.type = 'button'; button.value = label; button.onclick = RCNST[onclick]; controls.appendChild(button); }   addButton(RCNSTToggleAllLabel, 'toggleAll'); addButton(RCNSTInvertLabel, 'invert'); addButton(RCNSTRestoreLabel, 'restore');

if (RCNST.enh) { // replace enhanced RC event for the arrow things window.toggleVisibility = function(idNumber) { var openarrow = document.getElementById("mw-rc-openarrow-"+idNumber); var closearrow = document.getElementById("mw-rc-closearrow-"+idNumber); var subentries = document.getElementById("mw-rc-subentries-"+idNumber); if (openarrow.className == 'mw-changeslist-expanded') { openarrow.className = 'mw-changeslist-hidden'; closearrow.className = 'mw-changeslist-expanded'; subentries.className = subentries.className.replace(                   /\bmw-changeslist-hidden\b/, 'mw-changeslist-expanded'); } else { openarrow.className = 'mw-changeslist-expanded'; closearrow.className = 'mw-changeslist-hidden'; subentries.className = subentries.className.replace(                   /\bmw-changeslist-expanded\b/, 'mw-changeslist-hidden'); }       };    } };

// initialise on page load if (window.domReady !== undefined) domReady(RCNST.init); else $(RCNST.init);