MediaWiki:Common.js:修订间差异
外观
第1行: | 第1行: | ||
const | (function () { | ||
const | /* -------------------- 1⃣ 颜色工具 -------------------- */ | ||
(() => { | |||
const HEX_VAR = '--background-color-base'; | |||
const RGB_VAR = '--background-color-rgb'; | |||
// hex -> `r, g, b` | |||
const hex2rgb = hex => | |||
hex | |||
.replace(/^#/, '') | |||
.replace(/^([0-9a-f])([0-9a-f])([0-9a-f])$/i, '$1$1$2$2$3$3') // 3 -> 6 位 | |||
.match(/.{2}/g) // ["ff","aa","00"] | |||
.map(n => parseInt(n, 16)) // [255,170,0] | |||
.join(', '); // "255, 170, 0" | |||
const root = document.documentElement; | |||
const rgb = hex2rgb(getComputedStyle(root).getPropertyValue(HEX_VAR).trim()); | |||
root.style.setProperty(RGB_VAR, rgb); | |||
// console.debug(`[theme] ${HEX_VAR} -> ${RGB_VAR}:`, rgb); | |||
})(); | |||
/* -------------------- 2⃣ 上传页跳转弹窗 -------------------- */ | |||
(() => { | |||
mw.loader.using(['oojs-ui', 'mediawiki.util', 'mediawiki.storage'], () => { | |||
if (mw.config.get('wgCanonicalSpecialPageName') !== 'Upload') return; | |||
); | |||
const KEY = 'uploadPreference'; | |||
const store = mw.storage || window.localStorage; | |||
const choice = store.get(KEY); | |||
if (choice === 'wizard') return location.replace(mw.util.getUrl('Special:UploadWizard')); | |||
if (choice === 'classic') return; // 直接停留 | |||
/* ---- 弹窗 ---- */ | |||
class UploadDialog extends OO.ui.ProcessDialog { | |||
static static = { | |||
name : 'UploadDialog', | |||
title : '请选择上传方式', | |||
actions: [ { action: 'classic', label: '❌ 使用传统上传(更快)', flags: 'safe' } ] | |||
}; | |||
initialize() { | |||
super.initialize(); | |||
const remember = new OO.ui.CheckboxInputWidget(); | |||
this._remember = remember; | |||
const panel = new OO.ui.PanelLayout({ padded: true }); | |||
const wizardBtn = new OO.ui.ButtonWidget({ | |||
label : '✅ 使用上传向导(推荐)', | |||
flags : ['primary', 'progressive'], | |||
href : mw.util.getUrl('Special:UploadWizard'), | |||
target : '_self' | |||
}); | |||
wizardBtn.on('click', () => remember.isSelected() && store.set(KEY, 'wizard')); | |||
panel.$element.append( | |||
$('<p>').text('请选择要使用的上传方式:'), | |||
buildCard('✅ 上传向导(推荐)', [ | |||
'支持多文件批量上传', | |||
'显示上传进度条', | |||
'信息填写更完整' | |||
], wizardBtn.$element), | |||
buildCard('⚡️ 传统上传方式', [ | |||
'界面更简单', | |||
'加载速度快', | |||
'适合上传单个文件' | |||
]), | |||
new OO.ui.FieldLayout(remember, { | |||
label : '记住我的选择,下次不再提示', | |||
align : 'inline' | |||
}).$element.css('margin-top', '15px') | |||
); | |||
this.$body.append(panel.$element); | |||
} | |||
getActionProcess(action) { | |||
if (action === 'classic') { | |||
this._remember.isSelected() && store.set(KEY, 'classic'); | |||
return new OO.ui.Process(() => this.close()); | |||
} | |||
return super.getActionProcess(action); | |||
} | |||
} | } | ||
// | /* helper – 卡片组件 */ | ||
const buildCard = (title, points, extra = null) => $('<div>') | |||
.addClass('upload-card') | |||
.append( | |||
$('<strong>').text(title), | |||
$('<ul>').append(points.map(t => $('<li>').text(t))), | |||
extra && $('<div>').css('margin-top', '8px').append(extra) | |||
); | |||
/* 注入样式 */ | |||
mw.util.addCSS(` | |||
.upload-card{ | |||
margin:12px 0; | |||
padding:10px; | |||
// | border-radius:5px; | ||
background:#f8f9fa; | |||
border:1px solid #ccc; | |||
} | } | ||
.upload-card + .upload-card{ | |||
background:#fff; | |||
border-style:dashed; | |||
margin-top:15px; | |||
} | } | ||
`); | |||
/* 打开弹窗 */ | |||
const wm = new OO.ui.WindowManager(); | |||
$(document.body).append(wm.$element); | |||
wm.openWindow(new UploadDialog()); | |||
}) | }); | ||
})(); | |||
/* -------------------- 3⃣ 全局 Loading 指示器 -------------------- */ | |||
(() => { | |||
const IMG = 'https://wiki.ottohub.cn/images/0/02/Loading.png'; | |||
const TIMEOUT_INIT = 5000; // 首次进入最大等待 | |||
const TIMEOUT_CLICK = 15000; // 点链接最大等待 | |||
let timer; // 全局移除计时 | |||
let visible = false; | |||
/* 全局 style 只插一次 */ | |||
const styleText = ` | |||
#globalLoading{ | |||
position:fixed;bottom:20px;right:20px;z-index:99999; | |||
width:256px;height:256px;opacity:0;pointer-events:none; | |||
transition:opacity .25s; | |||
} | } | ||
#globalLoading.show{opacity:1;} | |||
`; | |||
document.head.insertAdjacentHTML('beforeend', `<style>${styleText}</style>`); | |||
const createNode = () => { | |||
const div = document.createElement('div'); | |||
div.id = 'globalLoading'; | |||
div.innerHTML = `<img src="${IMG}" alt="Loading" style="width:100%;height:100%;object-fit:contain">`; | |||
return div; | |||
}; | |||
const show = why => { | |||
if (visible) return; | |||
visible = true; | |||
// console.debug('[loading] show –', why); | |||
const node = document.getElementById('globalLoading') || createNode(); | |||
node.classList.add('show'); | |||
if (!node.isConnected) document.body.appendChild(node); | |||
resetTimer(TIMEOUT_CLICK, 'fallback-click'); | |||
}; | |||
} | |||
const hide = why => { | |||
if (!visible) return; | |||
visible = false; | |||
// console.debug('[loading] hide –', why); | |||
const node = document.getElementById('globalLoading'); | |||
node?.classList.remove('show'); | |||
resetTimer(); // 清计时 | |||
}; | |||
const resetTimer = (ms, reason) => { | |||
clearTimeout(timer); | |||
if (ms) timer = setTimeout(() => hide(reason), ms); | |||
}; | |||
/* ---- 首次页加载 ---- */ | |||
if (document.readyState === 'loading') { | |||
show('initial'); | |||
window.addEventListener('load', () => hide('window.load'), { once: true }); | |||
resetTimer(TIMEOUT_INIT, 'fallback-init'); | |||
} | } | ||
/* ---- 链接点击(传统跳转)---- */ | |||
document.addEventListener('click', e => { | |||
const a = e.target.closest('a'); | |||
if (!a || // 非 <a> | |||
a.target === '_blank' || // 新窗口 | |||
a.href.startsWith('javascript:') || | |||
a.href.startsWith('#') || | |||
. | a.href.startsWith('mailto:') || | ||
a.href.startsWith('tel:') | |||
// | ) return; | ||
show('link'); | |||
}, true); | |||
/* ---- 暴露给 SPA / AJAX 主动调用 ---- */ | |||
window.globalLoading = { show, hide }; | |||
})(); | |||
})(); | })(); |
2025年5月18日 (日) 14:12的版本
(function () { /* -------------------- 1⃣ 颜色工具 -------------------- */ (() => { const HEX_VAR = '--background-color-base'; const RGB_VAR = '--background-color-rgb'; // hex -> `r, g, b` const hex2rgb = hex => hex .replace(/^#/, '') .replace(/^([0-9a-f])([0-9a-f])([0-9a-f])$/i, '$1$1$2$2$3$3') // 3 -> 6 位 .match(/.{2}/g) // ["ff","aa","00"] .map(n => parseInt(n, 16)) // [255,170,0] .join(', '); // "255, 170, 0" const root = document.documentElement; const rgb = hex2rgb(getComputedStyle(root).getPropertyValue(HEX_VAR).trim()); root.style.setProperty(RGB_VAR, rgb); // console.debug(`[theme] ${HEX_VAR} -> ${RGB_VAR}:`, rgb); })(); /* -------------------- 2⃣ 上传页跳转弹窗 -------------------- */ (() => { mw.loader.using(['oojs-ui', 'mediawiki.util', 'mediawiki.storage'], () => { if (mw.config.get('wgCanonicalSpecialPageName') !== 'Upload') return; const KEY = 'uploadPreference'; const store = mw.storage || window.localStorage; const choice = store.get(KEY); if (choice === 'wizard') return location.replace(mw.util.getUrl('Special:UploadWizard')); if (choice === 'classic') return; // 直接停留 /* ---- 弹窗 ---- */ class UploadDialog extends OO.ui.ProcessDialog { static static = { name : 'UploadDialog', title : '请选择上传方式', actions: [ { action: 'classic', label: '❌ 使用传统上传(更快)', flags: 'safe' } ] }; initialize() { super.initialize(); const remember = new OO.ui.CheckboxInputWidget(); this._remember = remember; const panel = new OO.ui.PanelLayout({ padded: true }); const wizardBtn = new OO.ui.ButtonWidget({ label : '✅ 使用上传向导(推荐)', flags : ['primary', 'progressive'], href : mw.util.getUrl('Special:UploadWizard'), target : '_self' }); wizardBtn.on('click', () => remember.isSelected() && store.set(KEY, 'wizard')); panel.$element.append( $('<p>').text('请选择要使用的上传方式:'), buildCard('✅ 上传向导(推荐)', [ '支持多文件批量上传', '显示上传进度条', '信息填写更完整' ], wizardBtn.$element), buildCard('⚡️ 传统上传方式', [ '界面更简单', '加载速度快', '适合上传单个文件' ]), new OO.ui.FieldLayout(remember, { label : '记住我的选择,下次不再提示', align : 'inline' }).$element.css('margin-top', '15px') ); this.$body.append(panel.$element); } getActionProcess(action) { if (action === 'classic') { this._remember.isSelected() && store.set(KEY, 'classic'); return new OO.ui.Process(() => this.close()); } return super.getActionProcess(action); } } /* helper – 卡片组件 */ const buildCard = (title, points, extra = null) => $('<div>') .addClass('upload-card') .append( $('<strong>').text(title), $('<ul>').append(points.map(t => $('<li>').text(t))), extra && $('<div>').css('margin-top', '8px').append(extra) ); /* 注入样式 */ mw.util.addCSS(` .upload-card{ margin:12px 0; padding:10px; border-radius:5px; background:#f8f9fa; border:1px solid #ccc; } .upload-card + .upload-card{ background:#fff; border-style:dashed; margin-top:15px; } `); /* 打开弹窗 */ const wm = new OO.ui.WindowManager(); $(document.body).append(wm.$element); wm.openWindow(new UploadDialog()); }); })(); /* -------------------- 3⃣ 全局 Loading 指示器 -------------------- */ (() => { const IMG = 'https://wiki.ottohub.cn/images/0/02/Loading.png'; const TIMEOUT_INIT = 5000; // 首次进入最大等待 const TIMEOUT_CLICK = 15000; // 点链接最大等待 let timer; // 全局移除计时 let visible = false; /* 全局 style 只插一次 */ const styleText = ` #globalLoading{ position:fixed;bottom:20px;right:20px;z-index:99999; width:256px;height:256px;opacity:0;pointer-events:none; transition:opacity .25s; } #globalLoading.show{opacity:1;} `; document.head.insertAdjacentHTML('beforeend', `<style>${styleText}</style>`); const createNode = () => { const div = document.createElement('div'); div.id = 'globalLoading'; div.innerHTML = `<img src="${IMG}" alt="Loading" style="width:100%;height:100%;object-fit:contain">`; return div; }; const show = why => { if (visible) return; visible = true; // console.debug('[loading] show –', why); const node = document.getElementById('globalLoading') || createNode(); node.classList.add('show'); if (!node.isConnected) document.body.appendChild(node); resetTimer(TIMEOUT_CLICK, 'fallback-click'); }; const hide = why => { if (!visible) return; visible = false; // console.debug('[loading] hide –', why); const node = document.getElementById('globalLoading'); node?.classList.remove('show'); resetTimer(); // 清计时 }; const resetTimer = (ms, reason) => { clearTimeout(timer); if (ms) timer = setTimeout(() => hide(reason), ms); }; /* ---- 首次页加载 ---- */ if (document.readyState === 'loading') { show('initial'); window.addEventListener('load', () => hide('window.load'), { once: true }); resetTimer(TIMEOUT_INIT, 'fallback-init'); } /* ---- 链接点击(传统跳转)---- */ document.addEventListener('click', e => { const a = e.target.closest('a'); if (!a || // 非 <a> a.target === '_blank' || // 新窗口 a.href.startsWith('javascript:') || a.href.startsWith('#') || a.href.startsWith('mailto:') || a.href.startsWith('tel:') ) return; show('link'); }, true); /* ---- 暴露给 SPA / AJAX 主动调用 ---- */ window.globalLoading = { show, hide }; })(); })();