随手小说下载

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

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==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