您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
生成分卷和全本ePub文档
当前为
// ==UserScript== // @name 轻小说文库下载 // @namespace wenku8Haoa // @version 2.1.3 // @description 生成分卷和全本ePub文档 // @author HaoaW // @match https://www.wenku8.net/* // @match http://www.wenku8.net/* // @connect dl.wenku8.com // @connect picture.wenku8.com // @connect img.wenku8.com // @connect pic.wenku8.com // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js // @require https://cdn.bootcdn.net/ajax/libs/jszip/2.6.1/jszip.js // @require https://cdn.bootcdn.net/ajax/libs/FileSaver.js/2.0.5/FileSaver.js // @icon https://www.wenku8.net/favicon.ico // @grant GM_xmlhttpRequest // ==/UserScript== (function () { 'use strict'; let hrefUrl = new URL(window.location.href); const ePubEidterCfgUID = "24A08AE1-E132-458C-9E1D-6C998F16A666"; const ImgLocationFile = "ImgLocation"; //epub生成;使用addEventListener绑定; function EpubBuilder(e) { let bInfo = { Domain: "www.wenku8.net", dlDomain: "dl.wenku8.com", aid: article_id,//本书编号 article_id title: document.getElementById("title").childNodes[0].textContent, //标题 creator: document.getElementById('info').innerText,//作者 bookUrl: self.location.href, XHRArr: [],//下载请求;[{start:false,done:false,url:,loadFun:,data:,bookInfo:bInfo}] SpanFix: "Txt",//文字ID前缀 loadVolume: (xhr) => { xhr.start = true; let navToc = xhr.bookInfo.nav_toc[xhr.VolumeIndex]; GM_xmlhttpRequest({ method: 'GET', url: xhr.url, onload: function (response) { //xhr.VolumeIndex卷号 let chapterIndex = 0; let ImagesIndex = 0; let TextIndex = 0; let Text = xhr.bookInfo.Text[xhr.VolumeIndex]; if (response.status == 200) { //https://developer.mozilla.org/zh-CN/docs/Web/Guide/Parsing_and_serializing_XML //下载分卷文本,转换为html let domParser = new DOMParser(); let rspHtml = domParser.parseFromString( `<html> <head> <meta charset="utf-8"/> <title>${navToc.volumeName}</title> <link href="../Styles/default.css" rel="stylesheet" type="text/css"/> </head> <body><div class="volumetitle"><h2>${navToc.volumeName}</h2></div><br /></body> </html>`, "text/html"); rspHtml.body.innerHTML += response.responseText; //调用自带简转繁 if (currentEncoding != targetEncoding) { translateBody(rspHtml.body); } let removeChild = []; //处理章节、插图 和 contentdp for (let i = 0; i < rspHtml.body.children.length; i++) { let child = rspHtml.body.children[i]; if ("UL" == child.tagName && "contentdp" == child.id) { removeChild.push(child); } //章节 else if ("DIV" == child.tagName && "chaptertitle" == child.className) { chapterIndex++; //章节h3、分卷h2、书名h1(没有做) //<div class="chaptertitle"><div id="chapter_1" name="xxx"><h3>第一章</h3></div></div> let cTitle = child.innerText; let aName = child.firstChild.getAttribute("name"); let divEle = document.createElement("div"); divEle.id = `chapter_${chapterIndex}`; divEle.setAttribute("name", aName); divEle.innerHTML = `<h3>${cTitle}</h3>`; child.innerHTML = divEle.outerHTML; //添加章节导航 navToc.chapterArr.push({ chapterName: cTitle , chapterID: divEle.id , chapterHref: `${navToc.volumeHref}#${divEle.id}` }); //章节名接受拖放 let txtSpan = rspHtml.createElement("span"); txtSpan.id = `${xhr.bookInfo.SpanFix}_${divEle.id}`; txtSpan.className = "txtDropEnable"; txtSpan.setAttribute("ondragover", "return false"); child.parentElement.insertBefore(txtSpan, child); txtSpan.appendChild(child); } //内容 else if ("DIV" == child.tagName && "chaptercontent" == child.className) { for (let j = 0; j < child.childNodes.length; j++) { let contentChild = child.childNodes[j]; //文字 if (Node.TEXT_NODE == contentChild.nodeType && contentChild.textContent != '\n') { TextIndex++; let txtSpan = rspHtml.createElement("span"); txtSpan.id = `${xhr.bookInfo.SpanFix}_${xhr.VolumeIndex}_${TextIndex}`; txtSpan.className = "txtDropEnable"; txtSpan.setAttribute("ondragover", "return false"); child.insertBefore(txtSpan, contentChild); txtSpan.appendChild(contentChild); } //插图 else if ("DIV" == contentChild.tagName && "divimage" == contentChild.className) {//插图 //取得插图下载地址 let imgASrc = contentChild.getAttribute("title"); let imgUrl = new URL(imgASrc); let imgPath = `Images${imgUrl.pathname}`; //在html中加入img标签 let imgEle = document.createElement("img"); imgEle.setAttribute("loading", "lazy"); imgEle.setAttribute("src", `../${imgPath}`); contentChild.innerHTML = imgEle.outerHTML; //记录图片信息作为epub资源 ImagesIndex++; let ImagesID = `${xhr.bookInfo.ImagesFix}_${xhr.VolumeIndex}_${ImagesIndex}`; let images = { path: `${imgPath}`, content: null, id: ImagesID, idName: contentChild.id, TextId: Text.id }; //封面候补 第一卷的前两张图,高/宽 > 1 if (0 == xhr.VolumeIndex && 3 > ImagesIndex) { images.coverImgChk = true; } xhr.bookInfo.Images.push(images); //添加图片下载xhr请求 let xhrImg = { start: false, done: false, url: imgASrc, loadFun: xhr.bookInfo.loadImg, images: images, bookInfo: xhr.bookInfo }; xhr.bookInfo.XHRArr.push(xhrImg); xhrImg.loadFun(xhrImg); } } } } removeChild.forEach(c => rspHtml.body.removeChild(c)); Text.volumeName = navToc.volumeName; Text.content = rspHtml.body.innerHTML; //生成epub,还有资源未下载则只更新生成进度 xhr.done = true; xhr.bookInfo.buildEpub(xhr.bookInfo); } else { //重新下载 xhr.bookInfo.refreshProgress(xhr.bookInfo, `${navToc.volumeName} 下载失败,重新下载;`); xhr.loadFun(xhr); } }, onerror: () => { //重新下载 xhr.bookInfo.refreshProgress(xhr.bookInfo, `${navToc.volumeName} 下载失败,重新下载;`); xhr.loadFun(xhr); } }); },//分卷下载 loadImg: (xhr) => { xhr.start = true; GM_xmlhttpRequest({ method: 'GET', url: xhr.url, responseType: "arraybuffer", onload: function (response) { if (response.status == 200) { xhr.images.content = response.response; if (xhr.images.coverImgChk && (!xhr.bookInfo.Images.find(i => i.coverImg))) { xhr.images.Blob = new Blob([xhr.images.content], { type: "image/jpeg" }); xhr.images.ObjectURL = URL.createObjectURL(xhr.images.Blob); let imgEle = new Image(); imgEle.onload = () => { //高比宽大于1就能做封面 xhr.images.coverImg = (imgEle.naturalHeight / imgEle.naturalWidth > 1); xhr.done = true; xhr.bookInfo.buildEpub(xhr.bookInfo); }; imgEle.src = xhr.images.ObjectURL; } else { xhr.done = true; xhr.bookInfo.buildEpub(xhr.bookInfo); } } else { //重新下载 xhr.bookInfo.refreshProgress(xhr.bookInfo, `${xhr.images.idName} 下载失败,重新下载;`); xhr.loadFun(xhr); } }, onerror: () => { //重新下载 xhr.bookInfo.refreshProgress(xhr.bookInfo, `${xhr.images.idName} 下载失败,重新下载;`); xhr.loadFun(xhr); } }); },//图片下载 nav_toc: [],//导航菜单,第一层分卷,第二层章节{volumeName:,volumeID:,volumeHref:,chapterArr:[{chapterName:,chapterID:,chapterHref:}]} VolumeFix: "Volume",//分卷文件、ID前缀 Text: [],//下载后生成的XHTML;{path:`Text/${volumeHref}`,content:} ImagesFix: "Img",//图片文件、ID前缀 Images: [],//下载的图片;{path:`Images/${url.pathname}`,content:} buildEpub: (info) => { if (info.XHRArr.every(e => e.done)) { if (info.ePubEidt && (!info.ePubEidtDone)) { info.refreshProgress(info, `开始编辑ePub;`); info.ePubEidterInit(info); return; } info.refreshProgress(info, `开始生成ePub;`); let zip = new JSZip(); //epub固定内容 zip.file("mimetype", info.mimetype); zip.file("META-INF/container.xml", info.container_xml); //todo: new DOMParser(); 操作xml let manifest = document.createElement("manifest"); let spine = document.createElement("spine"); //保存插图位置 if (info.ePubEidterCfg.ImgLocation && 0 < info.ePubEidterCfg.ImgLocation.length) { let cfgJson = JSON.stringify(info.ePubEidterCfg, null, " "); zip.file("OEBPS/Other/ePubEidterCfg.json", cfgJson); } //保存css { let item = document.createElement("item"); item.setAttribute("id", info.defaultCSS.id); item.setAttribute("href", info.defaultCSS.path); item.setAttribute("media-type", "text/css"); manifest.appendChild(item); //保存css zip.file(`OEBPS/${info.defaultCSS.path}`, info.defaultCSS.content); } //生成并保存nav.xhtml //<ol><li><a href="Volume_0.xhtml">第一卷</a><ol><li><a href="Volume_0.xhtml#chapter_1">第一章</a></li></ol></li></ol> { //生成nav.xhtml let domParser = new DOMParser(); let navXhtmlDoc = domParser.parseFromString(info.nav_xhtml.content, "application/xhtml+xml"); let tocEle = navXhtmlDoc.getElementById("toc"); let bOlEle = navXhtmlDoc.createElement("ol"); for (let t of info.nav_toc) { //分卷 let vAEle = navXhtmlDoc.createElement("a"); vAEle.href = t.volumeHref; vAEle.innerText = t.volumeName; let vLiEle = navXhtmlDoc.createElement("li"); vLiEle.appendChild(vAEle); if (t.chapterArr && 0 < t.chapterArr.length) { //分卷的章节 let vOlEle = navXhtmlDoc.createElement("ol"); for (let c of t.chapterArr) { let cAEle = navXhtmlDoc.createElement("a"); cAEle.href = c.chapterHref; cAEle.innerText = c.chapterName; let cLiEle = navXhtmlDoc.createElement("li"); cLiEle.appendChild(cAEle); vOlEle.appendChild(cLiEle); } vLiEle.appendChild(vOlEle); } bOlEle.appendChild(vLiEle); } tocEle.appendChild(bOlEle); let nav_xhtml = `<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html> ${navXhtmlDoc.firstChild.outerHTML}`; //保存nav.xhtml信息到content.opf //manifest节点 let item = document.createElement("item"); item.setAttribute("id", info.nav_xhtml.id); item.setAttribute("href", info.nav_xhtml.path); item.setAttribute("media-type", "application/xhtml+xml"); item.setAttribute("properties", "nav"); manifest.appendChild(item); //spine节点 let itemref = document.createElement("itemref"); itemref.setAttribute("idref", info.nav_xhtml.id); itemref.setAttribute("linear", "no"); spine.appendChild(itemref); //保存nav.xhtml zip.file(`OEBPS/${info.nav_xhtml.path}`, nav_xhtml); } //保存分卷内容 for (let t of info.Text) { let item = document.createElement("item"); item.setAttribute("media-type", "application/xhtml+xml"); item.setAttribute("href", t.path); item.setAttribute("id", t.id); manifest.appendChild(item); let itemref = document.createElement("itemref"); itemref.setAttribute("idref", t.id); spine.appendChild(itemref); //转换html为xhtml let domParser = new DOMParser(); let rspHtml = domParser.parseFromString( `<html> <head> <meta charset="utf-8"/> <title>${t.volumeName}</title> <link href="../Styles/default.css" rel="stylesheet" type="text/css"/> </head> <body></body> </html>` , "text/html"); rspHtml.body.innerHTML = t.content; //添加插图并去除拖放标签 let vLocation = info.ePubEidterImgLocation.filter(i => t.vid == i.vid); let volumeImgs = info.Images.filter(i => i.TextId == t.id); let dropEleArr = rspHtml.body.getElementsByClassName("txtDropEnable"); let dropEle; while ((dropEle = dropEleArr[0])) { //加载已拖放的图片 let locArr; let dImg; if (vLocation && (locArr = vLocation.filter(j => j.spanID == dropEle.id)) ) { for (let loc of locArr) { if (dImg = volumeImgs.find(j => j.idName == loc.imgID)){ let divimage = rspHtml.createElement("div"); divimage.className = "divimage"; let imgEle = rspHtml.createElement("img"); imgEle.setAttribute("loading", "lazy"); imgEle.setAttribute("src", `../${dImg.path}`); divimage.innerHTML = imgEle.outerHTML; dropEle.parentNode.insertBefore(divimage, dropEle); } } } //去除文字span dropEle.parentNode.insertBefore(dropEle.firstChild, dropEle); dropEle.parentNode.removeChild(dropEle); } //转换html为xhtml let xmlSerializer = new XMLSerializer(); let rspXml = xmlSerializer.serializeToString(rspHtml); let rspXHtml = domParser.parseFromString(rspXml, "application/xhtml+xml"); rspXHtml.firstChild.setAttribute("xmlns:epub", "http://www.idpf.org/2007/ops"); //保存章节内容,作为epub资源 let tContent = `<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html> ${rspXHtml.firstChild.outerHTML}`; zip.file(`OEBPS/${t.path}`, tContent); } //保存图片 for (let t of info.Images) { let itemImg = document.createElement("item"); //media-type暂固定jpeg itemImg.setAttribute("media-type", "image/jpeg"); itemImg.setAttribute("href", t.path); itemImg.setAttribute("id", t.id); manifest.appendChild(itemImg); zip.file(`OEBPS/${t.path}`, t.content, { binary: true }); } //生成书籍信息 let coverMeta = ''; if (info.Images.length > 0) { let coverImg = info.Images.find(i => i.coverImg); if (!coverImg) { coverImg = info.Images.find(i => i.coverImgChk); } if (coverImg) { coverMeta = `<meta name="cover" content="${coverImg.id}" />`; } } let uuid = self.crypto.randomUUID(); let createTime = new Date(); let content_opf = `<?xml version="1.0" encoding="utf-8"?> <package version="3.0" unique-identifier="BookId" xmlns="http://www.idpf.org/2007/opf"> <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf"> <dc:language>zh-CN</dc:language> <dc:title>${bInfo.title}</dc:title> <meta property="dcterms:modified">${createTime.toISOString()}</meta> <dc:identifier id="BookId">urn:uuid:${uuid}</dc:identifier> <dc:creator>${info.creator}</dc:creator> <!--第一张插图做封面--> ${coverMeta} </metadata> <manifest> ${manifest.innerHTML} </manifest> <spine> ${spine.innerHTML} </spine> </package>`; zip.file("OEBPS/content.opf", content_opf); //书名.开始卷-结束卷.epub let epubName = `${bInfo.title}.${info.nav_toc[0].volumeName}`; if (1 < info.nav_toc.length) { epubName = epubName + '-' + info.nav_toc[info.nav_toc.length - 1].volumeName; } saveAs(zip.generate({ type: "blob", mimeType:"application/epub+zip" }), `${epubName}.epub`); info.refreshProgress(info, `ePub生成完成,文件名:${epubName}.epub;`); } else { info.refreshProgress(info); } },//生成epub,如果XHRArr已经都完成了 mimetype: 'application/epub+zip',//epub mimetype 文件内容 container_xml: `<?xml version="1.0" ?> <container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container"> <rootfiles> <rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml" /> </rootfiles> </container>`,//epub container.xml 文件内容 nav_xhtml: { content: ` <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="en" xml:lang="en"> <head> <title>ePub NAV</title> <meta charset="utf-8"/> <link href="../Styles/default.css" rel="stylesheet" type="text/css"/> </head> <body epub:type="frontmatter"> <nav epub:type="toc" id="toc" role="doc-toc"> <h2>目录</h2> </nav> </body> </html>` , path: `Text/nav.xhtml` , id: `nav_xhtml_id` },//epub nav.xhtml 文件模板 defaultCSS: { content: ` nav#landmarks { display:none; } nav#page-list { display:none; } ol { list-style-type: none; } .volumetitle , .chaptertitle { text-align: center; } ` , id: "default_css_id" , path: "Styles/default.css" },//epub default.css 样式文件 progressEle: null,//进度、日志 元素;{txt:,img:,err:} refreshProgress: (info, err) => { if (!info.progressEle) { //epub生成进度,下载文本进度:7/7;下载图片进度:77/77;日志:开始生成ePub;ePub生成完成; info.progressEle = {}; info.progressEle.txt = document.createElement("span"); info.progressEle.img = document.createElement("span"); info.progressEle.err = document.createElement("span"); let logDiv = document.createElement('div'); logDiv.appendChild(document.createTextNode("epub生成进度,下载文本进度:")); logDiv.appendChild(info.progressEle.txt); logDiv.appendChild(document.createTextNode(";下载图片进度:")); logDiv.appendChild(info.progressEle.img); logDiv.appendChild(document.createTextNode(";日志:")); logDiv.appendChild(info.progressEle.err); document.body.insertBefore(logDiv, document.getElementById('title')); } //日志 if (err) { info.progressEle.err.innerText = err + info.progressEle.err.innerText; } //文本进度 let txtProgress = info.Text.filter((value, key, arr) => { return value.content; }).length; info.progressEle.txt.innerText = `${txtProgress}/${info.Text.length}`; //图片进度,文本下载完成后才能得到图片总数 if (txtProgress == info.Text.length) { let imgProgress = info.Images.filter((value, key, arr) => { return value.content; }).length; info.progressEle.img.innerText = `${imgProgress}/${info.Images.length}`; } },//显示进度日志 start: (e) => { let ePubEidt = e.target.getAttribute("ePubEidt"); if (ePubEidt && "true" == ePubEidt) { bInfo.ePubEidt = true; } //加载从评论读取的配置 if (bInfo.ePubEidterCfgRef && 0 < bInfo.ePubEidterCfgRef.length) { for (let cfgRef of bInfo.ePubEidterCfgRef) { if (ePubEidterCfgUID == cfgRef.UID && bInfo.aid == cfgRef.aid && cfgRef.ImgLocation && 0 < cfgRef.ImgLocation.length ) { for (let loc of cfgRef.ImgLocation) { //插图位置记录{vid:,spanID:,imgID:} if (loc.vid && loc.spanID && loc.imgID) { if (!bInfo.ePubEidterImgLocation.find(f => f.vid == loc.vid && f.spanID == loc.spanID && f.imgID == loc.imgID )) { bInfo.ePubEidterImgLocation.push(loc); } } } } } } if (bInfo.ePubEidterImgLocation && 0 < bInfo.ePubEidterImgLocation.length) { bInfo.ePubEidterCfg.ImgLocation = bInfo.ePubEidterImgLocation; } //全本分卷 let vcssEle = null; let DownloadAll = e.target.getAttribute("DownloadAll"); if (DownloadAll && "true" == DownloadAll) { //全本下载 vcssEle = document.getElementsByClassName("vcss"); } else { vcssEle = [e.target.parentElement]; } for (let VolumeIndex = 0; VolumeIndex < vcssEle.length; VolumeIndex++) { let vcss = vcssEle[VolumeIndex]; let vid = vcss.parentElement.nextElementSibling.getElementsByTagName('a')[0].getAttribute('href').split('.')[0];; //let vid = vcss.getAttribute("vid"); let vcssText = vcss.childNodes[0].textContent; let navToc = bInfo.nav_toc[VolumeIndex] = { volumeName: vcssText , vid: vid , volumeID: `${bInfo.VolumeFix}_${VolumeIndex}` , volumeHref: `${bInfo.VolumeFix}_${VolumeIndex}.xhtml` , chapterArr: [] }; bInfo.Text[VolumeIndex] = { path: `Text/${navToc.volumeHref}` , content: "" , id: navToc.volumeID , vid: vid }; //分卷下载链接 let dlink = `https://${bInfo.dlDomain}/pack.php?aid=${bInfo.aid}&vid=${vid}`; let xhr = { start: false, done: false, url: dlink, loadFun: bInfo.loadVolume, VolumeIndex: VolumeIndex, data: { vid: vid, vcssText: vcssText }, bookInfo: bInfo }; bInfo.XHRArr.push(xhr); xhr.loadFun(xhr); } bInfo.buildEpub(bInfo); },//入口,开始下载文件并生成epub; novelTable: null, ePubEidterCfgRef: ePubEidterCfgRef,//读取到的配置 ePubEidterCfg: { UID: ePubEidterCfgUID, aid: article_id, pathname: hrefUrl.pathname, ImgLocation: null },//插图位置配置 ePubEidtImgRegExp: [/img/i, /插图/i, /插圖/i, /\.jpg/i, /\.png/i],//推测插图位置的正则 ePubEidtLink: [], ePubEidt: false, ePubEidtDone: false, ePubEidterHtml: ePubEidterHtml,//编辑器html代码 ePubEidter: null, ePubEidterImgLocation: [],//插图位置记录{vid:,spanID:,imgID:} ePubEidterLastVolumeUL:null, ePubEidterInit: (info) => { //隐藏目录 let downloadEleArr = document.getElementsByClassName("DownloadAll"); for (let f of downloadEleArr) { f.style.pointerEvents = "none"; } info.novelTable = document.body.getElementsByTagName("table")[0]; info.novelTable.style.display = "none"; //加载编辑器、样式 let linkEle = document.createElement("link"); linkEle.type = "text/css"; linkEle.rel = "stylesheet"; linkEle.href = "/themes/wenku8/style.css"; document.head.appendChild(linkEle); info.ePubEidtLink.push(linkEle); let divEle = document.createElement("div"); divEle.id = "ePubEidter"; divEle.style.display = "none"; linkEle.onload = () => { //显示编辑器 divEle.style.display = ""; }; divEle.innerHTML = info.ePubEidterHtml; info.ePubEidter = divEle; info.novelTable.parentElement.insertBefore(divEle, info.novelTable); document.getElementById("EidterBuildBtn").addEventListener("click", info.ePubEidterDoneFun(info)); document.getElementById("EidterImportBtn").addEventListener("click", info.ePubEidterImportCfgFun(info)); document.getElementById("VolumeImg").addEventListener("drop", info.ePubEidterImgDelDropFun(info)); //加载配置内容 if (info.ePubEidterCfg && info.ePubEidterCfg.ImgLocation && 0 < info.ePubEidterCfg.ImgLocation.length) { let cfgAreaEle = document.getElementById("CfgArea"); cfgAreaEle.value = JSON.stringify(info.ePubEidterCfg, null, " "); } //加载分卷列表 let liEleFirst = null; let VolumeULEle = document.getElementById("VolumeUL"); VolumeULEle.innerHTML = ""; for (let i = 0; i < info.Text.length; i++) { let text = info.Text[i]; let liEle = document.createElement("li"); VolumeULEle.appendChild(liEle); let aEle = document.createElement("a"); liEle.appendChild(aEle); aEle.href = "javascript:void(0);"; aEle.id = text.id; aEle.innerText = text.volumeName; liEle.addEventListener("click", info.ePubEidterVolumeULFun(info, text)); if (!liEleFirst) { liEleFirst = liEle; } } //加载第一卷 if (liEleFirst) { liEleFirst.click(); } },//编辑器初始化 ePubEidterDestroyer: (info) => { info.ePubEidter.parentElement.removeChild(info.ePubEidter); info.ePubEidtLink.forEach(f => f.parentElement.removeChild(f)); info.novelTable = document.body.getElementsByTagName("table")[0]; info.novelTable.style.display = ""; info = null; let downloadEleArr = document.getElementsByClassName("DownloadAll"); for (let f of downloadEleArr) { f.style.pointerEvents = "auto"; } },//编辑器销毁 ePubEidterDoneFun: (info) => { return () => { //生成ePub info.ePubEidtDone = true; info.buildEpub(info); //发送配置 let sendArticleEle = document.getElementById('SendArticle'); if (sendArticleEle.checked && 0 < info.ePubEidterImgLocation.length) { let cfgObj = Object.assign({}, info.ePubEidterCfg); //压缩位置 let imgLocJson = JSON.stringify(cfgObj.ImgLocation); let zip = new JSZip(); zip.file(ImgLocationFile, imgLocJson, { compression: "DEFLATE", compressionOptions: { level: 9 } }); let imgLocBase64 = zip.generate({ type: "base64", mimeType: "application/zip" }); cfgObj.ImgLocation = null; cfgObj.ImgLocationBase64 = imgLocBase64; let cfgJson = JSON.stringify(cfgObj); let vidSet = new Set(); let vName = []; for (let loc of info.ePubEidterImgLocation) { if (!vidSet.has(loc.vid)) { vidSet.add(loc.vid); let nToc = info.nav_toc.find(f => loc.vid == f.vid); if (nToc) { vName.push(nToc.volumeName); } } } let pcontent = `包含分卷列表:${vName} [code]${cfgJson}[/code]`; let map = new Map(); map.set("ptitle", "ePub插图位置"); map.set("pcontent", pcontent); let url = `https://${info.Domain}/modules/article/reviews.php?aid=${info.aid}`; //发送配置 info.ePubEidterSend(info, url, map); } let ePubEditerClose = document.getElementById('ePubEditerClose'); if (ePubEditerClose.checked) { info.ePubEidterDestroyer(info); } }; },//点击生成ePub事件 ePubEidterImportCfgFun: (info) => { return () => { let cfgAreaEle = document.getElementById("CfgArea"); let impCfg = JSON.parse(cfgAreaEle.value); if (impCfg && impCfg.UID == info.ePubEidterCfg.UID && impCfg.aid == info.ePubEidterCfg.aid && impCfg.ImgLocation && 0 < impCfg.ImgLocation.length ) { for (let iCfg of impCfg.ImgLocation) { if (info.ePubEidterImgLocation.find(i => i.spanID == iCfg.spanID && i.vid == iCfg.vid && i.imgID == iCfg.imgID) ) { continue; } else { info.ePubEidterImgLocation.push(iCfg); } } } info.ePubEidterCfg.ImgLocation = info.ePubEidterImgLocation; cfgAreaEle.value = JSON.stringify(info.ePubEidterCfg, null, " "); if (info.ePubEidterLastVolumeUL) { info.ePubEidterLastVolumeUL.click(); } }; },//点击导入配置事件 ePubEidterVolumeULFun: (info, text) => { return (ev) => { //最后点击的章节列表,导入配置后刷新 if (info.ePubEidterLastVolumeUL) { info.ePubEidterLastVolumeUL.firstElementChild.style.color = ""; } info.ePubEidterLastVolumeUL = ev.currentTarget; info.ePubEidterLastVolumeUL.firstElementChild.style.color = "fuchsia"; //加载文本内容 let VolumeTextEle = document.getElementById("VolumeText"); VolumeTextEle.innerHTML = text.content; //加载图片列表 let imgEleMap = new Map(); let VolumeImgEle = document.getElementById("VolumeImg"); VolumeImgEle.innerHTML = ""; let volumeImgs = info.Images.filter(i => i.TextId == text.id); for (let image of volumeImgs) { if (!image.ObjectURL) { image.Blob = new Blob([image.content], { type: "image/jpeg" }); image.ObjectURL = URL.createObjectURL(image.Blob); } let imgDivEle = document.createElement("div"); imgDivEle.style.float = "left"; imgDivEle.style.textAlign = "center"; imgDivEle.style.height = "155px"; imgDivEle.style.overflow = "hidden"; imgDivEle.style.margin = "0 2px"; VolumeImgEle.appendChild(imgDivEle); let imgEle = document.createElement("img"); imgEle.setAttribute("imgID", image.idName); imgEle.setAttribute("loading","lazy"); imgEle.src = image.ObjectURL; imgEle.height = 127; //加载用 imgEleMap.set(image.idName, imgEle); imgDivEle.appendChild(imgEle); imgDivEle.appendChild(document.createElement("br")); let imgTextEle = new Text(image.id) imgDivEle.appendChild(imgTextEle); //<div style="float: left; text-align: center; height: 155px; overflow: hidden; margin: 0 2px;"> // <img id="Img_160408" src="./160408.jpg" border="0" height="127"><br> //</div> } //推测插图处置 let ImgULEle = document.getElementById("ImgUL"); ImgULEle.innerHTML = ""; //加载已拖放的图片 let vLocation = info.ePubEidterImgLocation.filter(i => text.vid == i.vid); //拖放处理绑定 let dropEleArr = document.getElementsByClassName("txtDropEnable"); for (let dropEle of dropEleArr) { dropEle.addEventListener("drop", info.ePubEidterImgDropFun(info, text)); //加载已拖放的图片 let locArr; let dImgEle; if (vLocation && (locArr = vLocation.filter(j => j.spanID == dropEle.id))) { for (let loc of locArr) { if (dImgEle = imgEleMap.get(loc.imgID)) { let divimage = document.createElement("div"); divimage.className = "divimageM"; divimage.innerHTML = dImgEle.outerHTML; dropEle.parentNode.insertBefore(divimage, dropEle); //添加拖放开始事件,用于删除拖放的标签 let dropImg = divimage.firstChild; dropImg.id = `${loc.spanID}_${loc.imgID}`; dropImg.addEventListener("dragstart", info.ePubEidterImgDelStartFun(info, loc)); } } } //章节名不测试 if (!dropEle.firstElementChild || "chaptertitle" != dropEle.firstElementChild.className ) { //匹配插图正则 for (let reg of info.ePubEidtImgRegExp) { if (reg.test(dropEle.innerText)) { let liEle = document.createElement("li"); ImgULEle.appendChild(liEle); let aEle = document.createElement("a"); liEle.appendChild(aEle); aEle.href = "javascript:void(0);"; aEle.setAttribute("SpanID", dropEle.id); aEle.innerText = dropEle.innerText.replace(/\s/g, '').substring(0, 12); liEle.addEventListener("click", info.ePubEidterImgULFun(info, dropEle)); dropEle.style.color = "fuchsia";//fontWeight = "bold"; break; } } } } //加载章节列表 let ChapterULEle = document.getElementById("ChapterUL"); ChapterULEle.innerHTML = ""; let toc = info.nav_toc.find(i => i.volumeID == text.id); for (let chapter of toc.chapterArr) { let liEle = document.createElement("li"); ChapterULEle.appendChild(liEle); let aEle = document.createElement("a"); liEle.appendChild(aEle); aEle.href = "javascript:void(0);"; aEle.setAttribute("chapterID", chapter.chapterID); aEle.innerText = chapter.chapterName; liEle.addEventListener("click", info.ePubEidterChapterULFun(info, chapter)); } //滚动到分卷开始 VolumeTextEle.scroll({ top: 0 }); VolumeImgEle.scroll({ top: 0 }); }; },//点击分卷事件 ePubEidterImgDropFun: (info, text) => { return (ev) => { const data = ev.dataTransfer.getData("text/html"); let divimage = document.createElement("div"); divimage.className = "divimageM"; divimage.innerHTML = data; let dropImg = divimage.firstChild; let imgLocation = { "vid": text.vid, "spanID": ev.currentTarget.id, "imgID": dropImg.getAttribute("imgID") }; if (info.ePubEidterImgLocation.find(i => i.spanID == imgLocation.spanID && i.vid == imgLocation.vid && i.imgID == imgLocation.imgID) ) { alert("此位置已存在相同的图片"); } else { ev.currentTarget.parentNode.insertBefore(divimage, ev.currentTarget); info.ePubEidterImgLocation.push(imgLocation); //添加拖放开始事件,用于删除拖放的标签 dropImg.id = `${imgLocation.spanID}_${imgLocation.imgID}`; dropImg.addEventListener("dragstart", info.ePubEidterImgDelStartFun(info, imgLocation)); info.ePubEidterCfg.ImgLocation = info.ePubEidterImgLocation; let cfgAreaEle = document.getElementById("CfgArea"); cfgAreaEle.value = JSON.stringify(info.ePubEidterCfg, null, " "); //JSON.parse(cfgAreaEle.value); } } },//插图拖放完成事件 ePubEidterChapterULFun: (info, chapter) => { return (ev) => { let VolumeTextEle = document.getElementById("VolumeText"); let target = document.getElementById(chapter.chapterID); VolumeTextEle.scroll({ top: target.offsetTop, behavior: 'smooth' }); //(document.getElementById(chapter.chapterID)).scrollIntoView(); } },//点击章节事件 ePubEidterImgULFun: (info, dropEle) => { return (ev) => { let VolumeTextEle = document.getElementById("VolumeText"); VolumeTextEle.scroll({ top: dropEle.offsetTop - 50, behavior: 'smooth' }); } },//点击推测插图位置事件 ePubEidterSend: (info, url, map) => { let iframeEle = document.createElement("iframe"); iframeEle.style.display = 'none'; document.body.appendChild(iframeEle); let iBodyEle = iframeEle.contentWindow.document.body; let iDocument = iframeEle.contentWindow.document; let formEle = iDocument.createElement("form"); formEle.acceptCharset = "gbk"; formEle.method = "POST"; formEle.action = url; iBodyEle.appendChild(formEle); for (let [mk, mv] of map) { let inputEle = iDocument.createElement("input"); inputEle.type = "text"; inputEle.name = mk; inputEle.value = mv; formEle.appendChild(inputEle); } let subEle = iDocument.createElement("input"); subEle.type = "submit"; subEle.name = "submit"; subEle.value = "submit"; formEle.appendChild(subEle); subEle.click(); },//发送Post请求,无需转gbk ePubEidterImgDelDropFun: (info) => { return (ev) => { let vid = ev.dataTransfer.getData("vid"); let spanID = ev.dataTransfer.getData("spanID"); let imgID = ev.dataTransfer.getData("imgID"); let fromID = ev.dataTransfer.getData("fromID"); let fromEle = document.getElementById(fromID); if (fromEle && "divimageM" == fromEle.parentElement.className) { info.ePubEidterImgLocation = info.ePubEidterImgLocation.filter(i => !(i.spanID == spanID && i.vid == vid && i.imgID == imgID)); info.ePubEidterCfg.ImgLocation = info.ePubEidterImgLocation; let cfgAreaEle = document.getElementById("CfgArea"); cfgAreaEle.value = JSON.stringify(info.ePubEidterCfg, null, " "); fromEle.parentElement.parentElement.removeChild(fromEle.parentElement); } } },//插图拖放完成事件 ePubEidterImgDelStartFun: (info, imgLocation) => { return (ev) => { ev.dataTransfer.setData("vid", imgLocation.vid); ev.dataTransfer.setData("spanID", imgLocation.spanID); ev.dataTransfer.setData("imgID", imgLocation.imgID); ev.dataTransfer.setData("fromID", ev.srcElement.id); } },//插图拖放开始事件 }; bInfo.start(e); }; //替换所有 String.prototype.replaceAll = function (exp, newStr) { return this.replace(new RegExp(exp, "gm"), newStr); }; //标记含有html代码的行 var htmlLine = function (txt) { return 'c1ef6520' + txt + '1a5219e9b2b0'; }; //格式化内容 var contentFormat = function (txt) { //设置章节名样式 let txtTmp = txt.replaceAll(/^ {2}\S+.*$/, function (match) { return htmlLine('<br/><div style="background:#e4e1d8" id="title">' + match.trim() + '</div>'); } ); txtTmp = $('<div></div>').text(txtTmp).html(); txtTmp = txtTmp.replaceAll(/ /, ' '); txtTmp = txtTmp.replaceAll(/(c1ef6520).*(1a5219e9b2b0)/, function (match) { return $('<div></div>').html(match.replaceAll(/( )/, ' ').replace('c1ef6520', '').replace('1a5219e9b2b0', '')).text(); } ); txtTmp = txtTmp.replaceAll(/[\r\n]+/, '<br/>'); return txtTmp; }; //格式化图片 var imgFormat = function (txt) { let imgResultRoot = $('<div></div>'); let imgRoot = $('<div></div>').html(txt); $('.divimage', imgRoot).each(function () { let jqthis = $(this); let imgURL = jqthis.attr('title');//取得图片url let itemEle = $('<div class="divimage"><a target="_blank"><img border="0" class="imagecontent" /></a></div>'); $('a', itemEle).attr('href', imgURL); $('img', itemEle).attr('src', imgURL); imgResultRoot.append(itemEle); }); return imgResultRoot.html(); } //格式化从pack.php下载的带有Html的内容 var htmlFormat = function (htmlStr) { let resultRoot = $('<div></div>'); let strRoot = $('<div></div>').html(htmlStr); $('.chaptertitle', strRoot).each(function () { let jqthis = $(this); let cTitle = jqthis.text();//取得章节名 let itemEle = $('<div style="background-color:silver" id="title"></div>'); itemEle.text(cTitle); resultRoot.append(itemEle); resultRoot.append('<br />'); let nextHtml = jqthis.next().html(); if (-1 != cTitle.indexOf('插图')) { nextHtml = imgFormat(nextHtml); } resultRoot.append(nextHtml); resultRoot.append('<br /><br />'); }); return resultRoot.html(); } //目录或内容页面会声明章节变量。 if ('undefined' == typeof chapter_id || undefined === chapter_id) { } else { //本书编号 article_id //目录页面章节id定义为 '0' if ('0' == chapter_id) {//在章节名之后添加下载链接 //书名 let aname = $('#title').text(); //targetEncoding 1: 繁體中文, 2: 简体中文 let charsetDL = 'utf-8'; let charsetDLAll = 'utf8'; if ('1' == targetEncoding) { charsetDL = 'big5'; charsetDLAll = 'big5'; } //添加全本下载链接 let allDLink = `https://dl.wenku8.com/down.php?type=${charsetDLAll}&id=${article_id}&fname=${aname}`; let allaEle = `<a href="${allDLink}"> 全本文本下载(${charsetDLAll})</a>`; $('#title').append(allaEle); $('.vcss').each(function () { let jqthis = $(this); let vname = jqthis.text(); let nextChapter = jqthis.parent().next().children().first(); let vid = $('a', nextChapter).attr('href').split('.')[0]; //vid = (Number(vid)-1).toString();//部分文章无法使用章节减1来下载,直接使用章节可以下载(下载内容使用-1时章节名包含小说名,不-1不包含小说名) let dlink = `https://dl.wenku8.com/packtxt.php?aid=${article_id}&vid=${vid}&aname=${aname}&vname=${vname}&charset=${charsetDL}`; let aEle = `<a href="${dlink}"> 文本下载(${charsetDL})</a>`; jqthis.append(aEle); }); //添加 ePub下载(全本) let allaEpubEle = document.createElement("a"); allaEpubEle.className = "DownloadAll"; allaEpubEle.setAttribute("DownloadAll", "true"); allaEpubEle.innerText = " ePub下载(全本)"; allaEpubEle.href = "javascript:void(0);"; document.getElementById('title').append(allaEpubEle); allaEpubEle.addEventListener("click", EpubBuilder); let allaEpubEleEdt = document.createElement("a"); allaEpubEleEdt.className = "DownloadAll"; allaEpubEleEdt.setAttribute("ePubEidt", "true"); allaEpubEleEdt.setAttribute("DownloadAll", "true"); allaEpubEleEdt.innerText = " (调整插图)"; allaEpubEleEdt.href = "javascript:void(0);"; document.getElementById('title').append(allaEpubEleEdt); allaEpubEleEdt.addEventListener("click", EpubBuilder); //添加 ePub下载(本卷) for (let v of document.getElementsByClassName("vcss")) { let allaEle = document.createElement("a"); allaEle.href = "javascript:void(0);"; allaEle.innerText = " ePub下载(本卷)"; v.append(allaEle); allaEle.addEventListener("click", EpubBuilder); let allaEleEdt = document.createElement("a"); allaEleEdt.href = "javascript:void(0);"; allaEleEdt.innerText = " (调整插图)"; allaEleEdt.setAttribute("ePubEidt", "true"); v.append(allaEleEdt); allaEleEdt.addEventListener("click", EpubBuilder); }; } else { if ('0' != chapter_id) {//内容页面 //如果包含一个内容为 'null'的span则判定为版权限制 if ($('#contentmain span').first().text().trim() == 'null') { //设置下一页和上一页快捷键到目录页 preview_page = next_page = index_page; $('#content').text('正在下载,请稍候...'); //下载带有Html标记的内容 let dlink = 'http://dl.wenku8.com/pack.php?aid=' + article_id + '&vid=' + chapter_id; GM_xmlhttpRequest({ method: 'GET', url: dlink, onload: function (response) { if (response.status == 200) { var formatHtml = htmlFormat(response.responseText); $('#content').html(formatHtml); } else {//html下载可能会失败,使用下载文本的方法来下载 if (-1 != $('#title').text().indexOf('插图')) { $('#content').text('无法获取插图'); } else { dlink = 'http://dl.wenku8.com/packtxt.php?aid=' + article_id + '&vid=' + chapter_id; GM_xmlhttpRequest({ method: 'GET', url: dlink, onload: function (response) { if (response.status == 200) { let formatTxt = contentFormat(response.responseText); $('#content').html(formatTxt); } else { $('#content').text('下载失败'); } }, }); } } }, }); } } } } //评论页面 let articleReg = /\/modules\/article\//; if (articleReg.test(window.location.href)) { let rid = hrefUrl.searchParams.get('rid'); let page = hrefUrl.searchParams.get('page'); let codeEleArr = document.getElementsByClassName("jieqiCode"); let yidSet = new Set(); for (let code of codeEleArr) { let yidDivEle = code.parentElement.parentElement; let yid; for (let aEle of yidDivEle.getElementsByTagName('a')) { yid = aEle.getAttribute("name"); if (yid) { break; } } if (rid && yid) { let codeJson = code.innerText.replace(/\s/g,''); let locCfg try { locCfg = JSON.parse(codeJson); } catch (e) { console.log(e); continue; } if (locCfg && ePubEidterCfgUID == locCfg.UID && locCfg.aid && locCfg.pathname && (locCfg.ImgLocationBase64 ||(locCfg.ImgLocation && 0 < locCfg.ImgLocation.length)) && (!yidSet.has(yid)) ) { yidSet.add(yid); let titleDivEle = yidDivEle.firstElementChild; let epubRefEle = document.createElement('a'); epubRefEle.innerText = '[使用配置生成ePub]'; epubRefEle.style.color = "fuchsia"; epubRefEle.href = `${locCfg.pathname}?rid=${rid}&page=${page ? page : "1"}&yid=${yid}&CfgRef=1`; titleDivEle.insertBefore(epubRefEle, titleDivEle.firstElementChild); } } } } //读取到的配置 let ePubEidterCfgRef = []; ///modules/article/reviewshow.php?rid=270583 if ("1" == hrefUrl.searchParams.get('CfgRef')) { const ridCfg = hrefUrl.searchParams.get('rid'); const pageCfg = hrefUrl.searchParams.get('page'); const yidCfg = hrefUrl.searchParams.get('yid'); if (ridCfg && yidCfg) { let articleUrl = `${hrefUrl.origin}/modules/article/reviewshow.php?rid=${ridCfg}&page=${pageCfg}`; GM_xmlhttpRequest({ method: 'GET', url: articleUrl, onload: function (response) { if (response.status == 200) { ePubEidterCfgRef = []; let domParser = new DOMParser(); let rspHtml = domParser.parseFromString(response.responseText, "text/html"); let codeEleArr = rspHtml.getElementsByClassName("jieqiCode"); for (let code of codeEleArr) { let yidDivEle = code.parentElement.parentElement; let yid; for (let aEle of yidDivEle.getElementsByTagName('a')) { yid = aEle.getAttribute("name"); if (yid) { break; } } if (yid && yidCfg == yid) { let codeJson = code.innerText.replace(/\s/g, ''); let locCfg try { locCfg = JSON.parse(codeJson); } catch (e) { console.log(e); continue; } //解压 if (locCfg.ImgLocationBase64) { let zip = new JSZip(); let textDec = new TextDecoder(); zip.load(locCfg.ImgLocationBase64, { base64: true }); let fileArry = zip.file(ImgLocationFile)._data.getContent(); let imgLocJson = textDec.decode(fileArry); let ImgLocation = JSON.parse(imgLocJson); locCfg.ImgLocation = ImgLocation; } if (locCfg && ePubEidterCfgUID == locCfg.UID && locCfg.aid && locCfg.pathname && locCfg.ImgLocation && 0 < locCfg.ImgLocation.length ) { ePubEidterCfgRef.push(locCfg); } } } } else { console.log(articleUrl); console.log("配置下载失败"); } }, onerror: () => { console.log(articleUrl); console.log("配置下载失败"); } }); } } const ePubEidterHtml = ` <div class="main" style="width: 1200px;"> <!--左 章节--> <div id="left"> <div class="block" style="min-height: 230px;"> <div class="blocktitle"> <span class="txt">操作设置</span> <span class="txtr"></span> </div> <div class="blockcontent"> <div style="padding-left:10px"> <ul class="ulrow"> <li> <label for="SendArticle">将配置发送到书评:</label> <input type="checkbox" id="SendArticle" /> </li> <li> <label for="ePubEditerClose">生成后自动关闭:</label> <input type="checkbox" id="ePubEditerClose" checked="true" /> </li> <li>配置内容:</li> <li> <textarea id="CfgArea" class="textarea"></textarea> </li> <li><input type="button" id="EidterImportBtn" class="button" value="导入配置" /></li> <li><input type="button" id="EidterBuildBtn" class="button" value="生成ePub" /></li> </ul> <div class="cb"></div> </div> </div> </div> <div class="block" style="min-height: 230px;"> <div class="blocktitle"> <span class="txt">分卷</span> <span class="txtr"></span> </div> <div class="blockcontent"> <div style="padding-left:10px"> <ul id="VolumeUL" class="ulrow"> </ul> <div class="cb"></div> </div> </div> </div> </div> <!--左 章节--> <div id="left"> <div class="block" style="min-height: 230px;"> <div class="blocktitle"> <span class="txt">推测插图位置</span> <span class="txtr"></span> </div> <div class="blockcontent"> <div style="padding-left:10px"> <ul id="ImgUL" class="ulrow"> </ul> <div class="cb"></div> </div> </div> </div> <div class="block" style="min-height: 230px;"> <div class="blocktitle"> <span class="txt">章节</span> <span class="txtr"></span> </div> <div class="blockcontent"> <div style="padding-left:10px"> <ul id="ChapterUL" class="ulrow"> </ul> <div class="cb"></div> </div> </div> </div> </div> <!--右 内容--> <div id="centerm"> <!--内容--> <div id="content"> <table class="grid" width="100%" align="center"> <tbody> <tr> <td width="4%" align="center"><span style="font-size:16px;">分<br>卷<br>插<br>图</span></td> <td> <div ondragover="return false" id="VolumeImg" style="height:155px;overflow:auto"> </div> </td> </tr> </tbody> </table> <table class="grid" width="100%" align="center"> <caption>分卷内容</caption> <tbody> <tr> <td> <div id="VolumeText" style="height:500px;overflow: hidden scroll ;max-width: 900px;"> </div> </td> </tr> </tbody> </table> </div> </div> </div> `; // Your code here... })();