// ==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();
})();