User:SaoMikoto/js/Usergroup.js

From Test 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)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// <nowiki>

(function() {
    'use strict';

    // 用户组配置
    const USER_GROUPS = {
        'sysadmin': { label: '系', color: '#FF4500', name: '系统管理员' },
        'steward': { label: '裁', color: '#9400D3', name: '监管员' },
        'bureaucrat': { label: '行', color: '#008080', name: '行政员' },
        'checkuser': { label: '查', color: '#4B0082', name: '用户查核员' },
        'suppress': { label: '监', color: '#000000', name: '监督员' },
        'sysop': { label: '管', color: '#2E8B57', name: '管理员' },
        'interface-admin': { label: '界', color: '#FF8C00', name: '界面管理员' },
        'interwiki-admin': { label: '域', color: '#4682B4', name: '跨wiki管理员' },
        'electionadmin': { label: '选', color: '#9932CC', name: '选举管理员' },
        'abusefilter-admin': { label: '滤', color: '#DC143C', name: '滥用过滤器管理员' },
        'translateadmin': { label: '译', color: '#008B8B', name: '翻译管理员' },
        'chatmod': { label: '聊', color: '#20B2AA', name: '聊天版主' },
        'patroller': { label: '巡', color: '#3CB371', name: '巡查员' },
        'reviewer': { label: '核', color: '#4169E1', name: '复核员' },
        'autopatrol': { label: '免', color: '#6B8E23', name: '巡查豁免者' },
        'autoreview': { label: '复', color: '#008B8B', name: '自动复核用户' },
        'bot': { label: '机', color: '#696969', name: '机器人' },
        'confirmed': { label: '确', color: '#4682B4', name: '确认用户' }
    };

    const GROUP_ORDER = Object.keys(USER_GROUPS);
    const userGroupCache = new Map();
    const CACHE_DURATION = 5 * 60 * 1000;

    // 获取用户组信息
    function getUserGroups(usernames, callback) {
        if (!usernames || usernames.length === 0) {
            callback({});
            return;
        }

        var uncachedUsers = [];
        var result = {};

        usernames.forEach(function(username) {
            var cached = userGroupCache.get(username);
            if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
                result[username] = cached.groups;
            } else {
                uncachedUsers.push(username);
            }
        });

        if (uncachedUsers.length === 0) {
            callback(result);
            return;
        }

        var api = new mw.Api();
        api.get({
            action: 'query',
            list: 'users',
            ususers: uncachedUsers.join('|'),
            usprop: 'groups',
            formatversion: 2
        }).done(function(response) {
            if (response.query && response.query.users) {
                response.query.users.forEach(function(user) {
                    if (user.groups && !user.missing) {
                        var relevantGroups = user.groups.filter(function(group) {
                            return USER_GROUPS[group];
                        });
                        result[user.name] = relevantGroups;
                        
                        userGroupCache.set(user.name, {
                            groups: relevantGroups,
                            timestamp: Date.now()
                        });
                    }
                });
            }
            callback(result);
        }).fail(function(error) {
            console.warn('获取用户组信息失败:', error);
            callback(result);
        });
    }

    function createGroupIndicator(groups) {
        if (!groups || groups.length === 0) return null;

        var sortedGroups = groups.sort(function(a, b) {
            return GROUP_ORDER.indexOf(a) - GROUP_ORDER.indexOf(b);
        });

        var container = document.createElement('sup');
        container.style.cssText = 
            'font-size: 85%;' +
            'vertical-align: super;' +
            'margin-left: 2px;' +
            'line-height: 1;';

        sortedGroups.forEach(function(group, index) {
            var config = USER_GROUPS[group];
            if (!config) return;

            var span = document.createElement('span');
            span.textContent = config.label;
            span.style.cssText = 
                'color: ' + config.color + ';' +
                'cursor: help;' +
                (index > 0 ? 'margin-left: 1px;' : '');
            span.title = config.name;

            container.appendChild(span);
        });

        return container;
    }

    function processUserLinks() {
        var userLinks = document.querySelectorAll('.mw-userlink, .plainlinks .userlink');
        if (userLinks.length === 0) return;

        var usernames = [];
        var linkMap = new Map();

        userLinks.forEach(function(link) {
            if (link.dataset.groupProcessed) return;

            var username = '';
            
            if (link.classList.contains('mw-userlink')) {
                var href = link.getAttribute('href');
                if (href) {
                    var match = href.match(/User:([^/?#]+)/);
                    if (match) {
                        username = decodeURIComponent(match[1]).replace(/_/g, ' ');
                    }
                }
            } else if (link.classList.contains('userlink')) {
                username = link.textContent.trim();
            }

            if (username && !linkMap.has(username)) {
                usernames.push(username);
                linkMap.set(username, []);
            }
            
            if (username) {
                linkMap.get(username).push(link);
                link.dataset.groupProcessed = 'true';
            }
        });

        if (usernames.length === 0) return;

        // 获取用户组信息
        getUserGroups(usernames, function(userGroups) {
            Object.keys(userGroups).forEach(function(username) {
                var groups = userGroups[username];
                var links = linkMap.get(username);
                if (!links || groups.length === 0) return;

                var indicator = createGroupIndicator(groups);
                if (!indicator) return;

                links.forEach(function(link) {
                    if (link.nextElementSibling && link.nextElementSibling.tagName === 'SUP') {
                        return;
                    }
                    link.parentNode.insertBefore(indicator.cloneNode(true), link.nextSibling);
                });
            });
        });
    }

    // 初始化小工具
    function init() {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', processUserLinks);
        } else {
            processUserLinks();
        }

        const observer = new MutationObserver(function(mutations) {
            let shouldProcess = false;
            mutations.forEach(function(mutation) {
                if (mutation.type === 'childList') {
                    mutation.addedNodes.forEach(function(node) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            if (node.querySelector('.mw-userlink, .plainlinks .userlink')) {
                                shouldProcess = true;
                            }
                        }
                    });
                }
            });
            
            if (shouldProcess) {
                setTimeout(processUserLinks, 100);
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    if (typeof mw !== 'undefined' && mw.Api) {
        init();
    } else {
        if (typeof mw !== 'undefined') {
            mw.loader.using('mediawiki.api').then(init);
        }
    }

})();

// </nowiki>