论坛列表显示图片

论坛列表显示图片,支持phpwind和discuz

当前为 2023-09-08 提交的版本,查看 最新版本

// ==UserScript==
// @name         论坛列表显示图片
// @namespace    form_show_images_in_list
// @version      1.1
// @description  论坛列表显示图片,支持phpwind和discuz
// @license MIT
// @author       Gloduck
// @match        */forum.php?mod=forumdisplay*
// @match        */thread.php?fid*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// ==/UserScript==

(function () {
    'use strict';

    GM_addStyle(`
        .zoomable-image {
            cursor: pointer;
        }

        .zoomable-image.zoomed {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            object-fit: contain;
            background: rgba(0, 0, 0, 0.9);
            z-index: 9999;
        }
    `);

    // todo 添加extend继承的功能
    let settings = [
        {
            name: "discuz",
            articleListSelector: 'tbody[id^="normalthread_"]',
            postContentSelector: 'td[id^="postmessage_"]',
            ignoreImageRegs: [
                "/uc_server/images/*",
                "static/image/*",
                "/uc_server/data/avatar/*"
            ],
            maxShowLimit: 3,
            lazyLoad: true,
            postImageLinkCallback: function (element) {
                let fileLink = element.getAttribute('file');
                if (fileLink) {
                    return fileLink;
                }
                return element.getAttribute('src');
            },
            initElementDecorator: function (element) {
                let tbody = document.createElement("tbody");
                let tr = document.createElement("tr");
                tr.appendChild(element);
                tbody.appendChild(tr);
                return tbody;
            }
        },
        {
            name: "phpwind",
            articleListSelector: '#ajaxtable tbody:last-of-type tr[align=center]',
            postContentSelector: '#read_tpc',
            ignoreImageRegs: [],
            maxShowLimit: 3,
            lazyLoad: true,
            postImageLinkCallback: function (element) {
                return element.getAttribute('src');
            },
            initElementDecorator: function (element) {
                let tr = document.createElement("tr");
                tr.align = "center";
                let td = document.createElement("td");
                td.colSpan = 5;
                tr.appendChild(td);
                td.appendChild(element);
                return tr;
            }
        }
    ]

    let urlMatchers = [
        {
            name: "discuz",
            urlMatch: [
                "forum\\.php\\?mod=forumdisplay"
            ]
        },
        {
            name: "phpwind",
            urlMatch: [
                "thread\\.php\\?fid"
            ]
        }
    ]

    activeByUrl();

    function activeByUrl() {
        let activeSetting = "";
        for (let urlMatcher of urlMatchers) {
            let urlRegs = regStrToReg(urlMatcher.urlMatch);
            if (checkRegMatchStr(urlRegs, window.location.href)) {
                activeSetting = urlMatcher.name;
                break;
            }
        }
        if (!activeSetting) {
            console.log("无法找到要激活的配置");
        } else {
            console.log("激活的配置为:" + activeSetting);
            showImageInList(activeSetting);
        }
    }


    function showImageInList(type) {
        let setting = getSettingByType(type);
        let articleListElement = document.querySelectorAll(setting.articleListSelector);
        articleListElement.forEach(element => {
            if (setting.lazyLoad) {
                lazyLoadImageInList(element, setting);
            } else {
                loadImageInList(element, setting);
            }
        })
    }

    function lazyLoadImageInList(element, setting){
        // 注册(不可用)滚动事件,实现懒加载。同时通过节流来避免重复加载
        window.addEventListener('scroll', throttle(function () {
            const targetElementRect = element.getBoundingClientRect();
            if (targetElementRect.top < window.innerHeight && !element.getAttribute("imageLoad")) {
                handleSingleArticle(element, setting).then(toAppendElement => {
                    if (!element.getAttribute("imageLoad")) {
                        insertElementBelow(element, toAppendElement);
                        element.setAttribute("imageLoad", "true");
                    }
                })
            }
        }, 200, 500));
    }

    function loadImageInList(element, setting){
        handleSingleArticle(element, setting).then(toAppendElement => {
            insertElementBelow(element, toAppendElement);
        })
    }

    function insertElementBelow(targetElement, newElement) {
        var parentElement = targetElement.parentNode;
        parentElement.insertBefore(newElement, targetElement.nextSibling);
    }


    function getSettingByType(type) {
        let setting = settings.find(value => {
            return value.name === type;
        });
        if (setting == null) {
            throw new Error("不支持的类型");
        }
        return setting;
    }

    /**
     * 处理单个文章,返回最后需要拼接的element
     * @param element {Element}
     * @returns {Promise<void>}
     */
    async function handleSingleArticle(element, setting) {
        if (!element) {
            throw new Error("参数不能为空");
        }
        let link = findFirstAnchorLink(element);
        if (!link) {
            throw new Error("找不到链接");
        }
        let postResult = await httpRequest("GET", "/" + link);
        if (!postResult) {
            throw new Error("请求文章错误");
        }
        var htmlDivElement = document.createElement("div");
        // 初始化图片区域
        htmlDivElement.appendChild(getImagesDiv(setting, postResult));
        // todo 添加自定义元素
        return setting.initElementDecorator(htmlDivElement);
    }

    function getImagesDiv(setting, content) {
        let images = parsePostImages(setting, content);
        if (setting.maxShowLimit && setting.maxShowLimit > 0) {
            images = images.slice(0, setting.maxShowLimit);
        }
        let imageDiv = document.createElement("div");
        imageDiv.style = "display: flex;";
        imageDiv.className = "image_list";
        images.forEach(value => {
            let imgElement = document.createElement("img");
            imgElement.src = value;
            imgElement.style = "max-width: 300px;max-height: 300px;margin-right: 10px"
            imageDiv.appendChild(imgElement);
            imgElement.addEventListener('click', function () {
                // 创建一个新的图片元素
                var zoomedImg = document.createElement('img');
                zoomedImg.src = imgElement.src;

                // 添加类名以应用放大样式
                zoomedImg.classList.add('zoomable-image', 'zoomed');

                // 点击放大的功能
                zoomedImg.addEventListener('click', function () {
                    // 移除放大的图片元素
                    document.body.removeChild(zoomedImg);
                });

                // 将放大的图片元素添加到文档中
                document.body.appendChild(zoomedImg);
            });
        })
        return imageDiv;
    }

    /**
     *
     * @param setting
     * @param postDetails {string}
     */
    function parsePostImages(setting, postDetails) {
        let images = [];
        let content = new DOMParser().parseFromString(postDetails, "text/html");
        if (!content) {
            return images;
        }
        let postContentSelector = setting.postContentSelector;
        let postContent = content.querySelector(postContentSelector);
        if (!postContent) {
            return images;
        }
        let ignoreImageRegs = regStrToReg(setting.ignoreImageRegs);
        let imageElements = postContent.querySelectorAll('img');
        imageElements.forEach(imageElement => {
            let imageLink = setting.postImageLinkCallback(imageElement);
            if (checkRegMatchStr(ignoreImageRegs, imageLink)) {
                return;
            }
            images.push(imageLink);
        })
        return images;
    }

    function findFirstAnchorLink(element) {
        const linkElement = element.querySelector("a");
        if (linkElement) {
            return linkElement.getAttribute("href");
        } else {
            const childElements = element.children;
            for (let i = 0; i < childElements.length; i++) {
                const link = findFirstAnchorLink(childElements[i]);
                if (link) {
                    return link;
                }
            }
        }
        return null;
    }

    function regStrToReg(regs) {
        return regs.map(value => {
            return new RegExp(value);
        });
    }

    function checkRegMatchStr(regs, content) {
        if (!content || !regs) {
            throw new Error("参数不能为空");
        }
        for (var i = 0; i < regs.length; i++) {
            if (regs[i].test(content)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 防抖
     * @param func
     * @param wait
     * @param immediate
     * @returns {(function(): void)|*}
     */
    function debounce(func, wait, immediate) {
        // 定时器变量
        var timeout;
        return function () {
            // 每次触发 scroll handler 时先清除定时器
            clearTimeout(timeout);
            // 指定 xx ms 后触发真正想进行的操作 handler
            timeout = setTimeout(func, wait);
        };
    };

    /**
     * 节流
     * @param func
     * @param wait
     * @param mustRun
     * @returns {(function(): void)|*}
     */
    function throttle(func, wait, mustRun) {
        var timeout,
            startTime = new Date();

        return function () {
            var context = this,
                args = arguments,
                curTime = new Date();

            clearTimeout(timeout);
            // 如果达到了规定的触发时间间隔,触发 handler
            if (curTime - startTime >= mustRun) {
                func.apply(context, args);
                startTime = curTime;
                // 没达到触发间隔,重新设定定时器
            } else {
                timeout = setTimeout(func, wait);
            }
        };
    };

    function httpRequest(method, url) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: method,
                url: url,
                onload: function (response) {
                    resolve(response.responseText);
                },
                onerror: function (error) {
                    reject(error);
                }
            });
        });
    }

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址