打开/关闭菜单
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

MediaWiki:Gadget-site-js.js:修订间差异

MediaWiki界面页面
 
第171行: 第171行:


   return { show, hide };
   return { show, hide };
})();
/**********************
* 模块 4:vConsole(仅限移动端,异步加载)
**********************/
(function() {
  // 判断是否为移动端设备
  const isMobile = /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
  // 如果是移动端,且没有禁用 vConsole 的条件,才加载
  if (isMobile && !window.localStorage.getItem('disableVConsole')) {
    const script = document.createElement('script');
    script.src = 'https://cdn.bootcdn.net/ajax/libs/vConsole/3.3.4/vconsole.min.js';  // 使用CDN加载vConsole
    script.onload = () => {
      const vConsole = new VConsole();  // 初始化 vConsole
      console.log('vConsole 已启用');
    };
    document.body.appendChild(script);
  }
})();
})();

2025年6月20日 (五) 20:52的最新版本

/**********************
 * 模块 1:CSS 变量 RGB 转换
 **********************/
const ColorUtil = (function () {
  const OBS_VAR = '--background-color-base';
  let cachedRgb = '';

  function parseHex(hex) {
    hex = hex.replace(/^#/, '');
    if (hex.length === 3) {
      hex = hex.split('').map(c => c + c).join('');
    }
    if (hex.length !== 6) return '0, 0, 0';
    const [r, g, b] = [0, 2, 4].map(i => parseInt(hex.substring(i, i + 2), 16) || 0);
    return `${r}, ${g}, ${b}`;
  }

  function update() {
    const root = document.documentElement;
    const hex = getComputedStyle(root).getPropertyValue(OBS_VAR).trim();
    const rgb = parseHex(hex || '#000');
    if (rgb !== cachedRgb) {
      root.style.setProperty('--background-color-rgb', rgb);
      cachedRgb = rgb;
      console.log('[ColorUtil] RGB updated:', rgb);
    }
  }

  update();

  const mo = new MutationObserver(update);
  mo.observe(document.documentElement, { attributes: true, attributeFilter: ['style'] });

  return {
    destroy() { mo.disconnect(); },
    forceUpdate: update
  };
})();

/**********************
 * 模块 2:上传页引导弹窗(按需加载 OOUI)
 **********************/
(function (mw) {
  if (!mw || mw.config.get('wgCanonicalSpecialPageName') !== 'Upload') return;
  const KEY = 'uploadPreference';
  const pref = mw.storage.get(KEY);

  if (pref === 'wizard') return window.location = mw.util.getUrl('Special:UploadWizard');
  if (pref === 'classic') return;

  function initDialog() {
    document.removeEventListener('click', initDialog);
    mw.loader.using(['mediawiki.util', 'mediawiki.storage', 'oojs-ui-core', 'oojs-ui-widgets'])
      .then(() => {
        class UploadDialog extends OO.ui.ProcessDialog {
          static name = 'UploadDialog';
          static title = '请选择上传方式';
          static actions = [
            { action: 'classic', label: '❌ 使用传统上传方式', flags: ['safe'] }
          ];

          initialize() {
            super.initialize();
            const panel = new OO.ui.PanelLayout({ padded: true });
            this._remember = new OO.ui.CheckboxInputWidget();
            const rememberField = new OO.ui.FieldLayout(this._remember, {
              label: '记住我的选择', align: 'inline'
            });
            const wizardBtn = new OO.ui.ButtonWidget({
              label: '✅ 使用上传向导(推荐)',
              flags: ['primary', 'progressive'],
              href: mw.util.getUrl('Special:UploadWizard'),
              target: '_self'
            });
            wizardBtn.on('click', () => {
              if (this._remember.isSelected()) mw.storage.set(KEY, 'wizard');
            });
            panel.$element.append(
              $('<p>').text('请选择上传方式:'),
              $('<div>').append(wizardBtn.$element),
              $('<div>').append(rememberField.$element)
            );
            this.$body.append(panel.$element);
          }

          getActionProcess(action) {
            if (action === 'classic') {
              if (this._remember.isSelected()) mw.storage.set(KEY, 'classic');
              return new OO.ui.Process(() => this.close());
            }
            return super.getActionProcess(action);
          }
        }

        const wm = new OO.ui.WindowManager();
        document.body.appendChild(wm.$element);
        wm.addWindows([new UploadDialog()]);
        wm.openWindow('UploadDialog');
      });
  }

  document.addEventListener('click', initDialog);
})(window.mw);

/**********************
 * 模块 3:全局加载指示器
 **********************/
const LoadingIndicator = (function () {
  let indicator = null;
  let timerId = null;
  let startTs = 0;
  const minDisplay = 200;
  const cfg = {
    imageUrl: 'https://wiki.ottohub.cn/images/0/02/Loading.png',
    size: '64px', bottom: '20px', right: '20px',
    timeout: 15000, fade: 300, zIndex: 99999
  };

  function show(reason = '') {
    if (indicator) return resetTimeout();
    startTs = Date.now();
    indicator = document.createElement('div');
    Object.assign(indicator.style, {
      position: 'fixed', width: cfg.size, height: cfg.size,
      bottom: cfg.bottom, right: cfg.right,
      opacity: '0', transition: `opacity ${cfg.fade}ms`,
      zIndex: cfg.zIndex, pointerEvents: 'none'
    });
    indicator.setAttribute('aria-busy', 'true');
    const img = new Image();
    img.src = cfg.imageUrl;
    img.alt = '加载中';
    img.style.width = '100%';
    indicator.appendChild(img);
    document.body.appendChild(indicator);
    requestAnimationFrame(() => { indicator.style.opacity = '1'; });
    resetTimeout();
  }

  function hide(reason = '') {
    if (!indicator) return;
    const elapsed = Date.now() - startTs;
    const wait = Math.max(minDisplay - elapsed, 0);
    setTimeout(() => {
      indicator.style.opacity = '0';
      indicator.setAttribute('aria-busy', 'false');
      setTimeout(() => {
        indicator.remove();
        indicator = null;
      }, cfg.fade);
    }, wait);
    clearTimeout(timerId);
  }

  function resetTimeout() {
    clearTimeout(timerId);
    timerId = setTimeout(() => hide('timeout'), cfg.timeout);
  }

  window.addEventListener('offline', () => {
    console.warn('[Loading] 离线,强制隐藏');
    hide('offline');
  });

  window.addEventListener('online', () => {
    console.info('[Loading] 网络恢复');
  });

  document.addEventListener('DOMContentLoaded', () => show('DOMContentLoaded'));
  window.addEventListener('load', () => hide('load'));

  return { show, hide };
})();
/********************** 
 * 模块 4:vConsole(仅限移动端,异步加载)
 **********************/
(function() {
  // 判断是否为移动端设备
  const isMobile = /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);

  // 如果是移动端,且没有禁用 vConsole 的条件,才加载
  if (isMobile && !window.localStorage.getItem('disableVConsole')) {
    const script = document.createElement('script');
    script.src = 'https://cdn.bootcdn.net/ajax/libs/vConsole/3.3.4/vconsole.min.js';  // 使用CDN加载vConsole
    script.onload = () => {
      const vConsole = new VConsole();  // 初始化 vConsole
      console.log('vConsole 已启用');
    };
    document.body.appendChild(script);
  }
})();