MediaWiki

MediaWiki:Common.js

来自卡厄思梦境WIKI

律Rhyme留言 | 贡献2025年10月14日 (二) 16:58的版本

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

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
/* 这里的任何JavaScript将为所有用户在每次页面加载时加载。 */

mw.loader.load('/index.php?title=MediaWiki:CardSys.js&action=raw&ctype=text/javascript'); // 引入卡牌相关JS

/* 切换标签 */
(function() {
    'use strict';
    
    function initTabSwitcher() {
        var tabContainers = document.querySelectorAll('.resp-tabs');
        
        tabContainers.forEach(function(container) {
            var tabButtons = container.querySelectorAll('.czn-list-style');
            if (tabButtons.length === 0) return;
            
            var tabContents = container.querySelectorAll('.resp-tab-content');
            
            // 初始化
            tabButtons.forEach(function(button, index) {
                button.classList.toggle('active', index === 0);
            });
            
            if (tabContents.length > 0) {
                tabContents.forEach(function(content, index) {
                    content.style.display = index === 0 ? 'block' : 'none';
                });
            }
            
            // 点击事件
            tabButtons.forEach(function(button, index) {
                button.addEventListener('click', function(e) {
                    e.preventDefault();
                    
                    // 更新标签状态
                    tabButtons.forEach(function(btn, i) {
                        btn.classList.toggle('active', i === index);
                    });
                    
                    // 切换内容
                    if (tabContents.length > 0) {
                        tabContents.forEach(function(content, i) {
                            content.style.display = i === index ? 'block' : 'none';
                        });
                    }
                });
            });
        });
    }
    
    // 初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initTabSwitcher);
    } else {
        initTabSwitcher();
    }
})();

/* 角色立绘切换 */
$(document).ready(function() {
    // 使用事件委托来确保动态内容也能响应
    $(document).on('click', '.character-switch-btn', function() {
        // 如果点击的是已经激活的按钮,不执行任何操作
        if ($(this).hasClass('active')) {
            return;
        }
        
        var targetType = $(this).attr('data-target');
        var container = $(this).closest('#character-container');
        var imageWrapper = container.find('.character-image-wrapper');
        var allImages = imageWrapper.find('.character-image');
        
        // 先将所有图片淡出
        allImages.each(function() {
            $(this).css('opacity', '0');
        });
        
        // 延迟后切换显示状态
        setTimeout(function() {
            allImages.each(function() {
                $(this).css('display', 'none');
            });
            
            // 显示目标图片
            imageWrapper.find('[data-image-type="' + targetType + '"]').each(function() {
                $(this).css('display', 'block');
                // 强制重绘
                $(this)[0].offsetHeight;
                $(this).css('opacity', '1');
            });
        }, 300);
        
        // 更新按钮样式
        container.find('.character-switch-btn').each(function() {
            $(this).removeClass('active');
            $(this).css({
                'background': 'rgba(0,0,0,0.5)',
                'cursor': 'pointer'
            });
        });
        
        // 设置当前按钮为激活状态
        $(this).addClass('active');
        $(this).css({
            'background': 'rgba(70, 130, 255, 0.8)',
            'cursor': 'default'
        });
    });
    
    // 鼠标悬停效果(仅对非激活按钮有效)
    $(document).on('mouseenter', '.character-switch-btn:not(.active)', function() {
        $(this).css('background', 'rgba(70, 130, 255, 0.5)');
    });
    
    $(document).on('mouseleave', '.character-switch-btn:not(.active)', function() {
        $(this).css('background', 'rgba(0,0,0,0.5)');
    });
});

