MediaWiki:Gadget-readableRC-core.js

From Zeah RSPS - Wiki
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
// <nowiki>
// Formats the rows on Special:RecentChanges where all the information runs together
// into three columns (page, diff/byte change, and user links) to make it more readable
//
// @author Iiii_I_I_I
//
// @TODO: support "Group changes by page" option being unchecked in Special:Preferences?

(function ($, mw) {
    function runReadableRC($content) {
        if (!$content.hasClass('mw-changeslist')) {
            return;
        }

        $content.addClass('gadget-rc-enabled');

        var rows = document.querySelectorAll('.mw-changeslist-line, ' + // does not include grouped edits
                    '.mw-rcfilters-ui-highlights-enhanced-toplevel, ' + // first child in grouped edits
                    '.mw-rcfilters-ui-highlights-enhanced-nested'); // subsequent children in grouped edits

        for (var i = 0; i < rows.length; i++) {
            var row = rows[i];

            // nested rows (only visible after expanding grouped edits)
            if (row.classList.contains('mw-rcfilters-ui-highlights-enhanced-nested')) {
                row.classList.add('gadget-rc-nested');

                if (row.classList.contains('mw-changeslist-edit')) {
                    cleanNestedEdit(row);
                }
                else if (row.classList.contains('mw-changeslist-log')) {
                    cleanNestedLog(row);
                }
            }
            // top-level rows (visible without uncollapsing any groups)
            else {
                // ignore parent element of grouped edits (text is in children, not container)
                if (row.classList.contains('mw-collapsible')) {
                    continue;
                }

                // top-level row in grouped edits/log entries
                if (row.classList.contains('mw-rcfilters-ui-highlights-enhanced-toplevel')) {
                    var parent = row.closest('.mw-collapsible');

                    if (parent.classList.contains('mw-changeslist-edit')) {
                        cleanMultipleEdits(row);
                    }
                    else if (parent.classList.contains('mw-changeslist-log')) {
                        cleanMultipleLogs(row);
                    }
                }
                // row for single edit/log entries
                else {
                    if (row.classList.contains('mw-changeslist-edit')) {
                        cleanSingleEdit(row);
                    }
                    else if (row.classList.contains('mw-changeslist-log')) {
                        cleanSingleLog(row);
                    }
                }
            }
        }
    }

    function insert(row, newNode, referenceNode) {
        return row.querySelector(referenceNode).parentNode.insertBefore(row.querySelector(newNode), row.querySelector(referenceNode));
    }

    function wrap(referenceNode, wrapperEl, wrapperClass) {
        var wrapper = document.createElement(wrapperEl);

        referenceNode.parentNode.insertBefore(wrapper, referenceNode);
        wrapper.appendChild(referenceNode);
        wrapper.classList.add(wrapperClass);
    }

    function cleanNestedLog(row) {
        // wrap "log name" (actually timestamp) together
        wrap(row.querySelector('.mw-enhanced-rc-time'), 'td', 'gadget-rc-logname');

        // placeholder column with separator dots
        wrap(row.querySelector('.mw-changeslist-separator'), 'td', 'gadget-rc-logdots');

        cleanUserLinks(row);

        // rename <td>
        row.querySelector('td.mw-enhanced-rc-nested').className = 'gadget-rc-logentry';

        // rearrange newly created <td>s
        insert(row, '.gadget-rc-logname', '.gadget-rc-logentry');
        insert(row, '.gadget-rc-logdots', '.gadget-rc-logentry');
    }

    function cleanNestedEdit(row) {
        // wrap "page name" (actually revision links) together
        wrap(row.querySelector('.mw-enhanced-rc-time'), 'td', 'gadget-rc-pagename');

        // remove two dot separators
        row.querySelector('.mw-changeslist-separator').remove();
        row.querySelector('.mw-changeslist-separator').remove();

        // wrap user stuff together
        $('.mw-userlink, .mw-usertoollinks, .comment, .mw-rollback-link, .mw-tag-markers, .history-deleted', row).wrapAll('<td class="gadget-rc-userlinks" />');

        cleanUserLinks(row);

        // "rollback x edit(s)" -> "rollback"
        // link does not exist if it is a page creation or user does not have the right
        if (row.querySelector('.mw-rollback-link')) {
            row.querySelector('.mw-rollback-link a').textContent = 'rollback';
        }

        // rename <td>
        row.querySelector('td.mw-enhanced-rc-nested').className = 'gadget-rc-diff';

        // rearrange newly created <td>s
        insert(row, '.gadget-rc-userlinks', '.gadget-rc-diff');
        insert(row, '.gadget-rc-pagename', '.gadget-rc-userlinks');
        insert(row, '.gadget-rc-diff', '.gadget-rc-userlinks');
    }

    function cleanMultipleLogs(row) {
        // log name
        wrap(row.querySelector('.mw-rc-unwatched'), 'td', 'gadget-rc-logname');

        // remove square brackets from grouped usernames; cannot simply use remove()
        // since there might be other text in the same node, eg. "(4×)]"
        var users = row.querySelector('.changedby').childNodes;

        users[0].textContent = users[0].textContent.slice(1); // [
        users[users.length - 1].textContent = users[users.length - 1].textContent.slice(0, -1); // ]

        // placeholder column with separator dots
        wrap(row.querySelector('.mw-changeslist-separator'), 'td', 'gadget-rc-logdots');

        // rename <td>
        row.querySelector('.mw-changeslist-line-inner').className = 'gadget-rc-logentry';

        // rearrange newly created <td>s
        insert(row, '.gadget-rc-logname', '.gadget-rc-logentry');
        insert(row, '.gadget-rc-logdots', '.gadget-rc-logentry');

        // stupid empty node
        row.querySelector('.gadget-rc-logentry').childNodes[0].remove();
    }

    function cleanMultipleEdits(row) {
        // page name
        wrap(row.querySelector('.mw-title'), 'td', 'gadget-rc-pagename');

        // "x changes" -> "diff"
        if (row.querySelector('.mw-changeslist-groupdiff')) {
            row.querySelector('.mw-changeslist-groupdiff').textContent = 'diff';
        }
        // new pages have a text node instead of a link
        else {
            row.querySelector('.mw-changeslist-links span:first-child').textContent = 'diff';
        }

        // "history" -> "hist"
        if (row.querySelector('.mw-changeslist-history')) {
            row.querySelector('.mw-changeslist-history').textContent = 'hist';
        }
        // nonexistent pages (redirect-suppressed move or deleted) have a text node instead of a link
        else {
            var newHist = row.querySelector('.mw-changeslist-line-inner').childNodes[4].nodeValue.replace('history', 'hist');

            row.querySelector('.mw-changeslist-line-inner').childNodes[4].nodeValue = newHist;
        }

        // list of user(s)
        wrap(row.querySelector('.changedby'), 'td', 'gadget-rc-userlinks');

        // remove square brackets from grouped usernames; cannot simply use remove()
        // since there might be other text in the same node, eg. "(4×)]"
        var users = row.querySelector('.changedby').childNodes;

        users[0].textContent = users[0].textContent.slice(1); // [
        users[users.length - 1].textContent = users[users.length - 1].textContent.slice(0, -1); // ]

        // rename <td>
        row.querySelector('.mw-changeslist-line-inner').className = 'gadget-rc-diff';

        // rearrange newly created <td>s
        insert(row, '.gadget-rc-userlinks', '.gadget-rc-diff');
        insert(row, '.gadget-rc-pagename', '.gadget-rc-userlinks');
        insert(row, '.gadget-rc-diff', '.gadget-rc-userlinks');
    }

    function cleanSingleLog(row) {
        // log name
        wrap(row.querySelector('.mw-changeslist-line-inner-logLink'), 'td', 'gadget-rc-logname');

        // placeholder column with separator dots
        wrap(row.querySelector('.mw-changeslist-line-inner-separatorAfterLinks'), 'td', 'gadget-rc-logdots');

        cleanUserLinks(row);

        // rename <td>
        row.querySelector('.mw-changeslist-line-inner').className = 'gadget-rc-logentry';

        // rearrange newly created <td>s
        insert(row, '.gadget-rc-logname', '.gadget-rc-logentry');
        insert(row, '.gadget-rc-logdots', '.gadget-rc-logentry');
    }

    function cleanSingleEdit(row) {
        // page name
        wrap(row.querySelector('.mw-title'), 'td', 'gadget-rc-pagename');

        // "rollback x edit(s)" -> "rollback"
        // link does not exist if it is a page creation or user does not have the right
        if (row.querySelector('.mw-rollback-link')) {
            row.querySelector('.mw-rollback-link a').textContent = 'rollback';
        }

        // user info
        $('.mw-userlink, .mw-usertoollinks, .comment, .mw-rollback-link, .mw-tag-markers, .history-deleted', row).wrapAll('<td class="gadget-rc-userlinks" />');

        cleanUserLinks(row);

        // rename <td>
        row.querySelector('.mw-changeslist-line-inner').className = 'gadget-rc-diff';

        // rearrange newly created <td>s
        insert(row, '.gadget-rc-userlinks', '.gadget-rc-diff');
        insert(row, '.gadget-rc-pagename', '.gadget-rc-userlinks');
        insert(row, '.gadget-rc-diff', '.gadget-rc-userlinks');
    }

    // (talk | contribs | block) -> (t|c|b)
    // possible variations: (talk), (talk | contribs), (talk | block), (talk | contribs | block), (username removed)
    function cleanUserLinks(row) {
        // if username has been revdeled (shows "(username removed)"), then exit
        if (row.querySelector('.history-deleted')) {
            return;
        }

        row.querySelector('.mw-usertoollinks-talk').textContent = 't';

        // IPs don't have dedicated contribs link - it's linked in their username
        if (row.querySelector('.mw-usertoollinks-contribs')) {
            row.querySelector('.mw-usertoollinks-contribs').textContent = 'c';
        }

        // non-admins don't have block link
        if (row.querySelector('.mw-usertoollinks-block')) {
            row.querySelector('.mw-usertoollinks-block').textContent = 'b';
        }
    }

    function init() {
        var info = new OO.ui.PopupButtonWidget({
            classes: ['gadget-rc-button'],
            framed: false,
            label: 'Broken?',
            popup: {
                head: true,
                label: 'Is the RecentChanges layout broken?',
                $content: $("<p><a href='/w/MediaWiki:Gadget-readableRC-core.js'>ReadableRC</a>, which formats this " +
                            "page into columns, might have an error. If something looks wrong, disable this gadget in " +
                            "your preferences and report the issue to <a href='/w/User_talk:Iiii_I_I_I'>Iiii I I I</a>.</p>"),
                padded: true,
                align: 'force-right',
                hideCloseButton: true
            }
        })

        mw.hook('structuredChangeFilters.ui.initialized').add(function () {
            runReadableRC($('.mw-changeslist'));
            mw.hook('wikipage.content').add(runReadableRC);
            $('.mw-rcfilters-ui-liveUpdateButtonWidget').append(info.$element);
        });
    }

    $(init);
})(jQuery, mediaWiki);

// </nowiki>