轻小说文库美化

适配轻小说文库的阅读界面

// ==UserScript==
// @name         轻小说文库美化
// @namespace    http://tampermonkey.net/
// @version      2024.02.15.14
// @description  适配轻小说文库的阅读界面
// @author       Le_le
// @match        https://www.wenku8.net/novel/*/*/*
// @match        https://www.wenku8.cc/novel/*/*/*
// @icon         https://www.wenku8.net/favicon.ico
// @license MIT
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function () {
    'use strict';

    function index_function() {
        let check_box = document.createElement('input');
        check_box.type = 'checkbox';
        check_box.id = 'beautify';
        check_box.title = 'Beautify';
        check_box.addEventListener('change', function () {
            if (this.checked) {
                GM_setValue('beautify', true);
            }
            else {
                GM_setValue('beautify', false);
            }
            window.location.reload();
        }
        );
        if (GM_getValue('beautify', false)) {
            check_box.checked = true;
            index_changeherf();
        }
        let label = document.createElement('label');
        label.htmlFor = 'beautify';
        label.appendChild(document.createTextNode('启用美化'));
        document.getElementById('linkright').appendChild(label);
        document.getElementById('linkright').appendChild(check_box);
    }

    function index_changeherf() {
        let tbody = document.getElementsByTagName('tbody')[0];
        // 获取所有的tr
        let trs = tbody.getElementsByTagName('tr');
        let a;
        // 排除第一行
        for (let i = 1; i < trs.length; i++) {
            a = trs[i].getElementsByTagName('a');
            for (let i = 0; i < a.length; i++) {
                if (a[i].innerText.indexOf('插图') > -1) {
                    continue;
                }
                a[i].href = a[i].href + '/beautify';
            }
        }
    }

    async function beautify_function() {
        document.body.innerHTML = "";
        document.head.innerHTML = "";
        let url = window.location.href;
        // 去除最后的/beautify
        addhtml();
        url = url.substring(0, url.lastIndexOf('/'));
        await fetchAndDecode(url, 'gbk');

    }


    async function fetchAndDecode(file, encoding) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', file);
        xhr.responseType = 'arraybuffer';
        xhr.onload = function () {
            if (this.status == 200) {
                var dataView = new DataView(this.response);
                var decoder = new TextDecoder(encoding);
                var decodedString = decoder.decode(dataView);
                // 获取js的变量 var preview_page = "index.htm";
                var preview_page = decodedString.match(/var preview_page = "(.*?)";/)[1];
                var next_page = decodedString.match(/var next_page = "(.*?)";/)[1];
                var index_page = decodedString.match(/var index_page = "(.*?)";/)[1];
                console.log(preview_page);
                // 解析html
                let parser = new DOMParser();
                let doc = parser.parseFromString(decodedString, 'text/html');
                let content = doc.getElementById('content');
                window.contentvalue = content.innerHTML;
                let title = doc.title;
                document.title = title;
                title=doc.getElementById('title').innerText;
                document.getElementsByTagName('mdui-top-app-bar-title')[0].innerText = title;
                document.getElementById('content').innerHTML = content.innerHTML;
                // 添加事件
                document.getElementById('preview').addEventListener('click', function () {
                    if (preview_page == 'index.htm') {
                        mdui.snackbar({
                            message: '已经是第一章节了'
                        });
                        return;
                    }
                    var url = window.location.href;
                    url = url.substring(0, url.lastIndexOf('/'));
                    url = url.substring(0, url.lastIndexOf('/'));
                    url = url + "/" + preview_page + '/beautify';
                    window.location.href = url;
                }
                );
                document.getElementById('next').addEventListener('click', function () {
                    if (next_page.indexOf('lastchapter') > -1) {
                        mdui.snackbar({
                            message: '已经是最后章节了'
                        });
                        return;
                    }
                    // 返回目录两次
                    var url = window.location.href;
                    url = url.substring(0, url.lastIndexOf('/'));
                    url = url.substring(0, url.lastIndexOf('/'));
                    url = url + "/" + next_page + '/beautify';
                    window.location.href = url;
                }
                );
                document.getElementById('index').addEventListener('click', function () {
                    var url = window.location.href;
                    url = url.substring(0, url.lastIndexOf('/'));
                    url = url.substring(0, url.lastIndexOf('/'));
                    url = url + '/' + index_page;
                    window.location.href = url;
                }
                );
            } else {
                console.error('Error while requesting', file, this);
            }
        };
        xhr.send();
    }


    let locate = window.location;
    if (locate.href.indexOf('index') > -1) {
        index_function();
    } else if (locate.href.indexOf('beautify') > -1) {
        beautify_function();
    }

    function addhtml() {
        let div = `<html class="mdui-theme-auto">
        <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
        <link rel="stylesheet" href="https://unpkg.com/mdui@2.0.3/mdui.css">
        <script src="https://unpkg.com/mdui@2.0.3/mdui.global.js"></script>
        <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
        <!-- Filled -->
        <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

        <!-- Outlined -->
        <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet">

        <!-- Rounded -->
        <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet">

        <!-- Sharp -->
        <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Sharp" rel="stylesheet">

        <!-- Two Tone -->
        <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Two+Tone" rel="stylesheet">
        <style>
            #content {
                --pad: 40px;
                columns: 100vw auto;
                height: calc(99vh - var(--pad) * 2);
                column-gap: calc(var(--pad) * 2);
                font-size: 4vh;
                overflow: visible;
                transform: translateX(0vw);
                transition: transform 500ms var(--mdui-motion-easing-emphasized);
                padding: var(--pad);
                cursor: default;
                column-rule: solid 1px rgba(0, 0, 0, 0.1);
                word-break: break-all;

            }

            body {
                padding: 0;
                margin: 0;
                overflow: hidden;
                width: auto;
                background-color: rgba(var(--mdui-color-surface), 1);
            }

            h1 {
                margin: 20px auto;
                position: relative;
                left: 0;
                right: 0;
                width: fit-content;

            }

            .intro {
                padding: 20px;
                background-color: rgba(var(--mdui-color-inverse-primary), 1);
                text-align: center;
            }

            mdui-bottom-app-bar {
                background-color: rgba(var(--mdui-color-surface-bright), 0.8);
            }

            @media screen and (min-width: 600px) {
                #content {
                    columns: 40vw auto;
                }
            }

            .setting_item {
                text-align: center;
            }
            mdui-top-app-bar{
                box-shadow: var(--mdui-elevation-level1);
            }
        </style>

        <body>
            <mdui-top-app-bar style="position: fixed;">
                <mdui-button-icon icon="menu" onclick="openDrawer()"></mdui-button-icon>
                <mdui-top-app-bar-title>轻小说文库</mdui-top-app-bar-title>
                <div style="flex-grow: 1"></div>
                <mdui-button-icon icon="fullscreen" onclick="
                            document.body.requestFullscreen()
                        "></mdui-button-icon>
                <mdui-button-icon icon="light_mode" onclick="
                        if(this.icon == 'light_mode'){
                            this.icon = 'dark_mode'
                            mdui.setTheme('dark')
                        }else{
                            this.icon = 'light_mode'
                            mdui.setTheme('light')
                        }
                        "></mdui-button-icon>
                <mdui-button-icon icon="close" variant="tonal" onclick="hideBar(true)"></mdui-button-icon>
            </mdui-top-app-bar>
            <mdui-navigation-drawer close-on-overlay-click class="example-drawer" modal="true">
                <div class="intro">
                    <h1>轻小说文库美化</h1>
                    <p>Powered By 乐乐</p>
                </div>
                <mdui-divider></mdui-divider>
                <div class="settting">
                    <div class="setting_item">
                        <p>字体大小</p>
                        <mdui-slider id="font-slider" min="15" max="50" value="30" step="1" display-mark></mdui-slider>
                    </div>
                    <mdui-divider></mdui-divider>
                    <div class="setting_item">
                        <p>页边距</p>
                        <mdui-slider id="page-slider" min="0" max="100" value="30" step="1" display-mark></mdui-slider>
                    </div>
                    <mdui-divider></mdui-divider>
                    <div class="setting_item">
                        <p>行间距</p>
                        <mdui-slider id="line-slider" min="0" max="100" value="30" step="1" display-mark></mdui-slider>
                    </div>
                    <mdui-divider></mdui-divider>
                    <div class="setting_item">
                        <p>字间距</p>
                        <mdui-slider id="word-slider" min="0" max="30" value="5" step="1" display-mark></mdui-slider>
                    </div>
                </div>
            </mdui-navigation-drawer>
            <div id="content">
                正在加载文章...
            </div>
            <mdui-bottom-app-bar>
                <mdui-tooltip content="上一章">
                    <mdui-button-icon id="preview" icon="arrow_back"></mdui-button-icon>
                </mdui-tooltip>
                <mdui-tooltip content="章节">
                    <mdui-button-icon id="index" icon="collections_bookmark"></mdui-button-icon>
                </mdui-tooltip>
                <mdui-tooltip content="下一章">
                    <mdui-button-icon id="next" icon="arrow_forward"></mdui-button-icon>
                </mdui-tooltip>
                <div style="flex-grow: 1"></div>
                <mdui-fab extended icon="find_in_page" onclick="
                    document.getElementById('jumpto').open=true
                ">
                    <div>页数: <span id="page">1</span>/<span id="allpage">1</span></div>
                </mdui-fab>
            </mdui-bottom-app-bar>
            <mdui-snackbar id="tip-snackbar"></mdui-snackbar>

            <mdui-dialog class="example-dialog" id="jumpto" close-on-esc close-on-overlay-click>
                <span slot="headline">跳转到指定的页数</span>
                还没做完
                <mdui-button slot="action" variant="text" onclick="document.getElementById('jumpto').open=false" icon="close">取消</mdui-button>
                <mdui-button slot="action" variant="tonal" icon="done">确定</mdui-button>
            </mdui-dialog>
        </body>
        <script>
            window.page = 0;
            function openDrawer() {
                var flag = document.getElementsByTagName('mdui-navigation-drawer')[0].open
                console.log(flag)
                if (flag == false) {
                    document.getElementsByTagName('mdui-navigation-drawer')[0].open = true
                } else {
                    document.getElementsByTagName('mdui-navigation-drawer')[0].open = false
                }
            }
            function hideBar(flag) {
                var topBar = document.getElementsByTagName('mdui-top-app-bar')[0];
                var bottomBar = document.getElementsByTagName('mdui-bottom-app-bar')[0];
                if (flag == true) {
                    topBar.hide = true;
                    bottomBar.hide = true;
                } else {
                    topBar.hide = false;
                    bottomBar.hide = false;
                }
            }
            setTimeout(function () {
                document.body.style.paddingTop = 0;
                document.body.style.paddingBottom = 0;
            }, 10)
            function page_change(flag) {
                hideBar(true)
                window.page = flag + window.page;
                var allpage = document.getElementById('content').scrollWidth / document.body.clientWidth;
                document.getElementById('page').innerHTML = window.page + 1;
                document.getElementById('allpage').innerHTML = parseInt(allpage);
                console.log(allpage, window.page);
                if (window.page >= allpage - 1) {
                    document.getElementById('tip-snackbar').innerHTML = '已经是最后一页了';
                    document.getElementById('tip-snackbar').open = true;
                    hideBar(false)
                    window.page = parseInt(allpage);

                }
                if (window.page <= 0) {
                    document.getElementById('tip-snackbar').innerHTML = '已经是第一页了';
                    document.getElementById('tip-snackbar').open = true;
                    hideBar(false)
                    window.page = 0;

                }
                document.getElementById('content').style.transform = 'translateX(' + (-100 * window.page) + 'vw)';

            }
            window.addEventListener('mousewheel', function (e) {
                // content总宽度/页面宽度
                if (e.deltaY > 0) {
                    page_change(1);
                } else {
                    page_change(-1);
                }

            })
            window.addEventListener('keydown', function (e) {
                if (e.key == 'ArrowRight') {
                    page_change(1);
                } else if (e.key == 'ArrowLeft') {
                    page_change(-1);
                } else if (e.key == 'Escape') {
                    hideBar(false)
                }
            })
            document.getElementById('content').addEventListener('click', enent => {
                // 获取click的位置
                var x = enent.clientX;
                var width = document.body.clientWidth;
                if (x < width / 3) {
                    page_change(-1);
                } else if (x > width / 3 * 2) {
                    page_change(1);
                } else {
                    hideBar(false)
                }
            })
            document.getElementById('font-slider').addEventListener('input', function (e) {
                var value = e.target.value;
                document.getElementById('content').style.fontSize = value + 'px';
            })
            document.getElementById('page-slider').addEventListener('input', function (e) {
                var value = e.target.value;
                document.getElementById('content').style.setProperty('--pad', value + 'px');
            })
            document.getElementById('line-slider').addEventListener('input', function (e) {
                var value = e.target.value;
                document.getElementById('content').style.lineHeight = value + 'px';
            })
            document.getElementById('word-slider').addEventListener('input', function (e) {
                var value = e.target.value;
                document.getElementById('content').style.letterSpacing = value + 'px';
            })
        </script>

        </html>`

        document.write(div);
        document.close();
    }
})();