「MediaWiki:Common.js」:修訂間差異

出自Quickpedia wiki
無編輯摘要
無編輯摘要
 
(未顯示同一使用者於中間所作的 11 次修訂)
第1行: 第1行:
/* === MediaWiki Custom Language Variant Handling === */
/**
* MediaWiki:Common.js 強化完整版
* 適用對象:Quickpedia wiki
* 功能:語系導向、手動字詞轉換、自動暫存(防 Cloudflare 斷線)、持久連線心跳包、深色模式、時鐘
*/


$(function() {
mw.loader.using(['mediawiki.util', 'mediawiki.Uri', 'mediawiki.api']).then(function () {
    // 在這裡加入 console.log,方便除錯
    console.log("wgContentLanguageVariants:", mw.config.get('wgContentLanguageVariants'));
    console.log("wgUserVariant:", mw.config.get('wgUserVariant'));


     // 1. **自定義語系下拉選單**
     $(function () {
    var variants = mw.config.get('wgContentLanguageVariants'); // 獲取可用的語言變體


    // 篩選出只包含簡體和繁體的變體
        /* ============================================================
    var filteredVariants = [];
        * ① 語系自動導向與切換 (針對訪客預設 zh)
    if (variants) {
        * ============================================================ */
         for (var i = 0; i < variants.length; i++) {
        var isAnon = mw.config.get('wgUserName') === null;
             var variantCode = variants[i];
        var currentVariant = mw.util.getParamValue('variant');
             if (variantCode === 'zh-tw' || variantCode === 'zh-hant' ||
 
                variantCode === 'zh-cn' || variantCode === 'zh-hans') {
        // 訪客預設導向中文 (zh)
                 filteredVariants.push(variantCode);
        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();


    // 如果篩選後有至少兩種語系變體,才建立下拉選單
    if (filteredVariants.length > 1) {
        var currentVariant = mw.config.get('wgUserVariant');
        var languageDropdown = $('<div id="zh-variant-dropdown" class="portal"></div>'); // 增加portal class 讓外觀更像側邊欄組件
        languageDropdown.append('<h3>中文</h3>'); // 增加標題
        var variantSelect = $('<select id="zh-variant-select"></select>');


         for (var i = 0; i < filteredVariants.length; i++) {
         /* ============================================================
             var variantCode = filteredVariants[i];
        * ③ 自動暫存與持久編輯 (對抗 Cloudflare 驗證中斷)
             var variantName = '';
        * ============================================================ */
        if (['edit', 'submit'].indexOf(mw.config.get('wgAction')) !== -1) {
            var pageName = mw.config.get('wgPageName');
             var storageKey = 'edit_cache_' + pageName;
             var $textbox = $('#wpTextbox1');


             switch (variantCode) {
             // A. 心跳包:每 2 分鐘向伺服器發送請求,防止 Session 過期或驗證跳出
                 case 'zh-tw':
            setInterval(function() {
                case 'zh-hant':
                 new mw.Api().get({
                     variantName = '繁體中文';
                    action: 'query',
                    break;
                    titles: pageName,
                 case 'zh-cn':
                    prop: 'info'
                case 'zh-hans':
                }).done(function() {
                     variantName = '簡體中文';
                     console.log('Quickpedia wiki 連線維持中...');
                    break;
                });
                 default:
            }, 120000);
                     // 這裡應該不會執行到,因為上面已經過濾了
 
                     variantName = variantCode;
            // 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);
                     }
                }
             }
             }


             var option = $('<option>').val(variantCode).text(variantName);
             // D. 離開頁面警告
             if (variantCode === currentVariant) {
            window.onbeforeunload = function() {
                 option.prop('selected', true);
                if ($textbox.val().length > 50) return "內容尚未儲存!";
             }
             };
            variantSelect.append(option);
 
            // E. 儲存後清除暫存
            $('#wpSave, #wpSaveWidget, input#wpSave').on('click', function() {
                 localStorage.removeItem(storageKey);
                window.onbeforeunload = null;  
             });
         }
         }


         variantSelect.on('change', function() {
 
            var selectedVariant = $(this).val();
         /* ============================================================
             var currentUrl = new URL(window.location.href);
        * ④ 深色模式
             // 設置 URL 參數 'variant'
        * ============================================================ */
            currentUrl.searchParams.set('variant', selectedVariant);
        var darkBtn = mw.util.addPortletLink('p-personal', '#', '深色模式', 'pt-darkmode', '切換深色模式', '#pt-variant-switch');
            // 如果頁面當前有 /簡體 或 /繁體 這種路徑,需要移除它們,因為我們現在用 ?variant=
        $(darkBtn).on('click', function (e) {
            var pathParts = currentUrl.pathname.split('/');
             e.preventDefault();
             var lastPart = pathParts[pathParts.length - 1];
             $('body').toggleClass('dark-mode');
            if (lastPart === '簡體' || lastPart === '繁體') {
             localStorage.setItem('wiki-dark-mode', $('body').hasClass('dark-mode') ? '1' : '0');
                pathParts.pop(); // 移除最後一個部分
                currentUrl.pathname = pathParts.join('/');
            }
            window.location.href = currentUrl.toString();
         });
         });


         // 將下拉選單添加到側邊欄頂部 (通常是 #mw-panel 或 #p-navigation)
         if (localStorage.getItem('wiki-dark-mode') === '1') {
        // 選擇 #mw-panel 是為了讓它在整個側邊欄的頂部
            $('body').addClass('dark-mode');
        $('#mw-panel').prepend(languageDropdown.append(variantSelect));
        }
        // 如果您的維基百科使用的是不同的側邊欄 ID,請調整這裡
        // 例如:$('#p-navigation').prepend(languageDropdown.append(variantSelect));
    }


    // 2. **自動語系導向邏輯 (基於瀏覽器語系,並使用 ?variant= 參數)**
    // 只有當頁面URL中沒有 `?variant=` 參數時才自動導向
    var currentUrl = new URL(window.location.href);
    if (!currentUrl.searchParams.has('variant')) {
        var browserLang = (navigator.language || navigator.userLanguage).toLowerCase();
        var simplifiedCodes = ['zh-cn', 'zh-sg', 'zh-hans'];
        var traditionalCodes = ['zh-tw', 'zh-hk', 'zh-mo', 'zh-hant'];


         var targetVariant = '';
        /* ============================================================
        if (simplifiedCodes.includes(browserLang)) {
        * ⑤ 即時時鐘
            targetVariant = 'zh-hans'; // 導向簡體變體
        * ============================================================ */
        } else if (traditionalCodes.includes(browserLang)) {
         var $clock = $('<li>').attr('id', 'pt-clock').css({
             targetVariant = 'zh-hant'; // 導向繁體變體
             fontWeight: 'bold',
        } else {
             marginLeft: '10px',
             // 如果瀏覽器語系既不是簡體也不是繁體中文,預設導向繁體
             color: '#54595d'
             targetVariant = 'zh-hant';
         });
         }


         // 只有當目標變體與當前用戶變體不同時才導向
         function updateClock() {
        if (targetVariant && mw.config.get('wgUserVariant') !== targetVariant) {
             var now = new Date();
            currentUrl.searchParams.set('variant', targetVariant);
             $clock.text(now.toLocaleTimeString('zh-TW', { hour12: false }));
            // 同樣,如果頁面當前有 /簡體 或 /繁體 這種路徑,需要移除它們
             var pathParts = currentUrl.pathname.split('/');
             var lastPart = pathParts[pathParts.length - 1];
            if (lastPart === '簡體' || lastPart === '繁體') {
                pathParts.pop();
                currentUrl.pathname = pathParts.join('/');
            }
            window.location.href = currentUrl.toString();
         }
         }
    }


    // 3. **隱藏 MediaWiki 內建語系切換器 (如果存在)**
         $('#p-personal ul').append($clock);
    // 這部分是為了防止兩個語系選單同時出現,造成混淆
         setInterval(updateClock, 1000);
    $(document).ready(function() { // 這個 ready 函數其實可以省略,因為外層已經是 $(function(){})
         updateClock();
         var variantsMenu = $('#p-variants'); // MediaWiki 標準語系選單 ID
         if (variantsMenu.length > 0) {
            variantsMenu.hide(); // 直接隱藏整個內建選單
         }
    });


}); // 此處為最外層 $(function() { ... }); 的結束
// 等頁面完全載入後執行
window.addEventListener('load', function() {
    // 隱藏整個 CAPTCHA 區塊
    var captchaBlocks = document.querySelectorAll('.mw-captcha');
    captchaBlocks.forEach(function(el) {
        el.style.display = 'none';
     });
     });
    // 如果有單獨的輸入欄位,也隱藏它
    var captchaInput = document.getElementById('mw-input-captchaWord');
    if (captchaInput) {
        captchaInput.style.display = 'none';
    }
});
});

於 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();

    });
});