您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Make RJ code great again!
当前为
// ==UserScript== // @name DLSite RJ code preview // @namespace SettingDust // @description Make RJ code great again! // @include *://*/* // @version 2.1.3 // @license MIT // @grant GM.xmlHttpRequest // @grant GM_xmlhttpRequest // @run-at document-start // ==/UserScript== "use strict"; const RJ_REGEX = new RegExp("R[JE][0-9]{6}", "gi"); const VOICELINK_CLASS = "voicelink"; const RJCODE_ATTRIBUTE = "rjcode"; const css = ` .voicepopup { z-index: 50000; max-width: 80%; max-height: 80%; position: fixed; box-shadow: 0 0 0 2.5px rgba(0, 0, 0, 0.12); border-radius: 16px; background-color: white; display: flex; flex-direction: column; overflow: hidden; } .voicepopup img { width: 100%; height: auto; max-width: 360px; } .voicelink { text-shadow: 1px 1px 1px #333; color: #333; } .voicelink: hover { text-decoration: none; } .voicepopup > .voice-info { padding: 12px 16px; font-size: 0.88rem; line-height: 1.2; max-width: 360px; display: grid; grid-gap: 6px; box-sizing: border-box; } .voicepopup > .voice-info > p { margin: 0; font-weight: bold; } .voicepopup > .voice-info > p > span { font-weight: normal; } .voicepopup .voice-title { margin: 0; font-size: 1.1rem; font-weight: bold; line-height: 1; } .voicepopup .error { height: 210px; line-height: 210px; text-align: center; } .voicepopup.discord-dark { background-color: #36393f; color: #dcddde; font-size: 0.9375rem; }`; function getAdditionalPopupClasses() { const hostname = document.location.hostname; switch (hostname) { case "boards.4chan.org": return "post reply"; case "discordapp.com": return "discord-dark"; default: return null; } } function getXmlHttpRequest() { return typeof GM !== "undefined" && GM !== null ? GM.xmlHttpRequest : GM_xmlhttpRequest; } const Parser = { walkNodes: function (elem) { const rjNodeTreeWalker = document.createTreeWalker( elem, NodeFilter.SHOW_TEXT, { acceptNode: function (node) { if (node.parentElement.classList.contains(VOICELINK_CLASS)) return NodeFilter.FILTER_ACCEPT; if (node.nodeValue.match(RJ_REGEX)) return NodeFilter.FILTER_ACCEPT; }, }, false ); while (rjNodeTreeWalker.nextNode()) { const node = rjNodeTreeWalker.currentNode; if (node.parentElement.classList.contains(VOICELINK_CLASS)) Parser.rebindEvents(node.parentElement); else { Parser.linkify(node); } } }, wrapRJCode: function (rjCode) { var e; e = document.createElement("a"); e.classList = VOICELINK_CLASS; e.href = `https://www.dlsite.com/maniax/work/=/product_id/${rjCode}.html`; e.innerHTML = rjCode; e.target = "_blank"; e.rel = "noreferrer"; e.classList.add(rjCode); e.setAttribute(RJCODE_ATTRIBUTE, rjCode.toUpperCase()); e.addEventListener("mouseover", Popup.over); e.addEventListener("mouseout", Popup.out); e.addEventListener("mousemove", Popup.move); return e; }, linkify: function (textNode) { const nodeOriginalText = textNode.nodeValue; const matches = []; let match; while ((match = RJ_REGEX.exec(nodeOriginalText))) { matches.push({ index: match.index, value: match[0], }); } // Keep text in text node until first RJ code textNode.nodeValue = nodeOriginalText.substring(0, matches[0].index); // Insert rest of text while linkifying RJ codes let prevNode = null; for (let i = 0; i < matches.length; ++i) { // Insert linkified RJ code const rjLinkNode = Parser.wrapRJCode(matches[i].value); textNode.parentNode.insertBefore( rjLinkNode, prevNode ? prevNode.nextSibling : textNode.nextSibling ); // Insert text after if there is any let upper; if (i === matches.length - 1) upper = undefined; else upper = matches[i + 1].index; let substring; if ( (substring = nodeOriginalText.substring(matches[i].index + 8, upper)) ) { const subtextNode = document.createTextNode(substring); textNode.parentNode.insertBefore( subtextNode, rjLinkNode.nextElementSibling ); prevNode = subtextNode; } else { prevNode = rjLinkNode; } } }, rebindEvents: function (elem) { if (elem.nodeName === "A") { elem.addEventListener("mouseover", Popup.over); elem.addEventListener("mouseout", Popup.out); elem.addEventListener("mousemove", Popup.move); } else { const voicelinks = elem.querySelectorAll("." + VOICELINK_CLASS); for (var i = 0, ii = voicelinks.length; i < ii; i++) { const voicelink = voicelinks[i]; voicelink.addEventListener("mouseover", Popup.over); voicelink.addEventListener("mouseout", Popup.out); voicelink.addEventListener("mousemove", Popup.move); } } }, }; var globalCodes = []; const Popup = { makePopup: function (e, rjCode) { const popup = document.createElement("div"); popup.className = "voicepopup " + (getAdditionalPopupClasses() || ""); popup.id = "voice-" + rjCode; popup.style = "display: flex"; document.body.appendChild(popup); DLsite.request(rjCode, function (workInfo) { if (workInfo === null) popup.innerHTML = "<div class='error'>Work not found.</span>"; else { const img = document.createElement("img"); img.src = workInfo.img; let html = ` <div class='voice-info'> <h4 class='voice-title'>${workInfo.title.trim()}</h4> <p>社团名:<span>${workInfo.circle.trim()}</span></p> `; if (workInfo.date) html += `<p>贩卖日:<span>${workInfo.date}</span></p>`; else if (workInfo.dateAnnounce) html += `<p>发布日期:<span>${workInfo.dateAnnounce}</span></p>`; html += `<p>年龄指定:<span>${workInfo.rating.trim()}</span></p>`; if (workInfo.cv) html += `<p>声优:<span>${workInfo.cv}</span></p>`; if (workInfo.tags) { html += `<p>分类:<span>`; workInfo.tags.forEach((tag) => { html += tag + "\u3000"; }); html += "</span></p>"; } if (workInfo.filesize) html += `<p>文件容量:<span>${workInfo.filesize}</span></p>`; html += "</div>"; popup.innerHTML = html; popup.insertBefore(img, popup.childNodes[0]); } Popup.move(e); }); }, humanFileSize: function (size) { if (!size) return ""; var i = Math.floor(Math.log(size) / Math.log(1024)); return ( (size / Math.pow(1024, i)).toFixed(2) * 1 + " " + ["B", "kB", "MB", "GB", "TB"][i] ); }, over: function (e) { const rjCode = e.target.getAttribute(RJCODE_ATTRIBUTE); const popup = document.querySelector("div#voice-" + rjCode); if (popup) { const style = popup.getAttribute("style").replace("none", "flex"); popup.setAttribute("style", style); } else { Popup.makePopup(e, rjCode); } }, out: function (e) { const rjCode = e.target.getAttribute("rjcode"); const popup = document.querySelector("div#voice-" + rjCode); if (popup) { const style = popup.getAttribute("style").replace("flex", "none"); popup.setAttribute("style", style); } }, move: function (e) { const rjCode = e.target.getAttribute("rjcode"); const popup = document.querySelector("div#voice-" + rjCode); if (popup) { // 如果右侧没有超出屏幕范围 if (popup.offsetWidth + e.clientX + 24 < window.innerWidth) { popup.style.left = e.clientX + 8 + "px"; } else { // 显示在左侧 popup.style.left = e.clientX - popup.offsetWidth - 8 + "px"; } // 如果下方超出屏幕范围 if (popup.offsetHeight + e.clientY + 16 > window.innerHeight) { // 尽可能靠下 popup.style.top = window.innerHeight - popup.offsetHeight - 16 + "px"; } else { popup.style.top = e.clientY + "px"; } } }, }; const DLsite = { parseWorkDOM: function (dom, rj) { // workInfo: { // rj: any; // img: string; // title: any; // circle: any; // date: any; // rating: any; // tags: any[]; // cv: any; // filesize: any; // dateAnnounce: any; // } const workInfo = {}; workInfo.rj = rj; let rj_group; if (rj.slice(5) == "000") rj_group = rj; else { rj_group = (parseInt(rj.slice(2, 5)) + 1).toString() + "000"; rj_group = "RJ" + ("000000" + rj_group).substring(rj_group.length); } workInfo.img = "https://img.dlsite.jp/modpub/images2/work/doujin/" + rj_group + "/" + rj + "_img_main.jpg"; workInfo.title = dom.getElementById("work_name").textContent; workInfo.circle = dom.querySelector("span.maker_name").textContent; const table_outline = dom.querySelector("table#work_outline"); for (var i = 0, ii = table_outline.rows.length; i < ii; i++) { const row = table_outline.rows[i]; const row_header = row.cells[0].textContent; const row_data = row.cells[1]; switch (true) { case row_header.includes("贩卖日"): workInfo.date = row_data.textContent; break; case row_header.includes("年龄指定"): workInfo.rating = row_data.textContent; break; case row_header.includes("分类"): const tag_nodes = row_data.querySelectorAll("a"); workInfo.tags = [...tag_nodes].map((a) => { return a.textContent; }); break; case row_header.includes("声优"): workInfo.cv = row_data.textContent; break; case row_header.includes("文件容量"): workInfo.filesize = row_data.textContent.replace("合计", "").trim(); break; default: break; } } const work_date_ana = dom.querySelector("strong.work_date_ana"); if (work_date_ana) { workInfo.dateAnnounce = work_date_ana.innerText; workInfo.img = "https://img.dlsite.jp/modpub/images2/ana/doujin/" + rj_group + "/" + rj + "_ana_img_main.jpg"; } console.log(workInfo); return workInfo; }, request: function (rjCode, callback) { const url = `https://www.dlsite.com/maniax/work/=/product_id/${rjCode}.html/?locale=zh_CN`; getXmlHttpRequest()({ method: "GET", url, headers: { Accept: "text/xml", "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:67.0)", }, onload: function (resp) { if (resp.readyState === 4 && resp.status === 200) { const dom = new DOMParser().parseFromString( resp.responseText, "text/html" ); const workInfo = DLsite.parseWorkDOM(dom, rjCode); callback(workInfo); } else if (resp.readyState === 4 && resp.status === 404) DLsite.requestAnnounce(rjCode, callback); }, }); }, requestAnnounce: function (rjCode, callback) { const url = `https://www.dlsite.com/maniax/announce/=/product_id/${rjCode}.html/?locale=ja_JP`; getXmlHttpRequest()({ method: "GET", url, headers: { Accept: "text/xml", "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:67.0)", }, onload: function (resp) { if (resp.readyState === 4 && resp.status === 200) { const dom = new DOMParser().parseFromString( resp.responseText, "text/html" ); const workInfo = DLsite.parseWorkDOM(dom, rjCode); callback(workInfo); } else if (resp.readyState === 4 && resp.status === 404) callback(null); }, }); }, }; document.addEventListener("DOMContentLoaded", function () { const style = document.createElement("style"); style.innerHTML = css; document.head.appendChild(style); Parser.walkNodes(document.body); const observer = new MutationObserver(function (m) { for (let i = 0; i < m.length; ++i) { let addedNodes = m[i].addedNodes; for (let j = 0; j < addedNodes.length; ++j) { Parser.walkNodes(addedNodes[j]); } } }); document.addEventListener("securitypolicyviolation", function (e) { if (e.blockedURI.includes("img.dlsite.jp")) { const img = document.querySelector(`img[src="${e.blockedURI}"]`); img.remove(); } }); observer.observe(document.body, { childList: true, subtree: true }); });