MediaWiki:Common.js:修订间差异
MediaWiki界面页面
更多操作
无编辑摘要 |
无编辑摘要 |
||
| 第21行: | 第21行: | ||
}); | }); | ||
$(function () { | $(function () { | ||
// | // ==================== 样式注入 ==================== | ||
// 彩虹用户样式 | |||
var rainbowStyle = document.createElement('style'); | var rainbowStyle = document.createElement('style'); | ||
rainbowStyle.textContent = | rainbowStyle.textContent = | ||
| 第41行: | 第42行: | ||
document.head.appendChild(rainbowStyle); | document.head.appendChild(rainbowStyle); | ||
// | // 状态圆点样式 | ||
var statusStyle = document.createElement('style'); | var statusStyle = document.createElement('style'); | ||
statusStyle.textContent = | statusStyle.textContent = | ||
| 第57行: | 第58行: | ||
document.head.appendChild(statusStyle); | document.head.appendChild(statusStyle); | ||
// ---------- 辅助函数 | // 师徒标签样式 | ||
var tagStyle = document.createElement('style'); | |||
tagStyle.textContent = | |||
'.user-tag {' + | |||
'display: inline-block;' + | |||
'font-size: 0.75em;' + | |||
'padding: 0px 4px;' + | |||
'margin-left: 4px;' + | |||
'border-radius: 3px;' + | |||
'vertical-align: middle;' + | |||
'font-weight: normal;' + | |||
'}' + | |||
'.user-tag-mentor { background: #4CAF50; color: white; }' + // 导师绿色 | |||
'.user-tag-newbie { background: #FF9800; color: white; }'; // 新手橙色 | |||
document.head.appendChild(tagStyle); | |||
// ==================== 配置区 ==================== | |||
// 手动列出导师用户名(区分大小写,和页面上显示的名字完全一致) | |||
var mentorList = ['愤怒的郎朗', 'Yuyaabc','虚位以待','知更鸟头号粉丝']; | |||
// 新手编辑数阈值(≤此值自动判定为新手) | |||
var newbieEditThreshold = 25; | |||
// ==================== 辅助函数 ==================== | |||
function getUserName(link) { | function getUserName(link) { | ||
var $span = $(link).find('span').first(); | var $span = $(link).find('span').first(); | ||
| 第64行: | 第87行: | ||
} | } | ||
function isUserLink(link) { | function isUserLink(link) { | ||
return $(link).is('a.mw-userlink') || $(link).find('span').length > 0; | return $(link).is('a.mw-userlink') || $(link).find('span').length > 0; | ||
} | } | ||
// | // 核心:上色 + 状态 + 师徒标签 | ||
function colorizeAndStatus($links) { | function colorizeAndStatus($links) { | ||
if ($links.length === 0) return; | if ($links.length === 0) return; | ||
| 第89行: | 第111行: | ||
}); | }); | ||
// 1. | // 1. 应用颜色(所有用户链接,包括卡片) | ||
var batchSize = 50; | var batchSize = 50; | ||
var batches = []; | var batches = []; | ||
| 第134行: | 第156行: | ||
}); | }); | ||
// 2. | // 2. 非卡片用户:添加状态圆点 + 师徒标签 | ||
$fresh.each(function () { | $fresh.each(function () { | ||
if (isUserLink(this)) { | if (isUserLink(this)) { | ||
var isInsideCard = $(this).closest('.citizen-menu_card-content, .citizen-userMenu').length > 0; | |||
if (!isInsideCard) { | |||
addStatusDot($(this), | var username = getUserName(this); | ||
addStatusDot($(this), username); | |||
addMentorTag($(this), username); | |||
} | } | ||
} | } | ||
| 第145行: | 第169行: | ||
} | } | ||
// | // 状态圆点(同之前) | ||
function addStatusDot($link, username) { | function addStatusDot($link, username) { | ||
if ($link.data('status-dot-added')) return; | if ($link.data('status-dot-added')) return; | ||
| 第206行: | 第230行: | ||
} | } | ||
// 师徒标签(新增) | |||
function addMentorTag($link, username) { | |||
if ($link.data('mentor-tag-added')) return; | |||
// 查找是否已存在标签(防止重复) | |||
if ($link.next('.user-tag').length) return; | |||
var tagType = null; | |||
if (mentorList.indexOf(username) !== -1) { | |||
tagType = 'mentor'; | |||
} else { | |||
// 新手判定:从缓存或API拿编辑数。这里复用之前已查询的编辑数? | |||
// 为了不增加额外API,我们将新手判定延迟到颜色API返回后进行。 | |||
// 简单做法:在colorizeAndStatus流程中,颜色API已经获得了所有用户的编辑数, | |||
// 我们可以把编辑数保存起来供标签使用。 | |||
// 重构:将classMap扩展为包含editcount的map。 | |||
// 但由于colorizeAndStatus中$fresh和users的作用域,我们需要调整结构。 | |||
// 简便方案:单独用一次小查询获取编辑数(带缓存)。 | |||
// 为了性能,利用之前已经有的颜色查询结果是最佳,但需要较大改动。 | |||
// 这里提供一个折中方案:给新手标签也使用API+缓存,因为需要编辑数。 | |||
addNewbieTagIfNeeded($link, username); | |||
return; | |||
} | |||
$link.data('mentor-tag-added', true); | |||
var $tag = $('<span class="user-tag user-tag-mentor">导师</span>'); | |||
$link.after($tag); | |||
} | |||
// 新手标签(带缓存) | |||
function addNewbieTagIfNeeded($link, username) { | |||
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); | |||
// 缓存1小时,因为编辑数变化较慢 | |||
if (now - data.timestamp < 60 * 60 * 1000) { | |||
applyNewbieTag($link, data.editcount); | |||
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 | |||
})); | |||
applyNewbieTag($link, editcount); | |||
}).fail(function () {}); | |||
} | |||
function applyNewbieTag($link, editcount) { | |||
if (editcount <= newbieEditThreshold) { | |||
// 避免重复添加 | |||
if ($link.next('.user-tag-newbie').length) return; | |||
var $tag = $('<span class="user-tag user-tag-newbie">新手</span>'); | |||
$link.after($tag); | |||
} | |||
} | |||
// ==================== 执行入口 ==================== | |||
var linkSelector = 'a.mw-userlink, .citizen-menu_card-content a, .citizen-userMenu a'; | var linkSelector = 'a.mw-userlink, .citizen-menu_card-content a, .citizen-userMenu a'; | ||
2026年5月31日 (日) 07:59的版本
/* 这里的任何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;' +
'color: #FFD700;' +
'}' +
'.rainbow-user::after {' +
'content: "⭐⭐⭐⭐⭐";' +
'margin-left: 2px;' +
'font-weight: normal;' +
'-webkit-text-fill-color: initial;' +
'color: #FFD700;' +
'}';
document.head.appendChild(rainbowStyle);
// 状态圆点样式
var statusStyle = document.createElement('style');
statusStyle.textContent =
'.user-status-dot {' +
'display: inline-block;' +
'width: 8px;' +
'height: 8px;' +
'border-radius: 50%;' +
'margin-left: 4px;' +
'vertical-align: middle;' +
'}' +
'.status-online { background-color: #4CAF50; }' +
'.status-away { background-color: #FF9800; }' +
'.status-offline { background-color: #9E9E9E; }';
document.head.appendChild(statusStyle);
// 师徒标签样式
var tagStyle = document.createElement('style');
tagStyle.textContent =
'.user-tag {' +
'display: inline-block;' +
'font-size: 0.75em;' +
'padding: 0px 4px;' +
'margin-left: 4px;' +
'border-radius: 3px;' +
'vertical-align: middle;' +
'font-weight: normal;' +
'}' +
'.user-tag-mentor { background: #4CAF50; color: white; }' + // 导师绿色
'.user-tag-newbie { background: #FF9800; color: white; }'; // 新手橙色
document.head.appendChild(tagStyle);
// ==================== 配置区 ====================
// 手动列出导师用户名(区分大小写,和页面上显示的名字完全一致)
var mentorList = ['愤怒的郎朗', 'Yuyaabc','虚位以待','知更鸟头号粉丝'];
// 新手编辑数阈值(≤此值自动判定为新手)
var newbieEditThreshold = 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 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);
});
// 1. 应用颜色(所有用户链接,包括卡片)
var batchSize = 50;
var batches = [];
for (var i = 0; i < users.length; i += batchSize) {
batches.push(users.slice(i, i + batchSize));
}
var processColorBatch = function (batch) {
var api = new mw.Api();
return api.get({
action: 'query',
list: 'users',
ususers: batch.join('|'),
usprop: 'editcount'
}).then(function (data) {
var classMap = {};
if (data.query && data.query.users) {
data.query.users.forEach(function (u) {
var ec = u.editcount || 0;
if (ec >= 5000) classMap[u.name] = 'rainbow-user';
else if (ec >= 2000) 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 colorPromise = $.Deferred().resolve();
batches.forEach(function (batch) {
colorPromise = colorPromise.then(function () {
return processColorBatch(batch);
});
});
// 2. 非卡片用户:添加状态圆点 + 师徒标签
$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="离线">');
$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 tagType = null;
if (mentorList.indexOf(username) !== -1) {
tagType = 'mentor';
} else {
// 新手判定:从缓存或API拿编辑数。这里复用之前已查询的编辑数?
// 为了不增加额外API,我们将新手判定延迟到颜色API返回后进行。
// 简单做法:在colorizeAndStatus流程中,颜色API已经获得了所有用户的编辑数,
// 我们可以把编辑数保存起来供标签使用。
// 重构:将classMap扩展为包含editcount的map。
// 但由于colorizeAndStatus中$fresh和users的作用域,我们需要调整结构。
// 简便方案:单独用一次小查询获取编辑数(带缓存)。
// 为了性能,利用之前已经有的颜色查询结果是最佳,但需要较大改动。
// 这里提供一个折中方案:给新手标签也使用API+缓存,因为需要编辑数。
addNewbieTagIfNeeded($link, username);
return;
}
$link.data('mentor-tag-added', true);
var $tag = $('<span class="user-tag user-tag-mentor">导师</span>');
$link.after($tag);
}
// 新手标签(带缓存)
function addNewbieTagIfNeeded($link, username) {
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);
// 缓存1小时,因为编辑数变化较慢
if (now - data.timestamp < 60 * 60 * 1000) {
applyNewbieTag($link, data.editcount);
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
}));
applyNewbieTag($link, editcount);
}).fail(function () {});
}
function applyNewbieTag($link, editcount) {
if (editcount <= newbieEditThreshold) {
// 避免重复添加
if ($link.next('.user-tag-newbie').length) return;
var $tag = $('<span class="user-tag user-tag-newbie">新手</span>');
$link.after($tag);
}
}
// ==================== 执行入口 ====================
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);
});