В четверг, 7 мая, около 16 часов (MSK) регистратор заморозил домен «cyclowiki.org» без уведомления владельцев. Сайт недоступен из большинства стран. Правление изучает возможности решения проблемы.

Участник:Товарищ Аарон/Scripts/massRollback.js

Материал из Циклопедии
Перейти к навигации Перейти к поиску

Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.

  • Firefox / Safari: Удерживая клавишу Shift, нажмите на панели инструментов Обновить либо нажмите Ctrl+F5 или Ctrl+R (⌘+R на Mac)
  • Google Chrome: Нажмите Ctrl+Shift+R (⌘+Shift+R на Mac)
  • Internet Explorer / Edge: Удерживая Ctrl, нажмите Обновить либо нажмите Ctrl+F5
  • Opera: Нажмите Ctrl+F5.
/**
 * Улучшенная версия пользовательских скриптов для Cyclowiki
 * + поддержка Mass Rollback
 * март 2026
 */

(function () {

    "use strict";

    // ============================================================
    // IMPORT SCRIPT
    // ============================================================

    window.importScript = function (page) {

        mw.loader.using('mediawiki.util').done(function () {

            mw.loader.load(
                '//cyclowiki.su/w/index.php?title=' +
                mw.util.wikiUrlencode(page) +
                '&action=raw&ctype=text/javascript'
            );

        });

    };

    // ============================================================
    // WIKIBUGS
    // ============================================================

    importScript("MediaWiki:Wikibugs.js");

    // ============================================================
    // withJS
    // ============================================================

    (function () {

        try {

            var withJS = document.URL.match(/[&?]withjs=((mediawiki:)?([^&#]+))/i);

            if (withJS && withJS[3] && window.importScript) {

                var pageName = withJS[3];

                if (/^[a-zA-Z0-9_\.\-]+$/.test(pageName)) {

                    importScript('MediaWiki:' + pageName);

                }

            }

        } catch (e) {

            console.warn('Ошибка withJS:', e);

        }

    })();

    // ============================================================
    // COLLAPSIBLE TABLES
    // ============================================================

    var NavigationBar = {
        hide: '[скрыть]',
        show: '[показать]',
        showDefault: 2
    };

    function hasClass(element, className) {

        if (!element || !element.className) {
            return false;
        }

        if (element.classList) {
            return element.classList.contains(className);
        }

        return (' ' + element.className + ' ')
            .indexOf(' ' + className + ' ') !== -1;
    }

    function collapsibleTables() {

        try {

            var Table,
                HRow,
                HCell,
                btn,
                a,
                tblIdx = 0,
                colTables = [];

            var allTables = document.getElementsByTagName('table');

            for (var i = 0; i < allTables.length; i++) {

                Table = allTables[i];

                if (!hasClass(Table, 'collapsible')) {
                    continue;
                }

                if (!(HRow = Table.rows[0])) {
                    continue;
                }

                if (!(HCell = HRow.getElementsByTagName('th')[0])) {
                    continue;
                }

                Table.id = 'collapsibleTable' + tblIdx;

                btn = document.createElement('span');

                btn.style.cssText =
                    'float:right;font-weight:normal;font-size:smaller';

                a = document.createElement('a');

                a.id = 'collapseButton' + tblIdx;

                a.href = '#';

                a.setAttribute('data-table-idx', tblIdx);

                a.addEventListener('click', (function (index) {

                    return function (e) {

                        e.preventDefault();

                        collapseTable(index);

                    };

                })(tblIdx));

                a.style.color = HCell.style.color || 'inherit';

                a.appendChild(
                    document.createTextNode(NavigationBar.hide)
                );

                btn.appendChild(a);

                HCell.insertBefore(btn, HCell.firstChild);

                colTables[tblIdx] = Table;

                tblIdx++;

            }

            for (var j = 0; j < tblIdx; j++) {

                if (
                    (
                        tblIdx > NavigationBar.showDefault &&
                        hasClass(colTables[j], 'autocollapse')
                    ) ||
                    hasClass(colTables[j], 'collapsed')
                ) {

                    collapseTable(j);

                }

            }

        } catch (e) {

            console.warn('Ошибка collapsibleTables:', e);

        }

    }

    function collapseTable(idx) {

        try {

            var Table =
                document.getElementById('collapsibleTable' + idx);

            var btn =
                document.getElementById('collapseButton' + idx);

            if (!Table || !btn) {
                return false;
            }

            var Rows = Table.rows;

            var isShown =
                (
                    btn.firstChild &&
                    btn.firstChild.data === NavigationBar.hide
                );

            if (btn.firstChild) {

                btn.firstChild.data =
                    isShown
                        ? NavigationBar.show
                        : NavigationBar.hide;

            }

            var disp =
                isShown
                    ? 'none'
                    : (Rows[0] ? Rows[0].style.display : '');

            for (var i = 1; i < Rows.length; i++) {

                if (Rows[i]) {

                    Rows[i].style.display = disp;

                }

            }

            return true;

        } catch (e) {

            console.warn('Ошибка collapseTable:', e);

            return false;

        }

    }

    // ============================================================
    // COLLAPSIBLE DIVS
    // ============================================================

    function collapsibleDivs() {

        try {

            var navIdx = 0,
                colNavs = [],
                NavFrame;

            var content =
                document.getElementById('content') || document.body;

            var divs = content.getElementsByTagName('div');

            for (var i = 0; i < divs.length; i++) {

                NavFrame = divs[i];

                if (!hasClass(NavFrame, 'NavFrame')) {
                    continue;
                }

                NavFrame.id = 'NavFrame' + navIdx;

                var a = document.createElement('a');

                a.className = 'NavToggle';

                a.id = 'NavToggle' + navIdx;

                a.href = '#';

                a.setAttribute('data-div-idx', navIdx);

                a.addEventListener('click', (function (index) {

                    return function (e) {

                        e.preventDefault();

                        collapseDiv(index);

                    };

                })(navIdx));

                a.appendChild(
                    document.createTextNode(NavigationBar.hide)
                );

                for (
                    var j = 0;
                    j < NavFrame.childNodes.length;
                    j++
                ) {

                    if (
                        hasClass(
                            NavFrame.childNodes[j],
                            'NavHead'
                        )
                    ) {

                        NavFrame.childNodes[j]
                            .appendChild(a);

                        break;

                    }

                }

                colNavs[navIdx] = NavFrame;

                navIdx++;

            }

            for (var k = 0; k < navIdx; k++) {

                if (
                    (
                        navIdx > NavigationBar.showDefault &&
                        !hasClass(colNavs[k], 'expanded')
                    ) ||
                    hasClass(colNavs[k], 'collapsed')
                ) {

                    collapseDiv(k);

                }

            }

        } catch (e) {

            console.warn('Ошибка collapsibleDivs:', e);

        }

    }

    function collapseDiv(idx) {

        try {

            var div =
                document.getElementById('NavFrame' + idx);

            var btn =
                document.getElementById('NavToggle' + idx);

            if (!div || !btn) {
                return false;
            }

            var isShown =
                (
                    btn.firstChild &&
                    btn.firstChild.data === NavigationBar.hide
                );

            if (btn.firstChild) {

                btn.firstChild.data =
                    isShown
                        ? NavigationBar.show
                        : NavigationBar.hide;

            }

            var disp =
                isShown
                    ? 'none'
                    : 'block';

            for (
                var child = div.firstChild;
                child !== null;
                child = child.nextSibling
            ) {

                if (
                    hasClass(child, 'NavPic') ||
                    hasClass(child, 'NavContent')
                ) {

                    child.style.display = disp;

                }

            }

            return true;

        } catch (e) {

            console.warn('Ошибка collapseDiv:', e);

            return false;

        }

    }

    // ============================================================
    // MASS ROLLBACK
    // ============================================================

    function initializeMassRollback() {

        if (
            mw.config.get("wgCanonicalSpecialPageName") !==
            "Contributions"
        ) {
            return;
        }

        if ($("#ca-rollbackeverything").length) {
            return;
        }

        if ($("a[href*='action=rollback']").length <= 0) {

            console.log("Rollback ссылки не найдены");

            return;

        }

        console.log("MassRollback загружен");

        function rollbackOne(edit, api, summary) {

            var titleMatch =
                /title=([^&]+)/.exec(edit.href);

            if (!titleMatch) {
                return;
            }

            var pageTitle =
                decodeURIComponent(titleMatch[1]);

            var userName =
                mw.config.get("wgRelevantUserName");

            api.rollback(pageTitle, userName, {
                summary: summary || ''
            })

            .done(function () {

                console.log("Откат:", pageTitle);

                $(edit).after(
                    '<span style="color:green;font-weight:bold;"> [откачено]</span>'
                );

                $(edit).remove();

            })

            .fail(function (code, data) {

                console.error(code, data);

                $(edit).after(
                    '<span style="color:red;font-weight:bold;"> [ошибка]</span>'
                );

            });

        }

        $("ul.mw-contributions-list li").each(function () {

            if ($(this).find("input.revdelIds").length) {
                return;
            }

            var rollbackLink =
                $(this).find("a[href*='action=rollback']");

            if (rollbackLink.length > 0) {

                $(this)
                    .find("a.mw-changeslist-date")
                    .first()
                    .before(
                        "<input type='checkbox' class='revdelIds' style='margin-right:5px;'>"
                    );

            }

        });

        mw.util.addPortletLink(
            "p-tb",
            "#",
            "Rollback all",
            "ca-rollbackeverything",
            "Откатить все правки"
        );

        mw.util.addPortletLink(
            "p-tb",
            "#",
            "Rollback selected",
            "ca-rollbacksome",
            "Откатить выбранные"
        );

        $("#ca-rollbackeverything").click(function (event) {

            event.preventDefault();

            var summary =
                prompt("Комментарий отката:");

            if (summary === null) {
                return;
            }

            var api = new mw.Api();

            $("a[href*='action=rollback']").each(function (i, el) {

                rollbackOne(el, api, summary);

            });

        });

        $("#ca-rollbacksome").click(function (event) {

            event.preventDefault();

            var summary =
                prompt("Комментарий отката:");

            if (summary === null) {
                return;
            }

            var api = new mw.Api();

            $("input.revdelIds:checked")
                .parents("li")
                .find("a[href*='action=rollback']")
                .each(function (i, el) {

                    rollbackOne(el, api, summary);

                });

        });

    }

    // ============================================================
    // INIT
    // ============================================================

    function initializeAll() {

        collapsibleTables();

        collapsibleDivs();

        initializeMassRollback();

    }

    if (document.readyState === 'loading') {

        document.addEventListener(
            'DOMContentLoaded',
            initializeAll
        );

    } else {

        initializeAll();

    }

})();