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

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


mw.loader.using(['mediawiki.util', 'mediawiki.Uri']).then(function () {
mw.loader.using(['mediawiki.util', 'mediawiki.Uri', 'mediawiki.api']).then(function () {


     $(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 = [
         var variants = [
            { code: 'zh', label: '中文' },
             { code: 'zh-tw', label: '繁體' },
             { code: 'zh-tw', label: '繁體' },
             { code: 'zh-cn', label: '简体' }
             { code: 'zh-cn', label: '简体' }
         ];
         ];


        var current =
         var $langContainer = $('<span>').attr('id', 'custom-variant-switches');
            mw.config.get('wgUserVariant') ||
            mw.util.getParamValue('variant') ||
            'zh-tw';
 
         var $langMenu = $('<span>').css({
            marginLeft: '10px',
            fontWeight: 'bold'
        });
 
         variants.forEach(function (v, i) {
         variants.forEach(function (v, i) {
             var $link = $('<a>')
             var $link = $('<a>')
第36行: 第36行:
                 .css({
                 .css({
                     cursor: 'pointer',
                     cursor: 'pointer',
                     margin: '0 4px',
                     padding: '0 5px',
                     color: v.code === current ? '#000' : '#0645ad',
                     color: currentVariant === v.code ? '#000' : '#0645ad',
                     textDecoration: v.code === current ? 'underline' : 'none'
                     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) $langContainer.append('|');
        });


            $langMenu.append($link);
        mw.util.addPortletLink('p-personal', '#', '', 'pt-variant-switch', '切換語系', '#pt-logout');
             if (i < variants.length - 1) {
        $('#pt-variant-switch').append($langContainer);
                $langMenu.append(' / ');
 
 
        /* ============================================================
        * ② 手動字詞轉換 (確保站名正確顯示)
        * ============================================================ */
        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();


        mw.util.addPortletLink(
            'p-personal',
            '#',
            $langMenu.prop('outerHTML'),
            'pt-lang-switch',
            '語言切換'
        );


        /* ============================================================
        * ③ 自動暫存與持久編輯 (對抗 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);


        var darkBtn = mw.util.addPortletLink(
            // B. 高頻率自動暫存:每 20 秒存一次草稿
            'p-personal',
            setInterval(function () {
            '#',
                var content = $textbox.val();
             '深色模式',
                if (content && content.length > 10) {
            'pt-darkmode'
                    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) {
         $(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');
             var isDark = $('body').hasClass('dark-mode');
            localStorage.setItem('dark-mode', isDark ? '1' : '0');
         });
         });


         if (localStorage.getItem('dark-mode') === '1') {
         if (localStorage.getItem('wiki-dark-mode') === '1') {
             $('body').addClass('dark-mode');
             $('body').addClass('dark-mode');
         }
         }




         /* =========================
         /* ============================================================
         * ③ 右上角時鐘
         * ⑤ 即時時鐘
         * ========================= */
         * ============================================================ */
        var $clock = $('<li>').attr('id', 'pt-clock').css({
            fontWeight: 'bold',
            marginLeft: '10px',
            color: '#54595d'
        });


         function updateClock() {
         function updateClock() {
             var now = new Date();
             var now = new Date();
             return now.toLocaleTimeString('zh-TW', { hour12: false });
             $clock.text(now.toLocaleTimeString('zh-TW', { hour12: false }));
         }
         }


        var $clock = $('<span>')
         $('#p-personal ul').append($clock);
            .attr('id', 'mw-clock')
         setInterval(updateClock, 1000);
            .css({
         updateClock();
                marginLeft: '10px',
                fontWeight: 'bold',
                fontFamily: 'monospace'
            })
            .text(updateClock());
 
         $('#p-personal').append($clock);
 
         setInterval(function () {
            $clock.text(updateClock());
        }, 1000);
 
 
         /* =========================
        * ④ 自動暫存(24小時)
        * ========================= */
 
        if (mw.config.get('wgAction') === 'edit') {
 
            var interval = 600000; // 10分鐘
            var expire = 24 * 60 * 60 * 1000;
 
            function getKey() {
                return 'autosave_' + mw.config.get('wgPageName');
            }
 
            setInterval(function () {
                var text = $('#wpTextbox1').val();
                if (!text) return;
 
                var data = {
                    value: text,
                    time: Date.now()
                };
 
                localStorage.setItem(getKey(), JSON.stringify(data));
                console.log('已自動暫存');
            }, interval);
 
            var saved = localStorage.getItem(getKey());
 
            if (saved) {
                try {
                    var data = JSON.parse(saved);
                    if (Date.now() - data.time < expire) {
                        if (!$('#wpTextbox1').val()) {
                            $('#wpTextbox1').val(data.value);
                            console.log('已還原暫存');
                        }
                    } else {
                        localStorage.removeItem(getKey());
                    }
                } catch (e) {}
            }
        }
 
 
        /* =========================
        * ⑤ 訪客預設語言(不干擾轉換)
        * ========================= */
 
        var isAnon = mw.config.get('wgUserName') === null;
 
        if (isAnon && !mw.util.getParamValue('variant')) {
            var url = new mw.Uri(window.location.href);
            url.query.variant = 'zh-tw';
            window.location.replace(url.toString());
        }


     });
     });
});
});

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

    });
});