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

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

MediaWiki界面页面
第7行: 第7行:
}
}


if (isMobileDevice() && !localStorage.getItem('disableVConsole')) {
if (isMobileDevice() && !window.localStorage.getItem('disableVConsole')) {
   const vConsoleScript = document.createElement('script');
   const vConsoleScript = document.createElement('script');
   vConsoleScript.src = 'https://cdn.bootcdn.net/ajax/libs/vConsole/3.15.1/vconsole.min.js';
   vConsoleScript.src = 'https://cdn.bootcdn.net/ajax/libs/vConsole/3.15.1/vconsole.min.js';
    
    
   vConsoleScript.onload = () => {
   vConsoleScript.onload = function() {
     try {
     try {
       new VConsole();
       if (window.VConsole) {
        new window.VConsole();
      }
     } catch (e) {
     } catch (e) {
       alert('调试工具初始化失败,请重试或联系管理员');
       alert('调试工具初始化失败,请重试或联系管理员');
第20行: 第22行:
   };
   };
    
    
   vConsoleScript.onerror = () => {
   vConsoleScript.onerror = function() {
     alert('调试工具加载失败,请检查网络连接');
     alert('调试工具加载失败,请检查网络连接');
     console.error('vConsole failed to load');
     console.error('vConsole failed to load');
   };
   };
    
    
  // 添加到 body 底部确保不阻塞渲染
   document.body.appendChild(vConsoleScript);
   document.body.appendChild(vConsoleScript);
}
}
第38行: 第39行:
   function parseHex(hex) {
   function parseHex(hex) {
     hex = hex.replace(/^#/, '');
     hex = hex.replace(/^#/, '');
     if (hex.length === 3) hex = hex.split('').map(c => c + c).join('');
     if (hex.length === 3) {
      hex = hex.split('').map(function(c) { return c + c; }).join('');
    }
     if (hex.length !== 6) return '0, 0, 0';
     if (hex.length !== 6) return '0, 0, 0';
     const [r, g, b] = [0, 2, 4].map(i => parseInt(hex.substring(i, i + 2), 16));
     var r = parseInt(hex.substring(0, 2), 16);
     return `${r}, ${g}, ${b}`;
    var g = parseInt(hex.substring(2, 4), 16);
    var b = parseInt(hex.substring(4, 6), 16);
     return r + ', ' + g + ', ' + b;
   }
   }


   function update() {
   function update() {
     const root = document.documentElement;
     var root = document.documentElement;
     const hex = getComputedStyle(root).getPropertyValue(OBS_VAR).trim();
     var hex = getComputedStyle(root).getPropertyValue(OBS_VAR).trim();
     const rgb = parseHex(hex || '#000');
     var rgb = parseHex(hex || '#000');
     if (rgb !== cachedRgb) {
     if (rgb !== cachedRgb) {
       root.style.setProperty('--background-color-rgb', rgb);
       root.style.setProperty('--background-color-rgb', rgb);
第55行: 第60行:


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


   return {
   return {
     destroy() { mo.disconnect(); }
     destroy: function() { mo.disconnect(); }
   };
   };
})();
})();
第66行: 第71行:
  * 模块 2:上传页引导弹窗
  * 模块 2:上传页引导弹窗
  **********************/
  **********************/
if (mw?.config?.get('wgCanonicalSpecialPageName') === 'Upload') {
// 修复可选链操作符问题
   const KEY = 'uploadPreference';
if (window.mw && mw.config && mw.config.get('wgCanonicalSpecialPageName') === 'Upload') {
   const pref = mw.storage.get(KEY);
   var KEY = 'uploadPreference';
   var pref = mw.storage.get(KEY);


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


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


        initialize() {
      UploadDialog.prototype.initialize = function() {
          super.initialize();
        OO.ui.ProcessDialog.prototype.initialize.apply(this, arguments);
          this._remember = new OO.ui.CheckboxInputWidget();
        this._remember = new OO.ui.CheckboxInputWidget();
          const wizardBtn = new OO.ui.ButtonWidget({
        var wizardBtn = new OO.ui.ButtonWidget({
            label: '✅ 上传向导',
          label: '✅ 上传向导',
            flags: ['primary'],
          flags: ['primary'],
            href: mw.util.getUrl('Special:UploadWizard'),
          href: mw.util.getUrl('Special:UploadWizard'),
            target: '_self'
          target: '_self'
          });
        });
          wizardBtn.on('click', () => {
        wizardBtn.on('click', function() {
            if (this._remember.isSelected()) mw.storage.set(KEY, 'wizard');
          if (this._remember.isSelected()) {
           });
            mw.storage.set(KEY, 'wizard');
          this.$body.append(
           }
            $('<p>').text('请选择上传方式:'),
        }.bind(this));
            wizardBtn.$element,
       
            new OO.ui.FieldLayout(this._remember, { label: '记住选择', align: 'inline' }).$element
        var $body = this.$body;
          );
        $body.append(
        }
          $('<p>').text('请选择上传方式:'),
          wizardBtn.$element,
          new OO.ui.FieldLayout(this._remember, {  
            label: '记住选择',  
            align: 'inline'  
          }).$element
        );
      };


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


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


第120行: 第143行:
  * 模块 3:加载指示器
  * 模块 3:加载指示器
  **********************/
  **********************/
const LoadingIndicator = (function () {
var LoadingIndicator = (function () {
   const defaultConfig = {
   var defaultConfig = {
     imageUrl: 'https://wiki.ottohub.cn/images/0/02/Loading.png',
     imageUrl: 'https://wiki.ottohub.cn/images/0/02/Loading.png',
     position: { bottom: '20px', right: '20px' },
     position: { bottom: '20px', right: '20px' },
第130行: 第153行:
   };
   };


   let indicator = null;
   var indicator = null;
   let timerId = null;
   var timerId = null;
   let currentConfig = { ...defaultConfig };
   var currentConfig = Object.assign({}, defaultConfig);


   function createIndicator() {
   function createIndicator() {
     const div = document.createElement('div');
     var div = document.createElement('div');
     div.style.cssText = `
     div.style.position = 'fixed';
      position: fixed;
    div.style.width = currentConfig.size;
      width: ${currentConfig.size};
    div.style.height = currentConfig.size;
      height: ${currentConfig.size};
    div.style.bottom = currentConfig.position.bottom;
      bottom: ${currentConfig.position.bottom};
    div.style.right = currentConfig.position.right;
      right: ${currentConfig.position.right};
    div.style.zIndex = currentConfig.zIndex;
      z-index: ${currentConfig.zIndex};
    div.style.background = 'url(\'' + currentConfig.imageUrl + '\') center/contain no-repeat';
      background: url('${currentConfig.imageUrl}') center/contain no-repeat;
    div.style.opacity = '0';
      opacity: 0;
    div.style.transition = 'opacity ' + currentConfig.fadeDuration + 'ms';
      transition: opacity ${currentConfig.fadeDuration}ms;
    div.style.pointerEvents = 'none';
      pointer-events: none;
    `;
     return div;
     return div;
   }
   }
第157行: 第178行:
     document.body.appendChild(indicator);
     document.body.appendChild(indicator);
      
      
     setTimeout(() => {
     setTimeout(function() {
       indicator.style.opacity = '1';
       indicator.style.opacity = '1';
     }, 10);
     }, 10);
第168行: 第189行:
      
      
     indicator.style.opacity = '0';
     indicator.style.opacity = '0';
     setTimeout(() => {
     setTimeout(function() {
       indicator.remove();
       if (indicator && indicator.parentNode) {
        indicator.parentNode.removeChild(indicator);
      }
       indicator = null;
       indicator = null;
     }, currentConfig.fadeDuration);
     }, currentConfig.fadeDuration);
第177行: 第200行:


   function updateConfig(newConfig) {
   function updateConfig(newConfig) {
     currentConfig = { ...defaultConfig, ...newConfig };
     currentConfig = Object.assign({}, defaultConfig, newConfig);
   }
   }


第184行: 第207行:


   return {
   return {
     show,
     show: show,
     hide,
     hide: hide,
     updateConfig
     updateConfig: updateConfig
   };
   };
})();
})();

2025年6月21日 (六) 08:25的版本

/********************** 
 * 模块 0:vConsole 加载(移动端专用)
 **********************/
// 检测是否为移动设备
function isMobileDevice() {
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}

if (isMobileDevice() && !window.localStorage.getItem('disableVConsole')) {
  const vConsoleScript = document.createElement('script');
  vConsoleScript.src = 'https://cdn.bootcdn.net/ajax/libs/vConsole/3.15.1/vconsole.min.js';
  
  vConsoleScript.onload = function() {
    try {
      if (window.VConsole) {
        new window.VConsole();
      }
    } catch (e) {
      alert('调试工具初始化失败,请重试或联系管理员');
      console.error('vConsole init error:', e);
    }
  };
  
  vConsoleScript.onerror = function() {
    alert('调试工具加载失败,请检查网络连接');
    console.error('vConsole failed to load');
  };
  
  document.body.appendChild(vConsoleScript);
}

/**********************
 * 模块 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(function(c) { return c + c; }).join('');
    }
    if (hex.length !== 6) return '0, 0, 0';
    var r = parseInt(hex.substring(0, 2), 16);
    var g = parseInt(hex.substring(2, 4), 16);
    var b = parseInt(hex.substring(4, 6), 16);
    return r + ', ' + g + ', ' + b;
  }

  function update() {
    var root = document.documentElement;
    var hex = getComputedStyle(root).getPropertyValue(OBS_VAR).trim();
    var rgb = parseHex(hex || '#000');
    if (rgb !== cachedRgb) {
      root.style.setProperty('--background-color-rgb', rgb);
      cachedRgb = rgb;
    }
  }

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

  return {
    destroy: function() { mo.disconnect(); }
  };
})();

/**********************
 * 模块 2:上传页引导弹窗
 **********************/
// 修复可选链操作符问题
if (window.mw && mw.config && mw.config.get('wgCanonicalSpecialPageName') === 'Upload') {
  var KEY = 'uploadPreference';
  var pref = mw.storage.get(KEY);

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

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

      UploadDialog.prototype.initialize = function() {
        OO.ui.ProcessDialog.prototype.initialize.apply(this, arguments);
        this._remember = new OO.ui.CheckboxInputWidget();
        var wizardBtn = new OO.ui.ButtonWidget({
          label: '✅ 上传向导',
          flags: ['primary'],
          href: mw.util.getUrl('Special:UploadWizard'),
          target: '_self'
        });
        wizardBtn.on('click', function() {
          if (this._remember.isSelected()) {
            mw.storage.set(KEY, 'wizard');
          }
        }.bind(this));
        
        var $body = this.$body;
        $body.append(
          $('<p>').text('请选择上传方式:'),
          wizardBtn.$element,
          new OO.ui.FieldLayout(this._remember, { 
            label: '记住选择', 
            align: 'inline' 
          }).$element
        );
      };

      UploadDialog.prototype.getActionProcess = function(action) {
        if (action === 'classic') {
          if (this._remember.isSelected()) {
            mw.storage.set(KEY, 'classic');
          }
          return new OO.ui.Process(function() {
            this.close();
          }.bind(this));
        }
        return OO.ui.ProcessDialog.prototype.getActionProcess.apply(this, arguments);
      };

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

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

/**********************
 * 模块 3:加载指示器
 **********************/
var LoadingIndicator = (function () {
  var defaultConfig = {
    imageUrl: 'https://wiki.ottohub.cn/images/0/02/Loading.png',
    position: { bottom: '20px', right: '20px' },
    size: '256px',
    timeout: 15000,
    zIndex: '99999',
    fadeDuration: 300
  };

  var indicator = null;
  var timerId = null;
  var currentConfig = Object.assign({}, defaultConfig);

  function createIndicator() {
    var div = document.createElement('div');
    div.style.position = 'fixed';
    div.style.width = currentConfig.size;
    div.style.height = currentConfig.size;
    div.style.bottom = currentConfig.position.bottom;
    div.style.right = currentConfig.position.right;
    div.style.zIndex = currentConfig.zIndex;
    div.style.background = 'url(\'' + currentConfig.imageUrl + '\') center/contain no-repeat';
    div.style.opacity = '0';
    div.style.transition = 'opacity ' + currentConfig.fadeDuration + 'ms';
    div.style.pointerEvents = 'none';
    return div;
  }

  function show() {
    if (indicator) return;
    
    indicator = createIndicator();
    document.body.appendChild(indicator);
    
    setTimeout(function() {
      indicator.style.opacity = '1';
    }, 10);
    
    timerId = setTimeout(hide, currentConfig.timeout);
  }

  function hide() {
    if (!indicator) return;
    
    indicator.style.opacity = '0';
    setTimeout(function() {
      if (indicator && indicator.parentNode) {
        indicator.parentNode.removeChild(indicator);
      }
      indicator = null;
    }, currentConfig.fadeDuration);
    
    clearTimeout(timerId);
  }

  function updateConfig(newConfig) {
    currentConfig = Object.assign({}, defaultConfig, newConfig);
  }

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

  return {
    show: show,
    hide: hide,
    updateConfig: updateConfig
  };
})();