/* 悬浮目录 */
$(document).ready(function() {
    // 只在有目录的页面上执行
    if ($('.toc').length > 0) {
        // 创建侧边目录
        var $sidebar = $('<div id="toc-sidebar"><div id="toc-sidebar-trigger">展开目录</div><div id="toc-sidebar-content"></div></div>');
        $('body').append($sidebar);
        
        // 提取并修复目录内容
        var tocUl = $('<ul></ul>');
        
        // 避免重复的目录项
        var processedItems = new Set();
        
        // 从原始目录构建新目录
        $('.toc ul li').each(function() {
            var $link = $(this).find('a').first();
            var href = $link.attr('href');
            var $number = $link.find('.tocnumber').first();
            var $text = $link.find('.toctext').first();
            
            // 创建唯一标识符,避免重复添加
            var itemId = $number.text() + '-' + $text.text();
            
            if (!processedItems.has(itemId)) {
                processedItems.add(itemId);
                
                // 创建新的目录项
                var $li = $('<li></li>');
                var $newLink = $('').attr('href', href);
                
                // 如果有编号,则添加编号
                if ($number.length) {
                    $newLink.append($('<span class="tocnumber"></span>').text($number.text()));
                    $newLink.append(' ');
                }
                
                $newLink.append($('<span class="toctext"></span>').text($text.text()));
                $li.append($newLink);
                tocUl.append($li);
            }
        });
        
        $('#toc-sidebar-content').append(tocUl);
        
        // 点击展开/折叠事件处理
        $('#toc-sidebar-trigger').click(function() {
            $('#toc-sidebar').toggleClass('expanded');
            
            // 根据展开/折叠状态更改按钮文字
            if ($('#toc-sidebar').hasClass('expanded')) {
                $('#toc-sidebar-trigger').text('隐藏目录');
            } else {
                $('#toc-sidebar-trigger').text('展开目录');
            }
        });
    }
});

/* 卡牌文字滚动 */
(function() {
    'use strict';
    
    function initCardScroll() {
        var cards = document.querySelectorAll('.game-card.card-description-scrollable');
        
        cards.forEach(function(card) {
            var scrollContainer = card.querySelector('.card-description-scroll');
            var scrollInner = card.querySelector('.card-description-scroll-inner');
            var scrollText = card.querySelector('.card-description-text');
            
            if (!scrollContainer || !scrollInner || !scrollText) {
                return;
            }
            
            // 使用 requestAnimationFrame 优化性能
            requestAnimationFrame(function() {
                scrollInner.style.display = 'none';
                scrollInner.offsetHeight;
                scrollInner.style.display = 'block';
            });
        });
    }
    
    // 延迟初始化,使用防抖优化
    var initTimeout;
    function delayedInit() {
        clearTimeout(initTimeout);
        initTimeout = setTimeout(function() {
            initCardScroll();
        }, 100);
    }
    
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', delayedInit);
    } else {
        delayedInit();
    }
    
    // 监听动态内容加载(例如 AJAX 加载)
    var observer = new MutationObserver(function(mutations) {
        var shouldInit = false;
        
        mutations.forEach(function(mutation) {
            if (mutation.addedNodes.length > 0) {
                mutation.addedNodes.forEach(function(node) {
                    if (node.nodeType === 1 && 
                        (node.classList.contains('game-card') || 
                         node.querySelector('.game-card'))) {
                        shouldInit = true;
                    }
                });
            }
        });
        
        if (shouldInit) {
            delayedInit();
        }
    });
    
    // 只观察特定容器,减少性能开销
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
})();

/* 卡牌文字滚动 */
(function() {
    'use strict';
    
    function initCardScroll() {
        var cards = document.querySelectorAll('.game-card.card-description-scrollable');
        
        cards.forEach(function(card) {
            var scrollContainer = card.querySelector('.card-description-scroll');
            var scrollInner = card.querySelector('.card-description-scroll-inner');
            var scrollText = card.querySelector('.card-description-text');
            
            if (!scrollContainer || !scrollInner || !scrollText) {
                return;
            }
            
            // 强制触发重绘
            scrollInner.style.display = 'none';
            scrollInner.offsetHeight; // 触发重排
            scrollInner.style.display = 'block';
        });
    }
    
    // 延迟初始化,确保DOM完全加载
    function delayedInit() {
        setTimeout(initCardScroll, 100);
        setTimeout(initCardScroll, 500);
        setTimeout(initCardScroll, 1000);
    }
    
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', delayedInit);
    } else {
        delayedInit();
    }
    
})();

