打开/关闭菜单
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

MediaWiki:Gadget-ShowContributors.js

MediaWiki界面页面
OctoberSama留言 | 贡献2025年6月17日 (二) 17:00的版本

注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5

package.lua第80行Lua错误:module 'Module:TemplateParameters' not found

(function () {
    "use strict";

    function insertStyles() {
        mw.loader.addStyleTag([
            "#show-contributor-button { float: right; margin-left: .5em; margin-right: 0; }",
            "#show-contributor-header { background: #fff; border-bottom: 1px solid #aaa; font-weight: 600; padding: .3em; position: sticky; text-align: center; top: 0; }",
            "#show-contributor-headline { font-size: 1.3em; }",
            "#show-contributor-close { border-radius: 50%; cursor: pointer; position: absolute; right: 5px; top: 5px; }",
            "#show-contributor-close:hover { background-color: #eee; }",
            "#show-contributor-table { margin: 0; width: 100%; }",
            "#show-contributor-table .user-avatar { border-radius: 50%; height: 20px; width: 20px; }",
            ".skin-citizen #show-contributor-button { float: none; margin: 0 0.5em 0 0; display: inline-flex; align-items: center; vertical-align: middle; }",
            "@media (min-width: 1120px) { .skin-citizen .citizen-page-header-inner { display: flex; justify-content: space-between; align-items: center; } .skin-citizen .citizen-page-heading { flex-grow: 1; } }",
            "@media (max-width: 1119px) { .skin-citizen #show-contributor-button { margin: 0.5em 0; display: block; } }"
        ].join("\n"));
    }

    function isValidPage() {
        var ns = mw.config.get("wgNamespaceNumber");
        var action = mw.config.get("wgAction");
        return [0, 2, 4, 10, 12, 14, 828, 274].indexOf(ns) !== -1 &&
            mw.config.get("wgArticleId") !== 0 &&
            (action === "view" || action === "history");
    }

    function ShowContributorDialog(config) {
        ShowContributorDialog.super.call(this, config);
    }

    OO.inheritClass(ShowContributorDialog, OO.ui.Dialog);

    ShowContributorDialog.static.name = "ShowContributor";
    ShowContributorDialog.static.tagName = "div";

    ShowContributorDialog.prototype.initialize = function () {
        ShowContributorDialog.super.prototype.initialize.call(this);
        this.got = false;
        this.$table = $('<table id="show-contributor-table" class="wikitable" />');
        this.$tbody = $("<tbody />");
        this.$body.append(
            $('<div id="show-contributor-header" />').append(
                $('<div id="show-contributor-headline">本页贡献统计</div>'),
                new OO.ui.IconWidget({ icon: "close", id: "show-contributor-close" }).$element.on("click", this.close.bind(this))
            ),
            this.$table.append(
                $("<thead><th>用户</th><th>编辑数</th><th>增加字节数</th><th>删减字节数</th></thead>"),
                this.$tbody
            )
        );
    };

    ShowContributorDialog.prototype.getContributors = function () {
        var self = this;
        var api = new mw.Api();
        var users = {};
        var rvcontinue = "";
        var lastSize = 0;
        var params = {
            action: "query",
            format: "json",
            prop: "revisions",
            titles: mw.config.get("wgPageName"),
            rvprop: "user|size",
            rvlimit: "max",
            rvdir: "newer"
        };

        function handleResponse(data) {
            if (data.continue) rvcontinue = data.continue.rvcontinue;
            else rvcontinue = null;
            var revisions = Object.values(data.query.pages)[0].revisions;
            revisions.forEach(function (rev) {
                if (!users[rev.user]) users[rev.user] = [];
                users[rev.user].push(rev.size - lastSize);
                lastSize = rev.size;
            });
        }

        return new Promise(function (resolve) {
            function next() {
                if (rvcontinue !== null) {
                    if (rvcontinue) params.rvcontinue = rvcontinue;
                    api.get(params).done(function (data) {
                        handleResponse(data);
                        next();
                    }).fail(function (err) {
                        mw.notify("获取编辑记录失败:" + err, { type: "error" });
                        rvcontinue = null;
                        resolve(users);
                    });
                } else {
                    resolve(users);
                }
            }
            next();
        });
    };

    ShowContributorDialog.prototype.addRow = function ($tbody, data) {
        var $row = $("<tr />").append(
            $("<td />").append(
                $('<a href="' + mw.config.get("wgArticlePath").replace("$1", "User:" + data.user) + '" />').text(data.user)
            ),
            $("<td />").text(data.count),
            $("<td />").text(data.add),
            $("<td />").text(data.remove)
        );
        $tbody.append($row);
    };

    ShowContributorDialog.prototype.showContributors = function (users) {
        var self = this;
        this.$tbody.empty();
        Object.keys(users).forEach(function (user) {
            var edits = users[user];
            var add = edits.reduce(function (sum, n) { return n > 0 ? sum + n : sum; }, 0);
            var remove = edits.reduce(function (sum, n) { return n < 0 ? sum + n : sum; }, 0);
            self.addRow(self.$tbody, {
                user: user,
                count: edits.length,
                add: add,
                remove: remove
            });
        });
        this.got = true;
    };

    function main() {
        if (!isValidPage()) return;
        insertStyles();

        mw.loader.using([
            "mediawiki.api",
            "mediawiki.notification",
            "oojs-ui",
            "oojs-ui.styles.icons-interactions",
            "jquery.tablesorter"
        ], function () {
            var winManager = new OO.ui.WindowManager({ id: "show-contributor" });
            $(document.body).append(winManager.$element);
            var dialog = new ShowContributorDialog({ size: "large" });
            winManager.addWindows([dialog]);

            var button = new OO.ui.ButtonWidget({
                label: "本页贡献者",
                icon: "search",
                flags: "progressive",
                id: "show-contributor-button"
            });

            var insertButton = function () {
                $("#show-contributor-button").detach();
                var skin = mw.config.get("skin");
                if (skin === "citizen") {
                    if (window.innerWidth >= 1120) {
                        $(".page-actions").prepend(button.$element);
                    } else {
                        $(".firstHeading-container").append(button.$element);
                    }
                } else if (skin === "vector-2022") {
                    $("header.mw-body-header.vector-page-titlebar").append(button.$element);
                } else {
                    $("#bodyContent").prepend(button.$element);
                }
            };

            insertButton();

            var isWide = window.innerWidth >= 1120;
            $(window).on("resize", function () {
                clearTimeout(window.__showContributorResizeTimer);
                window.__showContributorResizeTimer = setTimeout(function () {
                    var nowWide = window.innerWidth >= 1120;
                    if (nowWide !== isWide) {
                        isWide = nowWide;
                        insertButton();
                    }
                }, 200);
            });

            button.on("click", function () {
                if (!dialog.got) {
                    button.setLabel("正在查询");
                    dialog.getContributors().then(function (users) {
                        dialog.showContributors(users);
                        dialog.$table.tablesorter();
                        button.setLabel("本页贡献者");
                        winManager.openWindow(dialog);
                    });
                } else {
                    winManager.openWindow(dialog);
                }
            });
        });
    }

    $(main);
})();