v2ex+

auto load item detail when mouseover title

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         v2ex+
// @namespace    http://tampermonkey.net/
// @version      0.19
// @description  auto load item detail when mouseover title
// @author       Silvio27
// @match        https://*.v2ex.com/*
// @match        https://v2ex.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=v2ex.com
// @license      GPLv3
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand

// ==/UserScript==

(function() {
    'use strict';
    console.log("script")
    topics_node_page(true, true, true, 600)
    member_replies_page(true, false, false, 0)
    linksToImgs()


    const menu_command_id = GM_registerMenuCommand("加载页面全部内容", function () {
        document.querySelectorAll(".triangle").forEach(e => {setTimeout(e.click(), 500)})
    }, "a");


    function topics_node_page(show_topic, show_fav_btn, show_reply, defaultHeight) {
        let topics
        if (document.getElementById("TopicsNode")) {
            // alert("TopicsNode")
            topics = document.querySelectorAll("#TopicsNode>.cell")
        } else {
            // alert("No TopicsNode")
            topics = document.querySelectorAll(".cell.item")
        }
        topics.forEach((element, index) => {
            let new_item = create_item("td", element.querySelector("tr"), element.querySelector("tr").lastElementChild,
                                       '▼', "triangle", 30)
            // 修改cursor_style
            cursor_style(new_item)
            new_item.onclick = function () {
                let url = element.querySelector(".topic-link").href
                let id = url.replace("https://www.v2ex.com/t/", "").split("#")[0] + "_topic"
                load_item_change_btn(element, id, url, new_item, show_topic, show_fav_btn, show_reply, defaultHeight)

            }

        })
    }

    function member_replies_page(show_topic, show_fav_btn, show_reply, defaultHeight) {
        let dock_areas = document.querySelectorAll(".dock_area")
        dock_areas.forEach((element, index) => {
            // 创建新的列
            let new_item = create_item("span", element.querySelector("td"), "",
                                       '▼', "triangle", 30)
            cursor_style(new_item)
            new_item.onclick = function () {
                let url = element.querySelectorAll("a")[2].href
                let id = url.replace("https://www.v2ex.com/t/", "").split("#")[0] + "_reply"
                load_item_change_btn(element, id, url, new_item, show_topic, show_fav_btn, show_reply, defaultHeight)
            }
        })
    }

    function load_item_change_btn(element, id, url, btn, show_topic, show_fav_btn, show_reply, defaultHeight) {
        let content = document.getElementById(id)
        if (content) {
            if (content.style.display === "none") {
                content.style.display = "block"
                btn.innerText = "▲"
            } else {
                content.style.display = "none"
                btn.innerText = "▼"
            }
        } else {
            btn.innerText = "加载中"
            document.body.style.cursor = "wait";
            btn.className = "content-loaded"
            load_item_topic_favBtn_reply(element, url, id, btn, show_topic, show_fav_btn, show_reply, defaultHeight)

        }
    }

    function load_item_topic_favBtn_reply(target_ele, url, id, load_btn, show_topic, show_fav_btn, show_reply, defaultHeight) {
        // 创建一个临时容器元素来容纳加载的内容
        const tempContainer = document.createElement('div');
        // 使用Ajax异步加载下一页的内容
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.onload = function () {
            if (xhr.status === 200) {
                tempContainer.innerHTML = xhr.responseText
                // 创建返回的内容元素
                let contents = document.createElement('div')
                contents.id = id
                contents.className = "woDeStyle"

                // 绑定Escape折叠contents
                mouse_escape(contents)

                if (show_topic) {
                    // 获得 topic_content
                    let topic_contents = tempContainer.querySelectorAll(".topic_content")
                    contents.innerHTML += '<div class="topic-content-box"></div>'
                    topic_contents.forEach((e) => {
                        // 即 .topic-content-box
                        contents.firstChild.appendChild(e.parentElement)
                    })
                }

                if (show_fav_btn) {
                    // 添加topic_buttons
                    let favorite_btn = tempContainer.querySelector(".topic_buttons")
                    // 点击收藏,页面在新标签中打开
                    let fav_ele = favorite_btn.querySelector("a")
                    fav_ele.onclick = function (e) {
                        // 阻止原始a标签打开新页面
                        e.preventDefault()
                        // 后台发送xhr
                        mark_favourite(this)
                    }
                    contents.firstElementChild.appendChild(favorite_btn)
                }

                if (show_reply) {
                    // todo 展示不清楚为什么box会出现不同的情况,待研究
                    // 获得 reply_content
                    let reply_box = tempContainer.querySelectorAll(".box")
                    reply_box.forEach((e, index) => {
                        if (e.innerText.includes("条回复")) {
                            contents.appendChild(e)
                        }
                    })

                    // 去除reply_content中头像及空格
                    contents.querySelectorAll("tbody>tr").forEach((e) => {
                        e.removeChild(e.firstElementChild)
                        e.removeChild(e.firstElementChild)
                    })

                    // 添加一个折叠按钮 todo 是否可以直接把tr中添加的拿来用?
                    let hideBtn = document.createElement("div")
                    hideBtn.innerText = "▲"
                    hideBtn.className = "content-loaded"
                    hideBtn.style.textAlign = "right"
                    hideBtn.onclick = (() => {
                        // 隐藏主题详情
                        contents.style.display = "none"
                        // 切换主题展开为关闭
                        contents.parentElement.querySelector(".content-loaded").innerText = "▼"
                    })
                    cursor_style(hideBtn)
                    contents.appendChild(hideBtn)
                }

                // content添加到target_ele
                target_ele.appendChild(contents)
                // 解析markdown图片
                linksToImgs()

                // 设置默认高度
                set_content_default_height(contents, defaultHeight)

                // 修改折叠按钮为展开,切换样式
                set_btn_up(load_btn)
            }
        };
        xhr.send();
    }

    function cursor_style(element) {
        element.addEventListener("mouseover", function () {
            document.body.style.cursor = "pointer";
        });
        element.addEventListener("mouseout", function () {
            document.body.style.cursor = "auto";
        });
    }

    function create_item(tagName, element, element_place, inner_text, class_name, width) {
        let item = document.createElement(tagName)
        item.setAttribute("width", width)
        item.setAttribute("align", "center")
        item.setAttribute("class", class_name)
        item.innerText = inner_text
        if (element_place) {
            element.insertBefore(item, element_place)
        } else {
            element.appendChild(item)
        }
        return item
    }

    function mark_favourite(ele) {
        let url = ele.href
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.onload = function () {
            if (xhr.status === 200) {
                if (ele.innerText !== "取消收藏") {
                    ele.innerText = "取消收藏"
                    ele.style.backgroundColor = "gold"
                    ele.href = url.replace("fav", "unfav")
                } else {
                    // todo 取消收藏,好像有页面重定向的问题
                    console("目前需要手动处理")
                    // ele.innerText = "加入收藏"
                    // ele.style.backgroundColor = ""
                    // ele.href = url.replace("unfav", "fav")
                }
            }
        }
        xhr.send();

    }

    function mouse_escape(element) {
        element.addEventListener('mouseover', function () {
            document.addEventListener('keydown', escKeyPressed);
        });

        element.addEventListener('mouseout', function () {
            document.removeEventListener('keydown', escKeyPressed);
        });

        function escKeyPressed(event) {
            if (event.key === 'Escape') {
                element.style.display = 'none';
                // todo 这个位置应该需要结构
                element.parentElement.querySelector(".content-loaded").innerText = "▼"
            }
        }
    }

    function set_content_default_height(element, defaultHeight) {
        // 如果没有设置,默认高度600px;如果输入0,完整显示
        if (defaultHeight === 0) {
            defaultHeight = element.offsetHeight
        } else if (!defaultHeight) {
            defaultHeight = 600
        }

        if (element.offsetHeight > defaultHeight) {
            element.style.height = defaultHeight + "px"
        } else {
            element.style.height = element.offsetHeight
        }
    }

    function set_btn_up(element) {
        element.innerText = "▲"
        element.className = "content-loaded"
        document.body.style.cursor = "auto";
    }


    // 链接转图片
    // 修改自 https://greasyfork.org/zh-CN/scripts/424246
    // size调整为30%;考虑可以点击图片,窗口最大化
    // todo 可以考虑把外面的括号进行替换
    function linksToImgs() {
        let links = document.links;
        Array.from(links).forEach(function (_this) {
            if (/^https.*\.(?:jpg|jpeg|jpe|bmp|png|gif)/i.test(_this.href) && !(/<img\s/i.test(_this.innerHTML))) {
                _this.innerHTML = `<img src="${_this.href}" style="max-width: 30%!important;" />`;
                // alert("有图片被替换了")
            } else if (/^https:\/\/imgur\.com\/[a-z]+$/i.test(_this.href)) { // 针对没有文件后缀的 imgur 图床链接
                _this.innerHTML = `<img src="${_this.href}.png" style="max-width: 30%!important;" />`;
            }
        });
    }





    let css = `
         .woDeStyle {
        /*height: 600px;*/
        padding: 10px;
        margin: 10px auto;
        border: 1px solid gray;
        border-radius: 10px;
        overflow: scroll;
    }

    .topic-content-box {
        border-bottom: 2px dashed gray;
        padding-bottom: 10px;
        margin-bottom: 10px;
    }

    .triangle {
        color: gray;
        padding-left: 10px;
    }

    .content-loaded {
        color: greenyellow;
        padding-left: 10px;
    }

    #Wrapper {
        background-color: var(--box-background-color) !important;
        background-image: none !important;
    }

    .toggle-more-nodes {
        display: none;
    }

    #nodes-more-children, #nodes-more-related {
        display: block !important;
    }


    `
    GM_addStyle(css)



})();