/* 卡牌Tooltip */
$(function() {
  // 初始化所有卡牌悬浮
  function initCardHoverElements() {
    $('.card-hover-container').each(function() {
      var $container = $(this);
      
      // 只初始化尚未处理的元素
      if ($container.data('initialized')) return;
      
      $container.data('initialized', true);
      
      // 获取卡牌数据
      var character = $container.data('character');
      var cardName = $container.data('card');
      var deckType = $container.data('deck') || '';
      var index = $container.data('index') || 0;
      var $popup = $container.find('.card-popup');
      
      // 预加载卡牌数据
      $container.on('mouseenter', function() {
        // 如果卡牌数据已经加载过,直接显示
        if ($popup.data('loaded')) {
          // 调整位置到右下方
          positionCardPopup($container);
          return;
        }
        
        // 加载卡牌数据
        var params = {
          action: 'parse',
          text: '{{#invoke:卡牌|main|' + character + '|' + cardName + '|' + deckType + '|' + index + '}}',
          prop: 'text',
          disablelimitreport: true,
          format: 'json'
        };
        
        $.ajax({
          url: mw.util.wikiScript('api'),
          data: params,
          dataType: 'json',
          success: function(data) {
            if (data && data.parse && data.parse.text) {
              var cardHtml = data.parse.text['*'];
              $popup.html(cardHtml).data('loaded', true);
              
              // 调整位置到右下方
              positionCardPopup($container);
            }
          },
          error: function() {
            $popup.html('<div style="color: #721c24;">无法加载卡牌数据</div>').data('loaded', true);
          }
        });
      });
    });
  }
  
  // 调整卡牌弹出位置到右下方
  function positionCardPopup($container) {
    var $popupContainer = $container.find('.card-popup-container');
    var containerWidth = $container.width();
    var windowWidth = $(window).width();
    var containerOffset = $container.offset();
    var rightSpace = windowWidth - containerOffset.left - containerWidth;
    
    // 重置位置
    $popupContainer.css({
      'left': 'auto',
      'right': 'auto',
      'top': '100%',
      'margin-top': '5px'
    });
    
    // 如果右侧空间足够,放在右下方
    if (rightSpace >= 168) { // 卡牌宽度大约168px
      $popupContainer.css({
        'left': '0',
      });
    } 
    // 如果右侧空间不够,但左侧空间足够,放在左下方
    else if (containerOffset.left >= 168) {
      $popupContainer.css({
        'right': '0',
      });
    }
    // 如果两侧都不够,尝试居中并确保完全可见
    else {
      var leftPosition = Math.max(0, Math.min(containerOffset.left - (168/2), windowWidth - 168));
      $popupContainer.css({
        'left': (leftPosition - containerOffset.left) + 'px'
      });
    }
  }
  
  // 初次加载页面时初始化
  $(document).ready(function() {
    initCardHoverElements();
  });
  
  // 使用 MutationObserver 监听 DOM 变化,处理动态加载的内容
  if (window.MutationObserver) {
    var observer = new MutationObserver(function(mutations) {
      var shouldInit = false;
      mutations.forEach(function(mutation) {
        if (mutation.addedNodes && mutation.addedNodes.length) {
          shouldInit = true;
        }
      });
      
      if (shouldInit) {
        initCardHoverElements();
      }
    });
    
    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  }
});

