Greasy Fork

摸鱼小说阅读器 Loafing-Reader

内嵌浏览器里用来上班摸鱼看小说

当前为 2023-07-21 提交的版本,查看 最新版本

// ==UserScript==
// @name         摸鱼小说阅读器 Loafing-Reader
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  内嵌浏览器里用来上班摸鱼看小说
// @author       HanaYabuki
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// ==/UserScript==
(function () {
    const cssText = `
        .loafing-reader {
            margin: 0; padding: 0;
            box-sizing: content-box;
            font-size: 12px;
            color: #222;
        }
        #lf-panel {
            height: 27em;
            width: 48em;
            background-color: #f000;
            top: 50%; left: 50%;
            zIndex: 10;
            position: fixed;
            border: 1px solid #fff;
            display: flex;
            flex-flow: column nowrap;
            user-select: none;
        }
        #lf-toolbar {
            background: #aaa3;
            width: 100%; height: 18px;
        }
        .lf-item {
            padding: 0 0 0 1em;
        }
        .lf-btn {
            color: #00f7;
        }
        .lf-btn:hover {
            color: #00f;
        }
        #lf-content {
            background-color: #fff3;
            flex: 1;
            padding: 0 0.5em;
            overflow: hidden;
        }
        #lf-text {
            background-color: #f000,
            position: relative;
        }
        .lf-hidden {
            display: none;
        }
    `;

    const elements = {};
    function create(tagName, id, ...clazz) {
        const tmp = document.createElement(tagName);
        if (id) {
            tmp.setAttribute('id', 'lf-' + id);
        }
        tmp.setAttribute('class', ['loafing-reader', ...(clazz.map(i => 'lf-' + i))].join(' '));
        return tmp;
    }
    elements.panel = create('div', 'panel')
    elements.toolbar = create('div', 'toolbar');
    elements.content = create('div', 'content');
    elements.jump = create('span', 'btn-jump', 'item', 'btn');
    elements.load = create('span', 'btn-load', 'item', 'btn');
    elements.move = create('span', 'btn-move', 'item', 'btn');
    elements.info = create('span', 'span-info', 'item');
    elements.fileholder = create('input', undefined, 'hidden');
    elements.text = create('div', 'text');

    elements.jump.href = '#'; elements.jump.innerText = '[跳转]';
    elements.load.href = '#'; elements.load.innerText = '[加载]';
    elements.move.href = '#'; elements.move.innerText = '[移动]';
    elements.fileholder.type = 'file';
    elements.fileholder.accept = '.txt';
    elements.info.innerText = '(无文件)';

    document.documentElement.appendChild(elements.panel);
    elements.panel.appendChild(elements.toolbar);
    elements.panel.appendChild(elements.content);
    elements.toolbar.appendChild(elements.jump);
    elements.toolbar.appendChild(elements.load);
    elements.toolbar.appendChild(elements.move);
    elements.toolbar.appendChild(elements.info);
    elements.toolbar.appendChild(elements.fileholder);
    elements.content.appendChild(elements.text);

    // file handle
    const fileInfo = {};

    function loadFile(filename, content) {
        clear();
        fileInfo.fileName = filename.substring(0, filename.lastIndexOf('.'));
        fileInfo.content = content.split(/(?:\r\n|\n)/)//.filter(s=>/\s*/.test(s));
        fileInfo.length = fileInfo.content.length;
        fileInfo.bookmark = 0;
        fileInfo.page = [];

        GM_setValue('lf_file_name', filename);
        GM_setValue('lf_file_content', content);
        GM_setValue('lf_bookmark', 0);

        jump(0);
    }

    // utils
    function updateInfo() {
        const filename = fileInfo.fileName;
        elements.info.innerText = `(${fileInfo.bookmark}/${fileInfo.length})-${filename}`;

        GM_setValue('lf_bookmark', fileInfo.bookmark);
    }
    function clear() {
        const ls = fileInfo.page;
        while (ls && ls.length > 0) {
            ls.pop().remove();
        }
    }
    function render(mark, removeNumber, direction) {
        const ls = fileInfo.page;
        for (let i = 0; i < removeNumber; ++i) {
            if (direction) {
                ls.shift().remove();
            }
            else {
                ls.pop().remove();
            }
        }

        let i = mark;
        while (i < fileInfo.length && i >= 0 && elements.text.offsetHeight < elements.content.offsetHeight) {
            const p = create('div');
            p.innerHTML = fileInfo.content[i] + '&nbsp;';
            if (direction) {
                elements.text.appendChild(p);
                ls.push(p);
                i++;
            }
            else {
                elements.text.insertBefore(p, elements.text.firstChild);
                ls.unshift(p);
                i--;
                if (i < 0) {
                    let t = ls.length;
                    while (t < fileInfo.length && elements.text.offsetHeight < elements.content.offsetHeight) {
                        const p = create('div');
                        p.innerHTML = fileInfo.content[t] + '&nbsp;';
                        elements.text.appendChild(p);
                        ls.push(p);
                        ++t
                    }
                }
            }
        }

        return direction ? mark : (i + 1);
    }

    function jump(index) {
        let i = index;

        const ls = fileInfo.page;
        render(i, ls.length, true);

        fileInfo.bookmark = index;
        fileInfo.page = ls;
        updateInfo();
    }
    function next() {
        const ls = fileInfo.page;
        if (fileInfo.bookmark + 1 >= fileInfo.length || ls.length === 0) {
            alert('已是最后一页');
            return;
        }

        let i = fileInfo.bookmark + fileInfo.page.length;

        const s = Math.max(ls.length - 1, 1);
        render(i, s, true);

        fileInfo.bookmark += s;
        fileInfo.page = ls;
        updateInfo();
    }
    function previous() {
        const ls = fileInfo.page;
        if (fileInfo.bookmark === 0 || ls.length === 0) {
            alert('已经是第一页');
            return
        }

        let i = fileInfo.bookmark;
        const mk = render(i, ls.length, false);

        fileInfo.bookmark = mk;
        fileInfo.page = ls;
        updateInfo();
    }

    // events
    elements.jump.addEventListener('click', function (e) {
        let value = prompt('跳转到?');
        value = parseInt(value);
        if (!isNaN(value) && fileInfo.content && fileInfo.length >= value) {
            jump(value);
        }
        else {
            alert('输入有误,跳转失败');
        }
    });
    elements.fileholder.addEventListener('change', function (e) {
        const file = elements.fileholder.files[0];
        const reader = new FileReader();
        reader.readAsText(file);
        reader.onload = function () {
            loadFile(file.name, this.result);
        }
    });
    elements.load.addEventListener('click', function (e) {
        elements.fileholder.click();
    });
    elements.content.addEventListener('contextmenu', function (e) {
        e.preventDefault();
    });
    elements.content.addEventListener('mousedown', function (e) {
        if (e.button === 0) {
            next();
        }
        else if (e.button === 2) {
            previous();
        }
    });

    // move window
    let mouseMemory = [0, 0];
    let moveWindow = false
    elements.move.addEventListener('mousedown', function (e) {
        mouseMemory = [e.screenX - mouseMemory[0], e.screenY - mouseMemory[1]];
        moveWindow = !moveWindow;
    });
    document.documentElement.addEventListener('mousemove', function (e) {
        if (moveWindow) {
            elements.panel.style.left = `calc(50% + ${e.screenX - mouseMemory[0]}px)`;
            elements.panel.style.top = `calc(50% + ${e.screenY - mouseMemory[1]}px)`;
        }
    });

    GM_addStyle(cssText);

    // wake up & sleep
    document.onkeydown = function (event) {
        event = event || window.event
        if (event.shiftKey && (event.key === 'r' || event.key === 'R')) {
            elements.panel.style.visibility = 'visible';

            if (!window.LOAFING_READER_INIT) {
                init();
                window.LOAFING_READER_INIT = true;
                console.log('loafing-reader loaded.')
            }

            const bookmark = GM_getValue('lf_bookmark');
            if (bookmark !== fileInfo.bookmark) {
                jump(bookmark);
            }
        }
    }
    elements.panel.addEventListener('mouseleave', function (event) {
        if (!moveWindow) {
            elements.panel.style.visibility = 'hidden';
        }
    })
    elements.panel.style.visibility = 'hidden';

    // INIT
    window.LOAFING_READER_INIT = false;
    function init() {
        const lfFileName = GM_getValue('lf_file_name');
        const lfFileContent = GM_getValue('lf_file_content');
        const lfBookmark = GM_getValue('lf_bookmark', 0);

        if (lfFileName && lfFileContent) {
            loadFile(lfFileName, lfFileContent);
        }
        if (fileInfo.content) {
            jump(lfBookmark);
        }
    }
})();