MediaWiki:Gadget-MoreMenu.js
外观
注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
- Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
- Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5。
/**
* MoreMenu gadget for test.zybkcn.com
* Loader: ResourceLoader
* Dependencies: mediawiki.api, mediawiki.util, mediawiki.Title, mediawiki.user, mediawiki.storage, user.options
*/
/**
* WARNING: GLOBAL GADGET FILE
* MoreMenu — 为 MediaWiki 添加 Page/User 下拉菜单
*
* Version: 5.2.2 (merged single-file for test.zybkcn.com)
* Author: MusikAnimal
* License: MIT
* GitHub: https://github.com/wikimedia-gadgets/MoreMenu
* Skins: Vector, Timeless, Monobook, Modern
*
* 本文件合并自:
* - MoreMenu.js (主引擎)
* - MoreMenu.messages.en.js → 内置简繁中文翻译
* - MoreMenu.page.js (精简版,仅保留本站链接)
* - MoreMenu.user.js (精简版,仅保留本站链接)
**/
"use strict";
(function () {
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol"
? function (o) { return typeof o; }
: function (o) { return o && typeof Symbol === "function" && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; };
/**
* 内置翻译表:中文(简繁)
* 根据 wgUserLanguage 自动选择
*/
var translations = (function () {
var lang = mw.config.get('wgUserLanguage') || 'zh';
// 检测是否繁体
var isHant = /hant|tw|hk|mo/i.test(lang);
// 简繁对照表
var strings = {
page: ['页面', '頁面'],
user: ['用户', '用戶'],
'page-logs': ['页面日志', '頁面日誌'],
'user-logs': ['用户日志', '用戶日誌'],
'all-logs': ['所有日志', '所有日誌'],
'abusefilter-log': ['滥用过滤器日志', '濫用過濾器日誌'],
'deletion-log': ['删除日志', '刪除日誌'],
'move-log': ['移动日志', '移動日誌'],
'protection-log': ['保护日志', '保護日誌'],
'spam-blacklist-log': ['垃圾链接黑名单日志', '垃圾連結黑名單日誌'],
'block-log': ['封禁日志', '封禁日誌'],
'checkuser-log': ['用户查核日志', '用戶查核日誌'],
'global-account-log': ['全域账号日志', '全域帳號日誌'],
'global-block-log': ['全域封禁日志', '全域封禁日誌'],
'mass-message-log': ['大量消息日志', '大量訊息日誌'],
'rename-log': ['重命名日志', '重新命名日誌'],
'review-log': ['审核日志', '審核日誌'],
'suppression-log': ['监督日志', '監督日誌'],
'thanks-log': ['感谢日志', '感謝日誌'],
'upload-log': ['上传日志', '上傳日誌'],
'user-creation-log': ['用户创建日志', '用戶建立日誌'],
'user-rights-log': ['用户权限日志', '用戶權限日誌'],
'blocks': ['封禁', '封禁'],
'block-user': ['封禁用户', '封禁用戶'],
'change-block': ['更改封禁', '變更封禁'],
'unblock-user': ['解除封禁', '解除封禁'],
'view-block': ['查看封禁', '檢視封禁'],
'view-block-log': ['查看封禁日志', '檢視封禁日誌'],
'analysis': ['分析', '分析'],
'basic-statistics': ['基本信息', '基本資訊'],
'transclusions': ['嵌入情况', '嵌入情況'],
'search': ['搜索', '搜尋'],
'latest-diff': ['最新差异', '最新差異'],
'search-subpages': ['搜索子页面', '搜尋子頁面'],
'delete-page': ['删除', '刪除'],
'undelete-page': ['恢复', '還原'],
'move-page': ['移动', '移動'],
'protect-page': ['保护', '保護'],
'change-protection': ['更改保护', '變更保護'],
'purge-cache': ['清除缓存', '清除快取'],
'subpages': ['子页面', '子頁面'],
'edit-intro': ['编辑导言', '編輯導言'],
'history': ['历史', '歷史'],
'watch': ['监视', '監視'],
'merge-page': ['合并', '合併'],
'change-model': ['更改内容模型', '變更內容模型'],
'contributions': ['贡献', '貢獻'],
'deleted-contributions': ['已删除贡献', '已刪除貢獻'],
'suppressed-contribs': ['被监督的贡献', '被監督的貢獻'],
'email-user': ['电邮用户', '電郵用戶'],
'change-rights': ['更改用户权限', '變更用戶權限'],
'checkuser': ['用户查核', '用戶查核'],
'uploads': ['上传记录', '上傳記錄'],
'user-groups': ['用户组', '用戶組'],
'sul': ['全域账户', '全域帳戶'],
'user-thanks-received': ['收到的感谢', '收到的感謝'],
'tools': ['工具', '工具']
};
var result = {};
Object.keys(strings).forEach(function (key) {
result[key] = isHant ? strings[key][1] : strings[key][0];
});
return result;
})();
/** ============================================================
* 页面菜单定义 (MoreMenu.page) — 精简版
* ============================================================ */
var pageMenu = function (config) {
return {
page: (function () {
var items = {};
// page-logs 子菜单
items['page-logs'] = {
'all-logs': {
url: mw.util.getUrl('Special:Log', { page: config.page.name }),
insertAfter: false
},
'abusefilter-log': {
url: mw.util.getUrl('Special:AbuseLog', { wpSearchTitle: config.page.name })
},
'deletion-log': {
url: mw.util.getUrl('Special:Log', { page: config.page.name, type: 'delete' })
},
'move-log': {
url: mw.util.getUrl('Special:Log', { page: config.page.name, type: 'move' })
},
'protection-log': {
url: mw.util.getUrl('Special:Log', { page: config.page.name, type: 'protect' })
},
'spam-blacklist-log': {
url: mw.util.getUrl('Special:Log', { page: config.page.name, type: 'spamblacklist' })
}
};
// analysis 子菜单 — 仅保留本站链接
items['analysis'] = {
'basic-statistics': {
url: mw.util.getUrl(config.page.name, { action: 'info' }),
pageExists: true,
insertAfter: false
},
'transclusions': {
url: 'https://' + config.project.domain + '/w/index.php?title=Special:WhatLinksHere/' + config.page.encodedName + '&hidelinks=1&hideredirs=1',
namespaceRestrict: [2, 4, 5, 10, 11, 12, 13, 100, 101]
}
};
// search 子菜单 — 仅保留本站链接
items['search'] = {
'latest-diff': {
url: mw.util.getUrl(config.page.name, { diff: 'cur', oldid: 'prev' }),
pageExists: true,
insertAfter: false
},
'search-subpages': {
url: mw.util.getUrl('Special:Search', { sort: 'relevance', prefix: config.page.name })
}
};
// 操作项
items['change-model'] = {
url: mw.util.getUrl('Special:ChangeContentModel/' + config.page.name),
currentUserRights: ['editcontentmodel'],
pageExists: true,
namespaceRestrict: [2, 4, 8, 100, 108, 828]
};
items['delete-page'] = {
url: mw.util.getUrl(config.page.name, {
action: 'delete',
wpReason: (function () {
try { return decodeURIComponent($('#delete-reason').text()).replace(/\+/g, ' '); }
catch (e) { return undefined; }
})()
}),
currentUserRights: ['delete'],
pageExists: true,
visible: !mw.config.get('wgIsMainPage')
};
items['edit-intro'] = {
url: '//' + config.project.domain + '/w/index.php?title=' + config.page.encodedName + '&action=edit§ion=0',
namespaceRestrict: [0, 1, 2, 3, 4, 5, 118],
pageExists: true,
visible: mw.user.options.get('gadget-edittop') !== '1' && $('.mw-editsection').length
};
// Monobook/Modern 历史占位符
items['history'] = {
url: '#',
visible: ['monobook', 'modern'].indexOf(config.currentUser.skin) !== -1
};
items['merge-page'] = {
url: mw.util.getUrl('Special:MergeHistory', { target: config.page.name }),
currentUserRights: ['mergehistory'],
pageExists: true,
visible: !mw.config.get('wgIsMainPage')
};
items['move-page'] = {
url: mw.util.getUrl('Special:MovePage/' + config.page.name),
currentUserRights: ['move'],
pageExists: true,
pageMovable: true
};
// 保护/更改保护(动态 key)
items[config.page.protected ? 'change-protection' : 'protect-page'] = {
url: mw.util.getUrl(config.page.name, { action: 'protect' }),
currentUserRights: ['protect', 'stablesettings']
};
items['purge-cache'] = {
url: mw.util.getUrl(config.page.name, { action: 'purge', forcelinkupdate: true }),
pageExists: true
};
items['subpages'] = {
url: mw.util.getUrl('Special:PrefixIndex/' + config.page.name + '/')
};
items['undelete-page'] = {
url: mw.util.getUrl('Special:Undelete/' + config.page.name),
currentUserRights: ['undelete'],
pageDeleted: true
};
// Monobook/Modern 监视占位符
items['watch'] = {
url: '#',
visible: ['monobook', 'modern'].indexOf(config.currentUser.skin) !== -1
};
return items;
})()
};
};
/** ============================================================
* 用户菜单定义 (MoreMenu.user) — 精简版
* ============================================================ */
var userMenu = function (config) {
return {
user: (function () {
var items = {};
// user-logs 子菜单
items['user-logs'] = {
'all-logs': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name }),
insertAfter: false
},
'abusefilter-log': {
url: mw.util.getUrl('Special:AbuseLog', { wpSearchUser: config.targetUser.name })
},
'block-log': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name, type: 'block' }),
targetUserRights: ['block']
},
'checkuser-log': {
url: mw.util.getUrl('Special:CheckUserLog', { cuSearch: config.targetUser.name, cuSearchType: 'initiator' }),
targetUserRights: ['checkuser-log'],
currentUserRights: ['checkuser-log']
},
'deletion-log': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name, type: 'delete' }),
targetUserRights: ['delete']
},
'global-account-log': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name, type: 'globalauth' }),
targetUserRights: ['centralauth-lock']
},
'global-block-log': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name, type: 'gblblock' }),
targetUserRights: ['globalblock']
},
'mass-message-log': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name, type: 'massmessage' }),
targetUserRights: ['massmessage']
},
'move-log': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name, type: 'move' }),
targetUserRights: ['move']
},
'protection-log': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name, type: 'protect' }),
targetUserRights: ['protect']
},
'spam-blacklist-log': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name, type: 'spamblacklist' })
},
'suppression-log': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name, type: 'suppress' }),
targetUserRights: ['suppressrevision'],
currentUserRights: ['suppressionlog']
},
'thanks-log': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name, type: 'thanks' }),
targetUserGroups: ['user']
},
'upload-log': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name, type: 'upload' }),
targetUserRights: ['upload']
},
'user-creation-log': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name, type: 'newusers' }),
targetUserGroups: ['user']
},
'user-rights-log': {
url: mw.util.getUrl('Special:Log', { user: config.targetUser.name, type: 'rights' }),
targetUserChangeGroups: true
}
};
// blocks 子菜单 — 删除了 block-globally 和 central-auth
items['blocks'] = {
'block-user': {
url: mw.util.getUrl('Special:Block/' + config.targetUser.name),
currentUserRights: 'block',
targetUserBlocked: false
},
'change-block': {
url: mw.util.getUrl('Special:Block/' + config.targetUser.name),
currentUserRights: 'block',
targetUserBlocked: true
},
'unblock-user': {
url: mw.util.getUrl('Special:Unblock/' + config.targetUser.name),
targetUserBlocked: true,
currentUserRights: 'block'
},
'view-block': {
url: mw.util.getUrl('Special:BlockList', { wpTarget: config.targetUser.name }),
targetUserBlocked: true,
style: 'color:#EE1111'
},
'view-block-log': {
url: mw.util.getUrl('Special:Log', { page: config.targetUser.name, type: 'block' })
}
};
// analysis 子菜单 — 仅保留 SUL
items['analysis'] = {
'sul': {
url: mw.util.getUrl('Special:CentralAuth/' + config.targetUser.name),
targetUserGroups: ['user'],
insertAfter: false
}
};
// 操作项
items['change-rights'] = {
url: mw.util.getUrl('Special:UserRights', { user: 'User:' + config.targetUser.name }),
targetUserGroups: ['user'],
currentUserChangeGroups: true
};
items['checkuser'] = {
url: mw.util.getUrl('Special:CheckUser/' + config.targetUser.name),
currentUserRights: ['checkuser']
};
items['contributions'] = {
url: mw.util.getUrl('Special:Contributions/' + config.targetUser.name)
};
items['deleted-contributions'] = {
url: mw.util.getUrl('Special:DeletedContributions/' + config.targetUser.name),
currentUserRights: ['deletedhistory', 'deletedtext'],
insertAfter: 'contributions'
};
items['suppressed-contribs'] = {
url: mw.util.getUrl('Special:Log/suppress', { offender: config.targetUser.name }),
currentUserRights: ['suppressionlog'],
insertAfter: 'deleted-contributions'
};
items['email-user'] = {
url: mw.util.getUrl('Special:EmailUser/' + config.targetUser.name),
targetUserGroups: ['user'],
visible: undefined !== config.targetUser.emailable
};
items['uploads'] = {
url: mw.util.getUrl('Special:ListFiles', { user: config.targetUser.name, ilshowall: '1' }),
targetUserGroups: ['user']
};
items['user-groups'] = {
url: mw.util.getUrl('Special:ListUsers', { limit: 1, username: config.targetUser.name }),
targetUserGroups: ['user']
};
items['user-thanks-received'] = {
url: mw.util.getUrl('Special:Log', { user: '', page: 'User:' + config.targetUser.name, type: 'thanks' }),
targetUserGroups: ['user']
};
return items;
})()
};
};
/** ============================================================
* MoreMenu 主引擎
* ============================================================ */
$(function () {
window.MoreMenu = window.MoreMenu || {};
window.MoreMenu.messages = translations; // 注入内置翻译
if (window.moreMenuDebug) {
console.info('[MoreMenu] Debugging enabled.');
}
var api = new mw.Api();
var ignoreI18nWarnings = false;
var isRtl = $('html').prop('dir') === 'rtl';
var leftKey = isRtl ? 'right' : 'left';
var rightKey = isRtl ? 'left' : 'right';
/** 配置对象 */
var config = new function () {
this.project = {
domain: mw.config.get('wgServerName'),
siteName: mw.config.get('wgSiteName'),
dbName: mw.config.get('wgDBname'),
noticeProject: mw.config.get('wgNoticeProject'),
contentLanguage: mw.config.get('wgContentLanguage')
};
this.page = {
name: mw.config.get('wgPageName'),
nsId: mw.config.get('wgNamespaceNumber'),
protected: !!mw.config.get('wgRestrictionEdit') && mw.config.get('wgRestrictionEdit').length ||
!!mw.config.get('wgRestrictionCreate') && mw.config.get('wgRestrictionCreate').length,
id: mw.config.get('wgArticleId'),
movable: !mw.config.get('wgIsMainPage') && !!$('#ca-move').length
};
// 特殊页面处理
if (this.page.nsId === -1 && mw.config.get('wgRelevantPageName') && this.page.name !== mw.config.get('wgRelevantPageName')) {
this.page.name = mw.config.get('wgRelevantPageName');
this.page.id = mw.config.get('wgRelevantArticleId');
this.page.nsId = mw.Title.newFromText(this.page.name).namespace;
}
this.page.escapedName = this.page.name.replace(/[?!'"()*]/g, escape);
this.page.encodedName = encodeURIComponent(this.page.name);
this.currentUser = {
skin: mw.config.get('skin'),
groups: mw.config.get('wgUserGroups'),
groupsData: {},
rights: []
};
this.targetUser = {
name: mw.config.get('wgRelevantUserName') || '',
groups: [],
rights: [],
blocked: false,
ipRange: false
};
// IP 范围处理
if (!this.targetUser.name && mw.config.get('wgCanonicalSpecialPageName') === 'Contributions' && !$('.mw-userpage-userdoesnotexist')[0]) {
this.targetUser.name = mw.config.get('wgTitle').split('/').slice(1).join('/');
this.targetUser.ipRange = true;
this.targetUser.ipv4Range = mw.util.isIPv4Address(this.targetUser.name.split('/')[0]);
}
this.targetUser.escapedName = this.targetUser.name.replace(/[?!'"()*]/g, escape);
this.targetUser.encodedName = encodeURIComponent(this.targetUser.name);
}();
function log(message, level) {
level = level || 'debug';
if (!(window.moreMenuDebug || level !== 'debug')) return;
message = '[MoreMenu] ' + message;
if (['warn', 'error'].indexOf(level) >= 0) {
message += '\nSee https://w.wiki/9Se for documentation.';
}
console[level](message);
}
function msg(key, ignore) {
var translation = translations[key];
if (!translation && !ignore && !ignoreI18nWarnings) {
log('Missing translation for "' + key + '"', 'warn');
}
return translation || key;
}
function msgExists(key) {
return translations[key] !== undefined;
}
function normalizeId(id) {
return id.toLowerCase().replace(/\s+/g, '-');
}
function getItemId(parentKey, itemKey, submenuKey) {
return 'mm-' + normalizeId(parentKey) +
(submenuKey ? '-' + normalizeId(submenuKey) : '') +
(typeof itemKey === 'string' ? '-' + normalizeId(itemKey) : '');
}
/** 翻译已内置,直接 resolve */
function loadTranslations() {
return $.Deferred().resolve();
}
function canAddRemoveGroups(groups, rights) {
if (rights && rights.indexOf('userrights') >= 0) return true;
var valid = groups.some(function (group) {
return config.currentUser.groupsData[group] && config.currentUser.groupsData[group].canAddRemoveGroups;
});
if (!valid) mw.storage.remove('metaUserGroups');
return valid;
}
function hasConditional(permitted, given) {
permitted = $.makeArray(permitted);
given = $.makeArray(given);
if (!permitted.length) return true;
if (!given.length) return false;
return given.some(function (item) { return permitted.indexOf(item) >= 0; });
}
function getItemHtml(parentKey, itemKey, itemData, submenuKey) {
var namespaceExclusion = itemData.namespaceExclude ? !hasConditional(itemData.namespaceExclude, config.page.nsId) : true;
var databaseExclusion = itemData.databaseExclude ? !hasConditional(itemData.databaseExclude, config.page.dbName) : true;
var conditions = {
noticeProject: hasConditional(itemData.noticeProjectRestrict, config.project.noticeProject),
database: hasConditional(itemData.databaseRestrict, config.project.dbName) && databaseExclusion,
namespaceRestrict: hasConditional(itemData.namespaceRestrict, config.page.nsId) && namespaceExclusion,
pageExists: itemData.pageExists && config.page.id > 0 || !itemData.pageExists,
pageDeleted: itemData.pageDeleted ? !config.page.id : true,
pageProtected: itemData.pageProtected ? config.page.protected : true,
pageMovable: itemData.pageMovable ? config.page.movable : true,
currentUserGroups: hasConditional(itemData.currentUserGroups, config.currentUser.groups),
currentUserRights: hasConditional(itemData.currentUserRights, config.currentUser.rights),
currentUserChangeGroups: itemData.currentUserChangeGroups ? canAddRemoveGroups(config.currentUser.groups, config.currentUser.rights) : true,
visibility: itemData.visible !== undefined ? !!itemData.visible : true
};
if (config.targetUser.name) {
conditions.targetUserGroups = hasConditional(itemData.targetUserGroups, config.targetUser.groups);
conditions.targetUserRights = hasConditional(itemData.targetUserRights, config.targetUser.rights);
conditions.targetUserBlocked = itemData.targetUserBlocked !== undefined ? config.targetUser.blocked === itemData.targetUserBlocked : true;
conditions.targetUserChangeGroups = itemData.targetUserChangeGroups ? canAddRemoveGroups(config.targetUser.groups, config.targetUser.rights) : true;
conditions.targetUserIp = itemData.targetUserIp ? mw.util.isIPAddress(config.targetUser.name) || (config.targetUser.ipRange && itemData.targetUserIpRange) : true;
conditions.targetUserIpRange = config.targetUser.ipRange ? itemData.targetUserIpRange : true;
}
for (var c in conditions) {
if (!conditions[c]) {
log(parentKey + '/' + itemKey + ' failed on ' + c);
return '';
}
}
var titleAttr = (msgExists(itemKey + '-desc') || itemData.description)
? ' title="' + (itemData.description || msg(itemKey + '-desc')) + '"' : '';
var styleAttr = itemData.style ? ' style="' + itemData.style + '"' : '';
return '\n<li id="' + getItemId(parentKey, itemKey, submenuKey) + '" class="mm-item mw-list-item">' +
'<a href="' + itemData.url + '"' + titleAttr + styleAttr + '>' +
'<span>' + msg(itemData.title || itemKey) + '</span></a></li>';
}
function addCSS() {
switch (config.currentUser.skin) {
case 'vector':
return mw.util.addCSS(
'.mm-tab .vector-menu-content{height:initial;overflow:initial!important}' +
'.mm-menu .mw-list-item{white-space:nowrap}' +
'.mm-submenu{background:var(--background-color-base,#fff);border:1px solid #a2a9b1;min-width:120px!important;' + rightKey + ':inherit!important;top:-1px!important}' +
'#p-views{padding-left:inherit!important;padding-right:inherit!important}' +
'#p-views .vector-menu-content::after,.rtl #p-views .vector-menu-content::before{display:none!important}' +
'.mm-submenu .mm-item{font-size:inherit!important}'
);
case 'vector-2022':
return mw.util.addCSS(
'#p-page-dropdown .vector-dropdown-content,#p-user-dropdown .vector-dropdown-content{height:initial;overflow-x:initial!important;overflow-y:initial!important;top:35px!important}' +
'#p-page-dropdown .vector-dropdown-content::after,#p-user-dropdown .vector-dropdown-content::after{display:none!important}' +
'.mm-menu .mw-list-item{white-space:nowrap}' +
'.mm-submenu{background:var(--background-color-base,#fff);box-shadow:0 2px 6px -1px rgba(0,0,0,.2);min-width:120px!important;padding:1px 10px;top:-1px!important}' +
'#p-views{padding-left:inherit!important;padding-right:inherit!important}'
);
case 'timeless':
return mw.util.addCSS(
'.mm-submenu-wrapper{cursor:default}' +
'.mm-submenu{background:#f8f9fa;border:1px solid #c8ccd1;box-shadow:0 2px 3px 1px rgba(0,0,0,.05);padding:1.2em 1.5em!important;top:-1.2em;white-space:nowrap;z-index:95}' +
'.mm-submenu::after{border-bottom:8px solid transparent;border-top:8px solid transparent;border-' + leftKey + ':8px solid #c8ccd1;content:"";height:0;padding-' + rightKey + ':4px;position:absolute;top:20px;width:0;' + rightKey + ':-13px}' +
'@media screen and (max-width:1339px) and (min-width:1100px){.mm-submenu::after{border-' + leftKey + ':none;border-' + rightKey + ':8px solid #c8ccd1;padding-' + leftKey + ':4px;padding-' + rightKey + ':inherit;' + rightKey + ':inherit;' + leftKey + ':-35px}}' +
'@media screen and (max-width:850px){.mm-submenu{top:-2.2em}}'
);
case 'monobook':
return mw.util.addCSS(
'.mm-tab{position:relative}' +
'.mm-menu{background:#fff;border-bottom:1px solid #aaa;' + leftKey + ':-1px;margin:0;position:absolute;z-index:99}' +
'.mm-menu~a{z-index:99!important}' +
'.mm-submenu{background:#fff;border-bottom:1px solid #aaa;border-top:1px solid #aaa;font-size:inherit;margin:0;min-width:75px;top:-1px;z-index:95}' +
'.mm-item,.mm-submenu-wrapper{background:#fff!important;border-top:0!important;display:block!important;margin:0!important;padding:0!important;width:100%}' +
'.mm-item a,.mm-submenu-wrapper a{background:transparent!important;text-transform:none!important}' +
'.mm-menu a:hover{text-decoration:underline!important}'
);
case 'modern':
return mw.util.addCSS(
'.mm-menu,.mm-submenu{background:#f0f0f0!important;border:solid 1px #666}' +
'.mm-menu{border-top:none;position:absolute;z-index:99}' +
'.mm-submenu-wrapper>a{cursor:default!important}' +
'.mm-item,.mm-submenu-wrapper{display:block!important;float:none!important;height:inherit!important;margin:0!important;padding:0!important}' +
'.mm-menu a{display:inline-block;padding:3px 10px!important;text-transform:none!important;text-decoration:none!important;white-space:nowrap;width:100%}' +
'.mm-menu a:hover{text-decoration:underline!important}' +
'.mm-submenu{' + leftKey + ':100%;min-width:120px!important;top:0}'
);
default:
return null;
}
}
function getSubmenuCss($element) {
switch (config.currentUser.skin) {
case 'vector':
case 'vector-2022':
var css = {}; css[leftKey] = $element.outerWidth(); return css;
case 'timeless':
var css2 = {};
css2[$(window).width() <= 1339 && $(window).width() >= 1100 ? leftKey : rightKey] = $element.outerWidth() + 11;
return css2;
case 'monobook':
var css3 = {}; css3[leftKey] = $element.outerWidth() - 2; return css3;
default:
return {};
}
}
function addListeners() {
$('.mm-submenu-wrapper').each(function () {
var $this = $(this);
$this.off('mouseenter mouseleave');
$this.on('mouseenter', function () {
$(this).find('.mm-submenu').css(getSubmenuCss($(this))).show();
}).on('mouseleave', function () {
$(this).find('.mm-submenu').hide();
});
});
}
function sortByTranslation(i18nKeys) {
return i18nKeys.sort(function (a, b) {
var nameA = msg(a).toLowerCase();
var nameB = msg(b).toLowerCase();
return nameA < nameB ? -1 : nameA > nameB ? 1 : 0;
});
}
function sortItems(items) {
var itemKeys = Object.keys(items);
var submenus = itemKeys.filter(function (k) { return !items[k].url; });
var sortedItemKeys = sortByTranslation(itemKeys.filter(function (k) { return !!items[k].url; }));
var newItemKeys = sortedItemKeys.slice();
sortedItemKeys.forEach(function (itemKey) {
var target = items[itemKey].insertAfter;
var newIndex;
if (target === false) newIndex = 0;
else if (target === true) newIndex = itemKeys.length;
else if (!target) return;
else {
newIndex = newItemKeys.indexOf(target);
newIndex = newIndex === -1 ? newItemKeys.length : newIndex + 1;
}
newItemKeys.splice(newItemKeys.indexOf(itemKey), 1);
newItemKeys.splice(newIndex, 0, itemKey);
});
return submenus.concat(newItemKeys);
}
function getMenuHtml(parentKey, items, submenuKey) {
var html = '';
var submenuClasses = (config.currentUser.skin === 'vector' || config.currentUser.skin === 'vector-2022') ? 'vector-menu-content-list' : '';
sortItems(items).forEach(function (itemKey) {
var item = items[itemKey];
var itemHtml = '';
if (!item.url) {
// 子菜单
itemHtml = '\n<li style="position:relative;" id="' + getItemId(parentKey, itemKey) + '" class="mm-submenu-wrapper mw-list-item">' +
'<a style="font-weight:bold"><span>' + msg(itemKey) + '…</span></a>' +
'<ul class="menu mm-submenu ' + submenuClasses + '" style="display:none;position:absolute">';
sortItems(item).forEach(function (subItemKey) {
itemHtml += getItemHtml(parentKey, subItemKey, item[subItemKey], itemKey);
});
itemHtml += '</ul></li>';
if ($(itemHtml).find('.mm-submenu li').length === 0) itemHtml = '';
} else {
itemHtml = getItemHtml(parentKey, itemKey, item, submenuKey);
}
html += itemHtml;
});
return html;
}
function drawMenuVector(parentKey, html) {
var menu = mw.util.addPortlet('p-' + parentKey, msg(parentKey), '#p-cactions');
menu.classList.add('mm-' + parentKey, 'mm-tab');
mw.util.addPortletLink('p-' + parentKey, '#', 'temp');
var menuContent = menu.querySelector('ul');
menuContent.innerHTML = html;
menuContent.classList.add('mm-menu');
}
function drawMenuTimeless(parentKey, html) {
html = '<div role="navigation" class="mw-portlet mm-' + parentKey + ' mm-tab" id="p-' + parentKey + '" aria-labelledby="p-' + parentKey + '-label">' +
'<h3 id="p-' + parentKey + '-label">' + msg(parentKey) + '</h3>' +
'<div class="mw-portlet-body"><ul class="mm-menu">' + html + '</ul></div></div>';
if ($('#p-cactions').length) $(html).insertBefore($('#p-cactions'));
else $('#page-tools .sidebar-inner').prepend(html);
}
function drawMenuMonobook(parentKey, html) {
html = '<li id="ca-' + parentKey + '" class="mm-' + parentKey + ' mm-tab">' +
'<a href="javascript:void(0)">' + msg(parentKey) + '</a>' +
'<ul class="mm-menu" style="display:none">' + html + '</ul></li>';
var $tab = $(html).insertAfter($('#ca-nstab-special, #ca-edit, #ca-ve-edit, #ca-page, #ca-viewsource, #ca-talk').last());
var $menu = $tab.find('.mm-menu');
$tab.on('mouseenter', function () { $menu.show(); $tab.find('> a').css('z-index', 99); })
.on('mouseleave', function () { $menu.hide(); $tab.find('> a').css('z-index', 'inherit'); });
}
function drawMenuModern(parentKey, html) {
html = '<li id="ca-' + parentKey + '" class="mm-' + parentKey + ' mm-tab">' +
'<a href="javascript:void(0)">' + msg(parentKey) + '</a>' +
'<ul class="mm-menu" style="display:none">' + html + '</ul></li>';
var $tab = $(html).insertAfter($('#ca-nstab-special, #ca-edit, #ca-ve-edit, #ca-page, #ca-viewsource, #ca-talk').last());
var $menu = $tab.find('.mm-menu');
$menu.css({
left: isRtl ? $tab.position().left - $menu.width() + $tab.width() + 7 : $tab.position().left,
top: $tab.offset().top + $tab.outerHeight()
});
$tab.on('mouseenter', function () { $menu.show(); }).on('mouseleave', function () { $menu.hide(); });
}
function drawMenus() {
var menus = {};
if (config.page.nsId >= 0) $.extend(menus, pageMenu(config));
if (config.targetUser.name) $.extend(menus, userMenu(config));
addCSS();
Object.keys(menus).forEach(function (key) {
var html = getMenuHtml(key, menus[key]);
switch (config.currentUser.skin) {
case 'vector-2022': case 'vector': drawMenuVector(key, html); break;
case 'monobook': drawMenuMonobook(key, html); break;
case 'modern': drawMenuModern(key, html); break;
case 'timeless': drawMenuTimeless(key, html); break;
default: log("'" + config.currentUser.skin + "' is not a supported skin.", 'error');
}
});
addListeners();
}
function handleHistoryAndWatchLinks(replace) {
replace = replace || false;
var monobookModern = ['monobook', 'modern'].indexOf(config.currentUser.skin) !== -1;
var $histLink = $('#ca-history');
var $watchLink = $('.mw-watchlink');
var $moveLink = $('#ca-move');
if (replace) {
if (monobookModern) {
$('#mm-page-watch').replaceWith($watchLink.addClass('mm-item').prop('id', 'mm-page-watch').show());
$('#mm-page-history').replaceWith($histLink.addClass('mm-item').prop('id', 'mm-page-history').show());
}
$('#mm-page-move-page').replaceWith($moveLink.addClass('mm-item').prop('id', 'mm-page-move-page').show());
return;
}
translations.watch = $watchLink.text() || 'watch';
translations.history = $histLink.text() || 'history';
if (monobookModern) { $watchLink.hide(); $histLink.hide(); }
$moveLink.hide();
}
function addMutationObserver(ids) {
if (typeof ids === 'string') ids = [ids];
ids.forEach(function (id) {
var $parent = $(id);
if (!$parent.length) return;
var $menu = $parent.find('ul');
var menuIsEmpty = function () { return $menu.html().trim() === ''; };
if (menuIsEmpty()) $parent.hide();
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.addedNodes.length) $parent.show();
else if (mutation.removedNodes.length && menuIsEmpty()) $parent.hide();
});
}).observe($menu.get(0), { childList: true });
});
}
function observeCactionsMenu() {
var reAddCount = parseInt(mw.storage.get('mmNativeMenuUsage'), 10) || 0;
if (['vector', 'vector-2022', 'timeless'].indexOf(config.currentUser.skin) === -1 ||
window.MoreMenu.disableAutoRemoval || reAddCount >= 5) return;
addMutationObserver('#p-cactions');
setTimeout(function () {
if ($('#p-cactions').find('li').length) {
mw.storage.set('mmNativeMenuUsage', reAddCount + 1, 60 * 60 * 24);
}
}, 5000);
}
function removeNativeLinks() {
var linksToRemove = ['#ca-protect', '#ca-unprotect', '#ca-delete', '#ca-undelete'];
if (config.currentUser.skin === 'timeless') {
linksToRemove = linksToRemove.concat(['#t-contributions', '#t-log', '#t-blockip', '#t-emailuser', '#t-userrights', '#t-info', '#t-pagelog']);
}
$(linksToRemove.join(',')).remove();
handleHistoryAndWatchLinks();
observeCactionsMenu();
if (config.currentUser.skin === 'timeless') addMutationObserver('#p-userpagetools');
}
function removeBlockLogLink() {
api.get({
action: 'query',
list: 'logevents',
letype: 'block',
letitle: 'User:' + config.targetUser.name,
lelimit: 1
}).done(function (data) {
if (!data.query.logevents.length) $('#mm-user-blocks-view-block-log').remove();
});
}
function removeUnneededLinks() {
handleHistoryAndWatchLinks(true);
if (!config.targetUser.name) return;
removeBlockLogLink();
addMutationObserver(['#mm-page-analysis', '#mm-page-search', '#mm-user-blocks', '#mm-user-analysis']);
if (config.targetUser.ipRange) {
$('#mm-user-user-logs').remove();
$('#mm-user-deleted-contributions').remove();
$('#mm-user-suppressed-contributions').remove();
}
}
function getPromises() {
var promises = new Array(4);
if (config.targetUser.name && !config.targetUser.ipv4Range) {
promises[0] = api.get({
action: 'query',
list: 'users|blocks',
ususers: config.targetUser.name,
bkusers: config.targetUser.name,
usprop: 'blockinfo|groups|rights|emailable',
bkprop: 'id'
});
}
config.currentUser.rights = JSON.parse(mw.storage.session.getObject('mmUserRights'));
if (!config.currentUser.rights) promises[1] = mw.user.getRights();
config.currentUser.groupsData = JSON.parse(mw.storage.session.getObject('mmMetaUserGroups'));
if (!config.currentUser.groupsData) {
promises[2] = api.get({ action: 'query', meta: 'siteinfo', siprop: 'usergroups' });
}
promises[3] = loadTranslations();
return promises;
}
function init() {
$.when.apply(this, getPromises()).done(function (targetUserData, userRightsData, metaData) {
if (targetUserData) {
$.extend(config.targetUser, targetUserData.query ? targetUserData.query.users[0] : targetUserData[0].query.users[0]);
var blocks = targetUserData.query ? targetUserData.query.blocks : targetUserData[0].query.blocks;
if (blocks.length) {
config.targetUser.blocked = true;
config.targetUser.blockid = blocks[0].id;
}
}
if (userRightsData) {
var rights = userRightsData.query ? userRightsData : userRightsData.slice ? userRightsData : userRightsData[0];
if (rights.slice) {
mw.storage.session.setObject('mmUserRights', JSON.stringify(rights));
config.currentUser.rights = rights.slice();
}
}
if (metaData) {
config.currentUser.groupsData = {};
var groups = metaData.query ? metaData.query.usergroups : metaData[0].query.usergroups;
groups.forEach(function (el) {
config.currentUser.groupsData[el.name] = {
rights: el.rights,
canAddRemoveGroups: !!el.add || !!el.remove
};
});
mw.storage.session.setObject('mmMetaUserGroups', JSON.stringify(config.currentUser.groupsData));
}
removeNativeLinks();
drawMenus();
removeUnneededLinks();
mw.hook('moremenu.ready').fire(config);
});
}
/** 公开 API */
MoreMenu.addItemCore = function (menu, items, insertAfter, submenu) {
if (!$('.mm-' + menu).length) return;
var menuId = submenu ? '#mm-' + menu + '-' + submenu : '.mm-' + menu;
var $menu = $(menuId);
if (!$menu.length) { log("'" + menu + (submenu ? ' ' + submenu : '') + "' not found.", 'warn'); return; }
ignoreI18nWarnings = true;
if (Object.keys(items).length !== 1) {
log('Multiple items given, ignoring all but the first.', 'warn');
items = items[Object.keys(items)[0]];
}
var $html = $(getMenuHtml(menu, items, submenu));
var $beforeItem = (function () {
var id = getItemId(menu, insertAfter || '', submenu);
return $('#' + $.escapeSelector(id));
})();
var isSubmenuItem = $beforeItem.parents('.mm-submenu').length;
if ($beforeItem.length && (!submenu || (submenu && isSubmenuItem))) {
$beforeItem.after($html);
} else {
var newI18nKey = normalizeId(Object.keys(items)[0]);
var newId = getItemId(menu, newI18nKey, submenu);
var $topItems = submenu ? $(menuId).find('.mm-submenu > .mm-item') : $(menuId).find('.mm-menu > .mm-item');
if (insertAfter === true) { $topItems.last().after($html); ignoreI18nWarnings = false; addListeners(); return; }
if (insertAfter === false) { $topItems.first().before($html); ignoreI18nWarnings = false; addListeners(); return; }
if (!$beforeItem.length && insertAfter) log('Invalid insertAfter.', 'warn');
var ids = $topItems.toArray().map(function (el) { return el.id; }).concat([newId]);
var i18nKeys = sortByTranslation(ids.map(function (id) {
return id.replace(new RegExp('^mm-' + menu + '-' + (submenu ? submenu + '-' : '')), '');
}));
var idx = i18nKeys.indexOf(newI18nKey) - 1;
if (idx < 0) $('#' + $.escapeSelector(ids[0])).before($html);
else $('#' + $.escapeSelector(getItemId(menu, i18nKeys[idx], submenu))).after($html);
}
addListeners();
ignoreI18nWarnings = false;
};
MoreMenu.addItem = function (menu, name, data, insertAfter) {
var item = {}; item[name] = data;
MoreMenu.addItemCore(menu, item, insertAfter);
};
MoreMenu.addSubmenuItem = function (menu, submenu, name, data, insertAfter) {
var item = {}; item[name] = data;
MoreMenu.addItemCore(menu, item, insertAfter, submenu);
};
MoreMenu.addSubmenu = function (menu, name, items, insertAfter) {
var item = {}; item[name] = items;
MoreMenu.addItemCore(menu, item, insertAfter);
};
MoreMenu.addLink = function (menu, name, url, insertAfter) {
var item = {}; item[name] = { url: url };
MoreMenu.addItemCore(menu, item, insertAfter);
};
MoreMenu.addSubmenuLink = function (menu, submenu, name, url, insertAfter) {
var item = {}; item[name] = { url: url };
MoreMenu.addItemCore(menu, item, insertAfter, submenu);
};
init();
});
})();