您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
增强第一版主小说网站的功能,详情请看脚本页面的介绍(另含去除广告的说明)
当前为
// ==UserScript== // @name 第一版主网站增强 // @namespace https://diyibanzhu.org/essence // @version 0.8 // @description 增强第一版主小说网站的功能,详情请看脚本页面的介绍(另含去除广告的说明) // @author Essence // @match https://*/* // @run-at document-end // @grant GM_setValue // @grant GM_getValue // @icon https://www.google.com/s2/favicons?sz=64&domain=diyibanzhu.org // @license MIT // ==/UserScript== // 站点名。域名经常变动,没有更好的判断方法 const SITE_NAME = "歪歪蒂艾斯" const TAG = "[第一版主]" // 持久存储到篡改猴数据中的键 const V_DYBZ_HISTORY = "DYBZ_HISTORY" // =========================== 公用函数 =========================== // // 等待 const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) /** * 等待指定元素出现后,执行回调 * @param selector 元素选择器。参考`document.querySelector` * @param callback 需要执行的回调。传递的参数为目标元素 */ const waitElem = (selector, callback) => { const observer = new MutationObserver(() => { const element = document.querySelector(selector) if (element) { callback(element) observer.disconnect() } }) observer.observe(document.body, { childList: true, subtree: true, }) } // 创建单手操作面板 const createOneHandPanel = () => { // 事件 const gotoNextPage = () => { const nextLink = document.querySelector("a.curr")?.nextElementSibling || document.querySelector("a.next") if (nextLink) { nextLink.click() } } const gotoTop = () => window.scrollTo({top: 0, behavior: "smooth"}) // 创建按钮 const bnNextPage = document.createElement('button') bnNextPage.textContent = "下一页" bnNextPage.addEventListener('click', gotoNextPage) const bnTop = document.createElement('button') bnTop.textContent = "顶部" bnTop.addEventListener('click', gotoTop) // 创建扩展面板 const oneHandPanel = document.createElement('div') // 增强面板的样式 oneHandPanel.style.display = "flex" oneHandPanel.style.flexDirection = "row" oneHandPanel.style.justifyContent = "space-between" oneHandPanel.style.gap = "8px" // 向扩展面板添加上面的组件 oneHandPanel.append(bnNextPage, bnTop) return oneHandPanel } /** * 读取网站存储到 localStorage 的所有值 * @param excludeRegexp 不读取的键的正则。如 /^Hm_lvt/ * @return {string} JSON 文本 */ const readLocalStorageValues = (excludeRegexp = undefined) => { const result = {} for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i) if (excludeRegexp && excludeRegexp.test(key)) { continue } result[key] = localStorage.getItem(key) } return JSON.stringify(result) } /** * 恢复数据到网站的 localStorage * @param {string} json */ const restoreLocalStorageValues = (json) => { const data = JSON.parse(json) Object.keys(data).forEach(key => localStorage.setItem(key, data[key])) } // =========================== 脚本页面 =========================== // // 脚本页面:阅读页面,预加载下一页 const nextPage = () => { // 依次找到"下一页"的元素:优先“下一页”,其次“下一章” const nextLink = document.querySelector("a.curr")?.nextElementSibling || document.querySelector("a.next") console.log(TAG, "脚本将预加载下一页", nextLink, nextLink?.href) // 存在链接 if (nextLink && nextLink.href) { // 通过"link prefetching"实现预加载下一页 const link = document.createElement("link") // 注意:最后第一版主网站的章中最后的一页的 URL 的 href 是以"javascript:"开头,其它页是正常的 URL let href = nextLink.href if (href.startsWith("javascript:")) { const params = href.match(/\d+/g) href = "/" + params.slice(0, -1).join("/") + "_" + params.slice(-1) + ".html" } link.href = href link.rel = "prefetch" document.head.appendChild(link) } } // 脚本页面: 阅读页面,单手操作面板 const oneHand = () => { // 操作章节页面元素的父元素,插入扩展面板和章节页面 const chapterPages = document.querySelector(".chapterPages") if (!chapterPages) { console.log(TAG, "chapterPages 元素为空") return } // 修正原样式 chapterPages.style.marginTop = "auto" chapterPages.style.lineHeight = "auto" const root = chapterPages.parentElement if (!root) { console.log(TAG, "父元素为空") return } // root 父元素的样式 root.style.display = "flex" root.style.justifyContent = "space-between" root.style.flexDirection = "row" root.style.marginTop = "30px" // 注意 `elem.cloneNode(true)`不能复制`事件`,所以要直接创建 const oneHandPanel1 = createOneHandPanel() const oneHandPanel2 = createOneHandPanel() root.append(oneHandPanel1, chapterPages, oneHandPanel2) } // 脚本页面: 保存历史阅读记录到篡改猴 // 注释:localstorage 中 bookList 保存书籍 ID列表(以"#"分隔),只保存这个是无效的,还要保存 ID 对应的阅读进度才是完整的历史记录。所以读取、保存整个 localstorage const saveHistoryBooks = () => { const history = readLocalStorageValues(/^Hm_lvt/) if (history) { // 保存到篡改猴 GM_setValue(V_DYBZ_HISTORY, history) } const values = GM_getValue(V_DYBZ_HISTORY) console.log(TAG, "已保存的历史阅读记录:", values) } // 脚本页面:准许复制文本 const enableCopyText = ()=>{ document.querySelector("body").oncontextmenu = null; const chapterBody = document.querySelector("body.chapter") if(chapterBody){ chapterBody.style.webkitUserSelect = "auto" } } // 增强作者文本为链接 const makeAuthorLink = (authorNode)=>{ const author = authorNode.textContent.trim().replace("作者:", "") const link = document.createElement("a") link.textContent = author link.href = `/author/${author}` link.classList.add("author") const parentElem = authorNode.parentElement authorNode.textContent = "作者:" parentElem.insertBefore(link, parentElem.childNodes[1]) } // 脚本页面:自动填充网站人机验证 const verifyPage = () => { document.querySelector("input#password").value = "1234" document.querySelector("div.login a").click() } // 脚本页面:自动填充 CF 人机验证 const cfPage = () => { waitElem("div#challenge-stage input[type='checkbox']", (elem) => { console.log(TAG, "CF 人机验证的选择框", elem) elem.click() }) } (function () { 'use strict' // Your code here... // CF 验证页面需要放在最前面。避免因为不是目标网站而跳过 // 脚本页面:自动填充 CF 人机验证 // 注意 CF 验证是通过 iframe 嵌入实现的,所以在指定 URL 的 iframe 中运行该函数 if (location.href.startsWith("https://challenges.cloudflare.com/cdn-cgi/challenge-platform/")) { console.log(TAG, "自动填充 CF 人机验证") cfPage() } // 不是目标小说网站 if (!document.title.includes(SITE_NAME) && !(document.title === "您的阅读足迹" && document.querySelector("h1.page-title").textContent === "您的阅读足迹")) { console.log(TAG, "不是目标小说网站,停止运行。", `网站标题:"${document.title}"`, `网站地址:${location.href}`) return } // 脚本页面:自动填充网站人机验证 if (document.querySelector("div.title")?.textContent?.includes("为防止恶意访问")) { console.log(TAG, "自动填充网站人机验证") verifyPage() } // 脚本页面:阅读页面 if (document.querySelector("h1.page-title")?.textContent?.trim()) { console.log(TAG, "预加载下一页") nextPage() console.log(TAG, "单手操作面板") oneHand() console.log(TAG, "保存历史阅读记录到篡改猴") saveHistoryBooks() console.log(TAG, "准许复制文本") enableCopyText() } // 脚本页面:增强作者文本为链接 if(/作者:.+/.test(document.querySelector("p.info")?.firstChild?.textContent?.trim())){ console.log(TAG, "增强作者文本为链接") makeAuthorLink(document.querySelector("p.info").firstChild) } })()