打开/关闭菜单
228
885
35
2802
植物大战僵尸杂交版Wiki
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

MediaWiki:Common.js:修订间差异

MediaWiki界面页面
无编辑摘要
无编辑摘要
 
(未显示同一用户的22个中间版本)
第2行: 第2行:


// 自动加载 MediaWiki:Footer 页面内容,并插入到每个页面底部
// 自动加载 MediaWiki:Footer 页面内容,并插入到每个页面底部
$(document).ready(function() {
$(document).ready(function() {
     const namespace = mw.config.get('wgNamespaceNumber');
     const namespace = mw.config.get('wgNamespaceNumber');
第20行: 第19行:
         });
         });
});
});
// 用户颜色、图标、状态、师徒、植物/僵尸筛选 主逻辑
$(function () {
$(function () {
     // 动态注入彩虹用户样式(避免 Common.css 校验警告,含 5 颗星)
     // ==================== 动态注入彩虹样式 ====================
var rainbowStyle = document.createElement('style');
    var rainbowStyle = document.createElement('style');
rainbowStyle.textContent =
    rainbowStyle.textContent =
    '.rainbow-user {' +
        '.rainbow-user {' +
        'font-weight: bold;' +
            'font-weight: bold;' +
        'background: repeating-linear-gradient(90deg, red 0px, orange 10px, yellow 20px, green 30px, blue 40px, indigo 50px, violet 60px);' +
            'background: repeating-linear-gradient(90deg, red 0px, orange 10px, yellow 20px, green 30px, blue 40px, indigo 50px, violet 60px);' +
        '-webkit-background-clip: text;' +
            '-webkit-background-clip: text;' +
        '-webkit-text-fill-color: transparent;' +
            '-webkit-text-fill-color: transparent;' +
        'background-clip: text;' +
            'background-clip: text;' +
         'color: #FFD700;' + /* 降级金色 */
         '}';
     '}' +
    document.head.appendChild(rainbowStyle);
     '.rainbow-user::after {' +
 
         'content: "⭐⭐⭐⭐⭐";' +
    // ==================== 配置区 ====================
         'margin-left: 2px;' +
    var mentorList = ['愤怒的郎朗', 'Yuyaabc', '虚位以待', '知更鸟头号粉丝', '凌玥'];  // 导师名单
         'font-weight: normal;' +
    var newbieEditThreshold = 25;                  // 新手编辑数 ≤ 25
         '-webkit-text-fill-color: initial;' +
 
         'color: #FFD700;' +
    // ==================== 辅助函数 ====================
    '}';
     function getUserName(link) {
document.head.appendChild(rainbowStyle);
        var $span = $(link).find('span').first();
        if ($span.length) return $span.text().trim();
        return $(link).text().trim();
    }
 
     function isUserLink(link) {
        return $(link).is('a.mw-userlink') || $(link).find('span').length > 0;
    }
 
    // 计算等级图标字符串
    function getLevelIcons(editcount) {
        var totalStars = Math.floor(editcount / 100);
        if (totalStars === 0) return '';
 
        var crowns = Math.floor(totalStars / 125);      // 5太阳 = 125星 = 1王冠
        var remaining = totalStars % 125;
        var suns = Math.floor(remaining / 25);          // 5月亮 = 25星
        remaining %= 25;
        var moons = Math.floor(remaining / 5);          // 5星 = 1月亮
        var stars = remaining % 5;
 
         var icons = '';
        if (crowns > 0) icons += '👑'.repeat(crowns);
        if (suns > 0)  icons += '☀️'.repeat(suns);
         if (moons > 0)  icons += '🌙'.repeat(moons);
        if (stars > 0)  icons += '⭐'.repeat(stars);
        return icons;
    }
 
    function updateLevelIcons($link, editcount) {
        var iconStr = getLevelIcons(editcount);
         var $iconSpan = $link.next('.user-level-icons');
        if (iconStr === '') {
            $iconSpan.remove();
            return;
        }
         if ($iconSpan.length === 0) {
            $iconSpan = $('<span class="user-level-icons"></span>');
            $link.after($iconSpan);
        }
        $iconSpan.text(iconStr);
    }
 
    // ==================== 核心:颜色 + 图标 + 状态 + 师徒 ====================
    function colorizeAndStatus($links) {
        if ($links.length === 0) return;
 
         var $fresh = $links.filter(function () {
            return !$(this).data('user-status-processed');
        });
        if ($fresh.length === 0) return;


    if (mw.config.get('wgAction') !== 'view') return;
        var users = [];
        $fresh.each(function () {
            var name = getUserName(this);
            if (name && users.indexOf(name) === -1) users.push(name);
        });
        if (users.length === 0) return;


    var $userlinks = $('a.mw-userlink');
        $fresh.each(function () {
    if ($userlinks.length === 0) return;
            $(this).data('user-status-processed', true);
        });


    var users = [];
        // 批量查询编辑数
    $userlinks.each(function () {
        var batchSize = 50;
         var name = $(this).text().trim();
         var batches = [];
         if (name && users.indexOf(name) === -1) {
         for (var i = 0; i < users.length; i += batchSize) {
             users.push(name);
             batches.push(users.slice(i, i + batchSize));
         }
         }
    });
    if (users.length === 0) return;


    var batchSize = 50;
        var processBatch = function (batch) {
    var batches = [];
            var api = new mw.Api();
     for (var i = 0; i < users.length; i += batchSize) {
            return api.get({
         batches.push(users.slice(i, i + batchSize));
                action: 'query',
                list: 'users',
                ususers: batch.join('|'),
                usprop: 'editcount'
            }).then(function (data) {
                var classMap = {};
                var editCountMap = {};
                if (data.query && data.query.users) {
                    data.query.users.forEach(function (u) {
                        var ec = u.editcount || 0;
                        editCountMap[u.name] = ec;
                        if (ec >= 5000) classMap[u.name] = 'rainbow-user';
                        else if (ec >= 2500) classMap[u.name] = 'gold-user';
                        else if (ec >= 1000) classMap[u.name] = 'platinum-user';
                        else if (ec >= 500) classMap[u.name] = 'silver-user';
                        else if (ec >= 1) classMap[u.name] = 'bronze-user';
                    });
                }
                $fresh.each(function () {
                    var $this = $(this);
                    var name = getUserName(this);
                    var cls = classMap[name];
                    if (cls) {
                        $this.removeClass('bronze-user silver-user platinum-user gold-user rainbow-user');
                        $this.addClass(cls);
                    }
                    var ec = editCountMap[name];
                    if (typeof ec !== 'undefined') {
                        updateLevelIcons($this, ec);
                    }
                });
            });
        };
 
        var colorPromise = $.Deferred().resolve();
        batches.forEach(function (batch) {
            colorPromise = colorPromise.then(function () {
                return processBatch(batch);
            });
        });
 
        // 非卡片区域:状态圆点 + 师徒标签
        $fresh.each(function () {
            if (isUserLink(this)) {
                var isInsideCard = $(this).closest('.citizen-menu_card-content, .citizen-userMenu').length > 0;
                if (!isInsideCard) {
                    var username = getUserName(this);
                    addStatusDot($(this), username);
                    addMentorTag($(this), username);
                }
            }
        });
     }
 
    // ==================== 状态圆点 ====================
    function addStatusDot($link, username) {
        if ($link.data('status-dot-added')) return;
        $link.data('status-dot-added', true);
 
        var $dot = $('<span class="user-status-dot status-offline" title="离线"></span>');
        $link.after($dot);
 
        var cacheKey = 'mw_user_status_' + mw.config.get('wgDBname') + '_' + username;
        var cached = localStorage.getItem(cacheKey);
        var now = Date.now();
        if (cached) {
            try {
                var data = JSON.parse(cached);
                if (now - data.timestamp < 5 * 60 * 1000) {
                    updateDotStyle($dot, data.lastEditTime);
                    return;
                }
            } catch (e) {}
        }
 
        var api = new mw.Api();
        api.get({
            action: 'query',
            list: 'usercontribs',
            ucuser: username,
            uclimit: 1,
            ucprop: 'timestamp'
        }).then(function (data) {
            var lastEditTime = null;
            if (data.query && data.query.usercontribs && data.query.usercontribs.length > 0) {
                lastEditTime = data.query.usercontribs[0].timestamp;
            }
            localStorage.setItem(cacheKey, JSON.stringify({
                lastEditTime: lastEditTime,
                timestamp: now
            }));
            updateDotStyle($dot, lastEditTime);
        }).fail(function () {});
    }
 
    function updateDotStyle($dot, lastEditTime) {
        if (!lastEditTime) {
            $dot.attr('title', '离线');
            $dot.removeClass('status-online status-away').addClass('status-offline');
            return;
        }
        var last = new Date(lastEditTime).getTime();
        var diffMinutes = (Date.now() - last) / 60000;
        if (diffMinutes < 15) {
            $dot.attr('title', '在线(15分钟内活跃)');
            $dot.removeClass('status-offline status-away').addClass('status-online');
         } else if (diffMinutes < 60) {
            $dot.attr('title', '近期活跃(1小时内)');
            $dot.removeClass('status-offline status-online').addClass('status-away');
        } else {
            $dot.attr('title', '离线');
            $dot.removeClass('status-online status-away').addClass('status-offline');
        }
     }
     }


     var processBatch = function (batch) {
     // ==================== 师徒标签(已修复 $tag 重复声明) ====================
    function addMentorTag($link, username) {
        if ($link.data('mentor-tag-added')) return;
        if ($link.next('.user-tag').length) return;
 
        var $tag;  // 统一声明一次
 
        if (mentorList.indexOf(username) !== -1) {
            $link.data('mentor-tag-added', true);
            $tag = $('<span class="user-tag user-tag-mentor">导师</span>');
            $link.after($tag);
            return;
        }
 
        if ($link.data('newbie-tag-checked')) return;
        $link.data('newbie-tag-checked', true);
 
        var cacheKey = 'mw_newbie_check_' + mw.config.get('wgDBname') + '_' + username;
        var cached = localStorage.getItem(cacheKey);
        var now = Date.now();
        if (cached) {
            try {
                var data = JSON.parse(cached);
                if (now - data.timestamp < 60 * 60 * 1000) {
                    if (data.editcount <= newbieEditThreshold) {
                        $tag = $('<span class="user-tag user-tag-newbie">新手</span>');
                        $link.after($tag);
                    }
                    return;
                }
            } catch (e) {}
        }
 
         var api = new mw.Api();
         var api = new mw.Api();
         return api.get({
         api.get({
             action: 'query',
             action: 'query',
             list: 'users',
             list: 'users',
             ususers: batch.join('|'),
             ususers: username,
             usprop: 'editcount'
             usprop: 'editcount'
         }).then(function (data) {
         }).then(function (data) {
             var classMap = {};
             var editcount = 0;
             if (data.query && data.query.users) {
             if (data.query && data.query.users && data.query.users.length > 0) {
                data.query.users.forEach(function (u) {
                editcount = data.query.users[0].editcount || 0;
                    var ec = u.editcount || 0;
            }
                    if (ec >= 5000) {
            localStorage.setItem(cacheKey, JSON.stringify({
                        classMap[u.name] = 'rainbow-user';
                editcount: editcount,
                    } else if (ec >= 2000) {
                timestamp: now
                        classMap[u.name] = 'gold-user';
            }));
                    } else if (ec >= 1000) {
            if (editcount <= newbieEditThreshold) {
                        classMap[u.name] = 'platinum-user';
                if ($link.next('.user-tag-newbie').length === 0) {
                    } else if (ec >= 500) {
                    $tag = $('<span class="user-tag user-tag-newbie">新手</span>');
                        classMap[u.name] = 'silver-user';
                     $link.after($tag);
                     } else if (ec >= 1) {
                 }
                        classMap[u.name] = 'bronze-user';
                    }
                 });
             }
             }
             $userlinks.each(function () {
        }).fail(function () {});
                 var $this = $(this);
    }
                 var userName = $this.text().trim();
 
                 var cls = classMap[userName];
    // ==================== 用户链接处理器启动 ====================
                 if (cls) {
    var linkSelector = 'a.mw-userlink, .citizen-menu_card-content a, .citizen-userMenu a';
                     $this.removeClass('bronze-user silver-user platinum-user gold-user rainbow-user');
    colorizeAndStatus($(linkSelector));
                     $this.addClass(cls);
 
    var observer = new MutationObserver(function () {
        colorizeAndStatus($(linkSelector));
    });
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
 
    setInterval(function () {
        colorizeAndStatus($(linkSelector));
    }, 3000);
 
    // ==================== 植物卡片筛选 ====================
    if ($('#pf-name').length) {
        var $cards = $('.pvzhe-card');
        var $name = $('#pf-name');
        var $sunMax = $('#pf-sun-max');
        var $cdMax = $('#pf-cd-max');
        var $type = $('#pf-type');
        var $reset = $('#pf-reset');
 
        function filterPlants() {
            var name = $name.val().toLowerCase();
            var sunRaw = $sunMax.val().trim();
            var cdRaw = $cdMax.val().trim();
            var sunTarget = sunRaw !== '' ? parseFloat(sunRaw) : null;
            var cdTarget = cdRaw !== '' ? parseFloat(cdRaw) : null;
            var type = $type.val();
 
             $cards.each(function () {
                 var $card = $(this);
                 var n = $card.find('.pvzhe-card-name').text().toLowerCase();
                var sun = parseFloat($card.data('sun')) || 0;
                var cd = parseFloat($card.data('cooldown')) || 0;
                var t = ($card.data('type') || '').toString();
 
                 var show = true;
                if (name && n.indexOf(name) === -1) show = false;
                if (sunTarget !== null && sun !== sunTarget) show = false;
                if (cdTarget !== null && cd !== cdTarget) show = false;
                 if (type) {
                     var cardTypes = t.split(',').map(function (s) { return s.trim(); });
                     if (cardTypes.indexOf(type) === -1) show = false;
                 }
                 }
                $card.toggleClass('hidden-card', !show);
             });
             });
        }
        $name.on('input', filterPlants);
        $sunMax.on('input keyup', filterPlants);
        $cdMax.on('input keyup', filterPlants);
        $type.on('change', filterPlants);
        $reset.on('click', function () {
            $name.val('');
            $sunMax.val('');
            $cdMax.val('');
            $type.val('');
            filterPlants();
         });
         });
     };
     }
 
    // ==================== 僵尸卡片筛选(分开的防具与血量) ====================
    // 辅助:提取防具数组(无“+”返回空数组)
    function getArmorArray(healthStr) {
        if (!healthStr || healthStr.indexOf('+') === -1) return [];
        var armorPart = healthStr.split('+')[0].trim();
        return armorPart.split(',').map(function(s) { return s.trim(); });
    }
 
    // 辅助:提取本体血量(最后一个数字)
    function extractHealth(healthStr) {
        var matches = healthStr.match(/\d+/g);
        if (matches && matches.length > 0) {
            return parseFloat(matches[matches.length - 1]) || 0;
        }
        return 0;
    }
 
    if ($('#zf-name').length) {
        var $zcards = $('.pvzhe-card');
        var $zname = $('#zf-name');
        var $healthMin = $('#zf-health-min');
        var $armor = $('#zf-armor');
        var $speed = $('#zf-speed');
        var $ztype = $('#zf-type');
        var $zreset = $('#zf-reset');
 
        function filterZombies() {
            var name = $zname.val().toLowerCase();
            var healthRaw = $healthMin.val().trim();
            var healthTarget = healthRaw !== '' ? parseFloat(healthRaw) : null;
            var armor = $armor.val();
            var speed = $speed.val();
            var type = $ztype.val();


    var promise = $.Deferred().resolve();
            $zcards.each(function () {
    batches.forEach(function (batch) {
                var $card = $(this);
         promise = promise.then(function () {
                var n = $card.find('.pvzhe-card-name').text().toLowerCase();
             return processBatch(batch);
                var t = ($card.data('type') || '').toString();
                var s = ($card.data('speed') || '').toString();
                var healthStr = ($card.data('health') || '').toString();
                var health = extractHealth(healthStr);
                var armors = getArmorArray(healthStr);
 
                var show = true;
                if (name && n.indexOf(name) === -1) show = false;
                if (healthTarget !== null && health !== healthTarget) show = false;
 
                // 防具筛选
                if (armor === 'none') {
                    // 只显示无防具的僵尸(armors 为空)
                    if (armors.length > 0) show = false;
                } else if (armor) {
                    // 选择了具体防具,数组中必须包含该防具
                    if (armors.indexOf(armor) === -1) show = false;
                }
 
                if (speed) {
                    var cardSpeeds = s.split(',').map(function (v) { return v.trim(); });
                    if (cardSpeeds.indexOf(speed) === -1) show = false;
                }
                if (type) {
                    var cardTypes = t.split(',').map(function (v) { return v.trim(); });
                    if (cardTypes.indexOf(type) === -1) show = false;
                }
                $card.toggleClass('hidden-card', !show);
            });
        }
 
        $zname.on('input', filterZombies);
        $healthMin.on('input keyup', filterZombies);
         $armor.on('change', filterZombies);
        $speed.on('change', filterZombies);
        $ztype.on('change', filterZombies);
        $zreset.on('click', function () {
             $zname.val('');
            $healthMin.val('');
            $armor.val('');
            $speed.val('');
            $ztype.val('');
            filterZombies();
         });
         });
     });
     }
});
 
}); // 结束主 $(function () { ... })

