笔趣阁外观优化

专注阅读

目前為 2021-11-14 提交的版本,檢視 最新版本

// ==UserScript==
// @name         笔趣阁外观优化
// @namespace    https://gitee.com/linhq1999/OhMyScript
// @version      2.9
// @description  专注阅读
// @author       LinHQ
// @match        http*://www.shuquge.com/*.html
// @exclude      http*://www.shuquge.com/*index.html
// @match        http*://www.sywx8.com/*.html
// @match        http*://www.biqugetv.com/*.html
// @match        http*://www.bqxs520.com/*.html
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @inject-into auto
// @license MIT
// ==/UserScript==
'use strict';
/** 配置示例
* "sites": [
*   {
*       "desc": "shuquge", 网站链接关键字
*       "main": "div.reader", 主要部分选择器
*       "title": ".reader h1", 标题选择器
*       "txt": "#content", 文字部分选择器
*       "toc": "dd a", 目录链接选择器
*       "tocJump": 12, 跳过前面多少章
*       "filter": ["div.header", "div.nav", "div.link"], 带有此选择器的元素将被删除
*       "txtfilter": ["shuqu"] 带有此关键字的行将被删除
*   }
* ]
*/
(() => {
    // 缺省值,一般不用修改
    const lineHeight = 1.3, defaultFont = "楷体";
    let configs = {
        "sites": [
            {
                "desc": "shuquge",
                "main": "div.reader",
                "title": ".reader h1",
                "txt": "#content",
                "toc": "dd a",
                "tocJump": 12,
                "filter": ["div.header", "div.nav", "div.link"],
                "txtfilter": ["shuqu"] /*带有此关键字的行将被删除*/
            },
            {
                "desc": "sywx",
                "main": "div#container",
                "title": "div>h1",
                "toc": "li a",
                "tocJump": 0,
                "txt": "div#BookText",
                "filter": ["div.top", ".link.xb", "#footer"],
                "txtfilter": ["最快更新", "松语"]
            },
            {
                "desc": "bqxs",
                "main": ".box_con",
                "title": "div.content_read h1",
                "toc": "#list dd a",
                "tocJump": 9,
                "txt": "#content",
                "filter": [".ywtop", ".header", ".nav", ".bottem1", ".lm", "#page_set", ".bookname~.box_con"],
                "txtfilter": []
            },
            {
                "desc": "biqugetv",
                "main": ".box_con",
                "title": "div.content_read h1",
                "toc": "#list dd a",
                "tocJump": 0,
                "txt": "#content",
                "filter": [".ywtop", ".header", ".nav", ".bottem1", ".lm", "#page_set"],
                "txtfilter": []
            }
        ],
        "states": {
            "fontSize": 16,
            "lineHeight": 16 * lineHeight,
            "toc": false
        },
        "style": `
            body {
                background-color: #EAEAEF !important;
            }

            .bqg.inject.win {
                width: 55vw !important;
                min-width: 600px;
                border: 2px double gray !important;
                border-radius: 8px;
            }

            .bqg.inject.txt {
                font-family: ${defaultFont}!important;
                background-color: #EAEAEF !important;
                padding: 0.5em 1em !important;
                margin: 0.5em auto !important;
                width: auto !important;
            }

            .bqg.inject.title {
                color: black;
                background-color: #EAEAEF;
                font-family: ${defaultFont}!important;
                cursor: pointer !important;
            }

            .bqg.inject.title:hover {
                color: #0258d8 !important;
            }
            
            .hq.inject.toc {
                font-family: Arial,微软雅黑,文泉驿微米黑;
                width: 275px;
                position: fixed;
                top: 30px;
                padding: 5px;
                display: flex;
                flex-flow: column;
                transition: left 0.5s cubic-bezier(0.35, 1.06, 0.83, 0.99);
                background: rgb(246 246 246 / 60%);
                border-radius: 8px;
            }

            .hq.inject ul {
                max-height: 280px;
                width: 100%;
                /*offsetTop 计算需要*/
                position:relative;
                overflow: auto;
            }

            .hq.inject ul li {
                cursor: pointer;
                margin: 2px;
                width: 95%;
                padding: 1px 4px;
                font-size: 12px;
                border-radius: 4px;
            }

            .hq.inject ul li:hover {
                background: #0258d8;
                color: #f6f6f6;
            }

            .hq.inject.toc>h3 {
                font-size: 1.1rem;
                font-weight: bold;
                border-radius: 2px;
                align-self: center;
                cursor: pointer;
                margin: 4px 0 8px 0;
            }

            .hq.inject.toc>h3:hover {
                color: #ffa631 !important;
            }
            `
    };
    // 查询已经保存的字体信息
    let savedStates = localStorage.getItem("bqg_cfg");
    // 检查是否存在已有设置且和当前版本相符
    let states;
    if (savedStates === null) {
        states = configs.states;
        console.warn("当前状态已保存");
    }
    else {
        let cfg = JSON.parse(savedStates);
        let defaultStates = Object.keys(configs.states);
        let cfg_ = Object.keys(cfg);
        let useSaved = true;
        // 检查键是否匹配
        if (defaultStates.length == cfg_.length) {
            for (let key of Object.keys(cfg)) {
                if (!defaultStates.includes(key)) {
                    useSaved = false;
                    break;
                }
            }
        }
        else {
            useSaved = false;
        }
        if (useSaved) {
            states = cfg;
        }
        else {
            states = configs.states;
            console.warn("检测到版本变化,状态已重置");
        }
    }
    // 检测当前的网址,应用对应的设置
    let tmp = configs.sites.filter(site => document.URL.includes(site.desc));
    if (tmp.length == 0) {
        console.warn("没有匹配的设置,脚本已终止!");
        return;
    }
    let cfg = tmp[0];
    // 完成样式注入
    GM_addStyle(configs.style);
    let saveStates = () => {
        localStorage.setItem("bqg_cfg", JSON.stringify(states));
    };
    // 上一章
    let prevChapter = () => {
        var _a;
        let prevs = document.querySelectorAll("a");
        for (const prev of prevs) {
            if ((_a = prev.textContent) === null || _a === void 0 ? void 0 : _a.includes("上一")) {
                prev.click();
                break;
            }
        }
    };
    // 下一章
    let nextChapter = () => {
        var _a;
        let nexts = document.querySelectorAll("a");
        for (const next of nexts) {
            if ((_a = next.textContent) === null || _a === void 0 ? void 0 : _a.includes("下一")) {
                next.click();
                break;
            }
        }
    };
    // 目录开关
    let toggleToc = () => {
        let toc = document.querySelector(".hq.inject.toc");
        if (parseInt(toc.style.left) < 0) {
            toc.style.left = "8px";
            states.toc = true;
        }
        else {
            toc.style.left = "-300px";
            states.toc = false;
        }
        // 每一次触发目录操作都保存一次状态
        saveStates();
    };
    // 对可变部分产生影响
    let doInject = function () {
        var _a, _b;
        // 执行元素过滤
        cfg.filter.forEach(filter => { var _a; return (_a = document.querySelectorAll(filter)) === null || _a === void 0 ? void 0 : _a.forEach(ele => ele.remove()); });
        // 应用已经保存的状态
        let textWin = document.querySelector(cfg.txt);
        textWin.setAttribute("style", `font-size:${states.fontSize}px;line-height:${states.lineHeight}px`);
        textWin.classList.add("bqg", "inject", "txt");
        // 执行文字过滤
        if (cfg.txtfilter !== undefined) {
            textWin.innerText = (_b = (_a = textWin.innerText) === null || _a === void 0 ? void 0 : _a.split("\n\n")) === null || _b === void 0 ? void 0 : _b.filter(line => {
                for (const key of cfg.txtfilter) {
                    if (line.includes(key)) {
                        return false;
                    }
                }
                return true;
            }).join("\n\n");
        }
        let mainWin = document.querySelector(cfg.main);
        mainWin.classList.add("bqg", "inject", "win");
        let title = document.querySelector(cfg.title);
        title.title = "点击显示目录";
        title.classList.add("bqg", "inject", "title");
        title.onclick = (ev) => {
            toggleToc();
            // 避免跳到上一章
            // 比下面的更为具体,所以有效。
            ev.stopPropagation();
        };
        // 阻止双击事件被捕获(双击会回到顶部)
        document.body.ondblclick = (ev) => ev.stopImmediatePropagation();
        document.body.onclick = (ev) => {
            let root = document.documentElement;
            let winHeight = window.innerHeight;
            // 下半屏单击下滚,反之上滚
            if (ev.clientY > root.clientHeight / 2) {
                if (root.scrollTop + winHeight >= root.scrollHeight) {
                    nextChapter();
                }
                window.scrollBy({ top: (window.innerHeight - lineHeight) * 1 });
            }
            else {
                if (root.scrollTop === 0) {
                    prevChapter();
                }
                window.scrollBy({ top: (window.innerHeight - lineHeight) * -1 });
            }
        };
        document.body.onkeydown = (ev) => {
            switch (ev.key) {
                case "-":
                    states.fontSize -= 2;
                    textWin.style.fontSize = `${states.fontSize}px`;
                    states.lineHeight = states.fontSize * lineHeight;
                    textWin.style.lineHeight = `${states.lineHeight}px`;
                    saveStates();
                    break;
                case "=":
                    states.fontSize += 2;
                    textWin.style.fontSize = `${states.fontSize}px`;
                    states.lineHeight = states.fontSize * lineHeight;
                    textWin.style.lineHeight = `${states.lineHeight}px`;
                    saveStates();
                    break;
                case "j":
                    window.scrollBy({ top: window.innerHeight - states.lineHeight });
                    break;
                case "k":
                    window.scrollBy({ top: -1 * (window.innerHeight - states.lineHeight) });
                    break;
                case "h":
                    prevChapter();
                    break;
                case "l":
                    nextChapter();
                    break;
                case "t":
                    toggleToc();
                    break;
                default:
                    break;
            }
        };
    };
    // 先调用一次,后面是有变化时才会触发,避免有时无法起作用
    doInject();
    // 强力覆盖
    new MutationObserver((_, ob) => {
        doInject();
    }).observe(document.body, { childList: true });
    // 添加目录
    let toc = document.createElement("div");
    toc.className = "hq inject toc";
    toc.onclick = ev => ev.stopPropagation();
    // 已保存状态读取
    toc.style.left = (states.toc) ? "8px" : "-300px";
    document.body.append(toc);
    // 目录状态指示灯
    let pointer = document.createElement("h3");
    let pointerColors = { "loaded": "#afdd22", "loading": "#ffa631", "unload": "#ed5736" };
    pointer.title = "点击以重新加载目录";
    pointer.innerHTML = "目<span style='display: inline-block;width: 1em'></span>录";
    pointer.style.color = pointerColors.unload;
    toc.append(pointer);
    // 目录列表
    let ul = document.createElement("ul");
    toc.append(ul);
    // fetchTOC 获取目录信息并重新渲染
    let fetchTOC = function (currentBookLink, pointer) {
        // 修改指示灯状态
        pointer.style.color = pointerColors.loading;
        GM_xmlhttpRequest({
            url: currentBookLink,
            // 直接返回 dom
            responseType: "document",
            onload: (resp) => {
                var _a, _b;
                let doc = resp.response;
                let tocs = doc.querySelectorAll(cfg.toc);
                let data = [];
                // 序列化存储准备
                for (let link of tocs) {
                    data.push({ "title": (_a = link.textContent) !== null && _a !== void 0 ? _a : "", "href": (_b = link.href) !== null && _b !== void 0 ? _b : "" });
                }
                if (cfg.tocJump)
                    data = data.slice(cfg.tocJump + 1);
                // 缓存目录信息
                sessionStorage.setItem(currentBookLink, JSON.stringify(data));
                renderToc(data, ul);
                pointer.style.color = pointerColors.loaded;
            },
            onerror: (_) => pointer.style.color = pointerColors.unload
        });
    };
    let renderToc = function (toc, ul) {
        // 清空旧内容
        ul.innerHTML = "";
        let current = null;
        // 进度计数器
        let counter = 1;
        for (let lnk of toc) {
            let li = document.createElement("li");
            li.textContent = lnk.title;
            if (current == null && document.URL == lnk.href) {
                li.innerHTML = `${lnk.title}<span style="flex: 1;"></span>${(counter / toc.length * 100).toFixed(1)}%`;
                current = li;
            }
            li.onclick = (ev) => {
                document.location.href = lnk.href;
                ev.stopPropagation();
            };
            ul.append(li);
            counter++;
        }
        // 滚动到当前位置,并高亮
        current === null || current === void 0 ? void 0 : current.setAttribute("style", "display:flex;font-weight:bold;background: #0258d8;color: #f6f6f6;");
        ul.scrollTo({ top: (current === null || current === void 0 ? void 0 : current.offsetTop) - 130 });
    };
    let source = document.URL.split("/");
    source.pop();
    // 最后加斜杠保险
    let currentBook = source.join("/") + "/";
    let currentBookToc = sessionStorage.getItem(currentBook);
    if (currentBookToc === null) {
        fetchTOC(currentBook, pointer);
    }
    else {
        pointer.style.color = pointerColors.loaded;
        renderToc(JSON.parse(currentBookToc), ul);
    }
    // 单击指示灯刷新目录缓存
    pointer.onclick = () => fetchTOC(currentBook, pointer);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址