跳转到内容

MediaWiki:Common.js

来自电棍ottowiki
棍牧典留言 | 贡献2025年5月18日 (日) 14:12的版本

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

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
(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 };
    })();
})();