2026年6月1日 (一) 10:20的最新版本

/* 这里的任何JavaScript将为所有用户在每次页面加载时加载。 */

// 自动加载 MediaWiki:Footer 页面内容,并插入到每个页面底部
$(document).ready(function() {
    const namespace = mw.config.get('wgNamespaceNumber');
    const action = mw.config.get('wgAction');
    
    if (namespace !== 0 || action !== 'view') {
        return;
    }

    fetch("/api.php?action=parse&page=MediaWiki:Footer&format=json")
        .then(res => res.json())
        .then(data => {
            if (data.parse && data.parse.text) {
                const html = data.parse.text['*'];
                $('#mw-content-text').append('<div class="global-footer">' + html + '</div>');
            }
        });
});

// 用户颜色、图标、状态、师徒、植物/僵尸筛选 主逻辑
$(function () {
    // ==================== 动态注入彩虹样式 ====================
    var rainbowStyle = document.createElement('style');
    rainbowStyle.textContent =
        '.rainbow-user {' +
            'font-weight: bold;' +
            'background: repeating-linear-gradient(90deg, red 0px, orange 10px, yellow 20px, green 30px, blue 40px, indigo 50px, violet 60px);' +
            '-webkit-background-clip: text;' +
            '-webkit-text-fill-color: transparent;' +
            'background-clip: text;' +
        '}';
    document.head.appendChild(rainbowStyle);

    // ==================== 配置区 ====================
    var mentorList = ['愤怒的郎朗', 'Yuyaabc', '虚位以待', '知更鸟头号粉丝', '凌玥'];   // 导师名单
    var newbieEditThreshold = 25;                  // 新手编辑数 ≤ 25

    // ==================== 辅助函数 ====================
    function getUserName(link) {
        var $span = $(link).find('span').first();
        if ($span.length) return $span.text().trim();
        return $(link).text().trim();
    }

    function isUserLink(link) {
        return $(link).is('a.mw-userlink') || $(link).find('span').length > 0;
    }

    // 计算等级图标字符串
    function getLevelIcons(editcount) {
        var totalStars = Math.floor(editcount / 100);
        if (totalStars === 0) return '';

        var crowns = Math.floor(totalStars / 125);      // 5太阳 = 125星 = 1王冠
        var remaining = totalStars % 125;
        var suns = Math.floor(remaining / 25);          // 5月亮 = 25星
        remaining %= 25;
        var moons = Math.floor(remaining / 5);          // 5星 = 1月亮
        var stars = remaining % 5;

        var icons = '';
        if (crowns > 0) icons += '👑'.repeat(crowns);
        if (suns > 0)   icons += '☀️'.repeat(suns);
        if (moons > 0)  icons += '🌙'.repeat(moons);
        if (stars > 0)  icons += '⭐'.repeat(stars);
        return icons;
    }

    function updateLevelIcons($link, editcount) {
        var iconStr = getLevelIcons(editcount);
        var $iconSpan = $link.next('.user-level-icons');
        if (iconStr === '') {
            $iconSpan.remove();
            return;
        }
        if ($iconSpan.length === 0) {
            $iconSpan = $('<span class="user-level-icons"></span>');
            $link.after($iconSpan);
        }
        $iconSpan.text(iconStr);
    }

    // ==================== 核心:颜色 + 图标 + 状态 + 师徒 ====================
    function colorizeAndStatus($links) {
        if ($links.length === 0) return;

        var $fresh = $links.filter(function () {
            return !$(this).data('user-status-processed');
        });
        if ($fresh.length === 0) return;

        var users = [];
        $fresh.each(function () {
            var name = getUserName(this);
            if (name && users.indexOf(name) === -1) users.push(name);
        });
        if (users.length === 0) return;

        $fresh.each(function () {
            $(this).data('user-status-processed', true);
        });

        // 批量查询编辑数
        var batchSize = 50;
        var batches = [];
        for (var i = 0; i < users.length; i += batchSize) {
            batches.push(users.slice(i, i + batchSize));
        }

        var processBatch = function (batch) {
            var api = new mw.Api();
            return api.get({
                action: 'query',
                list: 'users',
                ususers: batch.join('|'),
                usprop: 'editcount'
            }).then(function (data) {
                var classMap = {};
                var editCountMap = {};
                if (data.query && data.query.users) {
                    data.query.users.forEach(function (u) {
                        var ec = u.editcount || 0;
                        editCountMap[u.name] = ec;
                        if (ec >= 5000) classMap[u.name] = 'rainbow-user';
                        else if (ec >= 2500) classMap[u.name] = 'gold-user';
                        else if (ec >= 1000) classMap[u.name] = 'platinum-user';
                        else if (ec >= 500) classMap[u.name] = 'silver-user';
                        else if (ec >= 1) classMap[u.name] = 'bronze-user';
                    });
                }
                $fresh.each(function () {
                    var $this = $(this);
                    var name = getUserName(this);
                    var cls = classMap[name];
                    if (cls) {
                        $this.removeClass('bronze-user silver-user platinum-user gold-user rainbow-user');
                        $this.addClass(cls);
                    }
                    var ec = editCountMap[name];
                    if (typeof ec !== 'undefined') {
                        updateLevelIcons($this, ec);
                    }
                });
            });
        };

        var colorPromise = $.Deferred().resolve();
        batches.forEach(function (batch) {
            colorPromise = colorPromise.then(function () {
                return processBatch(batch);
            });
        });

        // 非卡片区域:状态圆点 + 师徒标签
        $fresh.each(function () {
            if (isUserLink(this)) {
                var isInsideCard = $(this).closest('.citizen-menu_card-content, .citizen-userMenu').length > 0;
                if (!isInsideCard) {
                    var username = getUserName(this);
                    addStatusDot($(this), username);
                    addMentorTag($(this), username);
                }
            }
        });
    }

    // ==================== 状态圆点 ====================
    function addStatusDot($link, username) {
        if ($link.data('status-dot-added')) return;
        $link.data('status-dot-added', true);

        var $dot = $('<span class="user-status-dot status-offline" title="离线"></span>');
        $link.after($dot);

        var cacheKey = 'mw_user_status_' + mw.config.get('wgDBname') + '_' + username;
        var cached = localStorage.getItem(cacheKey);
        var now = Date.now();
        if (cached) {
            try {
                var data = JSON.parse(cached);
                if (now - data.timestamp < 5 * 60 * 1000) {
                    updateDotStyle($dot, data.lastEditTime);
                    return;
                }
            } catch (e) {}
        }

        var api = new mw.Api();
        api.get({
            action: 'query',
            list: 'usercontribs',
            ucuser: username,
            uclimit: 1,
            ucprop: 'timestamp'
        }).then(function (data) {
            var lastEditTime = null;
            if (data.query && data.query.usercontribs && data.query.usercontribs.length > 0) {
                lastEditTime = data.query.usercontribs[0].timestamp;
            }
            localStorage.setItem(cacheKey, JSON.stringify({
                lastEditTime: lastEditTime,
                timestamp: now
            }));
            updateDotStyle($dot, lastEditTime);
        }).fail(function () {});
    }

    function updateDotStyle($dot, lastEditTime) {
        if (!lastEditTime) {
            $dot.attr('title', '离线');
            $dot.removeClass('status-online status-away').addClass('status-offline');
            return;
        }
        var last = new Date(lastEditTime).getTime();
        var diffMinutes = (Date.now() - last) / 60000;
        if (diffMinutes < 15) {
            $dot.attr('title', '在线(15分钟内活跃)');
            $dot.removeClass('status-offline status-away').addClass('status-online');
        } else if (diffMinutes < 60) {
            $dot.attr('title', '近期活跃(1小时内)');
            $dot.removeClass('status-offline status-online').addClass('status-away');
        } else {
            $dot.attr('title', '离线');
            $dot.removeClass('status-online status-away').addClass('status-offline');
        }
    }

    // ==================== 师徒标签(已修复 $tag 重复声明) ====================
    function addMentorTag($link, username) {
        if ($link.data('mentor-tag-added')) return;
        if ($link.next('.user-tag').length) return;

        var $tag;   // 统一声明一次

        if (mentorList.indexOf(username) !== -1) {
            $link.data('mentor-tag-added', true);
            $tag = $('<span class="user-tag user-tag-mentor">导师</span>');
            $link.after($tag);
            return;
        }

        if ($link.data('newbie-tag-checked')) return;
        $link.data('newbie-tag-checked', true);

        var cacheKey = 'mw_newbie_check_' + mw.config.get('wgDBname') + '_' + username;
        var cached = localStorage.getItem(cacheKey);
        var now = Date.now();
        if (cached) {
            try {
                var data = JSON.parse(cached);
                if (now - data.timestamp < 60 * 60 * 1000) {
                    if (data.editcount <= newbieEditThreshold) {
                        $tag = $('<span class="user-tag user-tag-newbie">新手</span>');
                        $link.after($tag);
                    }
                    return;
                }
            } catch (e) {}
        }

        var api = new mw.Api();
        api.get({
            action: 'query',
            list: 'users',
            ususers: username,
            usprop: 'editcount'
        }).then(function (data) {
            var editcount = 0;
            if (data.query && data.query.users && data.query.users.length > 0) {
                editcount = data.query.users[0].editcount || 0;
            }
            localStorage.setItem(cacheKey, JSON.stringify({
                editcount: editcount,
                timestamp: now
            }));
            if (editcount <= newbieEditThreshold) {
                if ($link.next('.user-tag-newbie').length === 0) {
                    $tag = $('<span class="user-tag user-tag-newbie">新手</span>');
                    $link.after($tag);
                }
            }
        }).fail(function () {});
    }

    // ==================== 用户链接处理器启动 ====================
    var linkSelector = 'a.mw-userlink, .citizen-menu_card-content a, .citizen-userMenu a';
    colorizeAndStatus($(linkSelector));

    var observer = new MutationObserver(function () {
        colorizeAndStatus($(linkSelector));
    });
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    setInterval(function () {
        colorizeAndStatus($(linkSelector));
    }, 3000);

    // ==================== 植物卡片筛选 ====================
    if ($('#pf-name').length) {
        var $cards = $('.pvzhe-card');
        var $name = $('#pf-name');
        var $sunMax = $('#pf-sun-max');
        var $cdMax = $('#pf-cd-max');
        var $type = $('#pf-type');
        var $reset = $('#pf-reset');

        function filterPlants() {
            var name = $name.val().toLowerCase();
            var sunRaw = $sunMax.val().trim();
            var cdRaw = $cdMax.val().trim();
            var sunTarget = sunRaw !== '' ? parseFloat(sunRaw) : null;
            var cdTarget = cdRaw !== '' ? parseFloat(cdRaw) : null;
            var type = $type.val();

            $cards.each(function () {
                var $card = $(this);
                var n = $card.find('.pvzhe-card-name').text().toLowerCase();
                var sun = parseFloat($card.data('sun')) || 0;
                var cd = parseFloat($card.data('cooldown')) || 0;
                var t = ($card.data('type') || '').toString();

                var show = true;
                if (name && n.indexOf(name) === -1) show = false;
                if (sunTarget !== null && sun !== sunTarget) show = false;
                if (cdTarget !== null && cd !== cdTarget) show = false;
                if (type) {
                    var cardTypes = t.split(',').map(function (s) { return s.trim(); });
                    if (cardTypes.indexOf(type) === -1) show = false;
                }
                $card.toggleClass('hidden-card', !show);
            });
        }

        $name.on('input', filterPlants);
        $sunMax.on('input keyup', filterPlants);
        $cdMax.on('input keyup', filterPlants);
        $type.on('change', filterPlants);
        $reset.on('click', function () {
            $name.val('');
            $sunMax.val('');
            $cdMax.val('');
            $type.val('');
            filterPlants();
        });
    }

    // ==================== 僵尸卡片筛选(分开的防具与血量) ====================
    // 辅助:提取防具数组(无“+”返回空数组)
    function getArmorArray(healthStr) {
        if (!healthStr || healthStr.indexOf('+') === -1) return [];
        var armorPart = healthStr.split('+')[0].trim();
        return armorPart.split(',').map(function(s) { return s.trim(); });
    }

    // 辅助:提取本体血量(最后一个数字)
    function extractHealth(healthStr) {
        var matches = healthStr.match(/\d+/g);
        if (matches && matches.length > 0) {
            return parseFloat(matches[matches.length - 1]) || 0;
        }
        return 0;
    }

    if ($('#zf-name').length) {
        var $zcards = $('.pvzhe-card');
        var $zname = $('#zf-name');
        var $healthMin = $('#zf-health-min');
        var $armor = $('#zf-armor');
        var $speed = $('#zf-speed');
        var $ztype = $('#zf-type');
        var $zreset = $('#zf-reset');

        function filterZombies() {
            var name = $zname.val().toLowerCase();
            var healthRaw = $healthMin.val().trim();
            var healthTarget = healthRaw !== '' ? parseFloat(healthRaw) : null;
            var armor = $armor.val();
            var speed = $speed.val();
            var type = $ztype.val();

            $zcards.each(function () {
                var $card = $(this);
                var n = $card.find('.pvzhe-card-name').text().toLowerCase();
                var t = ($card.data('type') || '').toString();
                var s = ($card.data('speed') || '').toString();
                var healthStr = ($card.data('health') || '').toString();
                var health = extractHealth(healthStr);
                var armors = getArmorArray(healthStr);

                var show = true;
                if (name && n.indexOf(name) === -1) show = false;
                if (healthTarget !== null && health !== healthTarget) show = false;

                // 防具筛选
                if (armor === 'none') {
                    // 只显示无防具的僵尸(armors 为空)
                    if (armors.length > 0) show = false;
                } else if (armor) {
                    // 选择了具体防具,数组中必须包含该防具
                    if (armors.indexOf(armor) === -1) show = false;
                }

                if (speed) {
                    var cardSpeeds = s.split(',').map(function (v) { return v.trim(); });
                    if (cardSpeeds.indexOf(speed) === -1) show = false;
                }
                if (type) {
                    var cardTypes = t.split(',').map(function (v) { return v.trim(); });
                    if (cardTypes.indexOf(type) === -1) show = false;
                }
                $card.toggleClass('hidden-card', !show);
            });
        }

        $zname.on('input', filterZombies);
        $healthMin.on('input keyup', filterZombies);
        $armor.on('change', filterZombies);
        $speed.on('change', filterZombies);
        $ztype.on('change', filterZombies);
        $zreset.on('click', function () {
            $zname.val('');
            $healthMin.val('');
            $armor.val('');
            $speed.val('');
            $ztype.val('');
            filterZombies();
        });
    }

}); // 结束主 $(function () { ... })