/* 词典Tooltip */
(function() {
    var tooltipContainer = null;
    var currentTerm = null;
    var hideTimeout = null;
    
    // 初始化
    $(function() {
        // 创建全局 tooltip 容器
        tooltipContainer = $('<div class="dictionary-tooltip-container"></div>');
        $('body').append(tooltipContainer);
        
        // 鼠标进入词条
        $(document).on('mouseenter', '.dictionary-term', function(e) {
            var $term = $(this);
            currentTerm = $term;
            
            // 清除隐藏定时器
            if (hideTimeout) {
                clearTimeout(hideTimeout);
                hideTimeout = null;
            }
            
            // 获取 tooltip 内容
            var content = $term.attr('data-tooltip-content');
            if (!content) return;
            
            // 设置内容
            tooltipContainer.html(content);
            
            // 计算位置
            updateTooltipPosition($term);
            
            // 显示 tooltip
            tooltipContainer.addClass('active');
        });
        
        // 鼠标离开词条
        $(document).on('mouseleave', '.dictionary-term', function() {
            hideTimeout = setTimeout(function() {
                tooltipContainer.removeClass('active');
                currentTerm = null;
            }, 100);
        });
        
        // 鼠标进入 tooltip(防止快速隐藏)
        tooltipContainer.on('mouseenter', function() {
            if (hideTimeout) {
                clearTimeout(hideTimeout);
                hideTimeout = null;
            }
        });
        
        // 鼠标离开 tooltip
        tooltipContainer.on('mouseleave', function() {
            hideTimeout = setTimeout(function() {
                tooltipContainer.removeClass('active');
                currentTerm = null;
            }, 100);
        });
        
        // 窗口滚动时更新位置
        $(window).on('scroll resize', function() {
            if (currentTerm && tooltipContainer.hasClass('active')) {
                updateTooltipPosition(currentTerm);
            }
        });
    });
    
    // 更新 tooltip 位置
    function updateTooltipPosition($term) {
        var rect = $term[0].getBoundingClientRect();
        var scrollTop = $(window).scrollTop();
        var scrollLeft = $(window).scrollLeft();
        
        // 基本位置:词条下方
        var top = rect.bottom + scrollTop + 2;
        var left = rect.left + scrollLeft;
        
        // 获取 tooltip 尺寸
        var tooltipWidth = 250; // 固定宽度
        var tooltipHeight = tooltipContainer.outerHeight();
        
        // 获取窗口尺寸
        var windowWidth = $(window).width();
        var windowHeight = $(window).height();
        
        // 检查右边界
        if (left + tooltipWidth > windowWidth + scrollLeft) {
            left = Math.max(scrollLeft, rect.right + scrollLeft - tooltipWidth);
        }
        
        // 检查下边界
        if (rect.bottom + tooltipHeight > windowHeight) {
            // 如果下方空间不足,显示在上方
            if (rect.top - tooltipHeight > 0) {
                top = rect.top + scrollTop - tooltipHeight - 2;
            }
        }
        
        // 设置位置
        tooltipContainer.css({
            top: top + 'px',
            left: left + 'px'
        });
    }
})();

