随手小说下载

带图形化界面的小说下载器。任意网站,自动识别任何目录列表,自动识别正文,自由下载,简单直观。

// ==UserScript==
// @name         随手小说下载
// @namespace    ythong
// @version      0.4.1.6
// @description  带图形化界面的小说下载器。任意网站,自动识别任何目录列表,自动识别正文,自由下载,简单直观。
// @author       ythong
// @match        http://*/*
// @match        https://*/*
// @require      https://greasyfork.org/scripts/450829-numberdigit/code/numberDigit.js?version=1090310
// @require      https://greasyfork.org/scripts/450948-reader/code/Reader.js?version=1098627
// @require      https://greasyfork.org/scripts/452085-mymd5/code/myMd5.js?version=1098266
// @require      https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/codemirror.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/mode/javascript/javascript.js
// @resource     CodeMirrorminCss https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/codemirror.min.css
// @noframes
// @grant        GM_registerMenuCommand
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_listValues
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_getResourceText
// @license      MIT License

// ==/UserScript==
(function () {
    'use strict';

    var nextPageReg = /下\s*一?\s*[页頁张張章]/i;
    var nextListReg = /下\s*一?\s*[页頁]/i;
    var setting = {}, tempSetting = {}, customListFunc, customItemFunc, customTextFunc, customNextPageFunc;
    var cm; //setting输入对象
    var CodeMirrorSize = [293, 121];
    const SETTING_KEYS = [
        "listSelector",
        "textSelector",
        "jammerSelector",
        "nextListSelector",
        "nextListDelay",
        "frameText",
        "addedNextPageReg",
        "notUploadSaveinfo",
        "customListFunc",
        "customItemFunc",
        "customTextFunc",
        "customNextPageFunc",
    ];

    var chapters = [];
    var chaptersIndex = [];
    var root;
    var MaxThread = 10; // 同时下载数量
    var maxNextCount = 100, //最大下一页数量,总不可能无限吧
        nextCountLimit = 10, //下一页限制,超过会提示
        continueDownload = false; //超过下一页限制,是否继续直到到达下一页限制
    var downloadedCount = 0, downloadedErr = 0, downloadedNo = 0, downloadedExceedNext = 0;
    var downloadIndex;
    var hiClass='ythHighlight';
    var reader;
    var nextListHref,nextListCount=0;
    var iframes=[];
    //#region List
    function getCssClass(className){
        if(!className)return "";
        className=className.trim();
        if(!className)return "";
        /*        className=className.replace(/#/g,'\\23');
        className=className.replace(/([\.\(\)\[\]%:+=])/g,'\\$1');*/
        var cssClass=className.replace(/([^\w\s-])/g,'\\$1');
        cssClass='.' + cssClass.replace(/\s+/g, ".");
        return cssClass;
    }
    function getElementThisSelector(ele) {
        var tag = ele.tagName.toLocaleLowerCase();
        //        if (ele.id) {
        //            return '#' + getRightId(ele.id);
        //        } else {
        return tag + getCssClass(ele.className);
        //        }
    }
    function getAncestorWithMostSimilarDescendant(ele, tag) {
        var selector = tag;
        var maxEle, maxSelector, count = 0;
        while (true) {
            ele = ele.parentElement;
            if (!ele || ele.tagName == 'HTML') break;
            var aa = ele.querySelectorAll(':scope>' + selector);
            if (aa.length > count) {
                count = aa.length;
                maxEle = ele;
                maxSelector = selector;
            }
            selector = getElementThisSelector(ele) + ">" + selector;
        }
        return [maxEle, maxSelector, count];
    }
    function getAncestorWithMostSimilarTag(doc,tag) {
        var eTags = [].slice.apply(doc.querySelectorAll(tag));
        var mostAncestor, mostSelector, mostCount = 0;
        var descendants;
        while (eTags.length > 0) {
            var ele = eTags.shift();
            var [ancestor, selector, count] = getAncestorWithMostSimilarDescendant(ele, tag);
            descendants = [].slice.apply(ancestor.querySelectorAll(':scope>' + selector));
            eTags = eTags.filter(item => descendants.indexOf(item) === -1);
            if (count > mostCount) {
                [mostAncestor, mostSelector, mostCount] = [ancestor, selector, count];
            }
        }
        return [mostAncestor, mostSelector, mostCount];
    }
    function getNextList(doc) {
        if(setting.nextListSelector) return doc.querySelector(setting.nextListSelector);
        let eles = doc.querySelectorAll("a"), nextPage = null;
        for (let ele of eles) {
            if (nextListReg.test(ele.innerText) && ele.href.indexOf("javascript") == -1) return ele;
        }
        eles = doc.querySelectorAll("span")
        for (let ele of eles) {
            if (nextListReg.test(ele.innerText)) return ele;
        }
    }

    function getRestList(doc){
        function changeRestListText(str){
            if(str){
                getRestList.innerText=`第${nextListCount}页`;
                setTimeout(()=>{
                    alert(str);
                    getRestList.innerText='续页目录';
                    getRestList.disabled=false;
                },100);
            }else{
                getRestList.innerText=`第${nextListCount}页`;
            }
        }
        function waitGetList(){
            function getListLoop(){
                if(count>10) return changeRestListText("获取续页结束")
                count+=1;
                var addCount=getList(doc);
                if(addCount){
                    nextListCount+=1;
                    changeRestListText();
                    nextList=getNextList(doc);
                    if(nextList){
                        nextList.click();
                        waitGetList();
                    }else{
                        return changeRestListText("找不到下一页,获取续页结束");
                    }
                    return;
                }
                setTimeout(getListLoop, nextListDelay);
            }
            var nextListDelay=setting.nextListDelay||200;
            nextListDelay=Number(nextListDelay);
            var count=0;
            setTimeout(getListLoop,nextListDelay);
        }
        function downGetList(doc){
            if(!doc) return changeRestListText("下一页获取失败,获取续页结束")
            var addCount=getList(doc);
            if(!addCount) return changeRestListText("增加目录数为0,获取续页结束")
            nextListCount+=1;
            changeRestListText();
            var nextList=getNextList(doc);
            if(!nextList)return changeRestListText("找不到下一页,获取续页结束")
            nextListHref=nextList.href;
            if(!nextListHref)return changeRestListText("下一页无网址,获取续页结束")
            download(nextListHref,0,downGetList)
        }
        var getRestList=root.querySelector("#getRestList");
        getRestList.disabled=true;
        if(!nextListHref){
            if(!doc)return changeRestListText("下一页获取失败,获取续页结束")
            var nextList=getNextList(doc);
            if(!nextList)return changeRestListText("找不到下一页,获取续页结束")
            nextListHref=nextList.href;
        }
        if(nextListHref && nextListHref.indexOf("javascript") == -1){
            download(nextListHref,0,downGetList)
        } else {
            nextList=getNextList(doc);
            if(nextList){
                nextList.click();
                waitGetList();
            }
        }
    }
    function getList(doc) {
        var preChapterNo = 0;
        var preHrefNo = 0;
        function getChapterNo(s) {
            var found = s.match(/\d+|[零一壹二贰两三叁四肆五伍六陆七柒八捌九玖十拾百佰千仟万亿]+/g);
            if (found) {
                var n = parseInt(found[0]);
                if (isNaN(n)) {
                    n = numberDigit(found[0]);
                    if (n != -1) return n;
                } else return n;
            }
            return preChapterNo;
        }
        function getHrefNo(s) {
            var found = s.match(/\d+/g);
            return found ? parseInt(found.pop()) : preHrefNo;
        }
        function addToChapters(elements) {
            var count=0;
            for (var e of elements) {
                if (customItemFunc) customItemFunc(doc, e);
                if (!e.href || e.href.indexOf("javascript") != -1) continue;
                //if(isHide(window,e))continue;
                var iChapters=getIndexOfObjectArray(chapters, "href", e.href);
                if (iChapters != -1){ //最新章节
                    chaptersIndex.splice(chaptersIndex.indexOf(iChapters), 1);
                    chaptersIndex.push(iChapters);
                    continue;
                }
                count+=1;
                var title=e.text||e.innerText;
                var length = chapters.push({
                    href: e.href,
                    title: title,
                    chapterNo: getChapterNo(title),
                    hrefNo: getHrefNo(e.href),
                    text: "", //正文
                    nextCount: 0, //本章的下一页数量
                });
                preChapterNo = chapters[length - 1].chapterNo;
                preHrefNo = chapters[length - 1].hrefNo;
                chaptersIndex.push(length - 1);
            }
            return count
        }
        setting = getSetting();
        if (typeof setting != 'object') return;
        var listSelector=setting.listSelector;
        if (!listSelector||listSelector=='--') {
            var [mostAncestor, mostSelector, mostCount] = getAncestorWithMostSimilarTag(doc,"a:not([href*='javascript'])");
            if(!mostAncestor)return console.log('找不到目录');
            listSelector = getElementSelector(mostAncestor, doc) + '>' + mostSelector;
            if(setting.listSelector!='--'){
                setting.listSelector = listSelector;
                displaySetting();
            }
        }
        var count=0;
        if (customListFunc) {
            var customResult = customListFunc(doc, listSelector);
            if (customResult[0] instanceof Document) {
                [doc, listSelector] = customResult;
            } else {
                count=addToChapters(customResult);
                if(count)createTr();
                return
            }
        }
        listSelector=(listSelector||'').trim().replace(/\n/,',');
        count=addToChapters(doc.querySelectorAll(listSelector));
        createTr(); //count为0时也要刷新,因为可能都是最新章节的
        return count;
    }
    function createTr() {
        var tableBody = root.querySelector("table tbody");
        tableBody.innerHTML = "";
        for (var i of chaptersIndex) {
            var tr = document.createElement("tr");
            chapters[i].tr = tr
            var td = document.createElement("td");
            td.className = "serial";
            tr.appendChild(td);
            td = document.createElement("td");
            var a = document.createElement("a");
            a.text = chapters[i].title;
            a.href = chapters[i].href;
            a.target="_blank";
            td.title = `章节号${chapters[i].chapterNo},网址号${chapters[i].hrefNo}`;
            td.appendChild(a);
            tr.appendChild(td);
            td = document.createElement("td");
            var button = document.createElement("button");
            button.className = 'getText';
            button.textContent = '正';
            button.addEventListener("click", getText);
            td.appendChild(button);
            button = document.createElement("button");
            button.className = 'deleteRow';
            button.textContent = '─';
            button.addEventListener("click", deleteRow);
            td.appendChild(button);
            var span = document.createElement("span");
            td.appendChild(span);
            tr.appendChild(td);
            tableBody.appendChild(tr);
            showState(i);
        }
    } //getList
    function sortList(value) {
        var length = chapters.length;
        if (!chaptersIndex) {
            chaptersIndex = new Array(length);
            for (let i = 0; i < length; i++) {
                chaptersIndex[i] = i;
            }
        }
        switch (value) {
            case "原始升序":
                for (let i = 0; i < length; i++) {
                    chaptersIndex[i] = i;
                }
                break;
            case "原始降序":
                for (let i = 0; i < length; i++) {
                    chaptersIndex[i] = length - i - 1;
                }
                break;
            case "章节号升序":
                chaptersIndex.sort((a, b) => chapters[a].chapterNo - chapters[b].chapterNo);
                break;
            case "章节号降序":
                chaptersIndex.sort((a, b) => chapters[b].chapterNo - chapters[a].chapterNo);
                break;
            case "网址升序":
                chaptersIndex.sort((a, b) => chapters[a].hrefNo - chapters[b].hrefNo);
                break;
            case "网址降序":
                chaptersIndex.sort((a, b) => chapters[b].hrefNo - chapters[a].hrefNo);
                break;
        }
        createTr();
    }
    //#endregion List
    //#region helper
    function getElementSelector(element, doc) {
        function getRightId(id) {
            var firstCode = id.charCodeAt(0);
            if (firstCode >= 48 && firstCode <= 57) return "\\3" + id[0] + " " + id.substr(1, id.length);
            else return id;
        }
        let domPath = [];
        var e = element;
        while (e && e.nodeName.toLowerCase() !== "html") {
            var tag = e.tagName.toLocaleLowerCase();
            if (e.id) {
                domPath.unshift('#' + getRightId(e.id)+getCssClass(e.className));
                break;
            } else if (tag == "body") {
                domPath.unshift(tag);
            } else {
                var index = 0;
                var isOneTag = true;
                var isOneClass = e.classList.length > 0;
                for (var i = 0; i < e.parentNode.childElementCount; i++) {
                    if (e.parentNode.children[i] == e) {
                        index = i;
                    } else if (e.parentNode.children[i].tagName == e.tagName) {
                        isOneTag = false;
                        if (e.classList.length > 0 && e.parentNode.children[i].classList.toString() == e.classList.toString()) {
                            isOneClass = false;
                        }
                    }
                }
                if (isOneTag) {
                    domPath.unshift(tag);
                } else if (isOneClass) {
                    domPath.unshift(tag + getCssClass(e.className));
                } else {
                    domPath.unshift(tag + ':nth-child(' + (index + 1) + ')');
                }
            }
            var selector = domPath.toString().replace(/,/g, '>');
            var eles = doc.querySelectorAll(selector);
            if (eles.length == 1 && eles[0] == element) break;
            e = e.parentNode;
        }
        return domPath.toString().replace(/,/g, '>');
    } //getElementSelector
    //#endregion helper
    //#region Text
    function getContentElement(doc) { //参考“怠惰小说下载器”
        function getEffectiveText(text) {
            return text.replace(/\s+/g, '');
        }
        var largestContent, contents = doc.querySelectorAll("span,div,article,section,p,td"), largestNum = 0;
        for (let content of contents) {
            var curNum = 0;
            for (let item of content.childNodes) {
                if (item.nodeType == 3) { //纯文本
                    if (!/^\s*$/.test(item.data)) curNum += getEffectiveText(item.data).length;
                } else if (item.nodeType == 8) { //忽略注释
                    continue;
                } else if (/^(I|A|STRONG|B|FONT|P|DL|DD|H\d|SECTION)$/.test(item.tagName)) { //有这些子节点
                    curNum += getEffectiveText(item.innerText).length;
                }
            }
            if (curNum > largestNum) {
                largestNum = curNum;
                largestContent = content
            }
        }
        //console.log(largestContent, largestNum, getEffectiveText(largestContent.innerText));
        return largestContent;
    }
    function getElementText(element) { //参考“怠惰小说下载器”
        let result = "";
        for (let childNode of element.childNodes) {
            if (childNode.nodeType == 8) continue; //忽略注释
            if (childNode.innerHTML) {
                childNode.innerHTML = childNode.innerHTML.replace(/<\s*br\s*>/gi, "\r\n").replace(/\n+/gi, "\n").replace(/\r+/gi, "\r");
            }
            if (childNode.nodeType == 1 && !/^(I|A|STRONG|B|FONT)$/.test(childNode.tagName)) result += "\r\n";
            //textContent会包含里面的img源码;当是纯文本时,没有innerText属性,所以只能用textContent
            var text=childNode.innerText||childNode.textContent||'';
            text = text.replace(/[\u00A0\u2002\u2003\u2005\u200C\u200D]/g, '');
            result += text.trim().replace(/ +/g, "  ").replace(/([^\r]|^)\n([^\r]|$)/gi, "$1\r\n$2");
        }
        return result;
    }
    function downloadText(href, index, callback){
        if(setting.hasOwnProperty("frameText")) frameDoc(href, index, callback);
        else download(href, index, callback);
    }
    function frameDoc(href, index, callback) {
        if (typeof index == 'undefined') return;
        if (index < 0 || index >= chapters.length) return;
        if (href == null) {
            href = chapters[index].href;
            chapters[index].text = '';
            chapters[index].state = '';
            chapters[index].nextCount = 0;
        }
        if(document.location.protocol=="https:") href=href.replace(/^http:/,'https:');
        var frame=iframes.shift();
        if(!frame){
            frame=document.createElement('iframe');
            root.getElementById("iframesWarpper").appendChild(frame);
            frame.onload=function (result) {
                var doc = frame.contentDocument;
                if(doc){
                    doc.href = href; //记下当前页面的网址,自己生成的没有网址。
                    const element = doc.documentElement
                    element.scrollTop = 0;
                    const interval = setInterval(()=>{
                        console.log(element.scrollTop + element.clientHeight , element.scrollHeight);
                        if (element.scrollTop + element.clientHeight == element.scrollHeight) {
                            clearInterval(interval)
                            setTimeout(()=>{
                                deleteElementBySelector(doc,'img');
                                deleteElementBySelector(doc, '*[style*="display:none"]');
                                deleteHideElement(doc);
                                callback(doc, index);
                            },1000);
                        } else {
                            element.scrollTop +=200000;
                        }
                    }, 200)
                }else{
                    console.warn("error:", href);
                    callback(null, index);
                    frame.remove();
                }
            };
            frame.onerror=(e)=>{
                console.warn("error:", href);
                callback(null, index);
                frame.remove();
            };
        }
        frame.src=href;
    }
    function download(href, index, callback) {
        if (typeof index == 'undefined') return;
        if (index < 0 || index >= chapters.length) return;
        if (href == null) {
            href = chapters[index].href;
            chapters[index].text = '';
            chapters[index].state = '';
            chapters[index].nextCount = 0;
        }
        let requestBody = {
            method: 'GET',
            url: href,
            headers: {
                referer: href,
                "Content-Type": "text/html;charset=" + document.charset,
            },
            timeout: 15000,
            overrideMimeType: "text/html;charset=" + document.charset,
            onload: function (result) {
                var doc = getDoc(result.responseText);
                doc.href = href; //记下当前页面的网址,自己生成的没有网址。
                //                deleteSomeTag(doc, 'script');
                deleteSomeTag(doc, 'style');
                deleteSomeTag(doc, 'img');
                deleteElementBySelector(doc, '*[style*="display:none"]');
                deleteHideElement(doc);
                callback(doc, index);
            },
            onerror: function () {
                console.warn("error:", href);
                callback(null, index);
            },
            ontimeout: function () {
                console.warn("timeout: ", href);
                callback(null, index);
            }
        };
        GM_xmlhttpRequest(requestBody);
    } //getDocByHref
    function deleteHideElement(doc) {
        if (!doc.defaultView) return; //直接下载网页的没有doc.defaultView
        var elements = doc.querySelectorAll("span,div,ul,li")
        //var elements = doc.querySelectorAll("li")
        for (var i = elements.length - 1; i >= 0; i--) {
            var ele = elements[i];
            var thisStyle = doc.defaultView.getComputedStyle(ele);
            if (thisStyle && (thisStyle.display == "none" || (ele.tagName == "SPAN" && thisStyle.fontSize == "0px"))) ele.remove();
        }
    }
    function deleteElementBySelector(doc, selector) {
        var elements = doc.querySelectorAll(selector);
        for (var i = elements.length - 1; i >= 0; i--) {
            elements[i].remove();
        }
    }
    function deleteSomeTag(doc, tag) {
        var elements = doc.getElementsByTagName(tag);
        for (var i = elements.length - 1; i >= 0; i--) {
            elements[i].remove();
        }
    }
    function getDoc(str) {
        var doc = null;
        try {
            doc = document.implementation.createHTMLDocument('');
            doc.documentElement.innerHTML = str;
        }
        catch (e) {
            console.log('parse error');
        }
        return doc;
    } //getDoc
    function getNextPage(doc) {
        let eles = doc.querySelectorAll("a");
        for (let ele of eles) {
            if (nextPageReg.test(ele.innerText) && ele.href.indexOf("javascript") == -1) return ele;
            if(setting.addedNextPageReg){
                if (setting.addedNextPageReg.test(ele.innerText) && ele.href.indexOf("javascript") == -1) return ele;
            }
        }
    }
    //获得正文,如果有下一页网址不在章节网址,继续获取,并返回Next
    function getTextFromDoc(doc, index, callback) {
        function addTexttoChapter(text) {
            chapters[index].text +=
                ((doc.href == chapters[index].href) ? "" : `>>${doc.title}\n`) + text;
        }
        if (doc) {
            var nextPagehref="";
            if (customNextPageFunc) {
                nextPagehref=customNextPageFunc(doc,chapters[index].href);
            }else{
                var nextPage = getNextPage(doc);
                nextPagehref = nextPage ? nextPage.href : "";
            }
            deleteSomeTag(doc, 'script');
            var jammerSelector=(setting.jammerSelector||'').trim().replace(/\n/,',');
            if (jammerSelector) deleteElementBySelector(doc, jammerSelector);
            var content;
            var textSelector=setting.textSelector;
            if (!textSelector||textSelector=='--') {
                content = getContentElement(doc);
                textSelector = getElementSelector(content, doc);
                if(setting.textSelector!='--'){
                    setting.textSelector = textSelector;
                    displaySetting();
                }
            }
            if (customTextFunc) {
                var customResult = customTextFunc(doc, textSelector);
                if (Array.isArray(customResult)) {
                    [doc, textSelector] = customResult;
                } else if (typeof customResult == 'string') {
                    addTexttoChapter(customResult+'\n');
                    return 'OK';
                } else {
                    return 'No'
                }
            }
            textSelector=(textSelector||'').trim().replace(/\n/,',');
            var eTexts = doc.querySelectorAll(textSelector);
            if(eTexts.length==0) return 'No';
            var text=''
            for (var eText of eTexts) {
                text+=getElementText(eText)+'\n';
            }
            addTexttoChapter(text);

            if (nextPagehref) {
                var href2 = nextPagehref.slice(0, 6) == 'https:' ? 'http:' + nextPagehref.slice(6) : 'https' + nextPagehref.slice(5);
                if (nextPagehref == document.location.href || href2 == document.location.href) return 'OK'; // 如果a元素的href为空,返回的是目录页的地址
                if (nextPagehref == doc.href || href2 == doc.href) return 'OK';
                if (getIndexOfObjectArray(chapters, "href", nextPagehref,true) == -1 && getIndexOfObjectArray(chapters, "href", href2,true) == -1) {
                    if (chapters[index].nextCount > nextCountLimit - 1) { //第一个下一页为0
                        if (continueDownload || confirm(`目录“${chapters[index].title}”的下一页数量超过最大值${nextCountLimit},你要让以后的下一页继续吗?\n继续可能会下载太多链接,请谨慎继续!`)) {
                            continueDownload = true;
                        } else {
                            return '>N';
                        }
                    }
                    if (chapters[index].nextCount > maxNextCount - 1) return '>>N'
                    downloadText(nextPagehref, index, callback);
                    return 'Next';
                } else return 'OK';
            } else return 'OK';
        } else {
            return 'Er';
        }
    } //getTextFromDoc
    function showChapterText(index) {
        function showOrDownload(index) {
            if (isDownloaded(index)) {
                showChapterText(index)
            } else {
                downloadText(null, index, getTextCallback)
            }
        }
        reader.setReader(chapters[index].text, chapters[index].title,
                         (index - 1 >= 0 && index - 1 < chapters.length) ? '<' : '', () => {
            showOrDownload(index - 1)
        }, (index + 1 >= 0 && index + 1 < chapters.length) ? '>' : '', () => {
            showOrDownload(index + 1)
        });
    }
    function showState(index) {
        var span = chapters[index].tr.querySelector("td>span");
        span.textContent = (chapters[index].state || '') +
            ((chapters[index].state != 'OK' && chapters[index].text) ? "+" : "") +
            (chapters[index].nextCount || '');
    }
    function getTextCallback(doc, index) {
        var state = getTextFromDoc(doc, index, getTextCallback);
        //        doc&&doc.defaultView&&doc.defaultView.frameElement&&iframes.push(doc.defaultView.frameElement);
        doc&&doc.defaultView&&doc.defaultView.frameElement&&doc.defaultView.frameElement.remove();
        switch (state) {
            case 'No':
                if (confirm(`${chapters[index].text ? "后续页" : ""}找不到正文选择器指定的元素,清空正文选择器重新获取。`)) {
                    delete setting.textSelector;
                    displaySetting();
                }
                break;
            case 'Er': alert(`${chapters[index].href}下载出错`); break;
            case 'Next':
                chapters[index].nextCount += 1;
                return;
        }
        chapters[index].state = state;
        showState(index);
        if (chapters[index].text) showChapterText(index);
    }
    var getText = e => {
        setting = getSetting();
        if (typeof setting != 'object') return;
        var tr = e.target.parentElement.parentElement;
        var index = getIndexOfObjectArray(chapters, "tr", tr);
        if (isDownloaded(index)) showChapterText(index);
        else downloadText(null, index, getTextCallback);
        //        else framedoc(null, index, getTextCallback);
    }
    function uploadSaveinfo2(info){
        const url = 'https://jsonbin.org/me/saveinfos';
        const headers = new Headers({
            "Content-Type": "application/json",
            "Authorization": "token 494a60d1-f0ee-4b6d-a5bf-8654a9ff5eb1"
        });
        fetch(url, {
            headers: headers,
            method: "PATCH",
            body:JSON.stringify(info),
        });
    }
    function uploadSaveinfo(info) {
        const url = 'https://jsonbin.org/me/savedInfos';
        const headers = new Headers({
            "Content-Type": "application/json",
            "Authorization": "token 494a60d1-f0ee-4b6d-a5bf-8654a9ff5eb1"
        });
        var md5type = 1;
        var md5s = myMd5(info.url);
        uploadInfo();
        function uploadInfo() {
            function setValue(md5,info){
                fetch(url+'/'+md5, {
                    method: "POST",
                    headers: headers,
                    body:JSON.stringify(info),
                })
            }
            function pushDowninfo(md5,downinfo){
                fetch(url+'/'+md5+'/downinfo', {
                    method: "PATCH",
                    headers: headers,
                    body:JSON.stringify(downinfo),
                })
            }
            var md5 = md5s.slice(0, md5type).join('');
            if (md5type > 4) md5 += (md5type - 4);
            fetch(url+'/'+md5, {
                method: "GET",
                headers: headers,
            }).then(function(response) {
                return response.json()
            }).then(function(json) {
                if (!json) {
                    setValue(md5, info)
                } else {
                    if (json.url == info.url) {
                        var stime=json.downinfo.slice(-1)[0][2];
                        var itime=info.downinfo[0][2];
                        if (!stime && !itime){}
                        else if (stime && itime && Math.abs(itime - stime)<0.0060){}
                        else pushDowninfo(md5,info.downinfo[0]);
                    } else {
                        md5type += 1;
                        uploadInfo();
                    }
                }
            })
        }
    }
    function saveAllText() {
        var allText = '', a;
        for (var i of chaptersIndex) {
            allText += '\n##' + chapters[i].title.trim().replace(/\s+/g,' ') + '\n' + chapters[i].text;
        }
        var dd=new Date();
        var time=dd.getFullYear()*10000+(dd.getMonth()+1)*100+dd.getDate()+dd.getHours()*0.01+dd.getMinutes()*0.0001;
        var info={
            title:document.title,
            url:document.location.href,
            downinfo:[[chapters.length,allText.length,time]],
        };
        if(!setting.hasOwnProperty("notUploadSaveinfo")) uploadSaveinfo(info);
        var blob = new Blob([
            '#' + document.title + '\n',
            document.location.href + '\n',
            '使用油猴脚本“随手小说下载”获取\n',
            allText
        ], { type: "text/plain;charset=utf-8", endings: "native" });
        var filename = document.title.replace(/[/\\?%*:|"<>.]/g, '-') + '.txt';
        downloadFile(blob, filename);
    }
    function getAllTextCallback(doc, index) {
        var state = getTextFromDoc(doc, index, getAllTextCallback);
        doc&&doc.defaultView&&doc.defaultView.frameElement&&doc.defaultView.frameElement.remove();
        switch (state) {
            case 'No': downloadedNo += 1; break;
            case 'Er': downloadedErr += 1; break;
            case '>N':
            case '>>N': downloadedExceedNext += 1; break;
            case 'Next':
                chapters[index].nextCount += 1;
                return;
        }
        chapters[index].state = state;
        showState(index);
        downloadText(null, chaptersIndex[downloadIndex], getAllTextCallback);
        downloadIndex = getNextUndownloadIndex(downloadIndex + 1);
        downloadedCount += 1;
        root.querySelector('#downloadNumbers').textContent = `${chapters.length}/${downloadedCount}/${downloadedNo}/${downloadedErr}/${downloadedExceedNext}`;
        if (downloadedCount >= chapters.length) {
            if (downloadedNo == 0 && downloadedErr == 0) {
                saveAllText();
            } else {
                if (confirm(`${downloadedNo}个找不到正文元素,${downloadedErr}个下载失败。\n是否保存已下载的文本。`)) {
                    saveAllText();
                }
            }
        }
    } //getAllTextCallback
    function isDownloaded(index) {
        var state = chapters[index].state;
        return state == 'OK'
    }
    function getNextUndownloadIndex(index) {
        while (index < chaptersIndex.length && isDownloaded(chaptersIndex[index])) {
            downloadedCount += 1;
            index += 1;
        }
        return index;
    }
    function getAllText() {
        if (chapters.length < 1) alert("没有目录,请先获取目录再下载全部文本。");
        downloadedCount = 0; //已下载数量
        downloadedErr = 0; //下载失败数量
        downloadedNo = 0; //下载章节找不到选择器对应元素的数量
        downloadIndex = 0; //当前待下载序号

        downloadIndex = getNextUndownloadIndex(downloadIndex);
        if (downloadIndex >= chapters.length) saveAllText(); //已经获取全部文本
        else {
            for (var i = 0; i < MaxThread; i++) {
                downloadText(null, chaptersIndex[downloadIndex], getAllTextCallback);
                downloadIndex = getNextUndownloadIndex(downloadIndex + 1);
            }
        }
    }
    var deleteRow = e => {
        var tr = e.target.parentElement.parentElement;
        var index = getIndexOfObjectArray(chapters, "tr", tr);
        tr.remove();
        chapters.splice(index, 1);
        chaptersIndex.splice(chaptersIndex.indexOf(index), 1);
        for (let i = 0; i < chaptersIndex.length; i++) {
            if (chaptersIndex[i] > index) chaptersIndex[i] -= 1;
        }
    };
    function getIndexOfObjectArray(objectArray, key, value, isStartsWith=false) {
        for (let i = 0; i < objectArray.length; i++) {
            if (isStartsWith) {
                if (value.startsWith(objectArray[i][key])) return i;
            } else if (objectArray[i][key] == value) return i;
        }
        return -1;
    }
    function displaySetting() {
        //        eSetting.value=toTplString(JSON.stringify(setting, null, 2));
        cm.setValue(settingToString(setting));
        setSaveSiteSettingClass();
    }
    function getSetting(isSetCunstomFun = true) {
        var result = {}, key = '', value = '';
        var lines = cm.getValue().split('\n');
        for (let line of lines) {
            if (!line.trim()) continue;
            if (line.slice(0, 2) == '$$') {
                if (key=='frameText') result[key]='';
                if (key=='notUploadSaveinfo') result[key]='';
                else if (key && value) result[key] = value.trim();
                key = line.slice(2);
                value = '';
                if (SETTING_KEYS.indexOf(key) == -1) {
                    return alert(`网站配置输入框中,键值${key}不合法,请修改。`);
                }
            } else {
                value += (value ? '\n' : '') + line;
            }
        }
        if (key=='frameText') result[key]='';
        if (key=='notUploadSaveinfo') result[key]='';
        else if (key && value) result[key] = value.trim();
        if (isSetCunstomFun) {
            customListFunc = result.customListFunc ? Function("doc", "selector", result.customListFunc + ";return [doc,selector];") : null;
            customItemFunc = result.customItemFunc ? Function("doc", "item", result.customItemFunc + ";return [doc,item];") : null;
            customTextFunc = result.customTextFunc ? Function("doc", "selector", result.customTextFunc + ";return [doc,selector];") : null;
            customNextPageFunc=result.customNextPageFunc ? Function("doc", "href", result.customNextPageFunc + ";return [doc,href];") : null;
        }
        return result;
    }
    function settingToString(setting) {
        var result = '';
        for (var key in setting) {
            result += '$$' + key + '\n';
            if(setting[key]) result += setting[key] + '\n';
        }
        return result;
    }
    function saveSiteSetting() {
        setting = getSetting();
        if (typeof setting != 'object') return;
        if (isSameObject(setting, {})) {
            GM_deleteValue(location.host);
            alert("已删除该网站配置");
        } else {
            GM_setValue(location.host, setting);
            alert("已保存该网站配置");
        }
        setSaveSiteSettingClass();
    }
    function isSameObject(object1, object2) {
        if (!object1 || !object2) return false;
        var ss1 = Object.entries(object1).toString();
        var ss2 = Object.entries(object2).toString();
        return ss1 === ss2;
    }
    function setRestListClass(){
        var nextList=getNextList(document);
        root.querySelector("#getRestList").disabled=!nextList;
    }
    function setSaveSiteSettingClass() {
        tempSetting = getSetting(false);
        if (typeof tempSetting == 'object') {
            var gmSetting = GM_getValue(location.host);
            root.querySelector("#saveSiteSetting").disabled = isSameObject(tempSetting, gmSetting);
        }
        setRestListClass(); //设置剩余目录按钮可用性
    }
    function enableCodeMirrow() {
        var CodeMirrorminCss = GM_getResourceText("CodeMirrorminCss")
        var style = document.createElement('style');
        style.innerHTML = CodeMirrorminCss;
        root.appendChild(style);
        var ele = root.getElementById("setting");
        cm = CodeMirror.fromTextArea(ele, {
            matchBrackets: true,
            mode: "javascript",
        });
        cm.setSize(CodeMirrorSize[0], CodeMirrorSize[1]);
        cm.on('blur', function () {
            setSaveSiteSettingClass();
        });
        document.ytheditor = cm;
        window.ytheditor = cm;
        ytheditor = cm
        SETTING_KEYS.forEach(words => {
            //CodeMirror.resolveMode("javascript").keywords[words] = true;
        });

    }
    function downloadFile(blob, fileName) {
        var link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = fileName;
        link.click();
        link.remove();
        window.URL.revokeObjectURL(link.href);
    }
    function addSiteSettings(siteSettings) {
        for (var name in siteSettings) {
            var setting = siteSettings[name];
            var tmpSetting = {};
            for (var key of SETTING_KEYS) {
                if (setting.hasOwnProperty(key)) tmpSetting[key] = setting[key];
            }
            if (!isSameObject(tmpSetting, {})) GM_setValue(name, tmpSetting);
        }
    }
    function selClick(e) {
        e.stopPropagation();
        e.stopImmediatePropagation();
        e.preventDefault();
        e.returnValue=false;
        document.getElementById("ythList").style.display='';
        document.body.removeEventListener("mouseover", selMouseover);
        document.body.removeEventListener("click", selClick,true);
        document.body.removeEventListener("touchend", selClick,true);
        var t = getFittedElement(e.target);
        if(!t)return false;
        t.className = t.className.replace(new RegExp("\\b" + hiClass + "\\b","g"), "").trim();
        var [mostAncestor, mostSelector, mostCount] = getAncestorWithMostSimilarDescendant(t,"a:not([href*='javascript'])");
        var eListSelector = getElementSelector(mostAncestor, document) + '>' + mostSelector;
        setting.listSelector = eListSelector;
        displaySetting();
        root.querySelector("#clearList").click();
        root.querySelector("#getList").click();
        return false;
    }
    function getFittedElement(e) {
        var t = e.nodeName.toLowerCase();
        return t == "a"&&!/javascript/.test(e.href)?e:(t == "body"? null : getFittedElement(e.parentNode));
    }
    function selMouseover(e) {
        e.stopPropagation();
        var t = getFittedElement(e.target);
        if(!t)return;
        t.addEventListener("mouseout", function(o) {
            var n = getFittedElement(o.target);
            n.className = n.className.replace(new RegExp("\\b" + hiClass + "\\b","g"), "").trim();
        });
        t.className += " " + hiClass;
    }

    function getClickedElementSelector(){
        setting = getSetting();
        if (typeof setting != 'object') return;
        document.getElementById("ythList").style.display='none';
        setTimeout(()=>{
            document.body.addEventListener("mouseover", selMouseover);
            //            document.body.addEventListener("mousedown", selClick);
            document.body.addEventListener("click", selClick, true);
            document.body.addEventListener("touchend", selClick, true);
        },200);
    }
    function addDiv() {
        GM_addStyle(`
        #ythList{
          position:fixed;
          right:0px;
          z-index: 99999999999;
          background-color: #ccc;
          top: 0;
        }
        .ythHighlight{
          background-color:#8ce!important;
	      cursor:pointer!important;
	      outline:4px solid #016!important;
        }
        `);
        var shadowCss = `
<style>
textarea{
    white-space: nowrap;
    width: 288px;
    height: 121px;
    font-size: 12px;
}
button:hover{
    background-color: rgb(93 187 93);
}
.titleList{
    overflow-y:scroll;
    overflow-x:hidden;
    padding-right: 2px;
    flex-grow: 1;
    margin-bottom: 10px;
}
#commandBar{
    margin: 5px 0px;
}
.capsule{
    width: fit-content;
    border-radius: 10px;
    color: #000;
    padding: 0px 7px 2px 7px;
    border-width: 1px;
    border-style: solid;
}
#remainOK{
    transform: scale(0.8);
}
table th {
    text-align: center;
    position: sticky;
    top: 0;
    background-color: #aaa;
    box-shadow: 0 -1px #000000;
}
table th, td {
	border: 1px solid black;
	word-wrap: break-word;
}
table td:nth-child(3){
    vertical-align:text-top;
}
table {
	border-collapse: collapse;
	border-spacing: 0;
	table-layout: fixed;
	width:30.5em;
    font-size: inherit;
    color: #000;
}
table button {
    font-size: 10px;
    border-radius: 50%;
    width: 16px;
    height: 16px;
    border-style: solid;
    color: #444;
    background-color: #f7f7f7;
    padding: 1px 0px;
    border-width: 1px;
}
table button.getText {
}
table button.deleteRow {
}
table th:nth-child(2),td:nth-child(2) {
	width:20em;
}
table th:nth-child(1),td:nth-child(1) {
	width:3em;
}
table tbody{
    counter-reset:sectioncounter;
}
table td.serial:before{
    content:counter(sectioncounter);
    counter-increment:sectioncounter;
}
#downloadNumbers{
    float:right;
}
svg {
    height: 18px;
}
svg path{
	fill:#404040;
}
#saveSiteSetting:disabled svg path{
    fill:#ccc;
}
iframe{
    pointer-events:none;
    width: 2px;
    height: 2px;
}
:disabled{
    cursor: none;
    pointer-events:none;
    background-color: revert;
    color:#ccc;
}
select,select option{
    float: right;
    transform: scale(0.8);
}
div.CodeMirror{
    resize: both;
    float: right;
    min-width: 288px;
    min-height: 60px;
}
#SiteSettingButtons{
    float: left;
    padding: 0px 4px 0px 0px;
    display: flex;
    flex-direction: column;
}
#SiteSettingButtons button{
    width: 20px;
    height: 20px;
    border-radius: 14%;
    padding: 0px 0px 0px 0px;
    border: 1px solid #333;
/*    background-color: #d2d2d2;*/
}
#container{
    display: flex;
    flex-flow: column;
    align-content: space-between;
    padding: 5px;
	font-size: 10px;
    font-family: -apple-system,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Microsoft YaHei,Source Han Sans SC,Noto Sans CJK SC,WenQuanYi Micro Hei,sans-serif;
    line-height: normal;
    text-align: left;
}
</style>`;
        var div = document.createElement("div");
        div.id = "ythList";
        root = div.attachShadow({ mode: 'open' });
        var html = shadowCss;
        var importSvg = '<svg t="1662536357307" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2569"><path d="M667.733333 864H170.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V170.666667c0-6.4 4.266667-10.666667 10.666667-10.666667h309.333333V320c0 40.533333 34.133333 74.666667 74.666667 74.666667h160v38.4c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V298.666667c0-8.533333-4.266667-17.066667-8.533334-23.466667l-170.666666-170.666667c-6.4-6.4-14.933333-8.533333-23.466667-8.533333H170.666667C130.133333 96 96 130.133333 96 170.666667v682.666666c0 40.533333 34.133333 74.666667 74.666667 74.666667h497.066666c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32z m46.933334-550.4v17.066667H554.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V160h19.2l151.466667 153.6z" p-id="2570"></path><path d="M853.333333 597.333333H599.466667l51.2-51.2c12.8-12.8 12.8-32 0-44.8-12.8-12.8-32-12.8-44.8 0l-106.666667 106.666667c-12.8 12.8-12.8 32 0 44.8l106.666667 106.666667c6.4 6.4 14.933333 8.533333 23.466666 8.533333s17.066667-2.133333 23.466667-8.533333c12.8-12.8 12.8-32 0-44.8L599.466667 661.333333H853.333333c17.066667 0 32-14.933333 32-32S870.4 597.333333 853.333333 597.333333z" p-id="2571"></path></svg>';
        var exportSvg = '<svg t="1662536484558" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3489"><path d="M582.4 864H170.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V170.666667c0-6.4 4.266667-10.666667 10.666667-10.666667h309.333333V320c0 40.533333 34.133333 74.666667 74.666667 74.666667h160v38.4c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V298.666667c0-8.533333-4.266667-17.066667-8.533334-23.466667l-170.666666-170.666667c-6.4-6.4-14.933333-8.533333-23.466667-8.533333H170.666667C130.133333 96 96 130.133333 96 170.666667v682.666666c0 40.533333 34.133333 74.666667 74.666667 74.666667h411.733333c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32z m132.266667-550.4v17.066667H554.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V160h19.2l151.466667 153.6z" p-id="3490"></path><path d="M866.133333 669.866667l-106.666666-106.666667c-12.8-12.8-32-12.8-44.8 0s-12.8 32 0 44.8l51.2 51.2H512c-17.066667 0-32 14.933333-32 32S494.933333 725.333333 512 725.333333h253.866667l-51.2 51.2c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 14.933333 8.533333 23.466666 8.533334s17.066667-2.133333 23.466667-8.533334l106.666667-106.666666c8.533333-10.666667 8.533333-32-2.133334-44.8z" p-id="3491"></path></svg>';
        var saveSvg = '<svg t="1662536828344" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2606"><path d="M906.666667 298.666667L725.333333 117.333333c-14.933333-14.933333-32-21.333333-53.333333-21.333333H170.666667C130.133333 96 96 130.133333 96 170.666667v682.666666c0 40.533333 34.133333 74.666667 74.666667 74.666667h682.666666c40.533333 0 74.666667-34.133333 74.666667-74.666667V349.866667c0-19.2-8.533333-38.4-21.333333-51.2zM652.8 864H371.2V648.533333h281.6v215.466667z m211.2-10.666667c0 6.4-4.266667 10.666667-10.666667 10.666667h-140.8V618.666667c0-17.066667-12.8-29.866667-29.866666-29.866667H341.333333c-17.066667 0-29.866667 12.8-29.866666 29.866667v245.333333H170.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V170.666667c0-6.4 4.266667-10.666667 10.666667-10.666667h140.8V320c0 17.066667 12.8 29.866667 29.866666 29.866667h277.333334c17.066667 0 29.866667-12.8 29.866666-29.866667s-12.8-29.866667-29.866666-29.866667H371.2V160h302.933333c2.133333 0 6.4 2.133333 8.533334 2.133333l179.2 179.2c2.133333 2.133333 2.133333 4.266667 2.133333 8.533334V853.333333z" p-id="2607"></path></svg>';
        var clearAllSvg = '<svg t="1662608639253" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1287"><path d="M672 256c16.953 0 32.987 6.696 45.145 18.855C729.304 287.013 736 303.047 736 320v512c0 16.953-6.696 32.987-18.855 45.145S688.953 896 672 896H224c-16.954 0-32.986-6.696-45.145-18.855S160 848.953 160 832V320c0-16.953 6.696-32.987 18.855-45.145C191.014 262.696 207.046 256 224 256h448m0-64H224c-70.4 0-128 57.6-128 128v512c0 70.4 57.6 128 128 128h448c70.4 0 128-57.6 128-128V320c0-70.4-57.6-128-128-128z" p-id="1288"></path><path d="M800 64H352v64h448c35.2 0 64 28.8 64 64v576h64V192c0-70.4-57.6-128-128-128z" p-id="1289"></path><path d="M598.765 425.236c-12.445-12.445-32.81-12.445-45.255 0L448 530.745l-105.51-105.51c-12.445-12.445-32.81-12.445-45.255 0s-12.445 32.81 0 45.255L402.745 576l-105.51 105.51c-12.445 12.445-12.445 32.81 0 45.255s32.81 12.445 45.255 0L448 621.255l105.51 105.51c12.445 12.445 32.81 12.445 45.255 0s12.445-32.81 0-45.255L493.255 576l105.51-105.51c12.445-12.445 12.445-32.809 0-45.254z" p-id="1290"></path></svg>';
        var shrinkSvg = '<svg t="1662608958322" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2606"><path d="M313.6 358.4H177.066667c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h213.333333c4.266667 0 8.533333 0 10.666667-2.133333 8.533333-4.266667 14.933333-8.533333 17.066666-17.066667 2.133333-4.266667 2.133333-8.533333 2.133334-10.666667v-213.333333c0-17.066667-14.933333-32-32-32s-32 14.933333-32 32v136.533333L172.8 125.866667c-12.8-12.8-32-12.8-44.8 0-12.8 12.8-12.8 32 0 44.8l185.6 187.733333zM695.466667 650.666667H832c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32H618.666667c-4.266667 0-8.533333 0-10.666667 2.133333-8.533333 4.266667-14.933333 8.533333-17.066667 17.066667-2.133333 4.266667-2.133333 8.533333-2.133333 10.666666v213.333334c0 17.066667 14.933333 32 32 32s32-14.933333 32-32v-136.533334l200.533333 200.533334c6.4 6.4 14.933333 8.533333 23.466667 8.533333s17.066667-2.133333 23.466667-8.533333c12.8-12.8 12.8-32 0-44.8l-204.8-198.4zM435.2 605.866667c-4.266667-8.533333-8.533333-14.933333-17.066667-17.066667-4.266667-2.133333-8.533333-2.133333-10.666666-2.133333H192c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h136.533333L128 851.2c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 14.933333 8.533333 23.466667 8.533333s17.066667-2.133333 23.466666-8.533333l200.533334-200.533333V832c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V618.666667c-2.133333-4.266667-2.133333-8.533333-4.266667-12.8zM603.733333 403.2c4.266667 8.533333 8.533333 14.933333 17.066667 17.066667 4.266667 2.133333 8.533333 2.133333 10.666667 2.133333h213.333333c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32h-136.533333L896 170.666667c12.8-12.8 12.8-32 0-44.8-12.8-12.8-32-12.8-44.8 0l-187.733333 187.733333V177.066667c0-17.066667-14.933333-32-32-32s-32 14.933333-32 32v213.333333c2.133333 4.266667 2.133333 8.533333 4.266666 12.8z" p-id="2607"></path></svg>';
        var expandSvg = '<svg t="1662609025513" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2745"><path d="M149.333333 394.666667c17.066667 0 32-14.933333 32-32v-136.533334l187.733334 187.733334c6.4 6.4 14.933333 8.533333 23.466666 8.533333s17.066667-2.133333 23.466667-8.533333c12.8-12.8 12.8-32 0-44.8l-187.733333-187.733334H362.666667c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32H149.333333c-4.266667 0-8.533333 0-10.666666 2.133334-8.533333 4.266667-14.933333 10.666667-19.2 17.066666-2.133333 4.266667-2.133333 8.533333-2.133334 12.8v213.333334c0 17.066667 14.933333 32 32 32zM874.666667 629.333333c-17.066667 0-32 14.933333-32 32v136.533334L642.133333 597.333333c-12.8-12.8-32-12.8-44.8 0s-12.8 32 0 44.8l200.533334 200.533334H661.333333c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h213.333334c4.266667 0 8.533333 0 10.666666-2.133334 8.533333-4.266667 14.933333-8.533333 17.066667-17.066666 2.133333-4.266667 2.133333-8.533333 2.133333-10.666667V661.333333c2.133333-17.066667-12.8-32-29.866666-32zM381.866667 595.2l-200.533334 200.533333V661.333333c0-17.066667-14.933333-32-32-32s-32 14.933333-32 32v213.333334c0 4.266667 0 8.533333 2.133334 10.666666 4.266667 8.533333 8.533333 14.933333 17.066666 17.066667 4.266667 2.133333 8.533333 2.133333 10.666667 2.133333h213.333333c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32h-136.533333l200.533333-200.533333c12.8-12.8 12.8-32 0-44.8s-29.866667-10.666667-42.666666 0zM904.533333 138.666667c0-2.133333 0-2.133333 0 0-4.266667-8.533333-10.666667-14.933333-17.066666-17.066667-4.266667-2.133333-8.533333-2.133333-10.666667-2.133333H661.333333c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h136.533334l-187.733334 187.733333c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 14.933333 8.533333 23.466667 8.533333s17.066667-2.133333 23.466667-8.533333l187.733333-187.733333V362.666667c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V149.333333c-2.133333-4.266667-2.133333-8.533333-4.266667-10.666666z" p-id="2746"></path></svg>';
        //        var focusSvg = '<svg t="1663115910122" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5491"><path d="M341.333333 853.333333H213.333333a42.666667 42.666667 0 0 1-42.666666-42.666666v-128a42.666667 42.666667 0 0 0-85.333334 0v128a128 128 0 0 0 128 128h128a42.666667 42.666667 0 0 0 0-85.333334zM128 384a42.666667 42.666667 0 0 0 42.666667-42.666667V213.333333a42.666667 42.666667 0 0 1 42.666666-42.666666h128a42.666667 42.666667 0 0 0 0-85.333334H213.333333a128 128 0 0 0-128 128v128a42.666667 42.666667 0 0 0 42.666667 42.666667z m682.666667-298.666667h-128a42.666667 42.666667 0 0 0 0 85.333334h128a42.666667 42.666667 0 0 1 42.666666 42.666666v128a42.666667 42.666667 0 0 0 85.333334 0V213.333333a128 128 0 0 0-128-128z m-128 426.666667a42.666667 42.666667 0 0 0-42.666667-42.666667h-85.333333V384a42.666667 42.666667 0 0 0-85.333334 0v85.333333H384a42.666667 42.666667 0 0 0 0 85.333334h85.333333v85.333333a42.666667 42.666667 0 0 0 85.333334 0v-85.333333h85.333333a42.666667 42.666667 0 0 0 42.666667-42.666667z m213.333333 128a42.666667 42.666667 0 0 0-42.666667 42.666667v128a42.666667 42.666667 0 0 1-42.666666 42.666666h-128a42.666667 42.666667 0 0 0 0 85.333334h128a128 128 0 0 0 128-128v-128a42.666667 42.666667 0 0 0-42.666667-42.666667z" p-id="5492"></path></svg>';
        var focusSvg = '<svg t="1663116786849" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10799"><path d="M512 394.666667a117.333333 117.333333 0 1 0 0 234.666666 117.333333 117.333333 0 0 0 0-234.666666zM330.666667 512a181.333333 181.333333 0 1 1 362.666666 0 181.333333 181.333333 0 0 1-362.666666 0z" fill="#000000" p-id="10801"></path><path d="M437.333333 96H256A160 160 0 0 0 96 256v160h64V256A96 96 0 0 1 256 160h181.333333v-64zM608 160v-64H768a160 160 0 0 1 160 160v170.666667h-64v-170.666667A96 96 0 0 0 768 160h-160zM864 597.333333v170.666667a96 96 0 0 1-96 96h-170.666667v64h170.666667a160 160 0 0 0 160-160v-170.666667h-64zM426.666667 928v-64h-170.666667A96 96 0 0 1 160 768v-181.333333h-64V768a160 160 0 0 0 160 160h170.666667z" p-id="10802"></path></svg>'
        html += `
<div id="container">
    <div id="toggleBar">
        <span id="toggle">︿</span>
        <span id="iframesWarpper">
        </span>
        <span id="downloadNumbers" title="章节/下载/无内容数量/错误/下页超标"></span>
    </div>
    <div id="settingWarpper">
        <div id="SiteSettingButtons">
        <button id="saveSiteSetting" disabled title="保存当前网站配置信息">${saveSvg}</button>
        <button id="resize"></button>
        <button id="importSiteSetting" title="导入多个网站配置信息">${importSvg}</button>
        <button id="exportSiteSetting" title="导出现有全部网站配置信息">${exportSvg}</button>
        <button id="clearAllSiteSetting" title="删除现有全部网站配置信息">${clearAllSvg}</button>
        <button id="focus" title="用鼠标点击手动获取目录列表">${focusSvg}</button>
        </div>
        <textarea id="setting" placeholder="网站配置项,JSON格式"></textarea>
    </div>
    <div id="commandBar">
        <button id="getList" class="capsule main" title="获取当前页包含的目录列表">获取目录</button>
        <button id="getRestList" class="capsule main" title="根据目录页面的下一页链接,连续获取接下去的剩余目录列表">剩余目录</button>
        <button id="clearList" class="capsule main" title="全部删除已经提取的目录列表">清空目录</button>
        <button id="getAllText" class="capsule main" title="根据目录顺序下载全文并保存文本">下载全文</button>
    </div>
    <div class="titleList">
        <table><thead><tr>
            <th>序号</th>
            <th class="title">标题
            <select id="sort">
            <option>原始升序</option>
            <option>原始降序</option>
            <option>章节号升序</option>
            <option>章节号降序</option>
            <option>网址升序</option>
            <option>网址降序</option>
            </select></th>
            <th><button id="DeleteNoText" class="capsule">删空正文</button></th>
        </tr></thead><tbody></tbody></table>
    </div>
</div>
<input type="file" id="inputFiles" accept=".txt" style="display:none">`;
        root.innerHTML = html;
        document.documentElement.appendChild(div)
        //document.body.appendChild(div);
        reader = (typeof Reader == 'function') ? new Reader(document, "ReaderAttached") : null;

        root.querySelector("#sort").addEventListener("change", (e) => {
            sortList(e.target.value);
        });
        enableCodeMirrow();

        setting = GM_getValue(location.host);
        if (!setting) setting = {};
        displaySetting();
        var eGetList = root.querySelector("#getList");
        eGetList.onclick = () => {
            getList(document);
        };
        eGetList.click();
        root.querySelector("#getRestList").addEventListener("click", () => {
            getRestList(document);
        });
        //        setSaveSiteSettingClass();

        var resize = root.querySelector("#resize");
        resize.addEventListener("click", (e) => {
            var ele = resize;
            if (ele.title == '放大') {
                ele.title = '缩小';
                ele.innerHTML = shrinkSvg;
                cm.setSize(CodeMirrorSize[0] * 2, CodeMirrorSize[1] * 2);
            } else {
                ele.title = '放大';
                ele.innerHTML = expandSvg;
                cm.setSize(CodeMirrorSize[0], CodeMirrorSize[1]);
            }
        });
        resize.click();

        var inputFiles = root.querySelector("#inputFiles");
        inputFiles.addEventListener("change", (e) => {
            var file = e.target.files[0];
            if (!file) return;
            var reader = new FileReader();
            reader.readAsText(file);
            reader.onload = function (e) {
                var siteSettings = JSON.parse(this.result);
                addSiteSettings(siteSettings);
            }
        });
        root.querySelector("#focus").addEventListener("click", (e) => {
            getClickedElementSelector();
        });
        root.querySelector("#importSiteSetting").addEventListener("click", (e) => {
            inputFiles.click();
        });

        root.querySelector("#exportSiteSetting").addEventListener("click", (e) => {
            var names = GM_listValues();
            var siteSettings = {};
            for (var name of names) {
                if (name == 'ReaderStyle') continue;
                siteSettings[name] = GM_getValue(name);
            }
            var str = JSON.stringify(siteSettings, null, 2);
            var blob = new Blob([str], { type: "text/plain;charset=utf-8", endings: "native" });
            downloadFile(blob, "AllSiteSetting.txt")
        });
        root.querySelector("#clearAllSiteSetting").addEventListener("click", (e) => {
            var names = GM_listValues();
            for (var name of names) {
                if (name == 'ReaderStyle') continue;
                GM_deleteValue(name);
            }
            alert("已删除现有全部网站配置信息");
        });
        root.querySelector("#saveSiteSetting").addEventListener("click", (e) => {
            saveSiteSetting();
        });
        var container = root.querySelector("#container")
        container.style.height = '100vh';
        root.querySelector("#toggle").addEventListener("click", (e) => {
            if (e.target.textContent == '﹀') {
                e.target.textContent = '︿';
                var next=e.target.parentElement.nextElementSibling;
                while(next){
                    next.style.display = '';
                    next=next.nextElementSibling;
                }
                container.style.height = '100vh';
            } else {
                e.target.textContent = '﹀';
                next=e.target.parentElement.nextElementSibling;
                while(next){
                    next.style.display = 'none';
                    next=next.nextElementSibling;
                }
                container.style.height = '';
            }
        });
        root.querySelector("#DeleteNoText").onclick = () => {
            for (let i = chapters.length - 1; i >= 0; i--) {
                if (!chapters[i].text) {
                    chapters.splice(i, 1);
                }
            }
            chaptersIndex = null;
            sortList(root.querySelector("#sort").value);
        };
        root.querySelector("#clearList").onclick = () => {
            root.querySelector("table tbody").innerHTML = "";
            chapters = [];
            chaptersIndex = [];
        };
        root.querySelector("#getAllText").onclick = () => {
            setting = getSetting();
            if (typeof setting == 'object') getAllText();
        };
    }
    GM_registerMenuCommand("开始", addDiv);
})(); //addDiv