跳转到内容

MediaWiki:Common.js

来自电棍ottowiki
棍牧典留言 | 贡献2025年5月11日 (日) 00:33的版本

注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
//上传重定向
mw.loader.using(['mediawiki.util', 'oojs-ui', 'mediawiki.storage'], function () {
    if (mw.config.get('wgCanonicalSpecialPageName') === 'Upload') {

        const STORAGE_KEY = 'uploadPreference';

        const savedChoice = mw.storage.get(STORAGE_KEY);

        // 用户之前选择了上传向导,自动跳转
        if (savedChoice === 'wizard') {
            window.location.href = mw.util.getUrl('Special:UploadWizard');
            return;
        }

        // 用户选择了继续使用传统上传,不再提示
        if (savedChoice === 'classic') {
            return;
        }

        // 否则弹出选择窗口
        var windowManager = new OO.ui.WindowManager();
        $(document.body).append(windowManager.$element);

        function UploadDialog(config) {
            UploadDialog.super.call(this, config);
        }

        OO.inheritClass(UploadDialog, OO.ui.ProcessDialog);

        UploadDialog.static.name = 'UploadDialog';
        UploadDialog.static.title = '请选择上传方式';
        UploadDialog.static.actions = [
            {
                action: 'classic',
                label: '❌ 使用传统上传方式(更快)',
                flags: ['safe']
            }
        ];

        UploadDialog.prototype.initialize = function () {
            UploadDialog.super.prototype.initialize.call(this);

            var panel = new OO.ui.PanelLayout({ padded: true, expanded: false });

            // 创建“记住我的选择”复选框
            var rememberCheckbox = new OO.ui.CheckboxInputWidget({ selected: false });
            this._rememberCheckbox = rememberCheckbox;

            var rememberField = new OO.ui.FieldLayout(rememberCheckbox, {
                label: '记住我的选择,下次不再提示',
                align: 'inline'
            });

            // 创建“上传向导”按钮(跳转链接形式)
            var wizardButton = new OO.ui.ButtonWidget({
                label: '✅ 使用上传向导(推荐)',
                flags: ['primary', 'progressive'],
                href: mw.util.getUrl('Special:UploadWizard'),
                target: '_self',
                framed: true
            });

            // 给按钮添加“记住选择”逻辑
            wizardButton.on('click', () => {
                if (rememberCheckbox.isSelected()) {
                    mw.storage.set(STORAGE_KEY, 'wizard');
                }
            });

            // 拼接内容
            panel.$element.append(
                $('<p>').text('请选择你要使用的上传方式:'),

                $('<div>').css({
                    'margin': '12px 0',
                    'background': '#f8f9fa',
                    'padding': '10px',
                    'border': '1px solid #ccc',
                    'border-radius': '5px'
                }).append(
                    $('<strong>').text('✅ 上传向导(推荐)'),
                    $('<ul>').append(
                        $('<li>').text('支持多文件批量上传'),
                        $('<li>').text('显示上传进度条'),
                        $('<li>').text('信息填写更完整')
                    ),
                    $('<div>').css('margin-top', '8px').append(wizardButton.$element)
                ),

                $('<div>').css({
                    'margin-top': '15px',
                    'background': '#ffffff',
                    'padding': '10px',
                    'border': '1px dashed #bbb',
                    'border-radius': '5px'
                }).append(
                    $('<strong>').text('⚡️ 传统上传方式'),
                    $('<ul>').append(
                        $('<li>').text('界面更简单'),
                        $('<li>').text('加载速度快'),
                        $('<li>').text('适合上传单个文件')
                    )
                ),

                $('<div>').css('margin-top', '15px').append(rememberField.$element)
            );

            this.$body.append(panel.$element);
        };

        UploadDialog.prototype.getActionProcess = function (action) {
            if (action === 'classic') {
                if (this._rememberCheckbox.isSelected()) {
                    mw.storage.set(STORAGE_KEY, 'classic');
                }
                return new OO.ui.Process(() => this.close());
            }
            return UploadDialog.super.prototype.getActionProcess.call(this, action);
        };

        var dialog = new UploadDialog();
        windowManager.addWindows([dialog]);
        windowManager.openWindow(dialog);
    }
});
//---
// IIFE (Immediately Invoked Function Expression)
(function() {
    // --- 1. 尽早执行和检查重复 ---
    // 如果已初始化,不再执行任何操作
    if (window.__loadingIndicatorActive) { // 使用一个更明确的标志,表示 loading 当前是否“活跃”
        return;
    }
    window.__loadingIndicatorActive = true; // 标记 loading 开始

    // --- 2. 尽早创建和插入 Loading DOM ---
    var loadingImgUrl = 'https://wiki.ottohub.cn/images/0/02/Loading.png';
    var loadingDiv = document.createElement('div');
    loadingDiv.id = 'loadingIndicator';
    loadingDiv.style.position = 'fixed';
    loadingDiv.style.bottom = '20px';
    loadingDiv.style.right = '20px';
    loadingDiv.style.width = '256px'; // 强制大小,如果图片本身就是这个尺寸,可以省略
    loadingDiv.style.height = '256px';
    loadingDiv.style.zIndex = '99999'; // 更高的 z-index 确保在最上层
    loadingDiv.style.pointerEvents = 'none'; // 允许点击穿透
    // loadingDiv.style.display = 'none'; // 先隐藏,通过 fadeIn 显示,可选

    var img = document.createElement('img');
    img.src = loadingImgUrl;
    img.alt = 'Loading...';
    img.style.width = '100%';
    img.style.height = '100%';
    img.style.objectFit = 'contain';

    loadingDiv.appendChild(img);

    // 确保 body 存在。如果此脚本在 <head> 中且没有 defer/async,
    // document.body 可能为 null。最安全的方式是把脚本放在 </body> 前。
    // 或者,如果必须在 head 中,则等待 DOMContentLoaded 附加。
    // 但为了“尽早显示”,我们这里会尝试立即附加,并提供回退。

    function appendLoadingIcon() {
        if (document.body) {
            document.body.appendChild(loadingDiv);
            // 可选:使用 fadeIn 效果,如果上面设置了 display: 'none'
            // loadingDiv.style.display = 'block'; // 或者使用 jQuery fadeIn
            // $(loadingDiv).stop(true, true).fadeIn(100); // 如果你还想用 jQuery 做动画
        } else {
            // 如果 body 还不存在,等待 DOMContentLoaded
            // 这意味着 loading 图标的显示会稍有延迟
            document.addEventListener('DOMContentLoaded', function onReady() {
                if (document.body && !document.getElementById('loadingIndicator')) { // 再次检查,防止重复
                    document.body.appendChild(loadingDiv);
                    // $(loadingDiv).stop(true, true).fadeIn(100);
                }
                document.removeEventListener('DOMContentLoaded', onReady); // 清理监听器
            });
        }
    }

    appendLoadingIcon(); // 尝试立即附加


    // --- 3. 移除逻辑 ---
    var removalTimeoutId = null; // 用于清除兜底 timeout

    function removeLoading(reason) {
        // 确保只移除一次,并清除兜底 timeout
        if (!window.__loadingIndicatorActive) return; // 如果已经移除了,则不操作

        var el = document.getElementById('loadingIndicator');
        if (el) {
            console.log(`[Common.js] 🔄 Removing loading. Reason: ${reason}`);
            // 使用原生 JS 实现 fadeOut 效果 (可选,或直接 remove)
            el.style.transition = 'opacity 0.3s ease-out';
            el.style.opacity = '0';
            setTimeout(function() {
                if (el.parentNode) {
                    el.parentNode.removeChild(el);
                }
                console.log(`[Common.js] ✅ Loading removed. Reason: ${reason}`);
            }, 300); // 等待 fadeOut 动画完成
        } else {
            // console.log(`[Common.js] ⚠️ Loading indicator already removed or not found when trying to remove. Reason: ${reason}`);
        }
        window.__loadingIndicatorActive = false; // 标记 loading 已移除
        if (removalTimeoutId) {
            clearTimeout(removalTimeoutId); // 清除兜底的10秒timeout
            removalTimeoutId = null;
        }
        // 清理事件监听器,防止内存泄漏(对于只触发一次的事件,现代浏览器通常会自动处理,但显式移除更安全)
        window.removeEventListener('load', onWindowLoad);
        document.removeEventListener('DOMContentLoaded', onDOMContentLoaded);
    }

    // --- 4. 检查即时状态并设置事件监听 ---

    // 立即检查 document.readyState (处理缓存或极快加载)
    if (document.readyState === 'complete') {
        // 使用 requestAnimationFrame 或短 setTimeout 给浏览器一点点时间渲染 loading 图标
        // 然后再移除,避免完全看不到。
        // 如果不希望看到闪烁,可以直接调用 removeLoading。
        requestAnimationFrame(function() { // 或者 setTimeout(..., 0)
            removeLoading('document.readyState was already complete on script exec');
        });
        return; // 已经加载完成,不需要后续的监听器和 timeout
    }

    // 定义事件处理函数,以便之后可以移除它们
    function onWindowLoad() {
        removeLoading('window.load');
    }
    function onDOMContentLoaded() {
        if (document.readyState === 'complete' || document.readyState === 'interactive') {
            // DOMContentLoaded 之后,如果已经是 complete 或 interactive,也可以认为主要内容加载完成
            // 使用 setTimeout 给一个微小的延迟,确保其他 DOMContentLoaded 任务有机会执行
            setTimeout(function() {
                removeLoading('DOMContentLoaded (interactive/complete)');
            }, 0); // 极短延迟
        }
        // 如果不是 complete/interactive,则 window.load 仍然是主要依赖
    }

    // 注册事件监听
    window.addEventListener('load', onWindowLoad);
    document.addEventListener('DOMContentLoaded', onDOMContentLoaded);

    // 兜底策略:10 秒后强制关闭
    removalTimeoutId = setTimeout(function() {
        removeLoading('timeout fallback (10s)');
    }, 10000);

})();