// ==UserScript== // @name V2EX快捷查看回复 // @namespace http://tampermonkey.net/ // @version 1.2.3 // @description V2EX快捷查看回复对象 // @author xiyue // @license MIT // @match https://v2ex.com/t/* // @match https://www.v2ex.com/t/* // @icon https://www.google.com/s2/favicons?domain=v2ex.com // @require https://unpkg.com/axios@0.25.0/dist/axios.min.js // @grant none // @downloadURL none // ==/UserScript== (function () { "use strict"; // 重写回复函数,给@后面增加楼层数 replyOne = function (username) { setReplyBoxSticky(); const replyContent = document.getElementById("reply_content"); const oldContent = replyContent.value; const prefix = "@" + username + " #" + event.target.offsetParent.querySelector(".no").innerText; let newContent = ""; if (oldContent.length > 0) { if (oldContent != prefix) { newContent = oldContent + "\n" + prefix; } } else { newContent = prefix; } replyContent.focus(); replyContent.value = newContent; moveEnd(replyContent); }; let style = document.querySelector("style").sheet, replyWidth = 642, // 悬浮窗口大小 svgIcon = ``, bgColor = window.getComputedStyle(document.querySelector("#Main .box"), null).backgroundColor, borderColor = window.getComputedStyle(document.querySelector(".cell"), null).borderBottomColor; style.insertRule( `.fixed-reply { transition: all 300ms; transform: translateY(-10px); pointer-events: none; opacity: 0; padding: 12px 20px; width: ${replyWidth}px; box-sizing: border-box; position: absolute; bottom: 30px; left: -14px; background: ${bgColor}; border-radius: 8px; box-shadow: 0 0 18px rgb(0 0 0 / 10%); border: solid 1px ${borderColor}; user-select: auto; }`, 1 ); style.insertRule( `.show-reply { position: relative; display: inline-flex; align-items: center; justify-content: flex-start; }`, 1 ); style.insertRule( ` .show-reply:hover>.fixed-reply{ transition: all 300ms; transform: translateY(0); pointer-events: auto; opacity: 1; }`, 1 ); style.insertRule( `.show-reply:hover:before { content: ""; position: absolute; width: 160px; height: 10px; left: 0; bottom: 100%; }`, 1 ); style.insertRule( `.cell { transition:all 300ms; }`, 1 ); style.insertRule( `.cell.highlight { background-color: #FFE97F; }`, 1 ); let replyList = []; let lastNum = 0; function linkReply() { replyList = document.querySelectorAll(".reply_content"); // 遍历回复列表 replyList.forEach((el, index) => { let texts = el.parentNode.parentNode.querySelector(".no").innerText * 1; if (lastNum > texts) { console.error("评论顺序加载错误!", lastNum, texts); } else { lastNum = texts; } // 获取所有@ el.querySelectorAll(".reply_content a").forEach((atEl) => { let quoteIndex = getIndex(el, atEl, index); let replyEl = getContent(atEl.innerText, quoteIndex, index); if (replyEl) { let tempNode = document.createElement("div"); tempNode.className = "show-reply"; tempNode.appendChild(replyEl); tempNode.appendChild(document.createRange().createContextualFragment(svgIcon)); atEl.parentNode.insertBefore(tempNode, atEl); replyEl.parentNode.insertBefore(atEl, replyEl); } }); }); // 添加监听事件,点击楼层号跳转到对应楼层 document.querySelectorAll(".reply_content").forEach((el) => { el.parentNode.parentNode.querySelector(".no").addEventListener("click", function (event) { event.preventDefault(); event.stopPropagation(); gotoReply(this.innerText); }); }); } // 搜索楼层 function getContent(userId, quoteIndex, maxIndex) { let lastContent = null; // 先尝试搜索引用楼层 if ( replyList[quoteIndex - 1] && replyList[quoteIndex - 1].parentNode.querySelector("strong a").innerText === userId ) { lastContent = replyList[quoteIndex - 1].parentNode.parentNode; } // 如果第一个范围没有找到则搜索第二范围 if (!lastContent) { for (var i = 0; i < maxIndex; i++) { if (replyList[i].parentNode.querySelector("strong a").innerText === userId) { lastContent = replyList[i].parentNode.parentNode; } } } if (lastContent) { var tempNode = document.createElement("div"); tempNode.className = "fixed-reply"; tempNode.addEventListener("click", function (event) { event.preventDefault(); }); tempNode.appendChild(lastContent.cloneNode(true)).querySelector("td:last-child").width = replyWidth - 48 - 10; tempNode.querySelector(".no").addEventListener("click", function (event) { event.preventDefault(); event.stopPropagation(); gotoReply(this.innerText); }); return tempNode; } else { return false; } } // 判断是否含有楼层号 function getIndex(el, atEl, index) { let elStr = el.innerHTML, atElStr = atEl.innerHTML, newReg = new RegExp(`@${atElStr}\\s+#\\d+`), regMatch = newReg.exec(elStr); if (regMatch) { let nums = regMatch[0].split("#")[1] * 1; return nums; } else { // 避免有人@自己导致引用出错 return index - 1 >= 0 ? index - 1 : 0; } } // 跳转到对应楼层 function gotoReply(index, page = 1) { for (var i = replyList.length - 1; i >= 0; i--) { let el = replyList[i]; // 如果没有找到指定楼层则跳转到最后一楼 if (el.parentNode.querySelector(".no").innerText == index || index === replyList.length - 1) { var replaceTop = el.parentNode.parentNode.parentNode.parentNode.parentNode.getBoundingClientRect().top, repId = el.parentNode.parentNode.parentNode.parentNode.parentNode.id; window.scrollTo({ top: window.scrollY + replaceTop, behavior: "smooth" }); document.querySelectorAll("#Main .cell[id]")[index - 1].className += " highlight"; (() => { setTimeout(() => { document.querySelectorAll("#Main .cell[id]")[index - 1].className = "cell"; }, 1500); })(); history.pushState( null, null, `${window.location.origin}${window.location.pathname}${page === "1" ? "" : `?p=${page}`}#${repId}` ); break; } } } // 处理评论翻页 let switchPage = document.querySelector(".page_normal"); if (switchPage) { autoLoadNextPage(switchPage.parentNode.querySelectorAll(".page_normal")); } else { linkReply(); } async function autoLoadNextPage(pages) { // 楼层跳转链接添加监听事件 let pageLink = document.querySelectorAll(".page_normal"), pageCurrent = document.querySelectorAll(".page_current"); var allEl = []; allEl.push.apply(allEl, pageLink); allEl.push.apply(allEl, pageCurrent); allEl.forEach((el) => { el.addEventListener("click", function (event) { event.preventDefault(); let clickPageNum = event.target.innerText; document.querySelectorAll(".page_current").forEach((el) => { el.className = "page_normal"; }); document.querySelectorAll(".page_normal").forEach((el) => { if (el.innerText === clickPageNum) { el.className = "page_current"; } }); let noNum = (clickPageNum - 1) * 100 + 1; gotoReply(noNum, clickPageNum); console.log(noNum); }); }); // 异步加载其他回复页面数据 for (let el of pages) { let req = await axios.get(el.getAttribute("href")), domData = req.data, template = document.createElement("template"), mainReplyEl = document.querySelectorAll("#Main .box .cell:last-child")[0]; template.innerHTML = domData; // 判断插入位置 const tempFirstId = template.content.querySelector("#Main .box .cell[id]").getAttribute("id").replace(/\D/g, "") * 1; const cellEl = document.querySelectorAll("#Main .box .cell[id]"); for (const el of cellEl) { let listId = el.getAttribute("id").replace(/\D/g, "") * 1; if (listId > tempFirstId) { mainReplyEl = el; break; } } template.content.querySelectorAll("#Main .box .cell[id]").forEach((el, index) => { mainReplyEl.parentNode.insertBefore(el, mainReplyEl); }); } linkReply(); } // 增加处理图床url的逻辑 function replaceImageBed() { const imgBedList = ["https://imgur.com", "https://i.imgur.com"]; const findImgBed = (item) => { for (let i = 0; i < imgBedList.length; i++) { if (item.href.startsWith(imgBedList[i])) { return true; } } return false; }; document.querySelectorAll("a").forEach((item) => { if (findImgBed(item) && item.href === item.innerText) { console.log(item); const a = document.createElement("a"); a.href = item.href; const img = document.createElement("img"); img.src = item.href + ".png"; a.appendChild(img); item.replaceWith(a); } }); } replaceImageBed(); // 增加每日自动签到 function autoSign() { if (document.querySelector(`[href="/mission/daily"]`)) { fetch("/mission/daily").then(async (res) => { if (res.status === 200) { console.log("签到成功"); const domparse = new DOMParser(); const doc = domparse.parseFromString(await res.text(), "text/html"); const url = doc.querySelector(`[value="领取 X 铜币"]`).getAttribute("onclick"); const regex = /location\.href\s*=\s*['"]([^'"]+)['"]/; const match = url.match(regex); const extractedUrl = match ? match[1] : null; if (extractedUrl) { fetch(extractedUrl).then((res) => { document.querySelector(`[href="/mission/daily"]`).innerText = "已签到"; }); } } }); } } autoSign(); })();