Greasy Fork

Greasy Fork is available in English.

链接预览

鼠标指向链接标识图标预览链接网页

当前为 2023-12-30 提交的版本,查看 最新版本

// ==UserScript==
// @name         链接预览
// @name:en      Link Previewer
// @namespace    http://greasyfork.icu/zh-CN/users/1073-hzhbest
// @version      1.0
// @description  鼠标指向链接标识图标预览链接网页
// @description:en Hovering to preview a link
// @author       hzhbest
// @match        *://*/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const ixonimg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAoUlEQVQ4y8XSyw3CMBAE0AekkHTBBZGkCa40QA9AOVADEkGiEJfCxZEsk+8F5mLZs7Mzay3/xmpm3Tm7v9DCeqHhDhdU3UORkHVKJAi4osEJj5RME1SxiZ64De444J2SRVbcRjdZslsUP7Efa6BH3DkHHHOD9UxxG8VlXjTUoExit2MRh0YI2MZzFGMjTIr7EtQTf/I1zmbBWoe4E7OS/Q4fA1sbIN+Dzp4AAAAASUVORK5CYIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
    const inonimg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAATr4AAE6+AGeOBvEAAAA3klEQVQ4y83SPUpDURAF4M8YooUiIi7BQBaQ2lqirZjnVrIFCwsLDbbZgLoAO8FC7EJcgNgZUpvYTOByee/513hguDB3zpm5cy5/xOovOI0qgUZJ5FjDBbbxnF5s4hHjLPYz8jVmOFwmm0n3PdzhJXIreM06H6OP23y0LbyjVzJ2C8O8c51AGwOsB/kqyEd1m00FephiF5dfkZs1Vp0H8RQ3kS/iHJV6mmADJziLRe4kAkXlpwgsMI8Y4AEHP3nCPTohtMTbdwUKdCtq5+HQpEzgA09R0K5xa5p8tH+CT7aEK2vlnm2YAAAAAElFTkSuQmCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
    const loadimg = "data:image/png;base64,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";
    const domainregex = /(?<=:\/\/)[^\/]+/;
    const minHgap = 20, minVgap = 10;       // 小窗距鼠标位置的右、下最大距离:像素
    var winWidth = 700, winHeight = 550;    // 小窗宽、高:像素
    const scale = 0.6;                      // 小窗内页面放大率:(0~1]
    const animationtime = 0.5;              // 动画时长:秒
    const iconsize = 20;                    // 图标放大后大小:像素
    const icontrpr = 0.7;                   // 图标放大后不透明度:(0~1)
    const pre = "░░░░░░ ";                  // 拖动手柄前缀:字符串
    const id = "__link__prev";
    const css = `
        /* 链接样式 */
        a.___prevlink>img.___previcon {
            width: 16px !important; height: 16px !important;
            opacity: 0%; transition: opacity 0.5s ease-out, background 0s;
            position: absolute;
            display: inline-block !important; margin: 0 !important; 
        }
        a.___prevlink.__pr {
            position: relative;
        }
        *:hover>a.___prevlink>img.___previcon {
            opacity: 30%;
        }
        a.___prevlink:hover>img.___previcon {
            opacity: ${icontrpr}; background: white; border: 1px solid #8d8d8db1;
            transition: opacity 0.5s linear; z-index: 10008;
            transform: scale(${iconsize / 16});
        }
        a.___prevlink>img.___previcon:hover {
            background: #3e3ed3 !important; transition: background 1s !important;
        }
        a.__link__preved {
            outline: 3px solid #3e3ed3;
        }
        /* 小窗样式 */
        div#__link__prev_win {
            opacity: 0; position: absolute; z-index: 10009; box-shadow: 1px 1px 7px 1px #717171;
            padding: 0; margin: 0; background-color: white; transition: ${animationtime}s ease;
        }
        div#__link__prev_win.__close {
            transition: 0s !important; display: none;
        } 
        div#__link__prev_win.__loading {
            background: url(${loadimg}) no-repeat center center; background-color: #e7e7e7;
        } 
        div#__link__prev_win.__link__visible {
            opacity: 1; transition: 0.2s !important;
        }
        div#__link__prev_win.visible .__link__prev_ifr {
            border: none; transform: scale(${scale});
        }
        div#__link__prev_win.__ondrag {
            transition: 0s !important; opacity: 0.7;
        }
        /* 关闭按钮样式 */
        @keyframes rotating_button {
            from {
                transform: rotate(0deg);
            }
            to {
                transform: rotate(360deg);
            }
        }
        div#__link__prev_win>.__link__clos_btn {
            position: absolute; top: -10px; right: -10px; z-index: 10010;
            line-height: 24px; width: 24px; height: 24px;
            color: white; font-family: Consolas; font-size: 11pt; text-align: center;
            background: #df2020; border: 1px solid black; border-radius: 50%; 
            box-shadow: 1px 1px 5px #333; cursor: default; padding: 0 !important; margin: 0;
        }
        div#__link__prev_win>.__link__clos_btn.__loading {
            animation: 3s linear 0s infinite rotating_button; background: #d76565;
        }
        div#__link__prev_win>.__link__clos_btn.__loading:hover {
            animation: none; background: #df2020;
        }
        div#__link__prev_win>.__link__clos_btn:hover {
            outline: 3px solid #be3737a2;
        }
        div#__link__prev_win>.__link__clos_btn.__close {
            display: none;
        }
        /* 拖动手柄样式 */
        div#__link__prev_win>div.__link__prev_han {
            position: absolute; top: -5px; left: 0px; z-index: 10010; color: #d7d7d7; 
            height: 6px; width: 30px; border: 1px solid #242424; background: #3e3e3e; cursor: move;
            border-radius: 3px; overflow: hidden; max-width: ${winWidth}px; 
        }
        div#__link__prev_win>div.__link__prev_han>* {
            display: inline-block; width: fit-content;
        }
        div#__link__prev_win>div.__link__prev_han:hover {
            color: #e0a52e; height: 20px; top: -20px; width: fit-content;
        }
        div#__link__prev_win.__ondrag>div.__link__prev_han {
            background: #6f6122; height: 50px; top: -20px;
        }
        div#__link__prev_win>.__link__prev_ttl {
            font-size: 14px; font-family: Arial; white-space: nowrap;
        }
        div#__link__prev_win>.__link__prev_btn {
            height: 18px; width: 18px; border: 1px solid #616161; color: #9a9276; font-size: 13px;
            position: absolute; right: 0; bottom: 0; z-index: 10011; background: #ccd0d3af;
        }
        div#__link__prev_win>.__link__prev_btn:hover {
            background: #2c96e24f; border-color: #2c96e2af;
        }
        div#__link__prev_win.__ondrag>.__link__prev_btn {
            height: 40px; width: 40px;
        }
    `;
    var previewwin, pre_ir, timer, linkprev, closebtn, draghand, titlebox, ondrag, rsbtn, rsdrag;

    addCSS(css, id);
    makeprevwin();
    var site = location.host;
    document.addEventListener('mouseover', (evt) => {
        const t = evt.target;
        if (t.tagName == "A" && ispagelink(t)) {
            addIconTo([t]);
        } else {
            var links = getLinksInThreeLayer(t);
            addIconTo(links);
        }
    });
    setTimeout(function () {									// 延时设置监视器,若标题变化则重新加载
        var watch = document.querySelector('title');
        new (window.MutationObserver || window.WebKitMutationObserver)(function (mutations) {
            // console.log('标题变了', document.title);
            if (!document.querySelector("#" + id)) {
                addCSS(css, id);
            }
            if (!document.querySelector("#" + "__link__prev_win")) {
                makeprevwin();
            }
            site = location.host;
        }).observe(watch, { childList: true, subtree: true, characterData: true });
    }, 1000);

    function addIconTo(links) {
        var lnkl = links.length;
        var exl = 0;
        // console.log(lnkl);
        for (let i = 0; i < lnkl; i++) {
            const lnk = links[i];
            if (lnk.classList.contains("___prevlink")) {
                continue;
            }
            lnk.classList.add("___prevlink");
            if (window.getComputedStyle(lnk).position == "static") {
                lnk.classList.add("__pr");
            }
            var lnkposit = getTrueSize(lnk);
            var textWidth = lnk.textContent.trim().length * parseInt(window.getComputedStyle(lnk).fontSize)
            textWidth = Math.min(lnkposit.w, textWidth);
            var img;
            img = creaElemIn('img', lnk);
            img.className = '___previcon';
            img.style.bottom = '0.1em';
            var adj = (lnkposit.r >= (4 + iconsize)) ? textWidth + 4 : Math.min(textWidth + 4, lnkposit.w - 5 - iconsize);
            img.style.left = adj + "px";
            if (domainregex.exec(lnk.href)[0] !== site) {
                img.src = ixonimg;
                exl += 1;
            } else {
                img.src = inonimg;
            }
            img.addEventListener('mouseover', (evt) => {
                //clearTimeout(timer);
                setTimeout(() => {
                    if (previewwin.classList.contains("__close")) {
                        setWinPosit(evt.x, evt.y);
                    }
                }, 1000);
                timer = setTimeout(previewlink, 1000, evt);
            });
            img.addEventListener('mouseleave', () => {
                clearTimeout(timer);
            });
        }
        //console.log("external link count:" + exl);

        window.addEventListener('mousedown', (e) => {
            var target = e.target;
            // 隐藏预览
            if (isprevvisual() && !previewwin.contains(target)) {
                hidepreview(e);
            }
        }, true);
    }

    function makeprevwin() {
        previewwin = creaElemIn('div', document.body);
        previewwin.id = "__link__prev_win";
        previewwin.style.width = '100px';  // 显示前小窗大小
        previewwin.style.height = '100px';
        previewwin.classList.add("__close");

        draghand = creaElemIn('div', previewwin);
        draghand.classList.add("__link__prev_han");
        draghand.innerHTML = pre;

        rsbtn = creaElemIn('div', previewwin);
        rsbtn.className = "__link__prev_btn";
        rsbtn.innerHTML = "┘";

        titlebox = creaElemIn('div', draghand);
        titlebox.className = "__link__prev_ttl";

        pre_ir = creaElemIn('iframe', previewwin);
        pre_ir.className = "__link__prev_ifr";
        pre_ir.style.border = 0;

        closebtn = creaElemIn('div', previewwin);
        closebtn.classList.add("__link__clos_btn");
        closebtn.innerHTML = "X";
        closebtn.addEventListener('click', hidepreview);

        ondrag = endrag(previewwin, { x: 'left', y: 'top' }, draghand);
        ondrag.hook('__drag_begin', () => {
            previewwin.classList.add("__ondrag");
        });
        ondrag.hook('__drag_end', () => {
            previewwin.classList.remove("__ondrag");
        });

        rsdrag = endrag(previewwin, { x: 'width', y: 'height' }, rsbtn);
        rsdrag.hook('__dragging', () => {
            if (!!rsdrag.position) {                            // 未开始拖动前该对象不存在,判断避免编译出错
                winWidth = rsdrag.position._x;
                winHeight = rsdrag.position._y;
                setIfrWinSize(winWidth, winHeight);
            }
        });
        rsdrag.hook('__drag_begin', () => {
            previewwin.classList.add("__ondrag");
        });
        rsdrag.hook('__drag_end', () => {
            previewwin.classList.remove("__ondrag");
        });
    }

    function previewlink(evt) {
        if (!!linkprev) {
            linkprev.classList.remove("__link__preved");
        }
        linkprev = evt.target.parentNode;
        linkprev.classList.add("__link__preved");
        pre_ir.src = linkprev.href;
        pre_ir.style.transform = "scale(" + scale + ")";
        previewwin.classList.remove("__close");
        previewwin.classList.add("__loading");
        closebtn.classList.remove("__close");
        closebtn.classList.add("__loading");
        titlebox.innerHTML = linkprev.textContent;
        setTimeout(() => {
            previewwin.classList.add("__link__visible");
            let l = Math.max(minHgap, Math.min(window.innerWidth - winWidth - minHgap, evt.x));
            let t = Math.max(minVgap, Math.min(window.innerHeight - winHeight - minVgap, evt.y));
            setWinPosit(l, t);
            setPrevWinSize(winWidth, winHeight);
        }, 100);
        pre_ir.onload = () => {
            closebtn.classList.remove("__loading");
            previewwin.classList.remove("__loading");
        };
    }

    function setWinPosit(l, t) {         // 根据屏幕位置设置网页位置
        previewwin.style.left = l + window.scrollX + 'px';
        previewwin.style.top = t + window.scrollY + 'px';
    }

    function setPrevWinSize(w,h) {      // 设置预览窗口大小
        winWidth = w;
        winHeight = h;
        previewwin.style.width = w + 'px';
        previewwin.style.height = h + 'px';
        setIfrWinSize(w, h);
    }

    function setIfrWinSize(w, h) {         // 根据放大率设置iframe窗口大小
        var f = (1 - scale) / 2 * -1 / scale;
        pre_ir.width = w / scale;
        pre_ir.height = h / scale;
        pre_ir.style.marginLeft = (w * f) + 'px';
        pre_ir.style.marginTop = (h * f) + 'px';
    }

    function hidepreview(evt) {
        setPrevWinSize(100, 100);           // 小窗缩小效果
        pre_ir.src = '';
        setWinPosit(evt.x, evt.y);          // 在鼠标位置消失
        previewwin.classList.remove('__link__visible');
        linkprev.classList.remove('__link__preved');
        closebtn.classList.add("__close");
        setTimeout(() => {
            previewwin.classList.add("__close");
        }, animationtime * 1000 + 10);
    }

    function isprevvisual() {
        return previewwin && previewwin.classList.contains('__link__visible');
    }

    function getLinksInThreeLayer(elem) {
        var links = [];
        elem.childNodes.forEach(cnode => {
            if (ispagelink(cnode)) {
                links.push(cnode);
            } else {
                cnode.childNodes.forEach(ccnode => {
                    if (ispagelink(ccnode)) {
                        links.push(ccnode);
                    } else {
                        ccnode.childNodes.forEach(cccnode => {
                            if (ispagelink(cccnode)) {
                                links.push(cccnode);
                            }
                        });
                    }
                });
            }
        });
        return links;
    }

    function ispagelink(node) {             // 若节点是A且指向实网址(非js非锚点)返回true
        if (node.tagName !== "A" || !node.href) {
            return false;
        }
        const h = node.href, l = location.href;
        if (h.indexOf('javascript:') == 0 || h.replace(l.split('#')[0], "").indexOf("#") == 0) {
            return false;
        } else {
            return true;
        }
    }

    function creaElemIn(tagname, destin) {	//在 destin 内末尾创建元素 tagname
        let theElem = destin.appendChild(document.createElement(tagname));
        return theElem;
    }

    function addCSS(css, cssid) {
        let stylenode = creaElemIn('style', document.getElementsByTagName('head')[0]);
        stylenode.textContent = css;
        stylenode.type = 'text/css';
        stylenode.id = cssid || '';
    }

    // 输入元素,返回对象{元素可见的宽、高、顶、左、一层父元素右侧余量}
    function getTrueSize(elem, posiz) {
        if (!posiz) {
            var p = elem.getBoundingClientRect();
            posiz = {
                w: p.width,
                h: p.height,
                t: p.top,
                l: p.left,
                r: 0
            };
        }
        var pp = elem.parentNode.getBoundingClientRect();
        if (posiz.r == 0) posiz.r = (pp.left + pp.width) - (posiz.l + posiz.w); // 父元素对当前元素的右侧余量
        if (pp.width >= posiz.w && pp.height >= posiz.h && pp.top <= posiz.t && pp.left <= posiz.l) {
            return posiz;
        } else {
            return getTrueSize(elem.parentNode, {
                w: Math.min(posiz.w, pp.width),
                h: Math.min(posiz.h, pp.height),
                t: Math.max(posiz.t, pp.top),
                l: Math.max(posiz.l, pp.left),
                r: posiz.r
            });
        }
    }

    // 对target拖动handle时,实现拖动的功能
    // 输入:目标元素target,拖动位置参考系opt,拖动手柄handle
    // 输入opt:形如【{x:'right',y:'bottom'}】,或者width、height(右下角拖动)
    function endrag(target, opt, handle) {
        var p_x, p_y, isDragging;
        endrag = function (target, opt, handle) {
            return new endrag.proto(target, opt || {}, handle);
        }
        endrag.proto = function (target, opt, handle) {
            var self = this;
            this.target = target;
            this.style = target.style;
            this.handle = handle;
            var _x = opt.x !== 'right';
            var _y = opt.y !== 'bottom';
            this.x = opt.x;  //_x ? 'left' : 'right';
            this.y = opt.y;  //_y ? 'top' : 'bottom';
            // p_x = this.x;
            // p_y = this.y;
            this.xd = _x ? -1 : 1;
            this.yd = _y ? -1 : 1;
            this.computed_style = document.defaultView.getComputedStyle(target, '');
            this.drag_begin = function (e) { self.__drag_begin(e); };
            this.handle.addEventListener('mousedown', this.drag_begin, false); //only drag on handler
            this.dragging = function (e) { self.__dragging(e); };
            document.addEventListener('mousemove', this.dragging, false);
            this.drag_end = function (e) { self.__drag_end(e); };
            document.addEventListener('mouseup', this.drag_end, false);
        };
        endrag.proto.prototype = {
            __drag_begin: function (e) {
                if (e.button == 0) {
                    var _c = this.computed_style;
                    this.isDragging = isDragging = true;
                    this.position = {
                        _x: parseFloat(_c[this.x]),
                        _y: parseFloat(_c[this.y]),
                        x: e.pageX,
                        y: e.pageY
                    };
                    e.preventDefault();
                }
            },
            __dragging: function (e) {
                if (!this.isDragging) return;
                var x = Math.floor(e.pageX), y = Math.floor(e.pageY), p = this.position;
                // prevent moving out of window
                var x_border = window.innerWidth - 40, y_border = window.innerHeight - 20;
                if (x - window.scrollX > x_border) x = window.scrollX + x_border;
                if (y - window.scrollY > y_border) y = window.scrollY + y_border;
                p._x = p._x + (p.x - x) * this.xd;
                p._y = p._y + (p.y - y) * this.yd;
                this.style[this.x] = p._x + 'px';
                this.style[this.y] = p._y + 'px';
                p.x = x;
                p.y = y;
            },
            __drag_end: function (e) {
                if (e.button == 0) {
                    if (this.isDragging) {
                        this.isDragging = isDragging = false;
                    }
                }
            },
            hook: function (method, func) {
                if (typeof this[method] === 'function') {
                    var o = this[method];
                    this[method] = function () {
                        if (func.apply(this, arguments) === false) {
                            return;
                        }
                        o.apply(this, arguments);
                    };
                }
            }
        };
        return endrag(target, opt, handle);
    }
})();