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

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

MediaWiki界面页面
OctoberSama
OctoberSama留言 | 贡献 (OctoberSama移动页面MediaWiki:Gadget-TheCommon.jsMediaWiki:Gadget-site-js.js:​快速重命名 → MediaWiki:Gadget-site-js.js
第1行: 第1行:
const rootStyles = getComputedStyle(document.documentElement);
/**********************
const hexColor = rootStyles.getPropertyValue('--background-color-base').trim();
* 模块 1:CSS 变量 RGB 转换
**********************/
const ColorUtil = (function () {
  const OBS_VAR = '--background-color-base';
  let cachedRgb = '';


function hexToRgb(hex) {
  function parseHex(hex) {
  hex = hex.replace('#', '');
    hex = hex.replace(/^#/, '');
 
    if (hex.length === 3) {
  if (hex.length === 3) {
      hex = hex.split('').map(c => c + c).join('');
    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}`;
   }
   }


   const r = parseInt(hex.substring(0, 2), 16);
   function update() {
  const g = parseInt(hex.substring(2, 4), 16);
    const root = document.documentElement;
  const b = parseInt(hex.substring(4, 6), 16);
    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);
    }
  }


   return `${r}, ${g}, ${b}`;  
   update();
}


const rgbColor = hexToRgb(hexColor);
  const mo = new MutationObserver(update);
document.documentElement.style.setProperty(
  mo.observe(document.documentElement, { attributes: true, attributeFilter: ['style'] });
  '--background-color-rgb',
  rgbColor
);


console.log('转换结果:', rgbColor);  
  return {
    destroy() { mo.disconnect(); },
    forceUpdate: update
  };
})();


//上传重定向
/**********************
mw.loader.using(['mediawiki.util', 'oojs-ui', 'mediawiki.storage'], function () {
* 模块 2:上传页引导弹窗(按需加载 OOUI)
    if (mw.config.get('wgCanonicalSpecialPageName') === 'Upload') {
**********************/
(function (mw) {
  if (!mw || mw.config.get('wgCanonicalSpecialPageName') !== 'Upload') return;
  const KEY = 'uploadPreference';
  const pref = mw.storage.get(KEY);


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


        const savedChoice = mw.storage.get(STORAGE_KEY);
  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() {
        if (savedChoice === 'wizard') {
            super.initialize();
             window.location.href = mw.util.getUrl('Special:UploadWizard');
            const panel = new OO.ui.PanelLayout({ padded: true });
             return;
            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 (savedChoice === 'classic') {
            if (action === 'classic') {
             return;
              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();
        var windowManager = new OO.ui.WindowManager();
         document.body.appendChild(wm.$element);
         $(document.body).append(windowManager.$element);
        wm.addWindows([new UploadDialog()]);
        wm.openWindow('UploadDialog');
      });
  }


        function UploadDialog(config) {
  document.addEventListener('click', initDialog);
            UploadDialog.super.call(this, config);
})(window.mw);
        }


        OO.inheritClass(UploadDialog, OO.ui.ProcessDialog);
/**********************
* 模块 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
  };


        UploadDialog.static.name = 'UploadDialog';
  function show(reason = '') {
        UploadDialog.static.title = '请选择上传方式';
    if (indicator) return resetTimeout();
        UploadDialog.static.actions = [
    startTs = Date.now();
            {
    indicator = document.createElement('div');
                action: 'classic',
    Object.assign(indicator.style, {
                label: '❌ 使用传统上传方式(更快)',
      position: 'fixed', width: cfg.size, height: cfg.size,
                flags: ['safe']
      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();
  }


        UploadDialog.prototype.initialize = function () {
  function hide(reason = '') {
            UploadDialog.super.prototype.initialize.call(this);
    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);
  }


            var panel = new OO.ui.PanelLayout({ padded: true, expanded: false });
  function resetTimeout() {
 
    clearTimeout(timerId);
            // 创建“记住我的选择”复选框
    timerId = setTimeout(() => hide('timeout'), cfg.timeout);
            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({
  window.addEventListener('offline', () => {
                    'margin-top': '15px',
    console.warn('[Loading] 离线,强制隐藏');
                    'background': '#ffffff',
    hide('offline');
                    '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)
  window.addEventListener('online', () => {
            );
    console.info('[Loading] 网络恢复');
  });


            this.$body.append(panel.$element);
  document.addEventListener('DOMContentLoaded', () => show('DOMContentLoaded'));
        };
  window.addEventListener('load', () => hide('load'));
 
        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);
    }
});
//---
// 全局加载指示器
(function() {
  // 配置系统
  const defaultConfig = {
    imageUrl: 'https://wiki.ottohub.cn/images/0/02/Loading.png',
    position: { bottom: '20px', right: '20px' },
    size: '256px',
    timeout: 15000,
    zIndex: '99999',
    renderIndicator: defaultRenderer,
    fadeDuration: 300
  };
 
  let config = Object.assign({}, defaultConfig);
  let loadingDiv = null;
  let removalTimeoutId = null;
  let isShowingLoading = false;
  let linkTrackingEnabled = false;
  const eventListeners = [];
  // 默认渲染器
  function defaultRenderer(container) {
    const img = document.createElement('img');
    img.src = config.imageUrl;
    img.alt = '加载中...';
    img.style.width = '100%';
    img.style.height = '100%';
    img.style.objectFit = 'contain';
    container.appendChild(img);
  }
  // 图片预加载
  const imgPreload = new Image();
  imgPreload.src = config.imageUrl;
  // 事件管理
  function addEventListener(target, type, handler) {
    target.addEventListener(type, handler);
    eventListeners.push({ target, type, handler });
  }
  function cleanupEventListeners() {
    eventListeners.forEach(({ target, type, handler }) => {
      target.removeEventListener(type, handler);
    });
    eventListeners.length = 0;
  }
  // --- 核心显示函数 ---
  function showLoading(reason) {
    if (isShowingLoading && document.getElementById('loadingIndicator')) {
      config.debug && console.log('[Loading] 已显示,触发原因:', reason);
      return;
    }
   
    isShowingLoading = true;
    config.debug && console.log('[Loading] 显示。原因:', reason);
    // 创建元素(如果不存在)
    if (!document.getElementById('loadingIndicator')) {
      loadingDiv = document.createElement('div');
      loadingDiv.id = 'loadingIndicator';
      loadingDiv.style.position = 'fixed';
      loadingDiv.style.bottom = config.position.bottom;
      loadingDiv.style.right = config.position.right;
      loadingDiv.style.width = config.size;
      loadingDiv.style.height = config.size;
      loadingDiv.style.zIndex = config.zIndex;
      loadingDiv.style.pointerEvents = 'none';
      loadingDiv.style.opacity = '0';
      loadingDiv.style.transition = `opacity ${config.fadeDuration}ms ease`;
     
      // 无障碍支持
      loadingDiv.setAttribute('role', 'status');
      loadingDiv.setAttribute('aria-live', 'polite');
      loadingDiv.setAttribute('aria-label', '页面加载中');
      loadingDiv.setAttribute('aria-busy', 'true');
      config.renderIndicator(loadingDiv);
      // 添加到DOM
      if (document.body) {
        document.body.appendChild(loadingDiv);
      } else {
        document.addEventListener('DOMContentLoaded', function onReady() {
          document.body.appendChild(loadingDiv);
          fadeInLoading();
          document.removeEventListener('DOMContentLoaded', onReady);
        });
        return;
      }
    } else {
      loadingDiv = document.getElementById('loadingIndicator');
    }
    fadeInLoading();
    // 重置超时
    if (removalTimeoutId) clearTimeout(removalTimeoutId);
   
    // 网络感知超时
    const isSlowConnection = navigator.connection &&
      (navigator.connection.saveData || /2g|3g/.test(navigator.connection.effectiveType));
   
    removalTimeoutId = setTimeout(
      () => hideLoading(`超时回退 (${isSlowConnection ? '慢速网络' : '常规'})`),
      isSlowConnection ? 30000 : config.timeout
    );
  }
  function fadeInLoading() {
    if (!loadingDiv) return;
   
    loadingDiv.style.display = 'block';
    requestAnimationFrame(() => {
      loadingDiv.style.opacity = '1';
    });
  }
  // --- 核心隐藏函数 ---
  function hideLoading(reason) {
    if (!isShowingLoading && !document.getElementById('loadingIndicator')) {
      config.debug && console.log('[Loading] 已隐藏,隐藏原因:', reason);
      return;
    }
    config.debug && console.log('[Loading] 隐藏。原因:', reason);
    const el = document.getElementById('loadingIndicator');
    if (el) {
      el.style.opacity = '0';
      el.setAttribute('aria-busy', 'false');
     
      setTimeout(() => {
        if (el.parentNode) {
          el.parentNode.removeChild(el);
        }
        loadingDiv = null;
      }, config.fadeDuration);
    }
    isShowingLoading = false;
    cleanupEventListeners();
   
    if (removalTimeoutId) {
      clearTimeout(removalTimeoutId);
      removalTimeoutId = null;
    }
  }
  // --- 初始页面加载逻辑 ---
  if (document.readyState === 'loading') {
    showLoading('初始页面加载 - 文档加载中');
  } else if (document.readyState === 'interactive') {
    showLoading('初始页面加载 - 文档交互状态');
  }
  // 事件监听器
  function onWindowLoad() {
    hideLoading('window.load 事件');
  }
 
  function onDOMContentLoaded() {
    hideLoading('DOMContentLoaded 事件');
  }
  if (isShowingLoading || document.readyState === 'loading') {
    addEventListener(window, 'load', onWindowLoad);
    addEventListener(document, 'DOMContentLoaded', onDOMContentLoaded);
  }
  // --- 链接点击跟踪 ---
  function enableLinkTracking() {
    if (linkTrackingEnabled) return;
    linkTrackingEnabled = true;
   
    addEventListener(document, 'click', function(event) {
      const targetElement = event.target.closest('a');
      if (!targetElement || !targetElement.href) return;
      const href = targetElement.getAttribute('href');
      const targetAttr = targetElement.getAttribute('target');
      // 排除特殊链接
      if (href.startsWith('#') ||
          href.startsWith('javascript:') ||
          href.startsWith('mailto:') ||
          href.startsWith('tel:')) {
        return;
      }


      if (targetAttr === '_blank') return;
  return { show, hide };
})();
      // 排除SPA处理的链接
      let isLikelyInternalSPALink = false;
      if (typeof window.MyAppRouter !== 'undefined' &&
          window.MyAppRouter.isHandling(href)) {
        isLikelyInternalSPALink = true;
      }
      if (!isLikelyInternalSPALink) {
        showLoading('链接点击 - 页面导航');
      }
    }, true);
  }
  // 公开API
  window.globalLoadingIndicator = {
    show: showLoading,
    hide: hideLoading,
    configure(newConfig) {
      config = config = Object.assign({}, config, newConfig);
    },
    initRouter(router) {
      if (router && typeof router.beforeEach === 'function') {
        router.beforeEach(() => this.show('路由器导航'));
      }
      if (router && typeof router.afterEach === 'function') {
        router.afterEach(() => this.hide('路由器导航'));
      }
    }
  };
  // 初始启用链接跟踪
  enableLinkTracking();
})();
mw.loader.using(['oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui.styles'], function () {
});
    (function(c,l,a,r,i,t,y){
        c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
        t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i+"?ref=bwt";
        y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
    })(window, document, "clarity", "script", "rsfbla4ho9");

2025年6月20日 (五) 20:44的版本

/**********************
 * 模块 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 };
})();