打开/关闭搜索
搜索
打开/关闭菜单
443
1201
41
4764
植物大战僵尸杂交版Wiki
导航
首页
最近更改
随机页面
MediaWiki帮助
特殊页面
上传文件
打开/关闭外观设置菜单
notifications
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。
user-interface-preferences
个人工具
创建账号
登录
查看“︁MediaWiki:Common.js”︁的源代码
MediaWiki界面页面
查看
阅读
查看源代码
查看历史
associated-pages
系统消息
讨论
更多操作
←
MediaWiki:Common.js
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
此页面为本wiki上的软件提供界面文本,并受到保护以防止滥用。 如欲修改所有wiki的翻译,请访问
translatewiki.net
上的MediaWiki本地化项目。
您无权编辑此JavaScript页面,因为编辑此页面可能会影响所有访问者。
您可以查看和复制此页面的源代码。
/* 这里的任何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; var milestoneThresholds = [10, 50, 100, 500, 1000, 2500, 5000, 10000, 20000, 50000]; // ==================== 辅助函数 ==================== 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); var remaining = totalStars % 125; var suns = Math.floor(remaining / 25); remaining %= 25; var moons = Math.floor(remaining / 5); 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 getTitleByEditcount(ec) { if (ec >= 10000) return '🐉 神话'; if (ec >= 5000) return '👑 传奇'; if (ec >= 2500) return '🏆 大师'; if (ec >= 1000) return '💎 专家'; if (ec >= 500) return '🔥 资深'; if (ec >= 100) return '⭐ 活跃'; if (ec >= 50) return '🌳 入门'; if (ec >= 10) return '🌿 新手'; return '🌱 萌新'; } function addTitleTag($link, editcount) { if ($link.data('title-tag-added')) return; $link.data('title-tag-added', true); var title = getTitleByEditcount(editcount); var $tag = $('<span class="user-title-tag">' + title + '</span>'); $link.after($tag); } // ==================== 核心:颜色 + 图标 + 状态 + 师徒 + 昵称 ==================== 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 isInsideCard = $this.closest('.citizen-menu_card-content, .citizen-userMenu').length > 0; if (!isInsideCard) { addTitleTag($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'); } } // ==================== 师徒标签 ==================== 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 currentUser = mw.config.get('wgUserName'); if (currentUser) { var api = new mw.Api(); api.get({ action: 'query', list: 'users', ususers: currentUser, 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; } var achievedMilestone = null; milestoneThresholds.forEach(function(m) { if (editcount >= m) achievedMilestone = m; }); if (achievedMilestone) { var storageKey = 'mw_milestone_shown_' + currentUser + '_' + achievedMilestone; if (!localStorage.getItem(storageKey)) { localStorage.setItem(storageKey, '1'); var $overlay = $('<div>', { css: { position: 'fixed', top: 0, left: 0, width: '100%', height: '100%', background: 'rgba(0,0,0,0.5)', 'z-index': 99998, display: 'flex', 'align-items': 'center', 'justify-content': 'center' } }); var $box = $('<div>', { css: { background: '#fff', 'border-radius': '12px', padding: '30px 40px', 'text-align': 'center', 'box-shadow': '0 4px 20px rgba(0,0,0,0.3)', 'max-width': '400px', width: '80%', position: 'relative' } }); var $title = $('<h2>', { text: '🎉 恭喜!', css: { 'margin-bottom': '15px', 'font-size': '24px', color: '#333' } }); var $msg = $('<p>', { text: '你已达到 ' + achievedMilestone + ' 次编辑!', css: { 'font-size': '18px', 'margin-bottom': '25px', color: '#555' } }); var $btn = $('<button>', { text: '太棒了!', css: { background: '#4CAF50', color: '#fff', border: 'none', padding: '10px 30px', 'border-radius': '8px', 'font-size': '16px', cursor: 'pointer' }, click: function() { $overlay.fadeOut(300, function() { $(this).remove(); }); } }); $box.append($title, $msg, $btn); $overlay.append($box); $('body').append($overlay); } } }).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 $version = $('#pf-version'); 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(); var version = $version.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 v = ($card.data('version') || '').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; } if (version && v !== version) 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); $version.on('change', filterPlants); $reset.on('click', function () { $name.val(''); $sunMax.val(''); $cdMax.val(''); $type.val(''); $version.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 $zversion = $('#zf-version'); 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 version = $zversion.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 v = ($card.data('version') || '').toString(); var show = true; if (name && n.indexOf(name) === -1) show = false; if (healthTarget !== null && health !== healthTarget) show = false; if (armor === 'none') { 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; } if (version && v !== version) 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); $zversion.on('change', filterZombies); $zreset.on('click', function () { $zname.val(''); $healthMin.val(''); $armor.val(''); $speed.val(''); $ztype.val(''); $zversion.val(''); filterZombies(); }); } // ==================== 返回顶部按钮 ==================== $('body').append('<button id="back-to-top" title="返回顶部">⬆</button>'); var $backBtn = $('#back-to-top'); $(window).scroll(function() { $backBtn.toggle($(this).scrollTop() > 300); }); $backBtn.click(function() { $('html, body').animate({ scrollTop: 0 }, 400); }); // ==================== 好友系统 ==================== var friendApi = new mw.Api(); function getFriendPageName(username) { return 'User:' + username + '/friends'; } function getFriendRequestsPageName(username) { return 'User:' + username + '/friendrequests'; } function loadFriendList(username, callback) { friendApi.get({ action: 'query', titles: getFriendPageName(username), prop: 'revisions', rvprop: 'content', rvlimit: 1 }).then(function(data) { var pages = data.query.pages; for (var id in pages) { if (pages[id].revisions && pages[id].revisions[0]) { try { callback(JSON.parse(pages[id].revisions[0]['*'])); return; } catch(e) {} } } callback([]); }).fail(function() { callback([]); }); } function loadFriendRequests(username, callback) { friendApi.get({ action: 'query', titles: getFriendRequestsPageName(username), prop: 'revisions', rvprop: 'content', rvlimit: 1 }).then(function(data) { var pages = data.query.pages; for (var id in pages) { if (pages[id].revisions && pages[id].revisions[0]) { try { callback(JSON.parse(pages[id].revisions[0]['*'])); return; } catch(e) {} } } callback([]); }).fail(function() { callback([]); }); } function saveToPage(pageName, data, summary, callback) { friendApi.postWithEditToken({ action: 'edit', title: pageName, text: JSON.stringify(data, null, 2), summary: summary, minor: true, bot: true }).then(function() { if (callback) callback(true); }).fail(function() { if (callback) callback(false); }); } function sendFriendRequest(toUser) { if (!toUser || toUser === currentUser) return; loadFriendList(currentUser, function(myFriends) { if (myFriends.indexOf(toUser) !== -1) { alert('你们已经是好友了!'); return; } loadFriendRequests(toUser, function(requests) { var alreadySent = requests.some(function(r) { return r.from === currentUser; }); if (alreadySent) { alert('你已经发送过好友请求了,请等待对方回应。'); return; } requests.push({ from: currentUser, time: Date.now() }); saveToPage(getFriendRequestsPageName(toUser), requests, currentUser + ' 发送了好友请求', function(success) { if (success) { alert('好友请求已发送给 ' + toUser + '!'); updateFriendButton(toUser, 'pending'); } else { alert('发送失败,请稍后重试。'); } }); }); }); } function acceptFriendRequest(fromUser) { loadFriendList(currentUser, function(myFriends) { myFriends.push(fromUser); saveToPage(getFriendPageName(currentUser), myFriends, '添加好友: ' + fromUser, function() { loadFriendList(fromUser, function(theirFriends) { theirFriends.push(currentUser); saveToPage(getFriendPageName(fromUser), theirFriends, '添加好友: ' + currentUser, function() { loadFriendRequests(currentUser, function(requests) { requests = requests.filter(function(r) { return r.from !== fromUser; }); saveToPage(getFriendRequestsPageName(currentUser), requests, '接受好友请求', function() { if ($('#friend-requests-list').length) showFriendRequests(); updateFriendButton(fromUser, 'added'); }); }); }); }); }); }); } function rejectFriendRequest(fromUser) { loadFriendRequests(currentUser, function(requests) { requests = requests.filter(function(r) { return r.from !== fromUser; }); saveToPage(getFriendRequestsPageName(currentUser), requests, '拒绝好友请求', function() { if ($('#friend-requests-list').length) showFriendRequests(); }); }); } function removeFriend(friendName) { if (!confirm('确定要删除好友 ' + friendName + ' 吗?')) return; loadFriendList(currentUser, function(myFriends) { myFriends = myFriends.filter(function(f) { return f !== friendName; }); saveToPage(getFriendPageName(currentUser), myFriends, '删除好友: ' + friendName, function() { loadFriendList(friendName, function(theirFriends) { theirFriends = theirFriends.filter(function(f) { return f !== currentUser; }); saveToPage(getFriendPageName(friendName), theirFriends, '删除好友: ' + currentUser, function() { if ($('#friend-list-container').length) showFriendList(); updateFriendButton(friendName, 'add'); }); }); }); }); } function updateFriendButton(username, status) { var $btn = $('#friend-action-btn'); if (!$btn.length) return; $btn.removeClass('friend-add-btn friend-added-btn friend-pending-btn'); if (status === 'added') { $btn.addClass('friend-added-btn').text('✓ 已添加'); $btn.off('click').click(function() { removeFriend(username); }); } else if (status === 'pending') { $btn.addClass('friend-pending-btn').text('⏳ 等待确认').off('click'); } else { $btn.addClass('friend-add-btn').text('+ 加好友'); $btn.off('click').click(function() { sendFriendRequest(username); }); } } function showFriendRequests() { loadFriendRequests(currentUser, function(requests) { var $list = $('#friend-requests-list'); if (!$list.length) return; $list.empty(); if (requests.length === 0) { $list.append('<p style="color:#999;">暂无好友请求</p>'); return; } requests.forEach(function(r) { var $item = $('<div>', { class: 'friend-request-item' }); $item.append('<span><a href="/w/User:' + encodeURIComponent(r.from) + '">' + r.from + '</a></span>'); var $actions = $('<div>'); $actions.append('<button class="friend-accept-btn" data-from="' + r.from + '">接受</button>'); $actions.append('<button class="friend-reject-btn" data-from="' + r.from + '">拒绝</button>'); $item.append($actions); $list.append($item); }); $list.off('click').on('click', '.friend-accept-btn', function() { acceptFriendRequest($(this).data('from')); }).on('click', '.friend-reject-btn', function() { rejectFriendRequest($(this).data('from')); }); }); } function showFriendList() { loadFriendList(currentUser, function(friends) { loadFriendRequests(currentUser, function(requests) { var $overlay = $('<div>', { class: 'friend-overlay' }); var $dialog = $('<div>', { class: 'friend-dialog' }); var realCount = friends.length; $dialog.append('<h3>👥 好友列表 <span class="friend-count">' + realCount + '人</span></h3>'); if (requests.length > 0) { $dialog.append('<p style="color:#f44336;cursor:pointer;" id="friend-req-notice">📩 有 ' + requests.length + ' 条好友请求,点击查看</p>'); } $dialog.append('<div id="friend-requests-list" style="display:none;margin:10px 0;"></div>'); if (realCount === 0) { $dialog.append('<p style="color:#999;">还没有好友,去其他用户页面加好友吧!</p>'); } else { var $container = $('<div>', { id: 'friend-list-container', style: 'margin:10px 0;' }); friends.forEach(function(f) { var $item = $('<span>', { class: 'friend-list-item' }); $item.append('<a href="/w/User:' + encodeURIComponent(f) + '">' + f + '</a>'); $item.append(' <a href="javascript:void(0)" class="friend-msg-link" data-friend="' + f + '" title="发私信">✉️</a>'); $item.append('<span class="friend-remove" data-friend="' + f + '"> ×</span>'); $container.append($item); }); $dialog.append($container); } $dialog.append('<button style="margin-top:10px;padding:8px 20px;cursor:pointer;background:#888;color:#fff;border:none;border-radius:6px;">关闭</button>'); $overlay.append($dialog); $('body').append($overlay); $overlay.on('click', function(e) { if ($(e.target).is($overlay) || $(e.target).text() === '关闭') { $overlay.remove(); } }); $dialog.on('click', '#friend-req-notice', function() { var $reqList = $('#friend-requests-list'); $reqList.toggle(); if ($reqList.is(':visible')) showFriendRequests(); }); $dialog.on('click', '.friend-remove', function() { removeFriend($(this).data('friend')); }); $dialog.on('click', '.friend-msg-link', function() { var friendName = $(this).data('friend'); $overlay.remove(); sendMessage(friendName); }); }); }); } if (mw.config.get('wgNamespaceNumber') === 2 && mw.config.get('wgTitle').indexOf('/') === -1) { var pageUser = mw.config.get('wgTitle'); if (pageUser !== currentUser) { var $btn = $('<button>', { id: 'friend-action-btn', class: 'friend-btn friend-add-btn', text: '+ 加好友' }); $('#firstHeading').append($btn); loadFriendList(currentUser, function(myFriends) { if (myFriends.indexOf(pageUser) !== -1) { updateFriendButton(pageUser, 'added'); } else { loadFriendRequests(pageUser, function(requests) { var alreadySent = requests.some(function(r) { return r.from === currentUser; }); if (alreadySent) { updateFriendButton(pageUser, 'pending'); } else { $btn.click(function() { sendFriendRequest(pageUser); }); } }); } }); } } if (currentUser) { var $userMenu = $('#pt-userpage, .citizen-userMenu').first(); if ($userMenu.length) { $userMenu.after(' <a href="javascript:void(0)" id="friend-list-trigger" style="font-size:13px;" title="好友列表">👥</a>'); } $(document).on('click', '#friend-list-trigger', function() { showFriendList(); }); setInterval(function() { loadFriendRequests(currentUser, function(requests) { var $notice = $('#friend-request-count'); if (requests.length > 0) { if (!$notice.length && $('#friend-list-trigger').length) { $('#friend-list-trigger').after('<span id="friend-request-count" style="color:#f44336;font-size:11px;vertical-align:super;">' + requests.length + '</span>'); } else if ($notice.length) { $notice.text(requests.length); } } else { $notice.remove(); } }); }, 60000); } // ==================== 私信系统 ==================== var msgApi = new mw.Api(); function getMsgPageName(username) { return 'User:' + username + '/messages'; } function loadMessages(callback) { if (!currentUser) { callback([]); return; } msgApi.get({ action: 'query', titles: getMsgPageName(currentUser), prop: 'revisions', rvprop: 'content', rvlimit: 1 }).then(function(data) { var pages = data.query.pages; for (var id in pages) { if (pages[id].revisions && pages[id].revisions[0]) { try { callback(JSON.parse(pages[id].revisions[0]['*'])); return; } catch(e) {} } } callback([]); }).fail(function() { callback([]); }); } function saveMessages(msgs, callback) { if (!currentUser) return; msgApi.postWithEditToken({ action: 'edit', title: getMsgPageName(currentUser), text: JSON.stringify(msgs, null, 2), summary: '更新私信', minor: true, bot: true }).then(function() { if (callback) callback(true); }).fail(function() { if (callback) callback(false); }); } function getUnreadCount(msgs) { var count = 0; msgs.forEach(function(m) { if (!m.read) count++; }); return count; } function updateMsgBadge() { loadMessages(function(msgs) { var count = getUnreadCount(msgs); var $badge = $('#msg-badge'); if ($badge.length === 0) { var $drawer = $('.citizen-drawer').first(); if ($drawer.length) { $drawer.css('position', 'relative'); $drawer.append('<span id="msg-badge" class="msg-badge" title="新私信">0</span>'); $badge = $('#msg-badge'); } } if ($badge.length) { $badge.text(count).toggle(count > 0); } }); } function showInbox() { loadMessages(function(msgs) { var $overlay = $('<div>', { class: 'msg-overlay' }); var $box = $('<div>', { class: 'msg-box' }); $box.append('<h3 style="margin-bottom:15px;">📬 私信箱</h3>'); msgs.sort(function(a, b) { return b.time - a.time; }); if (msgs.length === 0) { $box.append('<p style="color:#999;">暂无消息</p>'); } else { msgs.forEach(function(m, index) { var $item = $('<div>', { class: 'msg-item' + (m.read ? '' : ' unread') }); var timeStr = new Date(m.time).toLocaleString('zh-CN'); $item.append( '<div><span class="msg-sender">' + m.from + '</span><span class="msg-time">' + timeStr + '</span></div>', '<div class="msg-text">' + m.text.replace(/</g,'<').replace(/>/g,'>').replace(/\n/g,'<br>') + '</div>', '<div class="msg-actions">' + (m.read ? '' : '<button class="mark-read" data-index="' + index + '">已读</button>') + '<button class="del-msg" data-index="' + index + '">删除</button>' + '<button class="reply-msg" data-index="' + index + '" data-from="' + m.from + '">回复</button>' + '</div>' ); $box.append($item); }); } $box.append('<button style="margin-top:15px;padding:8px 20px;cursor:pointer;background:#888;color:#fff;border:none;border-radius:6px;">关闭</button>'); $overlay.append($box); $('body').append($overlay); $overlay.on('click', function(e) { if ($(e.target).is($overlay) || $(e.target).text() === '关闭') { $overlay.remove(); updateMsgBadge(); } }); $box.on('click', '.mark-read', function() { var idx = parseInt($(this).data('index')); msgs[idx].read = true; saveMessages(msgs, function() { updateMsgBadge(); $overlay.remove(); showInbox(); }); }); $box.on('click', '.del-msg', function() { var idx = parseInt($(this).data('index')); msgs.splice(idx, 1); saveMessages(msgs, function() { $overlay.remove(); showInbox(); }); }); $box.on('click', '.reply-msg', function() { var toUser = $(this).data('from'); $overlay.remove(); sendMessage(toUser); }); }); } function sendMessage(toUser) { if (!toUser) return; var $overlay = $('<div>', { class: 'msg-overlay' }); var $box = $('<div>', { class: 'msg-box' }); $box.append('<h3 style="margin-bottom:10px;">✉️ 发送私信给 ' + toUser + '</h3>'); $box.append('<textarea class="msg-textarea" placeholder="输入消息内容..."></textarea>'); $box.append( '<button class="msg-send-submit" style="margin-top:10px;padding:8px 20px;cursor:pointer;background:#4CAF50;color:#fff;border:none;border-radius:6px;">发送</button>', '<button style="margin-left:10px;padding:8px 20px;cursor:pointer;background:#888;color:#fff;border:none;border-radius:6px;">取消</button>' ); $overlay.append($box); $('body').append($overlay); $overlay.on('click', function(e) { if ($(e.target).is($overlay) || $(e.target).text() === '取消') { $overlay.remove(); } }); $box.on('click', '.msg-send-submit', function() { var text = $box.find('.msg-textarea').val().trim(); if (!text) return; $box.find('.msg-send-submit').prop('disabled', true).text('发送中...'); var tempApi = new mw.Api(); tempApi.get({ action: 'query', titles: getMsgPageName(toUser), prop: 'revisions', rvprop: 'content', rvlimit: 1 }).then(function(data) { var pages = data.query.pages; var msgs = []; for (var id in pages) { if (pages[id].revisions && pages[id].revisions[0]) { try { msgs = JSON.parse(pages[id].revisions[0]['*']); } catch(e) {} } } msgs.push({ from: currentUser, to: toUser, text: text, time: Date.now(), read: false }); tempApi.postWithEditToken({ action: 'edit', title: getMsgPageName(toUser), text: JSON.stringify(msgs, null, 2), summary: currentUser + ' 发来一条私信', minor: false, bot: false }).then(function() { $overlay.remove(); alert('私信已发送给 ' + toUser + '!'); }).fail(function(err) { $overlay.remove(); alert('发送失败:' + (err.error && err.error.info ? err.error.info : '未知错误')); }); }).fail(function() { $overlay.remove(); alert('发送失败,请稍后重试。'); }); }); } if (mw.config.get('wgNamespaceNumber') === 2 && mw.config.get('wgTitle').indexOf('/') === -1) { var msgPageUser = mw.config.get('wgTitle'); if (msgPageUser !== currentUser) { var $msgBtn = $('<button>', { text: '✉️ 发送私信', class: 'msg-send-btn', click: function() { sendMessage(msgPageUser); } }); $('#firstHeading').append($msgBtn); } } if (currentUser) { updateMsgBadge(); $(document).on('click', '#msg-badge', function() { showInbox(); }); var $msgEntry = $('<a>', { href: 'javascript:void(0)', id: 'msg-inbox-trigger', text: '✉️', title: '私信箱', css: { 'font-size': '13px', 'margin-left': '8px', 'cursor': 'pointer', 'text-decoration': 'none' } }); var $friendTrigger = $('#friend-list-trigger'); if ($friendTrigger.length) { $friendTrigger.after(' '); $friendTrigger.after($msgEntry); } $(document).on('click', '#msg-inbox-trigger', function() { showInbox(); }); setInterval(function() { updateMsgBadge(); }, 30000); } // ==================== 版本更新公告弹窗 ==================== function checkUpdateNotice() { var $versionEl = $('.update-version'); if ($versionEl.length === 0) return; var currentVersion = $versionEl.text().trim(); if (!currentVersion) return; var dismissedVersion = localStorage.getItem('mw_update_dismissed'); if (dismissedVersion !== currentVersion) { var $overlay = $('<div>', { css: { position: 'fixed', top: 0, left: 0, width: '100%', height: '100%', background: 'rgba(0,0,0,0.6)', 'z-index': 99999, display: 'flex', 'align-items': 'center', 'justify-content': 'center' } }); var $box = $('<div>', { css: { background: '#fff', 'border-radius': '12px', padding: '30px', 'max-width': '550px', width: '90%', 'max-height': '80vh', 'overflow-y': 'auto', 'box-shadow': '0 8px 30px rgba(0,0,0,0.4)', position: 'relative' } }); var $content = $('<div>').css({'text-align':'left','font-size':'14px','line-height':'1.8'}).html( '<div style="text-align:center;font-size:18px;font-weight:bold;margin-bottom:5px;">【植物大战僵尸杂交版0.22版本更新公告】</div>' + '<div style="text-align:center;font-size:12px;color:#888;margin-bottom:15px;">更新时间:2026年6月7号</div>'+ '<div style="text-align:center;font-size:12px;color:#888;margin-bottom:15px;">电脑(Windows)版链接:https://pan.quark.cn/s/b145573873c3</div>'+ '<div style="text-align:center;font-size:12px;color:#888;margin-bottom:15px;">其他版本:https://pan.quark.cn/s/3ffc82554918</div>'+ '<b>🌱 植物更新</b><br>全新的植物加入了我们,我们的实力将更加强悍!<br>'+ '<b>白卡:</b>幽灵吞噬者、僵尸地刺、魅惑咖啡豆、魅惑三叶草、灵感菇、蒜鸟、咖啡三叶草、叶子高坚果、巨型南瓜壳、绷带高坚果、磁力樱桃炸弹、樱桃土豆雷<br>' + '<b>至尊金卡:</b>黄金西瓜投手、大王钢齿花<br>' + '<b>臻享钻卡:</b>生命重塑者<br>' + '<b>星光闪卡:</b>黄金锤子<br><br>' + '<b>🧟 僵尸/障碍物更新</b><br>飞行器僵尸、胆小鬼僵尸、传送门僵尸<br><br>' + '<b>🗺️ 关卡更新</b><br>' + '冒险:第八章 1~4关<br>' + '金卡挑战:大王钢齿花 1~3,黄金西瓜投手 1~3<br>' + '钻卡挑战:生命重塑者 1~6<br><br>' + '<b>🎨 杂项更新</b><br>' + '植物皮肤更新:黄金西瓜投手-幸运之王、大王钢齿花-银锋锐影、生命重塑者-浮生净莲(商店15000购买)、魔鬼辣椒-拘灵炼狱(在线关卡5水晶兑换)<br>' + '道具更新:骷髅铲<br><br>' + '<b>⚙️ 全新功能</b><br>' + '新增游戏指令:/phonk [on/off] [弹性强度数]、/dismember [on/off]<br>' + '局内设置新增果冻弹性效果开关与强度调整<br><br>' + '<b>⚖️ 平衡性调整</b><br>' + '植物价格调整:巨型南瓜雪橇 200→300、墓碑爆破者 150→100<br>' + '植物强度调整:宝藏树桩亡语 1~3张黄金碎片→1张<br><br>' + '<b>🛠️ 游戏优化</b><br>' + '优化了关卡进度存档、返回选关体验、在线关卡分类筛选与通关率显示、更多模式板块内容返回体验<br>' + '修复商店与植物试玩切换假死BUG、追加宝藏树桩亡语掉落黄金碎片、荧光木槌可对僵尸使用、本次更新修复了一堆BUG<br><br>' + '<b>💎 水晶兑换商店</b><br>' + '巨型南瓜壳 5 | 磁力樱桃炸弹 10 | 绷带高坚果 10<br>' + '叶子高坚果 15 | 樱桃土豆雷 15 | 魔鬼辣椒皮肤-拘灵炼狱 5<br><br>' + '<span style="color:#888;">结语:我们会持续建立与玩家社群的紧密联系,您的支持就是对我们工作最大的认可</span>' ); var $closeBtn = $('<button>', { text: '我知道了', css: { display: 'block', margin: '20px auto 0', background: '#4CAF50', color: '#fff', border: 'none', padding: '10px 30px', 'border-radius': '8px', 'font-size': '16px', cursor: 'pointer' }, click: function() { localStorage.setItem('mw_update_dismissed', currentVersion); $overlay.fadeOut(300, function() { $(this).remove(); }); } }); $box.append($content, $closeBtn); $overlay.append($box); $('body').append($overlay); $overlay.on('click', function(e) { if ($(e.target).is($overlay)) { $overlay.fadeOut(300, function() { $(this).remove(); }); } }); } } setTimeout(function() { checkUpdateNotice(); }, 500); // ==================== Wiki 宠物 ==================== (function() { var petState = 'idle'; var petTimer = null; var idleGif = 'https://new.pvzhe.wiki/images/1/10/%E5%86%B0%E7%93%9C%E9%A6%99%E8%92%B2%E5%BE%85%E6%9C%BA.gif'; var attackGif = 'https://new.pvzhe.wiki/images/b/bd/%E5%86%B0%E7%93%9C%E9%A6%99%E8%92%B2%E5%B0%84%E5%87%BB.gif'; var pettingGif = 'https://new.pvzhe.wiki/images/4/47/%E5%86%B0%E7%93%9C%E9%A6%99%E8%92%B2%E6%8A%9A%E6%91%B8.gif'; var $pet = $('<div>', { class: 'wiki-pet', css: { opacity: 0 } }); var $img = $('<img>', { src: idleGif, alt: '冰瓜香蒲' }); var $tooltip = $('<div>', { class: 'pet-tooltip', text: '点击抚摸我~ 🐱' }); $pet.append($img, $tooltip); $('body').append($pet); setTimeout(function() { $pet.animate({ opacity: 1 }, 500); }, 1000); function setPetState(state) { if (petState === state) return; petState = state; clearTimeout(petTimer); $pet.removeClass('petting attacking'); if (state === 'idle') { $img.attr('src', idleGif); $tooltip.text('点击抚摸我~ 🐱'); } else if (state === 'attack') { $img.attr('src', attackGif); $pet.addClass('attacking'); $tooltip.text('看招!❄️'); petTimer = setTimeout(function() { setPetState('idle'); }, 600); } else if (state === 'petting') { $img.attr('src', pettingGif); $pet.addClass('petting'); $tooltip.text('好舒服~ 💚'); petTimer = setTimeout(function() { setPetState('idle'); }, 1200); } } $pet.on('click', function(e) { e.stopPropagation(); setPetState('petting'); }); $pet.on('dblclick', function(e) { e.stopPropagation(); setPetState('attack'); }); $(document).on('click', function(e) { if (!$(e.target).closest('.wiki-pet').length) { setPetState('attack'); } }); setInterval(function() { if (petState === 'idle' && Math.random() < 0.3) { $pet.css('transform', 'scale(1.05) translateY(-5px)'); setTimeout(function() { $pet.css('transform', 'scale(1) translateY(0)'); }, 500); } }, 10000); var touchTimer; $pet.on('touchstart', function(e) { e.stopPropagation(); setPetState('petting'); touchTimer = setTimeout(function() { setPetState('attack'); }, 500); }); $pet.on('touchend touchmove', function() { clearTimeout(touchTimer); }); })(); }); // 结束主 $(function () { ... })
返回
MediaWiki:Common.js
。
查看“︁MediaWiki:Common.js”︁的源代码
MediaWiki界面页面