Greasy Fork

Greasy Fork is available in English.

笔趣阁外观优化

专注阅读

当前为 2021-11-14 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         笔趣阁外观优化
// @namespace    https://gitee.com/linhq1999/OhMyScript
// @version      2.10
// @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);
})();