「MediaWiki:Common.js」:修訂間差異
無編輯摘要 |
無編輯摘要 |
||
| (未顯示同一使用者於中間所作的 5 次修訂) | |||
| 第1行: | 第1行: | ||
/** | /** | ||
* MediaWiki:Common.js 強化完整版 | * MediaWiki:Common.js 強化完整版 | ||
* | * 適用對象:Quickpedia wiki | ||
* | * 功能:語系導向、手動字詞轉換、自動暫存(防 Cloudflare 斷線)、持久連線心跳包、深色模式、時鐘 | ||
*/ | */ | ||
mw.loader.using(['mediawiki.util', 'mediawiki.Uri']).then(function () { | mw.loader.using(['mediawiki.util', 'mediawiki.Uri', 'mediawiki.api']).then(function () { | ||
$(function () { | $(function () { | ||
/* ============================================================ | /* ============================================================ | ||
* ① 語系自動導向與切換 (針對訪客預設 zh | * ① 語系自動導向與切換 (針對訪客預設 zh) | ||
* ============================================================ */ | * ============================================================ */ | ||
var isAnon = mw.config.get('wgUserName') === null; | var isAnon = mw.config.get('wgUserName') === null; | ||
var currentVariant = mw.util.getParamValue('variant'); | var currentVariant = mw.util.getParamValue('variant'); | ||
// | // 訪客預設導向中文 (zh) | ||
if (isAnon && !currentVariant && mw.config.get('wgAction') === 'view') { | if (isAnon && !currentVariant && mw.config.get('wgAction') === 'view') { | ||
var url = new mw.Uri(window.location.href); | var url = new mw.Uri(window.location.href); | ||
url.query.variant = 'zh | url.query.variant = 'zh'; | ||
window.location.replace(url.toString()); | window.location.replace(url.toString()); | ||
return; | return; | ||
| 第25行: | 第25行: | ||
// 建立右上角切換按鈕 | // 建立右上角切換按鈕 | ||
var variants = [ | var variants = [ | ||
{ code: 'zh', label: '中文' }, | |||
{ code: 'zh-tw', label: '繁體' }, | { code: 'zh-tw', label: '繁體' }, | ||
{ code: 'zh-cn', label: '简体' } | { code: 'zh-cn', label: '简体' } | ||
]; | ]; | ||
var $ | var $langContainer = $('<span>').attr('id', 'custom-variant-switches'); | ||
variants.forEach(function (v, i) { | variants.forEach(function (v, i) { | ||
var $link = $('<a>') | var $link = $('<a>') | ||
| 第35行: | 第36行: | ||
.css({ | .css({ | ||
cursor: 'pointer', | cursor: 'pointer', | ||
padding: '0 | padding: '0 5px', | ||
color: currentVariant === v.code ? '#000' : '#0645ad', | color: currentVariant === v.code ? '#000' : '#0645ad', | ||
fontWeight: currentVariant === v.code ? 'bold' : 'normal' | fontWeight: currentVariant === v.code ? 'bold' : 'normal' | ||
}) | }) | ||
.on('click', function () { | .on('click', function (e) { | ||
e.preventDefault(); | |||
var url = new mw.Uri(window.location.href); | var url = new mw.Uri(window.location.href); | ||
url.query.variant = v.code; | url.query.variant = v.code; | ||
window.location.href = url.toString(); | window.location.href = url.toString(); | ||
}); | }); | ||
$ | $langContainer.append($link); | ||
if (i < variants.length - 1) $ | if (i < variants.length - 1) $langContainer.append('|'); | ||
}); | }); | ||
mw.util.addPortletLink('p-personal', '#', | mw.util.addPortletLink('p-personal', '#', '', 'pt-variant-switch', '切換語系', '#pt-logout'); | ||
$('#pt-variant-switch').append($langContainer); | |||
/* ============================================================ | /* ============================================================ | ||
* ② | * ② 手動字詞轉換 (確保站名正確顯示) | ||
* ============================================================ */ | |||
function applyManualTranslation() { | |||
var v = mw.util.getParamValue('variant') || mw.config.get('wgUserVariant') || mw.config.get('wgUserLanguage'); | |||
if (v === 'zh-hans' || v === 'zh-cn') { | |||
$('h1#firstHeading, #p-logo-text, .mw-wiki-title, title').each(function() { | |||
var t = $(this).text(); | |||
$(this).text(t.replace(/Quickpedia wiki/g, 'Quickpedia wiki')); | |||
}); | |||
} | |||
} | |||
applyManualTranslation(); | |||
/* ============================================================ | |||
* ③ 自動暫存與持久編輯 (對抗 Cloudflare 驗證中斷) | |||
* ============================================================ */ | * ============================================================ */ | ||
if (['edit', 'submit'].indexOf(mw.config.get('wgAction')) !== -1) { | if (['edit', 'submit'].indexOf(mw.config.get('wgAction')) !== -1) { | ||
| 第59行: | 第77行: | ||
var $textbox = $('#wpTextbox1'); | var $textbox = $('#wpTextbox1'); | ||
// | // A. 心跳包:每 2 分鐘向伺服器發送請求,防止 Session 過期或驗證跳出 | ||
setInterval(function() { | |||
new mw.Api().get({ | |||
action: 'query', | |||
titles: pageName, | |||
prop: 'info' | |||
}).done(function() { | |||
console.log('Quickpedia wiki 連線維持中...'); | |||
}); | |||
}, 120000); | |||
// B. 高頻率自動暫存:每 20 秒存一次草稿 | |||
setInterval(function () { | setInterval(function () { | ||
var content = $textbox.val(); | var content = $textbox.val(); | ||
if (content && content.length > 10) { | if (content && content.length > 10) { | ||
localStorage.setItem(storageKey, JSON.stringify({ | |||
text: content, | text: content, | ||
timestamp: Date.now() | timestamp: Date.now() | ||
} | })); | ||
} | } | ||
}, | }, 20000); | ||
// | // C. 檢查並還原草稿 | ||
var savedCache = localStorage.getItem(storageKey); | var savedCache = localStorage.getItem(storageKey); | ||
if (savedCache) { | if (savedCache) { | ||
var data = JSON.parse(savedCache); | var data = JSON.parse(savedCache); | ||
if (Date.now() - data.timestamp < 86400000 && $textbox.val().length < 50) { | if (Date.now() - data.timestamp < 86400000 && $textbox.val().length < 50) { | ||
if (confirm('偵測到未儲存草稿(可能因 Cloudflare 驗證中斷),是否還原?')) { | |||
$textbox.val(data.text); | $textbox.val(data.text); | ||
} | } | ||
| 第85行: | 第110行: | ||
} | } | ||
// | // D. 離開頁面警告 | ||
$('#wpSave, #wpSaveWidget').on('click', function() { | window.onbeforeunload = function() { | ||
if ($textbox.val().length > 50) return "內容尚未儲存!"; | |||
}; | |||
// E. 儲存後清除暫存 | |||
$('#wpSave, #wpSaveWidget, input#wpSave').on('click', function() { | |||
localStorage.removeItem(storageKey); | localStorage.removeItem(storageKey); | ||
window.onbeforeunload = null; | |||
}); | }); | ||
} | } | ||
| 第93行: | 第124行: | ||
/* ============================================================ | /* ============================================================ | ||
* | * ④ 深色模式 | ||
* ============================================================ */ | * ============================================================ */ | ||
var darkBtn = mw.util.addPortletLink('p-personal', '#', '深色模式', 'pt-darkmode', ' | var darkBtn = mw.util.addPortletLink('p-personal', '#', '深色模式', 'pt-darkmode', '切換深色模式', '#pt-variant-switch'); | ||
$(darkBtn).on('click', function (e) { | $(darkBtn).on('click', function (e) { | ||
e.preventDefault(); | e.preventDefault(); | ||
$('body').toggleClass('dark-mode'); | $('body').toggleClass('dark-mode'); | ||
localStorage.setItem('wiki-dark-mode', $('body').hasClass('dark-mode') ? '1' : '0'); | |||
}); | }); | ||
if (localStorage.getItem('wiki-dark-mode') === '1') { | if (localStorage.getItem('wiki-dark-mode') === '1') { | ||
$('body').addClass('dark-mode'); | $('body').addClass('dark-mode'); | ||
| 第110行: | 第139行: | ||
/* ============================================================ | /* ============================================================ | ||
* | * ⑤ 即時時鐘 | ||
* ============================================================ */ | * ============================================================ */ | ||
var $clock = $('<li>').attr('id', 'pt-clock').css({ | var $clock = $('<li>').attr('id', 'pt-clock').css({ | ||
fontWeight: 'bold', | fontWeight: 'bold', | ||
marginLeft: '10px', | marginLeft: '10px', | ||
color: '#54595d' | |||
}); | }); | ||
function | function updateClock() { | ||
var now = new Date(); | var now = new Date(); | ||
$clock.text(now.toLocaleTimeString('zh-TW', { hour12: false })); | |||
} | } | ||
$('#p-personal ul').append($clock); | $('#p-personal ul').append($clock); | ||
setInterval( | setInterval(updateClock, 1000); | ||
updateClock(); | |||
}); | }); | ||
}); | }); | ||
於 2026年9月9日 (三) 03:44 的最新修訂
/**
* MediaWiki:Common.js 強化完整版
* 適用對象:Quickpedia wiki
* 功能:語系導向、手動字詞轉換、自動暫存(防 Cloudflare 斷線)、持久連線心跳包、深色模式、時鐘
*/
mw.loader.using(['mediawiki.util', 'mediawiki.Uri', 'mediawiki.api']).then(function () {
$(function () {
/* ============================================================
* ① 語系自動導向與切換 (針對訪客預設 zh)
* ============================================================ */
var isAnon = mw.config.get('wgUserName') === null;
var currentVariant = mw.util.getParamValue('variant');
// 訪客預設導向中文 (zh)
if (isAnon && !currentVariant && mw.config.get('wgAction') === 'view') {
var url = new mw.Uri(window.location.href);
url.query.variant = 'zh';
window.location.replace(url.toString());
return;
}
// 建立右上角切換按鈕
var variants = [
{ code: 'zh', label: '中文' },
{ code: 'zh-tw', label: '繁體' },
{ code: 'zh-cn', label: '简体' }
];
var $langContainer = $('<span>').attr('id', 'custom-variant-switches');
variants.forEach(function (v, i) {
var $link = $('<a>')
.text(v.label)
.css({
cursor: 'pointer',
padding: '0 5px',
color: currentVariant === v.code ? '#000' : '#0645ad',
fontWeight: currentVariant === v.code ? 'bold' : 'normal'
})
.on('click', function (e) {
e.preventDefault();
var url = new mw.Uri(window.location.href);
url.query.variant = v.code;
window.location.href = url.toString();
});
$langContainer.append($link);
if (i < variants.length - 1) $langContainer.append('|');
});
mw.util.addPortletLink('p-personal', '#', '', 'pt-variant-switch', '切換語系', '#pt-logout');
$('#pt-variant-switch').append($langContainer);
/* ============================================================
* ② 手動字詞轉換 (確保站名正確顯示)
* ============================================================ */
function applyManualTranslation() {
var v = mw.util.getParamValue('variant') || mw.config.get('wgUserVariant') || mw.config.get('wgUserLanguage');
if (v === 'zh-hans' || v === 'zh-cn') {
$('h1#firstHeading, #p-logo-text, .mw-wiki-title, title').each(function() {
var t = $(this).text();
$(this).text(t.replace(/Quickpedia wiki/g, 'Quickpedia wiki'));
});
}
}
applyManualTranslation();
/* ============================================================
* ③ 自動暫存與持久編輯 (對抗 Cloudflare 驗證中斷)
* ============================================================ */
if (['edit', 'submit'].indexOf(mw.config.get('wgAction')) !== -1) {
var pageName = mw.config.get('wgPageName');
var storageKey = 'edit_cache_' + pageName;
var $textbox = $('#wpTextbox1');
// A. 心跳包:每 2 分鐘向伺服器發送請求,防止 Session 過期或驗證跳出
setInterval(function() {
new mw.Api().get({
action: 'query',
titles: pageName,
prop: 'info'
}).done(function() {
console.log('Quickpedia wiki 連線維持中...');
});
}, 120000);
// B. 高頻率自動暫存:每 20 秒存一次草稿
setInterval(function () {
var content = $textbox.val();
if (content && content.length > 10) {
localStorage.setItem(storageKey, JSON.stringify({
text: content,
timestamp: Date.now()
}));
}
}, 20000);
// C. 檢查並還原草稿
var savedCache = localStorage.getItem(storageKey);
if (savedCache) {
var data = JSON.parse(savedCache);
if (Date.now() - data.timestamp < 86400000 && $textbox.val().length < 50) {
if (confirm('偵測到未儲存草稿(可能因 Cloudflare 驗證中斷),是否還原?')) {
$textbox.val(data.text);
}
}
}
// D. 離開頁面警告
window.onbeforeunload = function() {
if ($textbox.val().length > 50) return "內容尚未儲存!";
};
// E. 儲存後清除暫存
$('#wpSave, #wpSaveWidget, input#wpSave').on('click', function() {
localStorage.removeItem(storageKey);
window.onbeforeunload = null;
});
}
/* ============================================================
* ④ 深色模式
* ============================================================ */
var darkBtn = mw.util.addPortletLink('p-personal', '#', '深色模式', 'pt-darkmode', '切換深色模式', '#pt-variant-switch');
$(darkBtn).on('click', function (e) {
e.preventDefault();
$('body').toggleClass('dark-mode');
localStorage.setItem('wiki-dark-mode', $('body').hasClass('dark-mode') ? '1' : '0');
});
if (localStorage.getItem('wiki-dark-mode') === '1') {
$('body').addClass('dark-mode');
}
/* ============================================================
* ⑤ 即時時鐘
* ============================================================ */
var $clock = $('<li>').attr('id', 'pt-clock').css({
fontWeight: 'bold',
marginLeft: '10px',
color: '#54595d'
});
function updateClock() {
var now = new Date();
$clock.text(now.toLocaleTimeString('zh-TW', { hour12: false }));
}
$('#p-personal ul').append($clock);
setInterval(updateClock, 1000);
updateClock();
});
});