/* 轮播图 */
(function () {
  'use strict';

  function initCarousel(root) {
    if (!root || root.__inited) return;
    root.__inited = true;

    const wrapper = root.querySelector('.carousel-wrapper');
    const slides = Array.from(root.querySelectorAll('.carousel-slide'));
    const titles = Array.from(root.querySelectorAll('.carousel-title-item'));
    const btnPrev = root.querySelector('.carousel-prev');
    const btnNext = root.querySelector('.carousel-next');

    if (!wrapper || !slides.length) {
      root.style.display = 'none';
      return;
    }

    // 单张图隐藏按钮
    if (slides.length === 1) {
      root.classList.add('single');
    }

    // 标题索引与幻灯索引对齐
    titles.forEach((t, i) => {
      t.dataset.slide = String(i);
    });

    let index = 0;
    let timer = null;
    const autoplay = (root.getAttribute('data-autoplay') || '1') !== '0';
    const interval = Math.max(1500, parseInt(root.getAttribute('data-interval') || '4000', 10) || 4000);

    function update() {
      wrapper.style.transform = 'translateX(' + (-index * 100) + '%)';
      titles.forEach((t, i) => {
        if (i === index) t.classList.add('active');
        else t.classList.remove('active');

        const text = t.querySelector('.title-text');
        const ind = t.querySelector('.title-indicator');
        if (text) text.style.opacity = i === index ? '1' : '0.75';
        if (ind) ind.style.background = i === index ? '#ff6600' : 'transparent';
      });
    }

    function next() {
      index = (index + 1) % slides.length;
      update();
    }

    function prev() {
      index = (index - 1 + slides.length) % slides.length;
      update();
    }

    // 标题点击
    titles.forEach((t) => {
      t.addEventListener('click', function () {
        const i = parseInt(t.dataset.slide || '0', 10) || 0;
        index = Math.min(Math.max(i, 0), slides.length - 1);
        update();
        restartAutoplay();
      });
    });

    // 按钮点击
    if (btnPrev) {
      btnPrev.addEventListener('click', function () {
        prev();
        restartAutoplay();
      });
    }
    if (btnNext) {
      btnNext.addEventListener('click', function () {
        next();
        restartAutoplay();
      });
    }

    // 自动播放控制
    function startAutoplay() {
      if (!autoplay || slides.length <= 1) return;
      stopAutoplay();
      timer = setInterval(next, interval);
    }

    function stopAutoplay() {
      if (timer) {
        clearInterval(timer);
        timer = null;
      }
    }

    function restartAutoplay() {
      stopAutoplay();
      startAutoplay();
    }

    // 悬停/聚焦暂停
    root.addEventListener('mouseenter', stopAutoplay);
    root.addEventListener('mouseleave', startAutoplay);
    root.addEventListener('focusin', stopAutoplay);
    root.addEventListener('focusout', startAutoplay);

    // 触摸滑动
    let startX = 0;
    let curX = 0;
    let dragging = false;
    const threshold = 30;

    root.addEventListener(
      'touchstart',
      function (e) {
        if (!e.touches || !e.touches.length) return;
        startX = e.touches[0].clientX;
        curX = startX;
        dragging = true;
        root.classList.add('grabbing');
        stopAutoplay();
      },
      { passive: true }
    );

    root.addEventListener(
      'touchmove',
      function (e) {
        if (!dragging || !e.touches || !e.touches.length) return;
        curX = e.touches[0].clientX;
        const dx = curX - startX;
        wrapper.style.transform = 'translateX(calc(' + (-index * 100) + '% + ' + dx + 'px))';
      },
      { passive: true }
    );

    root.addEventListener('touchend', function () {
      if (!dragging) return;
      const dx = curX - startX;
      root.classList.remove('grabbing');
      dragging = false;
      if (Math.abs(dx) > threshold) {
        if (dx < 0) next();
        else prev();
      } else {
        update();
      }
      startAutoplay();
    });

    // 鼠标拖动(可选)
    let mouseDown = false;
    let mStartX = 0;

    root.addEventListener('mousedown', function (e) {
      mouseDown = true;
      mStartX = e.clientX;
      root.classList.add('grab');
      stopAutoplay();
    });

    window.addEventListener('mouseup', function (e) {
      if (!mouseDown) return;
      const dx = e.clientX - mStartX;
      mouseDown = false;
      root.classList.remove('grab');
      if (Math.abs(dx) > threshold) {
        if (dx < 0) next();
        else prev();
      } else {
        update();
      }
      startAutoplay();
    });

    // 初始化
    update();
    startAutoplay();
  } // <- 确保此处仅关闭 initCarousel 函数

  function initAll(context) {
    const scope = context && context.querySelectorAll ? context : document;
    scope.querySelectorAll('.carousel-container').forEach(initCarousel);
  }

  // 初次加载
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function () {
      initAll();
    });
  } else {
    initAll();
  }

  // 动态内容(如可视化编辑器或AJAX)再次初始化
  if (window.mw && mw.hook) {
    mw.hook('wikipage.content').add(function (content) {
      // content 可能是 jQuery 对象,也可能是原生节点
      if (content && content[0] && content.find) {
        // jQuery 对象
        content.find('.carousel-container').each(function () {
          initCarousel(this);
        });
      } else if (content && content.querySelectorAll) {
        // 原生节点
        initAll(content);
      } else {
        initAll();
      }
    });
  }
})();

// 标签切换功能
(function() {
  function initRespTabs() {
    // 获取所有标签容器
    var tabContainers = document.querySelectorAll('.resp-tabs');
    
    tabContainers.forEach(function(container) {
      var tabs = container.querySelectorAll('.czn-list-style');
      var contents = container.querySelectorAll('.resp-tab-content');
      
      // 为每个标签添加点击事件
      tabs.forEach(function(tab, index) {
        tab.addEventListener('click', function() {
          // 移除所有激活状态
          tabs.forEach(function(t) {
            t.classList.remove('active');
          });
          
          // 隐藏所有内容
          contents.forEach(function(content) {
            content.style.display = 'none';
          });
          
          // 激活当前标签
          tab.classList.add('active');
          
          // 显示对应内容
          if (contents[index]) {
            contents[index].style.display = 'block';
          }
        });
      });
    });
  }
  
  // 页面加载完成后初始化
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initRespTabs);
  } else {
    initRespTabs();
  }
  
  // 支持动态加载的内容
  var observer = new MutationObserver(function(mutations) {
    initRespTabs();
  });
  
  observer.observe(document.body, {
    childList: true,
    subtree: true
  });
})();