跳转到内容

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

勤求古训,博采众方
无编辑摘要
无编辑摘要
 
第1行: 第1行:
/*
  本小工具可以將[[Template:僻字]]的提示由原來的title提示改為元素式彈出提示,使觸控式裝置可以觀看有關提示
*/
(function() {
(function() {
     // 使用MediaWiki的Cookie方法
     /*===== 初始化检查 =====*/
    // Cookie 检查(保持原始逻辑)
     const dontLoad = mw.util.getParamValue("UTdontload");
     const dontLoad = mw.util.getParamValue("UTdontload");
     if (dontLoad && !isNaN(dontLoad)) {
     if (dontLoad && !isNaN(dontLoad)) {
第10行: 第8行:
     if (mw.cookie.get("UTdontload") === "1") return;
     if (mw.cookie.get("UTdontload") === "1") return;


    /*===== 主逻辑 =====*/
     (function() {
     (function() {
         // 使用MediaWiki提供的content容器
         // 容器和目标命名空间检查
         const bodyContent = mw.util.$content || document.body;
         const bodyContent = mw.util.$content || document.body;
         const canonicalNamespace = mw.config.get('wgCanonicalNamespace');
         const canonicalNamespace = mw.config.get('wgCanonicalNamespace');
       
        // 只在这些命名空间运行
         if (!['', 'Project', 'Help'].includes(canonicalNamespace)) return;
         if (!['', 'Project', 'Help'].includes(canonicalNamespace)) return;


         // 更可靠的触摸检测
         // 触摸设备检测(优化后的逻辑)
const isTouchscreen = window.matchMedia('(hover: none), (pointer: coarse)').matches ||
        const isTouchscreen = window.matchMedia('(hover: none), (pointer: coarse)').matches ||
                            mw.config.get('wgDisplayResolution') === 'mobile' ||  
                            mw.config.get('wgDisplayResolution') === 'mobile' ||  
                            'ontouchstart' in document.documentElement;
                            'ontouchstart' in document.documentElement;
         const timerLength = isTouchscreen ? 0 : 200;
         const hoverDelay = isTouchscreen ? 0 : 200;


        /*===== 工具提示核心逻辑 =====*/
         $(".inline-unihan").each(function() {
         $(".inline-unihan").each(function() {
             let tooltipNode = null;
             let tooltipNode = null;
             let hideTimer = null;
             let hideTimer = null;
             let showTimer = null;
             let showTimer = null;
            const element = this;


             function hide(refLink) {
            // 保存原始title并添加高亮类
                 if (tooltipNode && tooltipNode.parentNode === bodyContent) {
            const originalTitle = this.title;
                    hideTimer = setTimeout(() => {
            this.title = "";
                        $(tooltipNode).animate({
            $(this).addClass("UHTarget");
                            opacity: 0
 
                        }, 100, function() {
            /*-- 工具提示控制函数 --*/
                            if (tooltipNode && tooltipNode.parentNode) {
             function hideTooltip() {
                                tooltipNode.parentNode.removeChild(tooltipNode);
                 if (!tooltipNode || tooltipNode.parentNode !== bodyContent) return;
                            }
               
                        });
                clearTimeout(hideTimer);
                    }, isTouchscreen ? 16 : 100);
                hideTimer = setTimeout(() => {
                }
                    $(tooltipNode).animate({ opacity: 0 }, 100, () => {
                        tooltipNode.parentNode.removeChild(tooltipNode);
                    });
                }, isTouchscreen ? 16 : 100);
             }
             }


             function show() {
             function showTooltip() {
                 if (!tooltipNode.parentNode || tooltipNode.parentNode.nodeType === 11) {
                 if (!tooltipNode.parentNode || tooltipNode.parentNode.nodeType === 11) {
                     bodyContent.appendChild(tooltipNode);
                     bodyContent.appendChild(tooltipNode);
                 }
                 }
                 $(tooltipNode).stop().animate({
                 $(tooltipNode).stop().animate({ opacity: 1 }, 100);
                    opacity: 1
                }, 100);
                 clearTimeout(hideTimer);
                 clearTimeout(hideTimer);
             }
             }


             // 保存原始title并清空
             /*-- 工具提示创建/更新 --*/
             const originalTitle = this.title;
             function createOrUpdateTooltip() {
             this.title = "";
                if (!tooltipNode) {
                    // 首次创建工具提示
                    tooltipNode = document.createElement("ul");
                    tooltipNode.className = "unihantooltip";
                   
                    // 内容容器 (第一个li)
                    const contentLi = document.createElement("li");
                   
                    // 设置齿轮图标
                    const settingsIcon = document.createElement("div");
                    settingsIcon.className = "UHsettings";
                    settingsIcon.title = "设置";
                    $(settingsIcon).on("click", (e) => {
                        e.stopPropagation();
                        // 这里添加设置图标的点击处理逻辑
                    });
                   
                    // 箭头元素 (第二个li)
                    const arrowLi = document.createElement("li");
                   
                    // 组装DOM结构
                    contentLi.appendChild(createContentElement());
                    contentLi.appendChild(settingsIcon);
                    tooltipNode.appendChild(contentLi);
                    tooltipNode.appendChild(arrowLi);
 
                    // 非触摸设备的悬停处理
                    if (!isTouchscreen) {
                        $(tooltipNode).on("mouseenter", showTooltip)
                                      .on("mouseleave", hideTooltip);
                    }
                } else {
                    // 更新已有内容
                    const contentContainer = tooltipNode.firstChild.firstChild;
                    tooltipNode.firstChild.replaceChild(
                        createContentElement(),
                        contentContainer
                    );
                }
 
                // 定位并显示
                positionTooltip();
                showTooltip();
            }
 
             /*-- 内容生成函数 --*/
            function createContentElement() {
                const container = document.createElement("div");
                originalTitle.split("\n").forEach(line => {
                    if (line.trim()) {
                        const p = document.createElement("p");
                        p.textContent = line;
                        container.appendChild(p);
                    }
                });
                return container;
            }


             // 使用jQuery的on方法代替hover
             /*-- 精确定位逻辑 (关键调整) --*/
             $(this).on(isTouchscreen ? 'click' : 'mouseenter mouseleave', function(e) {
             function positionTooltip() {
                 const element = this;
                const $element = $(element);
                const elementPos = $element.offset();
                const elementHeight = $element.outerHeight();
                const elementWidth = $element.outerWidth();
               
                // 临时显示以获取尺寸
                tooltipNode.style.display = 'block';
                const tooltipWidth = tooltipNode.offsetWidth;
                const contentHeight = tooltipNode.firstChild.offsetHeight;
                const arrowHeight = 12; // 对应CSS中的箭头高度
               
                // 视口边界检测
                const viewport = {
                    top: $(window).scrollTop(),
                    bottom: $(window).scrollTop() + $(window).height(),
                    left: 0,
                    right: $(window).width()
                };
               
                /*===== 垂直定位 =====*/
                // 默认上方显示 (减内容高度和箭头高度)
                let top = elementPos.top - contentHeight - arrowHeight;
               
                // 检查是否需要翻转 (空间不足时)
                 const needFlip = (top < viewport.top) ||
                                (elementPos.top + contentHeight > viewport.bottom);
               
                $(tooltipNode).toggleClass("UHflipped", needFlip);
               
                // 应用翻转偏移量 (13px对应CSS的padding-top)
                top = needFlip
                    ? elementPos.top + elementHeight + arrowHeight - 13
                    : top;
               
                // 确保不超出视口
                top = Math.max(viewport.top, Math.min(top, viewport.bottom - contentHeight));
               
                /*===== 水平定位 =====*/
                // 中心对齐 (7px对应CSS箭头的margin-left)
                let left = elementPos.left + (elementWidth - tooltipWidth) / 2;
                  
                  
                // 边界保护 (左右各留10px安全边距)
                left = Math.max(viewport.left + 10,
                              Math.min(left, viewport.right - tooltipWidth - 10));
               
                /*===== 箭头定位 =====*/
                const arrow = tooltipNode.lastChild;
                const arrowCenterOffset = elementPos.left - left + elementWidth / 2 - 7;
                $(arrow).css("margin-left", arrowCenterOffset + "px");
               
                // 应用最终位置
                $(tooltipNode).css({
                    top: top + "px",
                    left: left + "px",
                    display: '' // 恢复原始display
                });
            }
            /*-- 触摸设备的外部点击处理 --*/
            function setupClickOutsideHandler() {
                const handler = function(e) {
                    if (!tooltipNode.contains(e.target) && e.target !== element) {
                        hideTooltip();
                        $(document).off("click touchstart", handler);
                    }
                };
                $(document).on("click touchstart", handler);
            }
            /*===== 事件绑定 =====*/
            $(element).on(isTouchscreen ? 'click' : 'mouseenter mouseleave', function(e) {
                // 触摸设备处理
                 if (isTouchscreen && e.type === 'click') {
                 if (isTouchscreen && e.type === 'click') {
                     e.preventDefault();
                     e.preventDefault();
                      
                      
                     if (!tooltipNode || tooltipNode.parentNode !== bodyContent) {
                     if (!tooltipNode || tooltipNode.parentNode !== bodyContent) {
                         requestAnimationFrame(() => {
                         createOrUpdateTooltip();
                            const closeHandler = (e) => {
                        setupClickOutsideHandler();
                                const target = e.target || e.srcElement;
                                let currentElement = target;
                               
                                while (currentElement && !currentElement.classList.contains("unihantooltip")) {
                                    currentElement = currentElement.parentNode;
                                }
                               
                                if (!currentElement) {
                                    clearTimeout(showTimer);
                                    hide(element);
                                    $(bodyContent).off("click touchstart", closeHandler);
                                }
                            };
                           
                            $(bodyContent).on("click touchstart", closeHandler);
                        });
                     }
                     }
                    return;
                 }
                 }
 
               
                // 鼠标设备处理
                 if (e.type === 'mouseleave') {
                 if (e.type === 'mouseleave') {
                     clearTimeout(showTimer);
                     clearTimeout(showTimer);
                     hide(this);
                     hideTooltip();
                     return;
                     return;
                 }
                 }
 
               
                 showTimer && clearTimeout(showTimer);
                // mouseenter处理
                 showTimer = setTimeout(() => {
                 clearTimeout(showTimer);
                    const tooltipContent = $("<ul class='reflist'></ul>");
                 showTimer = setTimeout(createOrUpdateTooltip, hoverDelay);
                    const lines = originalTitle.split("\n");
                   
                    lines.forEach(line => {
                        if (line.trim()) {
                            tooltipContent.append($("<li></li>").text(line)).append("<br>");
                        }
                    });
 
                    if (!tooltipNode) {
                        tooltipNode = document.createElement("div");
                        tooltipNode.className = "unihantooltip";
                        $(tooltipNode).append(tooltipContent);
                       
                        if (!isTouchscreen) {
                            $(tooltipNode).on("mouseenter", show)
                                          .on("mouseleave", () => hide(element));
                        }
                    } else {
                        $(tooltipNode).empty().append(tooltipContent);
                    }
 
                    show();
                   
                    const position = $(element).position();
                    const viewport = {
                        width: document.documentElement.clientWidth,
                        height: document.documentElement.clientHeight
                    };
                   
                    let top = position.top - tooltipNode.offsetHeight;
                    let left = position.left - 7;
 
                    // 边界检测
                    if (top < 0) {
                        $(tooltipNode).addClass("UHflipped");
                        top = position.top + $(element).outerHeight();
                    } else {
                        $(tooltipNode).removeClass("UHflipped");
                    }
 
                    if (left + tooltipNode.offsetWidth > viewport.width) {
                        left = viewport.width - tooltipNode.offsetWidth - 10;
                    }
 
                    $(tooltipNode).css({
                        top: Math.max(0, top),
                        left: Math.max(0, left),
                        opacity: 0 // 初始设置为透明以准备动画
                    }).stop().animate({ opacity: 1 }, 100);
                }, timerLength);
             });
             });
         });
         });
     })();
     })();
})();
})();

2025年11月2日 (日) 18:14的最新版本

(function() {
    /*===== 初始化检查 =====*/
    // Cookie 检查(保持原始逻辑)
    const dontLoad = mw.util.getParamValue("UTdontload");
    if (dontLoad && !isNaN(dontLoad)) {
        mw.cookie.set("UTdontload", "1", { path: "/", expires: parseInt(dontLoad) });
    }
    if (mw.cookie.get("UTdontload") === "1") return;

    /*===== 主逻辑 =====*/
    (function() {
        // 容器和目标命名空间检查
        const bodyContent = mw.util.$content || document.body;
        const canonicalNamespace = mw.config.get('wgCanonicalNamespace');
        if (!['', 'Project', 'Help'].includes(canonicalNamespace)) return;

        // 触摸设备检测(优化后的逻辑)
        const isTouchscreen = window.matchMedia('(hover: none), (pointer: coarse)').matches ||
                             mw.config.get('wgDisplayResolution') === 'mobile' || 
                             'ontouchstart' in document.documentElement;
        const hoverDelay = isTouchscreen ? 0 : 200;

        /*===== 工具提示核心逻辑 =====*/
        $(".inline-unihan").each(function() {
            let tooltipNode = null;
            let hideTimer = null;
            let showTimer = null;
            const element = this;

            // 保存原始title并添加高亮类
            const originalTitle = this.title;
            this.title = "";
            $(this).addClass("UHTarget");

            /*-- 工具提示控制函数 --*/
            function hideTooltip() {
                if (!tooltipNode || tooltipNode.parentNode !== bodyContent) return;
                
                clearTimeout(hideTimer);
                hideTimer = setTimeout(() => {
                    $(tooltipNode).animate({ opacity: 0 }, 100, () => {
                        tooltipNode.parentNode.removeChild(tooltipNode);
                    });
                }, isTouchscreen ? 16 : 100);
            }

            function showTooltip() {
                if (!tooltipNode.parentNode || tooltipNode.parentNode.nodeType === 11) {
                    bodyContent.appendChild(tooltipNode);
                }
                $(tooltipNode).stop().animate({ opacity: 1 }, 100);
                clearTimeout(hideTimer);
            }

            /*-- 工具提示创建/更新 --*/
            function createOrUpdateTooltip() {
                if (!tooltipNode) {
                    // 首次创建工具提示
                    tooltipNode = document.createElement("ul");
                    tooltipNode.className = "unihantooltip";
                    
                    // 内容容器 (第一个li)
                    const contentLi = document.createElement("li");
                    
                    // 设置齿轮图标
                    const settingsIcon = document.createElement("div");
                    settingsIcon.className = "UHsettings";
                    settingsIcon.title = "设置";
                    $(settingsIcon).on("click", (e) => {
                        e.stopPropagation();
                        // 这里添加设置图标的点击处理逻辑
                    });
                    
                    // 箭头元素 (第二个li)
                    const arrowLi = document.createElement("li");
                    
                    // 组装DOM结构
                    contentLi.appendChild(createContentElement());
                    contentLi.appendChild(settingsIcon);
                    tooltipNode.appendChild(contentLi);
                    tooltipNode.appendChild(arrowLi);

                    // 非触摸设备的悬停处理
                    if (!isTouchscreen) {
                        $(tooltipNode).on("mouseenter", showTooltip)
                                      .on("mouseleave", hideTooltip);
                    }
                } else {
                    // 更新已有内容
                    const contentContainer = tooltipNode.firstChild.firstChild;
                    tooltipNode.firstChild.replaceChild(
                        createContentElement(),
                        contentContainer
                    );
                }

                // 定位并显示
                positionTooltip();
                showTooltip();
            }

            /*-- 内容生成函数 --*/
            function createContentElement() {
                const container = document.createElement("div");
                originalTitle.split("\n").forEach(line => {
                    if (line.trim()) {
                        const p = document.createElement("p");
                        p.textContent = line;
                        container.appendChild(p);
                    }
                });
                return container;
            }

            /*-- 精确定位逻辑 (关键调整) --*/
            function positionTooltip() {
                const $element = $(element);
                const elementPos = $element.offset();
                const elementHeight = $element.outerHeight();
                const elementWidth = $element.outerWidth();
                
                // 临时显示以获取尺寸
                tooltipNode.style.display = 'block';
                const tooltipWidth = tooltipNode.offsetWidth;
                const contentHeight = tooltipNode.firstChild.offsetHeight;
                const arrowHeight = 12; // 对应CSS中的箭头高度
                
                // 视口边界检测
                const viewport = {
                    top: $(window).scrollTop(),
                    bottom: $(window).scrollTop() + $(window).height(),
                    left: 0,
                    right: $(window).width()
                };
                
                /*===== 垂直定位 =====*/
                // 默认上方显示 (减内容高度和箭头高度)
                let top = elementPos.top - contentHeight - arrowHeight;
                
                // 检查是否需要翻转 (空间不足时)
                const needFlip = (top < viewport.top) || 
                                (elementPos.top + contentHeight > viewport.bottom);
                
                $(tooltipNode).toggleClass("UHflipped", needFlip);
                
                // 应用翻转偏移量 (13px对应CSS的padding-top)
                top = needFlip 
                    ? elementPos.top + elementHeight + arrowHeight - 13
                    : top;
                
                // 确保不超出视口
                top = Math.max(viewport.top, Math.min(top, viewport.bottom - contentHeight));
                
                /*===== 水平定位 =====*/
                // 中心对齐 (7px对应CSS箭头的margin-left)
                let left = elementPos.left + (elementWidth - tooltipWidth) / 2;
                
                // 边界保护 (左右各留10px安全边距)
                left = Math.max(viewport.left + 10, 
                               Math.min(left, viewport.right - tooltipWidth - 10));
                
                /*===== 箭头定位 =====*/
                const arrow = tooltipNode.lastChild;
                const arrowCenterOffset = elementPos.left - left + elementWidth / 2 - 7;
                $(arrow).css("margin-left", arrowCenterOffset + "px");
                
                // 应用最终位置
                $(tooltipNode).css({
                    top: top + "px",
                    left: left + "px",
                    display: '' // 恢复原始display
                });
            }

            /*-- 触摸设备的外部点击处理 --*/
            function setupClickOutsideHandler() {
                const handler = function(e) {
                    if (!tooltipNode.contains(e.target) && e.target !== element) {
                        hideTooltip();
                        $(document).off("click touchstart", handler);
                    }
                };
                $(document).on("click touchstart", handler);
            }

            /*===== 事件绑定 =====*/
            $(element).on(isTouchscreen ? 'click' : 'mouseenter mouseleave', function(e) {
                // 触摸设备处理
                if (isTouchscreen && e.type === 'click') {
                    e.preventDefault();
                    
                    if (!tooltipNode || tooltipNode.parentNode !== bodyContent) {
                        createOrUpdateTooltip();
                        setupClickOutsideHandler();
                    }
                    return;
                }
                
                // 鼠标设备处理
                if (e.type === 'mouseleave') {
                    clearTimeout(showTimer);
                    hideTooltip();
                    return;
                }
                
                // mouseenter处理
                clearTimeout(showTimer);
                showTimer = setTimeout(createOrUpdateTooltip, hoverDelay);
            });
        });
    })();
})();