轻小说文库下载

生成分卷和全本ePub文档、ePub文档插图拖放、部分小说的在线阅读

  1. // ==UserScript==
  2. // @name 轻小说文库下载
  3. // @namespace wenku8Haoa
  4. // @version 2.2.2
  5. // @description 生成分卷和全本ePub文档、ePub文档插图拖放、部分小说的在线阅读
  6. // @author HaoaW
  7. // @match *://www.wenku8.net/*
  8. // @match *://www.wenku8.cc/*
  9. // @connect wenku8.com
  10. // @connect 777743.xyz
  11. // @require https://cdn.jsdelivr.net/npm/opencc-js@1.0.5/dist/umd/full.js
  12. // @require https://cdn.jsdelivr.net/npm/jszip@2.6.1/dist/jszip.js
  13. // @require https://cdn.jsdelivr.net/npm/file-saver@2.0.5/dist/FileSaver.js
  14. // @icon https://www.wenku8.net/favicon.ico
  15. // @grant GM_xmlhttpRequest
  16. // ==/UserScript==
  17.  
  18. (function () {
  19. 'use strict';
  20. let hrefUrl = new URL(window.location.href);
  21. const ePubEidterCfgUID = "24A08AE1-E132-458C-9E1D-6C998F16A666";
  22. const ImgLocationFile = "ImgLocation";
  23. const xmlIllegalCharacters = /[\x00-\x08\x0B\x0C\x0E-\x1F]/g;
  24. function OpenCCConver() {
  25. let OpenCCInfo = {
  26. OpenCCCookieKey: "OpenCCwenku8",//存放设置的cookie的key
  27. OpenCCCookie: null,//存放设置的cookie的值
  28. translateButtonId: translateButtonId,//GB_BIG5转换元素ID
  29. GB_BIG5_Simplized: Simplized,//GB_BIG5转换方法
  30. GB_BIG5_Traditionalized: Traditionalized,//GB_BIG5转换方法
  31. currentEncoding: currentEncoding,// 1: 繁體中文, 2: 简体中文
  32. targetEncodingCookie: targetEncodingCookie,//GB_BIG5翻译目标
  33. translateBody: translateBody,//翻译元素方法
  34. setCookie: setCookie,//设置cookie
  35. getCookie: getCookie,//读cookie
  36. CookieDays: 7,//cookie天数
  37. OpenCCEle: null,//开关元素
  38. OpenCCEleClick: () => {
  39. //关闭
  40. if (OpenCCInfo.OpenCCCookie) {
  41. OpenCCInfo.setCookie(OpenCCInfo.OpenCCCookieKey, "", OpenCCInfo.CookieDays);
  42. location.reload();
  43. }
  44. //开启
  45. else {
  46. OpenCCInfo.setCookie(OpenCCInfo.targetEncodingCookie, "2", OpenCCInfo.CookieDays);
  47. OpenCCInfo.setCookie(OpenCCInfo.OpenCCCookieKey, "1", OpenCCInfo.CookieDays);
  48. location.reload();
  49. }
  50. },//开关元素点击事件
  51. start: () => {
  52. OpenCCInfo.OpenCCEle = document.createElement("a");
  53. OpenCCInfo.OpenCCEle.href = "javascript:void(0);"
  54. OpenCCInfo.OpenCCEle.innerHTML = "開啟(OpenCC)";
  55. OpenCCInfo.OpenCCEle.addEventListener("click", OpenCCInfo.OpenCCEleClick);
  56. //如果有设置就替换GB_BIG5的转换
  57. if (OpenCCInfo.OpenCCCookie) {
  58. OpenCCInfo.OpenCCEle.innerHTML = "关闭(OpenCC)";
  59.  
  60. Traditionalized = OpenCC.Converter({ from: "cn", to: "tw" });
  61. Simplized = OpenCC.Converter({ from: "tw", to: "cn" });
  62.  
  63. if ("1" == OpenCCInfo.OpenCCCookie) {
  64. targetEncoding = OpenCCInfo.OpenCCCookie;
  65. translateBody();
  66. }
  67. }
  68. //添加开关元素
  69. let tranBtn = document.querySelector(`#${OpenCCInfo.translateButtonId}`);
  70. if (tranBtn) {
  71. tranBtn.parentElement.appendChild(document.createTextNode(" "));
  72. tranBtn.parentElement.appendChild(OpenCCInfo.OpenCCEle);
  73. }
  74. },//
  75. };
  76. OpenCCInfo.OpenCCCookie = OpenCCInfo.getCookie(OpenCCInfo.OpenCCCookieKey);
  77. return OpenCCInfo.start;
  78. };//使用OpenCCC进行简转繁
  79. OpenCCConver()();
  80. function AppApi() {
  81. let AppApiInfo = {
  82. VolumeMap: new Map(),
  83. appApiDomain: "app.wenku8.com",//app接口域名
  84. appApiPath: "/android.php",//app接口路径
  85. appApiLangDis:true,//禁用app接口请求繁体内容。由页面自行转换。
  86. appApiLang: (info) => {
  87. if (AppApiInfo.appApiLangDis) {
  88. return "0";
  89. }
  90. //0 simplified Chinese;1 traditional Chinese
  91. let rst = "0";
  92. if ("1" == info.targetEncoding) {
  93. rst = "1";
  94. }
  95. else if ("2" == info.targetEncoding) {
  96. rst = "0";
  97. }
  98. return rst;
  99. },//语言选择
  100. appApiGetEncrypted: (body) => {
  101. return `appver=1.0&timetoken=${Number(new Date())}&request=${btoa(body)}`;
  102. },//编码请求内容
  103. appApiListLoad: (xhr) => {
  104. xhr.start = true;
  105. xhr.bookInfo.refreshProgress(xhr.bookInfo,`下载app章节目录;`);
  106. let lang = AppApiInfo.appApiLang(xhr.bookInfo);
  107. let body = `action=book&do=list&aid=${xhr.bookInfo.aid}&t=${lang}`;
  108. body = AppApiInfo.appApiGetEncrypted(body);
  109. GM_xmlhttpRequest({
  110. method: 'POST',
  111. url: xhr.url,
  112. headers: { "content-type": "application/x-www-form-urlencoded;charset=utf-8" },
  113. data: body,
  114. onload: function (response) {
  115. if (response.status == 200) {
  116. xhr.done = true;
  117. let rspRaw = response.responseText;
  118. //格式化xml内容
  119. let domParser = new DOMParser();
  120. let rspXml = domParser.parseFromString(rspRaw.replaceAll(xmlIllegalCharacters, ''), "application/xml");
  121. AppApiInfo.appApiList = rspXml;
  122. //继续下载在等待章节列表的分卷
  123. for (let x of AppApiInfo.appApiListWait) {
  124. AppApiInfo.appApiLoadVolume(x);
  125. }
  126. } else {
  127. //重新下载
  128. xhr.XHRRetryFun(xhr, `app章节目录下载失败,重新下载;`);
  129. }
  130. },
  131. onerror: () => {
  132. //重新下载
  133. xhr.XHRRetryFun(xhr, `app章节目录下载失败,重新下载;`);
  134. }
  135. });
  136. },//下载章节列表,全本下载只下载一次
  137. appApiList: null,//章节列表docum,XML
  138. appApiListStart: false,//章节列表已开始下载
  139. appApiListWait: [],//等待章节列表的xhr
  140. appApiDoList: (xhr) => {
  141. if (!AppApiInfo.appApiListStart) {
  142. AppApiInfo.appApiListStart = true;
  143. //下载章节列表
  144. let dlink = `http://${AppApiInfo.appApiDomain}${AppApiInfo.appApiPath}`;
  145. let lXhr = { start: false, done: false, url: dlink, loadFun: AppApiInfo.appApiListLoad, VolumeIndex: xhr.VolumeIndex, bookInfo: xhr.bookInfo };
  146. lXhr.bookInfo.XHRAdd(lXhr);
  147. }
  148. AppApiInfo.appApiListWait.push(xhr);
  149. },//下载章节列表,处理等待队列
  150. appApiLoadVolume: (xhr) => {
  151. //如果没有章节列表就去下载
  152. if (!AppApiInfo.appApiList) {
  153. AppApiInfo.appApiDoList(xhr);
  154. return;
  155. }
  156.  
  157. let vol;
  158. for (vol of AppApiInfo.appApiList.getElementsByTagName("volume")) {
  159. if (xhr.data.vid == vol.getAttribute("vid")) {
  160. break;
  161. }
  162. }
  163. //找不到分卷,停止
  164. if (!vol) {
  165. xhr.bookInfo.refreshProgress(xhr.bookInfo,`<span style="color:fuchsia;">app章节目录未找到分卷${xhr.data.vid},无法生成ePub;</span>`);
  166. xhr.done = false;
  167. xhr.bookInfo.XHRFail = true;
  168. return;
  169. }
  170. let chArr = [];
  171. //添加章节下载,完成失败的分卷下载;pack.php
  172. for (let ch of vol.children) {
  173. let cid = ch.getAttribute("cid");
  174. let cName = ch.textContent;
  175. //let xhr = { start: false, done: false, url: dlink, loadFun: bInfo.loadVolume, VolumeIndex: VolumeIndex, data: { vid: vid, vcssText: vcssText }, bookInfo: bInfo };
  176. let dlink = `http://${AppApiInfo.appApiDomain}${AppApiInfo.appApiPath}`;
  177. let cXhr = { start: false, done: false, url: dlink, loadFun: AppApiInfo.appApiLoadChapter, dealVolume: xhr.dealVolume, VolumeIndex: xhr.VolumeIndex, data: { vid: xhr.data.vid, vcssText: xhr.data.vcssText, Text: xhr.data.Text, isAppApi: true, cid: cid }, bookInfo: xhr.bookInfo };
  178. cXhr.bookInfo.XHRAdd(cXhr);
  179.  
  180. chArr.push({ cid: cid, cName: cName, content :null});
  181. }
  182. AppApiInfo.VolumeMap.set(xhr.data.vid, chArr);
  183.  
  184. xhr.done = true;
  185. xhr.bookInfo.buildEpub(xhr.bookInfo);
  186. },//下载分卷,app接口只能下载章节
  187. appApiLoadChapter: (xhr) => {
  188. xhr.start = true;
  189. let lang = AppApiInfo.appApiLang(xhr.bookInfo);
  190. let body = `action=book&do=text&aid=${xhr.bookInfo.aid}&cid=${xhr.data.cid}&t=${lang}`;
  191. body = AppApiInfo.appApiGetEncrypted(body);
  192. let msg = `${xhr.data.cName} 下载失败,重新下载;`;
  193. GM_xmlhttpRequest({
  194. method: 'POST',
  195. url: xhr.url,
  196. headers: { "content-type": "application/x-www-form-urlencoded;charset=utf-8" },
  197. data: body,
  198. onload: function (response) {
  199. if (response.status == 200) {
  200. let rspRaw = response.responseText;
  201. let chArr = AppApiInfo.VolumeMap.get(xhr.data.vid);
  202. let ch = chArr.find(f => f.cid == xhr.data.cid);
  203. ch.content = rspRaw;
  204.  
  205. xhr.done = true;
  206.  
  207. let vid = xhr.data.vid;
  208. //分卷的章节都下载完成了
  209. if (xhr.bookInfo.XHRDone(vid)) {
  210. let VolumeText = '';
  211. //处理格式,拼接章节
  212. for (let c of chArr) {
  213. if (!c.content) { continue; }
  214. let cName = c.cName;
  215. let cid = c.cid;
  216. //章节名
  217. c.content = c.content.replace(cName, `<div class="chaptertitle"><a name="${cid}">${cName}</a></div><div class="chaptercontent">`);
  218. //换行
  219. c.content = c.content.replace(/\r\n/g, "<br />\r\n");
  220. //替换插图
  221. if (-1 < c.content.indexOf('<!--image-->http')) {
  222. c.content = c.content.replaceAll('<!--image-->http', `<div class="divimage" title="http`);
  223. c.content = c.content.replaceAll('<!--image-->', `"></div>`);
  224. }
  225. c.content += `</div>`;
  226.  
  227. VolumeText += c.content;
  228. }
  229. //处理分卷文本
  230. xhr.dealVolume(xhr, VolumeText);
  231. }
  232.  
  233. } else {
  234. //重新下载
  235. xhr.XHRRetryFun(xhr, msg);
  236. }
  237. },
  238. onerror: () => {
  239. //重新下载
  240. xhr.XHRRetryFun(xhr, msg);
  241. }
  242. });
  243. },//下载章节,全部完成后拼成分卷格式
  244. };
  245. return AppApiInfo.appApiLoadVolume;
  246. };//用app接口下载分卷、章节
  247. function LoadVolume() {
  248. let LoadVolumeInfo = {
  249. imgDomain:'img.wenku8.com',
  250. appApiLoadVolume: AppApi(),//调用app接口下载文档
  251. loadVolume: (xhr) => {
  252. let navToc = xhr.bookInfo.nav_toc[xhr.VolumeIndex];
  253. let msg = `${navToc.volumeName} 下载失败,重新下载;`;
  254. xhr.start = true;
  255. GM_xmlhttpRequest({
  256. method: 'GET',
  257. url: xhr.url,
  258. onload: function (response) {
  259. if (response.status == 200) {
  260. xhr.done = true;
  261. LoadVolumeInfo.dealVolume(xhr, response.responseText);
  262. }
  263. //部分小说会404,用app接口
  264. else if (404 == response.status) {
  265. xhr.dealVolume = LoadVolumeInfo.dealVolume;
  266. LoadVolumeInfo.appApiLoadVolume(xhr);
  267. }
  268. else {
  269. //重新下载
  270. xhr.XHRRetryFun(xhr, msg);
  271. }
  272. },
  273. onerror: () => {
  274. //重新下载
  275. xhr.XHRRetryFun(xhr, msg);
  276. }
  277. });
  278. },//分卷下载
  279. ImagesFix: "Img",//图片文件、ID前缀
  280. SpanFix: "Txt",//文字ID前缀
  281. loadImg: (xhr) => {
  282. xhr.start = true;
  283. let msg = `${xhr.images.idName} 下载失败,重新下载;`;
  284. GM_xmlhttpRequest({
  285. method: 'GET',
  286. url: xhr.url,
  287. responseType: "arraybuffer",
  288. onload: function (response) {
  289. if (response.status == 200) {
  290. xhr.images.content = response.response;
  291. if (xhr.images.coverImgChk && (!xhr.bookInfo.Images.find(i => i.coverImg))) {
  292. xhr.images.Blob = new Blob([xhr.images.content], { type: "image/jpeg" });
  293. xhr.images.ObjectURL = URL.createObjectURL(xhr.images.Blob);
  294. let imgEle = new Image();
  295. imgEle.onload = () => {
  296. //高比宽大于1就能做封面
  297. xhr.images.coverImg = (imgEle.naturalHeight / imgEle.naturalWidth > 1);
  298. xhr.done = true;
  299. xhr.bookInfo.buildEpub(xhr.bookInfo);
  300. };
  301. imgEle.src = xhr.images.ObjectURL;
  302. }
  303. else {
  304. xhr.done = true;
  305. xhr.bookInfo.buildEpub(xhr.bookInfo);
  306. }
  307. } else {
  308. //重新下载
  309. xhr.XHRRetryFun(xhr, msg);
  310. }
  311. },
  312. onerror: () => {
  313. //重新下载
  314. xhr.XHRRetryFun(xhr, msg);
  315. }
  316. });
  317. },//图片下载
  318. dealVolume: (xhr, txt) => {
  319. let chapterIndex = 0;
  320. let ImagesIndex = 0;
  321. let TextIndex = 0;
  322. let Text = xhr.data.Text;
  323. let navToc = Text.navToc;
  324.  
  325. //https://developer.mozilla.org/zh-CN/docs/Web/Guide/Parsing_and_serializing_XML
  326. //下载分卷文本,转换为html
  327. let domParser = new DOMParser();
  328. let rspHtml = domParser.parseFromString(
  329. `<html>
  330. <head>
  331. <meta charset="utf-8"/>
  332. <title>${xhr.data.vcssText}</title>
  333. <link href="../Styles/default.css" rel="stylesheet" type="text/css"/>
  334. </head>
  335. <body><div class="volumetitle"><h2>${xhr.data.vcssText}</h2></div><br /></body>
  336. </html>`.replaceAll(xmlIllegalCharacters, '')
  337. , "text/html");
  338. rspHtml.body.innerHTML += txt;
  339.  
  340. //调用简转繁
  341. if (currentEncoding != targetEncoding) {
  342. translateBody(rspHtml.body);
  343. }
  344.  
  345. //HTML DOM 中的 HTMLCollection 是即时更新的(live);当其所包含的文档结构发生改变时,它会自动更新。
  346. //因此,最好是创建副本(例如,使用 Array.from)后再迭代这个数组以添加、移动或删除 DOM 节点。
  347. let removeChild = [];
  348. //处理章节、插图 和 contentdp
  349. let bodyChildArr = Array.from(rspHtml.body.children);
  350. for (let i = 0; i < bodyChildArr.length; i++) {
  351. let child = bodyChildArr[i];
  352. if ("UL" == child.tagName && "contentdp" == child.id) {
  353. removeChild.push(child);
  354. }
  355. //章节
  356. else if ("DIV" == child.tagName && "chaptertitle" == child.className) {
  357. chapterIndex++;
  358. //章节h3、分卷h2、书名h1(没有做)
  359. //<div class="chaptertitle"><div id="chapter_1" name="xxx"><h3>第一章</h3></div></div>
  360. let cTitle = child.innerText;
  361. if (child.firstChild.hasAttribute("name")) {
  362. child.firstChild.remove("name");
  363. }
  364. //let aName = child.firstChild.getAttribute("name");
  365. let divEle = document.createElement("div");
  366. divEle.id = `chapter_${chapterIndex}`;
  367. //divEle.setAttribute("name", aName);
  368. divEle.innerHTML = `<h3>${cTitle}</h3>`;
  369. child.innerHTML = divEle.outerHTML;
  370. if (navToc) {
  371. //添加章节导航
  372. navToc.chapterArr.push({
  373. chapterName: cTitle
  374. , chapterID: divEle.id
  375. , chapterHref: `${navToc.volumeHref}#${divEle.id}`
  376. });
  377. }
  378. //章节名接受拖放
  379. let txtSpan = rspHtml.createElement("span");
  380. txtSpan.id = `${LoadVolumeInfo.SpanFix}_${divEle.id}`;
  381. txtSpan.className = "txtDropEnable";
  382. txtSpan.setAttribute("ondragover", "return false");
  383. child.parentElement.insertBefore(txtSpan, child);
  384. txtSpan.appendChild(child);
  385. }
  386. //内容
  387. else if ("DIV" == child.tagName && "chaptercontent" == child.className) {
  388. let chapterChildArr = Array.from(child.childNodes);
  389. for (let j = 0; j < chapterChildArr.length; j++) {
  390. let contentChild = chapterChildArr[j];
  391. //文字
  392. if (Node.TEXT_NODE == contentChild.nodeType && contentChild.textContent != '\n') {
  393. TextIndex++;
  394. let txtSpan = rspHtml.createElement("span");
  395. txtSpan.id = `${LoadVolumeInfo.SpanFix}_${xhr.VolumeIndex}_${TextIndex}`;
  396. txtSpan.className = "txtDropEnable";
  397. txtSpan.setAttribute("ondragover", "return false");
  398. child.insertBefore(txtSpan, contentChild);
  399. txtSpan.appendChild(contentChild);
  400. }
  401. //插图
  402. else if ("DIV" == contentChild.tagName && "divimage" == contentChild.className) {//插图
  403. //取得插图下载地址
  404. let imgASrc = contentChild.getAttribute("title");
  405. let imgUrl = new URL(imgASrc);
  406. let imgPath = `Images${imgUrl.pathname}`;
  407. let imgURL = new URL(imgASrc);
  408. let pathNameArr = imgURL.pathname.split('/');
  409. let imgIdName = pathNameArr[pathNameArr.length - 1];
  410. //在html中加入img标签
  411. let imgEle = document.createElement("img");
  412. imgEle.setAttribute("loading", "lazy");
  413. imgEle.setAttribute("src", `../${imgPath}`);
  414. contentChild.innerHTML = imgEle.outerHTML;
  415. //记录图片信息作为epub资源
  416. ImagesIndex++;
  417. let ImagesID = `${LoadVolumeInfo.ImagesFix}_${xhr.VolumeIndex}_${ImagesIndex}`;
  418. let images = { path: `${imgPath}`, content: null, id: ImagesID, idName: imgIdName, TextId: Text.id };
  419. //封面候补 第一卷的前两张图,高/宽 > 1
  420. if (0 == xhr.VolumeIndex && 3 > ImagesIndex) {
  421. images.coverImgChk = true;
  422. }
  423. xhr.bookInfo.Images.push(images);
  424. //添加图片下载xhr请求
  425. let xhrImg = { start: false, done: false, url: imgASrc, loadFun: LoadVolumeInfo.loadImg, images: images, bookInfo: xhr.bookInfo };
  426.  
  427. xhr.bookInfo.XHRAdd(xhrImg);
  428. }
  429. }
  430. }
  431. }
  432. removeChild.forEach(c => rspHtml.body.removeChild(c));
  433.  
  434.  
  435. Text.content = rspHtml.body.innerHTML;
  436.  
  437. //没有图片则添加书籍缩略图
  438. if (xhr.bookInfo.Images.length==0)
  439. {
  440. let pathArry = location.pathname.replace('novel','image').split('/');
  441. pathArry.pop();
  442. let ImagesID = `${pathArry.findLast(e => e)}s`;
  443. pathArry.push(`${ImagesID}.jpg`);
  444. let imgASrc = `https://${LoadVolumeInfo.imgDomain}${pathArry.join('/')}`;
  445. let imgUrl = new URL(imgASrc);
  446. let imgPath = `Images${imgUrl.pathname}`;
  447.  
  448. let images = { path: `${imgPath}`, content: null, id: ImagesID, idName: ImagesID, TextId: "", smallCover:true };
  449. xhr.bookInfo.Images.push(images);
  450. //添加图片下载xhr请求
  451. let xhrImg = { start: false, done: false, url: imgASrc, loadFun: LoadVolumeInfo.loadImg, images: images, bookInfo: xhr.bookInfo };
  452.  
  453. xhr.bookInfo.XHRAdd(xhrImg);
  454. }
  455.  
  456. //生成epub,还有资源未下载则只更新生成进度
  457. xhr.bookInfo.buildEpub(xhr.bookInfo);
  458. },
  459. };
  460. return LoadVolumeInfo.loadVolume;
  461. };//下载分卷内容及插图
  462. function EPubEidter() {
  463. let EPubEidterInfo = {
  464. Domain: "www.wenku8.net",
  465. novelTable: null,
  466. ePubEidterCfg: {
  467. UID: ePubEidterCfgUID,
  468. aid: article_id,
  469. pathname: hrefUrl.pathname,
  470. ImgLocation: []
  471. },//插图位置配置
  472. ePubEidtImgRegExp: [/img/i, /插图/i, /插圖/i, /\.jpg/i, /\.png/i],//推测插图位置的正则
  473. ePubEidtLink: [],
  474. ePubEidt: false,
  475. ePubEidtDone: false,
  476. ePubEidterHtml: ePubEidterHtml,//编辑器html代码
  477. ePubEidter: null,
  478. ePubEidterLastVolumeUL: null,
  479. ePubEidterInit: (info) => {
  480. //隐藏目录
  481. let downloadEleArr = document.querySelectorAll(".DownloadAll");
  482. for (let f of downloadEleArr) {
  483. f.style.pointerEvents = "none";
  484. }
  485. EPubEidterInfo.novelTable = document.body.getElementsByTagName("table")[0];
  486. EPubEidterInfo.novelTable.style.display = "none";
  487.  
  488. //加载编辑器、样式
  489. let linkEle = document.createElement("link");
  490. linkEle.type = "text/css";
  491. linkEle.rel = "stylesheet";
  492. linkEle.href = "/themes/wenku8/style.css";
  493. document.head.appendChild(linkEle);
  494. EPubEidterInfo.ePubEidtLink.push(linkEle);
  495.  
  496. let divEle = document.createElement("div");
  497. divEle.id = "ePubEidter";
  498. divEle.style.display = "none";
  499. linkEle.onload = () => {
  500. //显示编辑器
  501. divEle.style.display = "";
  502. };
  503. divEle.innerHTML = EPubEidterInfo.ePubEidterHtml;
  504. EPubEidterInfo.ePubEidter = divEle;
  505.  
  506. EPubEidterInfo.novelTable.parentElement.insertBefore(divEle, info.novelTable);
  507. document.getElementById("EidterBuildBtn").addEventListener("click", EPubEidterInfo.ePubEidterDoneFun(info));
  508. document.getElementById("EidterImportBtn").addEventListener("click", EPubEidterInfo.ePubEidterImportCfgFun(info));
  509. document.getElementById("VolumeImg").addEventListener("drop", EPubEidterInfo.ePubEidterImgDelDropFun(info));
  510.  
  511. //加载配置内容
  512. let cfgAreaEle = document.getElementById("CfgArea");
  513. EPubEidterInfo.ePubEidterCfg.ImgLocation = info.ImgLocation;
  514. cfgAreaEle.value = JSON.stringify(EPubEidterInfo.ePubEidterCfg, null, " ");
  515.  
  516. //加载分卷列表
  517. let liEleFirst = null;
  518. let VolumeULEle = document.getElementById("VolumeUL");
  519. VolumeULEle.innerHTML = "";
  520. for (let i = 0; i < info.Text.length; i++) {
  521. let text = info.Text[i];
  522. let liEle = document.createElement("li");
  523. VolumeULEle.appendChild(liEle);
  524. let aEle = document.createElement("a");
  525. liEle.appendChild(aEle);
  526. aEle.href = "javascript:void(0);";
  527. aEle.id = text.id;
  528. aEle.innerText = text.volumeName;
  529. liEle.addEventListener("click", EPubEidterInfo.ePubEidterVolumeULFun(info, text));
  530. if (!liEleFirst) {
  531. liEleFirst = liEle;
  532. }
  533. }
  534.  
  535. //加载第一卷
  536. if (liEleFirst) {
  537. liEleFirst.click();
  538. }
  539. },//编辑器初始化
  540. ePubEidterDestroyer: () => {
  541. EPubEidterInfo.ePubEidter.parentElement.removeChild(EPubEidterInfo.ePubEidter);
  542. EPubEidterInfo.ePubEidtLink.forEach(f => f.parentElement.removeChild(f));
  543. EPubEidterInfo.novelTable = document.body.getElementsByTagName("table")[0];
  544. EPubEidterInfo.novelTable.style.display = "";
  545. EPubEidterInfo = null;
  546. let downloadEleArr = document.querySelectorAll(".DownloadAll");
  547. for (let f of downloadEleArr) {
  548. f.style.pointerEvents = "auto";
  549. }
  550. },//编辑器销毁
  551. ePubEidterDoneFun: (info) => {
  552. return (ev) => {
  553. ev.currentTarget.disabled = true;
  554. //生成ePub
  555. info.ePubEidtDone = true;
  556. info.buildEpub(info);
  557.  
  558. //发送配置
  559. let sendArticleEle = document.getElementById('SendArticle');
  560. if (sendArticleEle.checked && 0 < info.ImgLocation.length) {
  561. let cfgObj = Object.assign({}, EPubEidterInfo.ePubEidterCfg);
  562. //压缩位置
  563. let imgLocJson = JSON.stringify(info.ImgLocation);
  564. let zip = new JSZip();
  565. zip.file(ImgLocationFile, imgLocJson, {
  566. compression: "DEFLATE",
  567. compressionOptions: {
  568. level: 9
  569. }
  570. });
  571. let imgLocBase64 = zip.generate({ type: "base64", mimeType: "application/zip" });
  572. cfgObj.ImgLocation = null;
  573. cfgObj.ImgLocationBase64 = imgLocBase64;
  574.  
  575. let cfgJson = JSON.stringify(cfgObj);
  576.  
  577. let vidSet = new Set();
  578. let vName = [];
  579. for (let loc of info.ImgLocation) {
  580. if (!vidSet.has(loc.vid)) {
  581. vidSet.add(loc.vid);
  582. let nToc = info.nav_toc.find(f => loc.vid == f.vid);
  583. if (nToc) {
  584. vName.push(nToc.volumeName);
  585. }
  586. }
  587. }
  588.  
  589. let pcontent = `包含分卷列表:${vName}
  590. [code]${cfgJson}[/code]`;
  591.  
  592. let map = new Map();
  593. map.set("ptitle", "ePub插图位置");
  594. map.set("pcontent", pcontent);
  595. let url = `https://${EPubEidterInfo.Domain}/modules/article/reviews.php?aid=${info.aid}`;
  596. //发送配置
  597. EPubEidterInfo.ePubEidterSend(info, url, map);
  598. }
  599. let ePubEditerClose = document.getElementById('ePubEditerClose');
  600. ev.currentTarget.disabled = false;
  601. if (ePubEditerClose.checked) {
  602. EPubEidterInfo.ePubEidterDestroyer();
  603. }
  604. };
  605. },//点击生成ePub事件
  606. ePubEidterImportCfgFun: (info) => {
  607. return (ev) => {
  608. ev.currentTarget.disabled = true;
  609. let cfgAreaEle = document.getElementById("CfgArea");
  610. let impCfg;
  611. try { impCfg = JSON.parse(cfgAreaEle.value); } catch { }
  612. if (impCfg
  613. && impCfg.UID == EPubEidterInfo.ePubEidterCfg.UID
  614. && impCfg.aid == EPubEidterInfo.ePubEidterCfg.aid
  615. && impCfg.ImgLocation
  616. && 0 < impCfg.ImgLocation.length
  617. ) {
  618. for (let iCfg of impCfg.ImgLocation) {
  619. if (info.ImgLocation.find(i =>
  620. i.spanID == iCfg.spanID
  621. && i.vid == iCfg.vid
  622. && i.imgID == iCfg.imgID)
  623. ) {
  624. continue;
  625. }
  626. else if (!info.Text.find(f => f.vid == iCfg.vid)) {
  627. continue;
  628. }
  629. else {
  630. info.ImgLocation.push(iCfg);
  631. }
  632. }
  633. }
  634. EPubEidterInfo.ePubEidterCfg.ImgLocation = info.ImgLocation;
  635. cfgAreaEle.value = JSON.stringify(EPubEidterInfo.ePubEidterCfg, null, " ");
  636. if (EPubEidterInfo.ePubEidterLastVolumeUL) {
  637. EPubEidterInfo.ePubEidterLastVolumeUL.click();
  638. }
  639. ev.currentTarget.disabled = false;
  640. };
  641. },//点击导入配置事件
  642. ePubEidterVolumeULFun: (info, text) => {
  643. return (ev) => {
  644. //最后点击的章节列表,导入配置后刷新
  645. if (EPubEidterInfo.ePubEidterLastVolumeUL) {
  646. EPubEidterInfo.ePubEidterLastVolumeUL.firstElementChild.style.color = "";
  647. }
  648. EPubEidterInfo.ePubEidterLastVolumeUL = ev.currentTarget;
  649. EPubEidterInfo.ePubEidterLastVolumeUL.firstElementChild.style.color = "fuchsia";
  650.  
  651. //加载文本内容
  652. let VolumeTextEle = document.getElementById("VolumeText");
  653. VolumeTextEle.style.display = "none";
  654. VolumeTextEle.innerHTML = text.content;
  655.  
  656. //加载图片列表
  657. let imgEleMap = new Map();
  658. let VolumeImgEle = document.getElementById("VolumeImg");
  659. VolumeImgEle.innerHTML = "";
  660. let volumeImgs = info.Images.filter(i => i.TextId == text.id);
  661. for (let image of volumeImgs) {
  662. if (!image.ObjectURL) {
  663. image.Blob = new Blob([image.content], { type: "image/jpeg" });
  664. image.ObjectURL = URL.createObjectURL(image.Blob);
  665. }
  666. let imgDivEle = document.createElement("div");
  667. imgDivEle.style.float = "left";
  668. imgDivEle.style.textAlign = "center";
  669. imgDivEle.style.height = "155px";
  670. imgDivEle.style.overflow = "hidden";
  671. imgDivEle.style.margin = "0 2px";
  672. VolumeImgEle.appendChild(imgDivEle);
  673. let imgEle = document.createElement("img");
  674. imgEle.setAttribute("imgID", image.idName);
  675. imgEle.setAttribute("loading", "lazy");
  676. imgEle.src = image.ObjectURL;
  677. imgEle.height = 127;
  678. //加载用
  679. imgEleMap.set(image.idName, imgEle);
  680. imgDivEle.appendChild(imgEle);
  681. imgDivEle.appendChild(document.createElement("br"));
  682. let imgTextEle = new Text(image.id)
  683. imgDivEle.appendChild(imgTextEle);
  684.  
  685. //<div style="float: left; text-align: center; height: 155px; overflow: hidden; margin: 0 2px;">
  686. // <img id="Img_160408" src="./160408.jpg" border="0" height="127"><br>
  687. //</div>
  688. }
  689.  
  690. //推测插图处置
  691. let ImgULEle = document.getElementById("ImgUL");
  692. ImgULEle.innerHTML = "";
  693. //加载已拖放的图片
  694. let vLocation = info.ImgLocation.filter(i => text.vid == i.vid);
  695. //拖放处理绑定
  696. let dropEleArr = document.querySelectorAll(".txtDropEnable");
  697. for (let dropEle of dropEleArr) {
  698. dropEle.addEventListener("drop", EPubEidterInfo.ePubEidterImgDropFun(info, text));
  699.  
  700. //加载已拖放的图片
  701. let locArr;
  702. let dImgEle;
  703. if (vLocation && (locArr = vLocation.filter(j => j.spanID == dropEle.id))) {
  704. for (let loc of locArr) {
  705. if (dImgEle = imgEleMap.get(loc.imgID)) {
  706. let divimage = document.createElement("div");
  707. divimage.className = "divimageM";
  708. divimage.innerHTML = dImgEle.outerHTML;
  709. dropEle.parentNode.insertBefore(divimage, dropEle);
  710. //添加拖放开始事件,用于删除拖放的标签
  711. let dropImg = divimage.firstChild;
  712. dropImg.id = `${loc.spanID}_${loc.imgID}`;
  713. dropImg.addEventListener("dragstart", EPubEidterInfo.ePubEidterImgDelStartFun(info, loc));
  714. }
  715. }
  716. }
  717. //章节名不测试
  718. if (!dropEle.firstElementChild || "chaptertitle" != dropEle.firstElementChild.className) {
  719. //匹配插图正则
  720. for (let reg of EPubEidterInfo.ePubEidtImgRegExp) {
  721. if (reg.test(dropEle.innerText)) {
  722. let liEle = document.createElement("li");
  723. ImgULEle.appendChild(liEle);
  724. let aEle = document.createElement("a");
  725. liEle.appendChild(aEle);
  726. aEle.href = "javascript:void(0);";
  727. aEle.setAttribute("SpanID", dropEle.id);
  728. aEle.innerText = dropEle.innerText.replace(/\s/g, '').substring(0, 12);
  729. liEle.addEventListener("click", EPubEidterInfo.ePubEidterImgULFun(info, dropEle));
  730. dropEle.style.color = "fuchsia";//fontWeight = "bold";
  731. break;
  732. }
  733. }
  734. }
  735. }
  736.  
  737. //加载章节列表
  738. let ChapterULEle = document.getElementById("ChapterUL");
  739. ChapterULEle.innerHTML = "";
  740. let toc = info.nav_toc.find(i => i.volumeID == text.id);
  741. for (let chapter of toc.chapterArr) {
  742.  
  743. let liEle = document.createElement("li");
  744. ChapterULEle.appendChild(liEle);
  745. let aEle = document.createElement("a");
  746. liEle.appendChild(aEle);
  747. aEle.href = "javascript:void(0);";
  748. aEle.setAttribute("chapterID", chapter.chapterID);
  749. aEle.innerText = chapter.chapterName;
  750. liEle.addEventListener("click", EPubEidterInfo.ePubEidterChapterULFun(info, chapter));
  751. }
  752.  
  753. VolumeTextEle.style.display = "";
  754. //滚动到分卷开始
  755. VolumeTextEle.scroll({ top: 0 });
  756. VolumeImgEle.scroll({ top: 0 });
  757. };
  758. },//点击分卷事件
  759. ePubEidterImgDropFun: (info, text) => {
  760. return (ev) => {
  761. const data = ev.dataTransfer.getData("text/html");
  762. let divimage = document.createElement("div");
  763. divimage.className = "divimageM";
  764. divimage.innerHTML = data;
  765. let dropImg = divimage.firstChild;
  766. let imgLocation = { "vid": text.vid, "spanID": ev.currentTarget.id, "imgID": dropImg.getAttribute("imgID") };
  767.  
  768. if (info.ImgLocation.find(i =>
  769. i.spanID == imgLocation.spanID
  770. && i.vid == imgLocation.vid
  771. && i.imgID == imgLocation.imgID)
  772. ) {
  773. alert("此位置已存在相同的图片");
  774. }
  775. else {
  776. ev.currentTarget.parentNode.insertBefore(divimage, ev.currentTarget);
  777. info.ImgLocation.push(imgLocation);
  778. //添加拖放开始事件,用于删除拖放的标签
  779. dropImg.id = `${imgLocation.spanID}_${imgLocation.imgID}`;
  780. dropImg.addEventListener("dragstart", EPubEidterInfo.ePubEidterImgDelStartFun(info, imgLocation));
  781.  
  782. EPubEidterInfo.ePubEidterCfg.ImgLocation = info.ImgLocation;
  783. let cfgAreaEle = document.getElementById("CfgArea");
  784. cfgAreaEle.value = JSON.stringify(EPubEidterInfo.ePubEidterCfg, null, " ");
  785. //JSON.parse(cfgAreaEle.value);
  786. }
  787. }
  788. },//插图拖放完成事件
  789. ePubEidterChapterULFun: (info, chapter) => {
  790. return (ev) => {
  791. let VolumeTextEle = document.getElementById("VolumeText");
  792. let target = document.getElementById(chapter.chapterID);
  793. VolumeTextEle.scroll({
  794. top: target.offsetTop,
  795. behavior: 'smooth'
  796. });
  797. //(document.getElementById(chapter.chapterID)).scrollIntoView();
  798. }
  799. },//点击章节事件
  800. ePubEidterImgULFun: (info, dropEle) => {
  801. return (ev) => {
  802. let VolumeTextEle = document.getElementById("VolumeText");
  803. VolumeTextEle.scroll({
  804. top: dropEle.offsetTop - 130,
  805. behavior: 'smooth'
  806. });
  807. }
  808. },//点击推测插图位置事件
  809. ePubEidterSend: (info, url, map) => {
  810. let iframeEle = document.createElement("iframe");
  811. iframeEle.style.display = 'none';
  812. document.body.appendChild(iframeEle);
  813. let iBodyEle = iframeEle.contentWindow.document.body;
  814. let iDocument = iframeEle.contentWindow.document;
  815.  
  816. let formEle = iDocument.createElement("form");
  817. formEle.acceptCharset = "gbk";
  818. formEle.method = "POST";
  819. formEle.action = url;
  820. iBodyEle.appendChild(formEle);
  821. for (let [mk, mv] of map) {
  822. let inputEle = iDocument.createElement("input");
  823. inputEle.type = "text";
  824. inputEle.name = mk;
  825. inputEle.value = mv;
  826. formEle.appendChild(inputEle);
  827. }
  828. let subEle = iDocument.createElement("input");
  829. subEle.type = "submit";
  830. subEle.name = "submit";
  831. subEle.value = "submit";
  832. formEle.appendChild(subEle);
  833. subEle.click();
  834. },//发送Post请求,无需转gbk
  835. ePubEidterImgDelDropFun: (info) => {
  836. return (ev) => {
  837. let vid = ev.dataTransfer.getData("vid");
  838. let spanID = ev.dataTransfer.getData("spanID");
  839. let imgID = ev.dataTransfer.getData("imgID");
  840. let fromID = ev.dataTransfer.getData("fromID");
  841. let fromEle = document.getElementById(fromID);
  842. if (fromEle && "divimageM" == fromEle.parentElement.className) {
  843. info.ImgLocation =
  844. info.ImgLocation.filter(i => !(i.spanID == spanID && i.vid == vid && i.imgID == imgID));
  845.  
  846. EPubEidterInfo.ePubEidterCfg.ImgLocation = info.ImgLocation;
  847. let cfgAreaEle = document.getElementById("CfgArea");
  848. cfgAreaEle.value = JSON.stringify(EPubEidterInfo.ePubEidterCfg, null, " ");
  849.  
  850. fromEle.parentElement.parentElement.removeChild(fromEle.parentElement);
  851. }
  852. }
  853. },//插图拖放完成事件
  854. ePubEidterImgDelStartFun: (info, imgLocation) => {
  855. return (ev) => {
  856. ev.dataTransfer.setData("vid", imgLocation.vid);
  857. ev.dataTransfer.setData("spanID", imgLocation.spanID);
  858. ev.dataTransfer.setData("imgID", imgLocation.imgID);
  859. ev.dataTransfer.setData("fromID", ev.srcElement.id);
  860. }
  861. },//插图拖放开始事件
  862. };
  863. return [EPubEidterInfo.ePubEidterInit, EPubEidterInfo.ePubEidterCfg];
  864. };//epub编辑器,拖动调整插图位置
  865. function XHRDownloader() {
  866. let XHRDownloaderInfo = {
  867. XHRFail: false,//下载失败,不生成ePub
  868. XHRRetry: 3,//xhr重试次数
  869. XHRRetryFun: (xhr, msg) => {
  870. //下载失败,不重试,不会生成ePub
  871. if (XHRDownloaderInfo.XHRFail) { return; }
  872. if (
  873. (!xhr.XHRRetryCount)
  874. || 0 == XHRDownloaderInfo.XHRRetry
  875. || xhr.XHRRetryCount < XHRDownloaderInfo.XHRRetry
  876. ) {
  877. xhr.XHRRetryCount = (xhr.XHRRetryCount ?? 0) + 1;
  878. xhr.loadFun(xhr);
  879. xhr.bookInfo.refreshProgress(xhr.bookInfo,msg);
  880. }
  881. else {
  882. XHRDownloaderInfo.XHRFail = true;
  883. xhr.bookInfo.refreshProgress(xhr.bookInfo, `<span style="color:fuchsia;">超出最大重试次数,下载失败,无法生成ePub;</span>`);
  884. }
  885. },//xhr重试
  886. XHRArr: [],//下载请求;[{start:false,done:false,url:,loadFun:,data:,bookInfo:bInfo}]
  887. XHRAdd: (xhr) => {
  888. xhr.XHRRetryFun = xhr.XHRRetryFun ?? XHRDownloaderInfo.XHRRetryFun;
  889. XHRDownloaderInfo.XHRArr.push(xhr);
  890. xhr.loadFun(xhr);
  891. },
  892. XHRDone: (vid) => {
  893. let arr = XHRDownloaderInfo.XHRArr;
  894. if (vid) {
  895. arr = arr.filter(f => f.data && f.data.vid && vid == f.data.vid);
  896. }
  897. return arr.every(e => e.done);
  898. },
  899. };
  900. return [XHRDownloaderInfo.XHRAdd, XHRDownloaderInfo.XHRDone ];
  901. };//XHR下载、重试
  902. function Builder() {
  903. let BuilderInfo = {
  904. mimetype: 'application/epub+zip',//epub mimetype 文件内容
  905. container_xml: container_xml,//epub container.xml 文件内容
  906. nav_xhtml: {
  907. content: nav_xhtml_content
  908. , path: `Text/nav.xhtml`
  909. , id: `nav_xhtml_id`
  910. },//epub nav.xhtml 文件模板
  911. defaultCSS: {
  912. content: defaultCSS_content
  913. , id: "default_css_id"
  914. , path: "Styles/default.css"
  915. },//epub default.css 样式文件
  916. contentDocument: null,
  917. manifest: null,
  918. spine: null,
  919. manifestItemAdd: (id, href, mediaType) => {
  920. let doc = BuilderInfo.contentDocument
  921. let manifest = BuilderInfo.manifest;
  922. if (!manifest) {
  923. manifest = doc.createElement("manifest");
  924. doc.firstChild.appendChild(manifest);
  925. BuilderInfo.manifest = manifest;
  926. }
  927. let item = doc.createElement("item");
  928. if ('undefined' != typeof (id)) {
  929. item.setAttribute("id", id);
  930. }
  931. if ('undefined' != typeof (href)) {
  932. item.setAttribute("href", href);
  933. }
  934. if ('undefined' != typeof (mediaType)) {
  935. item.setAttribute("media-type", mediaType);
  936. }
  937. manifest.appendChild(item);
  938. return item;
  939. },
  940. spineItemAdd: (idref) => {
  941. let doc = BuilderInfo.contentDocument
  942. let spine = BuilderInfo.spine;
  943. if (!spine) {
  944. spine = doc.createElement("spine");
  945. doc.firstChild.appendChild(spine);
  946. BuilderInfo.spine = spine;
  947. }
  948.  
  949. let itemref = doc.createElement("itemref");
  950. if ('undefined' != typeof (idref)) {
  951. itemref.setAttribute("idref", idref);
  952. }
  953. spine.appendChild(itemref);
  954. return itemref;
  955. },
  956. manifestSpineItemAdd: (id, href, mediaType) => {
  957. let item = BuilderInfo.manifestItemAdd(id, href, mediaType);
  958. let itemref = BuilderInfo.spineItemAdd(id);
  959.  
  960. return [item, itemref];
  961. },
  962. buildEpub: (info) => {
  963. if (info.XHRDone()) {
  964. if (info.ePubEidt && (!info.ePubEidtDone)) {
  965. info.refreshProgress(info,`开始编辑ePub;`);
  966. info.ePubEidterInit(info);
  967. return;
  968. }
  969.  
  970. info.refreshProgress(info, `开始生成ePub;`);
  971. let zip = new JSZip();
  972. //epub固定内容
  973. zip.file("mimetype", BuilderInfo.mimetype);
  974. zip.file("META-INF/container.xml", BuilderInfo.container_xml);
  975.  
  976. let content_opf = `<?xml version="1.0" encoding="utf-8"?><package></package>`;
  977. let paraser = new DOMParser()
  978. BuilderInfo.contentDocument = paraser.parseFromString(content_opf,'text/xml')
  979.  
  980. //保存插图位置
  981. if (info.ePubEidterCfg && info.ePubEidterCfg.ImgLocation && 0 < info.ePubEidterCfg.ImgLocation.length) {
  982. let cfgJson = JSON.stringify(info.ePubEidterCfg, null, " ");
  983. zip.file("OEBPS/Other/ePubEidterCfg.json", cfgJson);
  984. }
  985.  
  986. //保存css
  987. {
  988. BuilderInfo.manifestItemAdd(BuilderInfo.defaultCSS.id, BuilderInfo.defaultCSS.path, "text/css");
  989. //保存css
  990. zip.file(`OEBPS/${BuilderInfo.defaultCSS.path}`, BuilderInfo.defaultCSS.content);
  991. }
  992.  
  993. //生成并保存nav.xhtml
  994. //<ol><li><a href="Volume_0.xhtml">第一卷</a><ol><li><a href="Volume_0.xhtml#chapter_1">第一章</a></li></ol></li></ol>
  995. {
  996. //生成nav.xhtml
  997. let domParser = new DOMParser();
  998. let navXhtmlDoc = domParser.parseFromString(BuilderInfo.nav_xhtml.content.replaceAll(xmlIllegalCharacters, ''), "application/xhtml+xml");
  999. let tocEle = navXhtmlDoc.getElementById("toc");
  1000. let bOlEle = navXhtmlDoc.createElement("ol");
  1001. for (let t of info.nav_toc) {
  1002. //分卷
  1003. let vAEle = navXhtmlDoc.createElement("a");
  1004. vAEle.href = t.volumeHref;
  1005. vAEle.innerText = t.volumeName;
  1006. let vLiEle = navXhtmlDoc.createElement("li");
  1007. vLiEle.appendChild(vAEle);
  1008. if (t.chapterArr && 0 < t.chapterArr.length) {
  1009. //分卷的章节
  1010. let vOlEle = navXhtmlDoc.createElement("ol");
  1011. for (let c of t.chapterArr) {
  1012. let cAEle = navXhtmlDoc.createElement("a");
  1013. cAEle.href = c.chapterHref;
  1014. cAEle.innerText = c.chapterName;
  1015. let cLiEle = navXhtmlDoc.createElement("li");
  1016. cLiEle.appendChild(cAEle);
  1017. vOlEle.appendChild(cLiEle);
  1018. }
  1019. vLiEle.appendChild(vOlEle);
  1020. }
  1021. bOlEle.appendChild(vLiEle);
  1022. }
  1023. tocEle.appendChild(bOlEle);
  1024. let nav_xhtml = `<?xml version="1.0" encoding="utf-8"?>
  1025. <!DOCTYPE html>
  1026.  
  1027. ${navXhtmlDoc.firstChild.outerHTML}`;
  1028.  
  1029. //保存nav.xhtml信息到content.opf
  1030. //manifest节点
  1031.  
  1032. let [item, itemref] = BuilderInfo.manifestSpineItemAdd(
  1033. BuilderInfo.nav_xhtml.id, BuilderInfo.nav_xhtml.path, "application/xhtml+xml"
  1034. );
  1035. item.setAttribute("properties", "nav");
  1036. itemref.setAttribute("linear", "no");
  1037. //保存nav.xhtml
  1038. zip.file(`OEBPS/${BuilderInfo.nav_xhtml.path}`, nav_xhtml);
  1039. }
  1040.  
  1041. //保存分卷内容
  1042. for (let t of info.Text) {
  1043. BuilderInfo.manifestSpineItemAdd(t.id, t.path, "application/xhtml+xml");
  1044.  
  1045. //转换html为xhtml
  1046. let domParser = new DOMParser();
  1047. let rspHtml = domParser.parseFromString(
  1048. `<html>
  1049. <head>
  1050. <meta charset="utf-8"/>
  1051. <title>${t.volumeName}</title>
  1052. <link href="../Styles/default.css" rel="stylesheet" type="text/css"/>
  1053. </head>
  1054. <body></body>
  1055. </html>`.replaceAll(xmlIllegalCharacters, '')
  1056. , "text/html");
  1057. rspHtml.body.innerHTML = t.content;
  1058.  
  1059. //添加插图并去除拖放标签
  1060. let vLocation = info.ImgLocation.filter(i => t.vid == i.vid);
  1061. let volumeImgs = info.Images.filter(i => i.TextId == t.id);
  1062. let dropEleArr = rspHtml.querySelectorAll(".txtDropEnable");
  1063. for (let dropEle of dropEleArr) {
  1064. //加载已拖放的图片
  1065. let locArr;
  1066. let dImg;
  1067. if (vLocation
  1068. && (locArr = vLocation.filter(j => j.spanID == dropEle.id))
  1069. ) {
  1070. for (let loc of locArr) {
  1071. if (dImg = volumeImgs.find(j => j.idName == loc.imgID)) {
  1072. let divimage = rspHtml.createElement("div");
  1073. divimage.className = "divimage";
  1074.  
  1075. let imgEle = rspHtml.createElement("img");
  1076. imgEle.setAttribute("loading", "lazy");
  1077. imgEle.setAttribute("src", `../${dImg.path}`);
  1078. divimage.innerHTML = imgEle.outerHTML;
  1079.  
  1080. dropEle.parentNode.insertBefore(divimage, dropEle);
  1081.  
  1082. }
  1083. }
  1084. }
  1085. //去除文字span
  1086. dropEle.parentNode.insertBefore(dropEle.firstChild, dropEle);
  1087. dropEle.parentNode.removeChild(dropEle);
  1088. }
  1089.  
  1090. //转换html为xhtml
  1091. let xmlSerializer = new XMLSerializer();
  1092. let rspXml = xmlSerializer.serializeToString(rspHtml);
  1093. let rspXHtml = domParser.parseFromString(rspXml.replaceAll(xmlIllegalCharacters, ''), "application/xhtml+xml");
  1094. rspXHtml.firstChild.setAttribute("xmlns:epub", "http://www.idpf.org/2007/ops");
  1095. //保存章节内容,作为epub资源
  1096. let tContent = `<?xml version="1.0" encoding="utf-8"?>
  1097. <!DOCTYPE html>
  1098.  
  1099. ${rspXHtml.firstChild.outerHTML}`;
  1100.  
  1101. zip.file(`OEBPS/${t.path}`, tContent);
  1102. }
  1103. //保存图片
  1104. for (let t of info.Images) {
  1105. //media-type暂固定jpeg
  1106. BuilderInfo.manifestItemAdd(t.id, t.path, "image/jpeg");
  1107. zip.file(`OEBPS/${t.path}`, t.content, { binary: true });
  1108. }
  1109.  
  1110. //生成书籍信息
  1111. let coverMeta = '';
  1112. if (info.Images.length > 0) {
  1113. let coverImg = info.Images.find(i => i.coverImg);
  1114. if (!coverImg) {
  1115. coverImg = info.Images.find(i => i.coverImgChk);
  1116. }
  1117. if (!coverImg) {
  1118. coverImg = info.Images.find(i => i.smallCover);
  1119. }
  1120. if (coverImg) {
  1121. coverMeta = `<meta name="cover" content="${coverImg.id}" />`;
  1122. }
  1123. }
  1124.  
  1125. let uuid = self.crypto.randomUUID();
  1126. //CCYY-MM-DDThh:mm:ssZ
  1127. let createTime = new Date().toISOString();
  1128. createTime = `${createTime.split(".")[0]}Z`;
  1129. //<?xml version="1.0" encoding="utf-8"?>
  1130. content_opf = `<?xml version="1.0" encoding="utf-8"?>
  1131. <package version="3.0" unique-identifier="BookId" xmlns="http://www.idpf.org/2007/opf">
  1132. <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
  1133. <dc:language>zh-CN</dc:language>
  1134. <dc:title>${info.title}</dc:title>
  1135. <meta property="dcterms:modified">${createTime}</meta>
  1136. <dc:identifier id="BookId">urn:uuid:${uuid}</dc:identifier>
  1137. <dc:creator>${info.creator}</dc:creator>
  1138. <!--第一张插图做封面-->
  1139. ${coverMeta}
  1140. </metadata>
  1141. ${BuilderInfo.manifest.outerHTML}
  1142. ${BuilderInfo.spine.outerHTML}
  1143. </package>`;
  1144. zip.file("OEBPS/content.opf", content_opf);
  1145. //书名.开始卷-结束卷.epub
  1146. let epubName = `${info.title}.${info.nav_toc[0].volumeName}`;
  1147. if (1 < info.nav_toc.length) {
  1148. epubName = epubName + '-' + info.nav_toc[info.nav_toc.length - 1].volumeName;
  1149. }
  1150. saveAs(zip.generate({ type: "blob", mimeType: "application/epub+zip" }), `${epubName}.epub`);
  1151. info.refreshProgress(info, `ePub生成完成,文件名:${epubName}.epub;`);
  1152. }
  1153. else {
  1154. info.refreshProgress(info);
  1155. }
  1156. },//生成epub,如果XHRArr已经都完成了
  1157. };
  1158. return BuilderInfo.buildEpub;
  1159. };//epub生成
  1160. function RefreshLog() {
  1161. let LogInfo = {
  1162. progressEle: null,//进度、日志 元素;{txt:,img:,err:}
  1163. refreshProgress: (info,err) => {
  1164. if (!LogInfo.progressEle) {
  1165. //epub生成进度,下载文本进度:7/7;下载图片进度:77/77;日志:开始生成ePub;ePub生成完成;
  1166. LogInfo.progressEle = {};
  1167. LogInfo.progressEle.txt = document.createElement("span");
  1168. LogInfo.progressEle.img = document.createElement("span");
  1169. LogInfo.progressEle.err = document.createElement("span");
  1170. let logDiv = document.createElement('div');
  1171. logDiv.appendChild(document.createTextNode("epub生成进度,下载文本进度:"));
  1172. logDiv.appendChild(LogInfo.progressEle.txt);
  1173. logDiv.appendChild(document.createTextNode(";下载图片进度:"));
  1174. logDiv.appendChild(LogInfo.progressEle.img);
  1175. logDiv.appendChild(document.createTextNode(";日志:"));
  1176. logDiv.appendChild(LogInfo.progressEle.err);
  1177. document.body.insertBefore(logDiv, document.getElementById('title'));
  1178. }
  1179. //日志
  1180. if (err) { LogInfo.progressEle.err.innerHTML = err + LogInfo.progressEle.err.innerHTML; }
  1181. //文本进度
  1182. let txtProgress = info.Text.filter((value) => { return value.content; }).length;
  1183. LogInfo.progressEle.txt.innerText = `${txtProgress}/${info.Text.length}`;
  1184. //图片进度,文本下载完成后才能得到图片总数
  1185. if (txtProgress == info.Text.length) {
  1186. let imgProgress = info.Images.filter((value) => { return value.content; }).length;
  1187. LogInfo.progressEle.img.innerText = `${imgProgress}/${info.Images.length}`;
  1188. }
  1189. },//显示进度日志
  1190. };
  1191. return LogInfo.refreshProgress;
  1192. };
  1193. function EpubBuilder() {
  1194. let bInfo = {
  1195. XHRAdd: null,
  1196. XHRDone: null,
  1197. ePubEidterInit: null,
  1198. ePubEidterCfg: null,
  1199. buildEpub: Builder(),
  1200. loadVolume: LoadVolume(),//下载章节方法
  1201. refreshProgress: RefreshLog(),
  1202. start: (e) => {
  1203. [bInfo.XHRAdd, bInfo.XHRDone] = XHRDownloader();
  1204. let ePubEidt = e.target.getAttribute("ePubEidt");
  1205. if (ePubEidt && "true" == ePubEidt) {
  1206. bInfo.ePubEidt = true;
  1207. [bInfo.ePubEidterInit, bInfo.ePubEidterCfg] = EPubEidter();
  1208. }
  1209.  
  1210. //全本分卷
  1211. let vcssEle = null;
  1212. let DownloadAll = e.target.getAttribute("DownloadAll");
  1213. if (DownloadAll && "true" == DownloadAll) {
  1214. //全本下载
  1215. vcssEle = document.querySelectorAll(".vcss");
  1216. }
  1217. else {
  1218. vcssEle = [e.target.parentElement];
  1219. }
  1220. for (let VolumeIndex = 0; VolumeIndex < vcssEle.length; VolumeIndex++) {
  1221.  
  1222. let vcss = vcssEle[VolumeIndex];
  1223. //分卷ID
  1224. let vid = vcss.getAttribute("vid");
  1225. //pack.php下载整卷,每个章节不带卷名,用分卷的第一个章节做分卷vid
  1226. let vid1 = vcss.parentElement.nextElementSibling.getElementsByTagName('a')[0].getAttribute('href').split('.')[0];
  1227. //let vid = vcss.getAttribute("vid");
  1228. let vcssText = vcss.childNodes[0].textContent;
  1229. let navToc = bInfo.nav_toc[VolumeIndex] = {
  1230. volumeName: vcssText
  1231. , vid: vid
  1232. , volumeID: `${bInfo.VolumeFix}_${VolumeIndex}`
  1233. , volumeHref: `${bInfo.VolumeFix}_${VolumeIndex}.xhtml`
  1234. , chapterArr: []
  1235. };
  1236. let Text = {
  1237. path: `Text/${navToc.volumeHref}`
  1238. , content: ""
  1239. , id: navToc.volumeID
  1240. , vid: vid
  1241. , volumeName: vcssText
  1242. };
  1243. Text.navToc = navToc;
  1244. bInfo.Text[VolumeIndex] = Text;
  1245. //分卷下载链接
  1246. let dlink = `https://${bInfo.dlDomain}/pack.php?aid=${bInfo.aid}&vid=${vid1}`;
  1247. let xhr = { start: false, done: false, url: dlink, loadFun: bInfo.loadVolume, VolumeIndex: VolumeIndex, data: { vid: vid, vcssText: vcssText, Text: Text }, bookInfo: bInfo };
  1248. bInfo.XHRAdd(xhr);
  1249. }
  1250.  
  1251. //加载从评论读取的配置
  1252. if (bInfo.ImgLocationCfgRef && 0 < bInfo.ImgLocationCfgRef.length) {
  1253. for (let cfgRef of bInfo.ImgLocationCfgRef) {
  1254. if (ePubEidterCfgUID == cfgRef.UID
  1255. && bInfo.aid == cfgRef.aid
  1256. && cfgRef.ImgLocation
  1257. && 0 < cfgRef.ImgLocation.length
  1258. ) {
  1259. for (let loc of cfgRef.ImgLocation) {
  1260. //插图位置记录{vid:,spanID:,imgID:}
  1261. if (loc.vid && loc.spanID && loc.imgID
  1262. && bInfo.Text.find(f => f.vid == loc.vid)
  1263. ) {
  1264. if (!bInfo.ImgLocation.find(f =>
  1265. f.vid == loc.vid
  1266. && f.spanID == loc.spanID
  1267. && f.imgID == loc.imgID
  1268. )) {
  1269. bInfo.ImgLocation.push(loc);
  1270. }
  1271. }
  1272. }
  1273. }
  1274. }
  1275. }
  1276. if (bInfo.ePubEidterCfg && bInfo.ImgLocation && 0 < bInfo.ImgLocation.length) {
  1277. bInfo.ePubEidterCfg.ImgLocation = bInfo.ImgLocation;
  1278. }
  1279.  
  1280. bInfo.buildEpub(bInfo);
  1281. },//入口,开始下载文件并生成epub;
  1282.  
  1283. nav_toc: [],//导航菜单,第一层分卷,第二层章节{volumeName:,volumeID:,volumeHref:,chapterArr:[{chapterName:,chapterID:,chapterHref:}]}
  1284. Text: [],//下载后生成的XHTML;{path:`Text/${volumeHref}`,content:}
  1285. Images: [],//下载的图片;{path:`Images/${url.pathname}`,content:}
  1286.  
  1287. VolumeFix: "Volume",//分卷文件、ID前缀
  1288. dlDomain: "dl.wenku8.com",
  1289. ImgLocationCfgRef: ImgLocationCfgRef,//读取到的配置
  1290. ImgLocation: [],//插图位置记录{vid:,spanID:,imgID:}
  1291. targetEncoding: targetEncoding,// 1: 繁體中文, 2: 简体中文
  1292. aid: article_id,//本书编号 article_id
  1293. title: document.getElementById("title").childNodes[0].textContent, //标题
  1294. creator: document.getElementById('info').innerText,//作者
  1295. bookUrl: self.location.href,
  1296.  
  1297. };
  1298. return bInfo;
  1299. };//epub生成;使用addEventListener绑定start;
  1300.  
  1301. //目录或内容页面会声明章节变量。
  1302. if ('undefined' == typeof chapter_id || undefined === chapter_id) { }
  1303. else {
  1304. //本书编号 article_id
  1305. //目录页面章节id定义为 '0'
  1306. if ('0' == chapter_id) {//在章节名之后添加下载链接
  1307. //书名
  1308. let titleEle = document.querySelector("#title");
  1309. let aname = titleEle.innerText;
  1310. //targetEncoding 1: 繁體中文, 2: 简体中文
  1311. let charsetDL = 'utf-8';
  1312. let charsetDLAll = 'utf8';
  1313. if ('1' == targetEncoding) {
  1314. charsetDL = 'big5';
  1315. charsetDLAll = 'big5';
  1316. }
  1317.  
  1318. //添加全本下载链接
  1319. {
  1320. let DLink = `https://dl.wenku8.com/down.php?type=${charsetDLAll}&id=${article_id}&fname=${aname}`;
  1321. let aEle = document.createElement("a");
  1322. aEle.href = DLink;
  1323. aEle.innerText = ` 全本文本下载(${charsetDLAll})`;
  1324. titleEle.appendChild(aEle);
  1325.  
  1326. //添加 ePub下载(全本)
  1327. let aEleEpub = document.createElement("a");
  1328. aEleEpub.className = "DownloadAll";
  1329. aEleEpub.setAttribute("DownloadAll", "true");
  1330. aEleEpub.innerText = " ePub下载(全本)";
  1331. aEleEpub.href = "javascript:void(0);";
  1332. titleEle.append(aEleEpub);
  1333. aEleEpub.addEventListener("click", (e) => EpubBuilder().start(e));
  1334.  
  1335. let allaEpubEleEdt = document.createElement("a");
  1336. allaEpubEleEdt.className = "DownloadAll";
  1337. allaEpubEleEdt.setAttribute("ePubEidt", "true");
  1338. allaEpubEleEdt.setAttribute("DownloadAll", "true");
  1339. allaEpubEleEdt.innerText = " (调整插图)";
  1340. allaEpubEleEdt.href = "javascript:void(0);";
  1341. titleEle.append(allaEpubEleEdt);
  1342. allaEpubEleEdt.addEventListener("click", (e) => EpubBuilder().start(e));
  1343. }
  1344.  
  1345. //添加分卷下载链接
  1346. let vcssArry = document.querySelectorAll(".vcss");
  1347. for (let vcss of vcssArry)
  1348. {
  1349. let vname = vcss.innerText;
  1350. let vid = vcss.getAttribute("vid");
  1351. let dlink = `https://dl.wenku8.com/packtxt.php?aid=${article_id}&vid=${vid}&aname=${aname}&vname=${vname}&charset=${charsetDL}`;
  1352. let aEle = document.createElement("a");
  1353. aEle.href = dlink;
  1354. aEle.innerText = ` 文本下载(${charsetDL})`;
  1355. vcss.appendChild(aEle);
  1356.  
  1357. //添加 ePub下载(分卷)
  1358. let aEleEpub = document.createElement("a");
  1359. aEleEpub.href = "javascript:void(0);";
  1360. aEleEpub.innerText = " ePub下载(本卷)";
  1361. vcss.append(aEleEpub);
  1362. aEleEpub.addEventListener("click", (e) => EpubBuilder().start(e));
  1363.  
  1364. let aEleEpubEdt = document.createElement("a");
  1365. aEleEpubEdt.href = "javascript:void(0);";
  1366. aEleEpubEdt.innerText = " (调整插图)";
  1367. aEleEpubEdt.setAttribute("ePubEidt", "true");
  1368. vcss.append(aEleEpubEdt);
  1369. aEleEpubEdt.addEventListener("click", (e) => EpubBuilder().start(e));
  1370. }
  1371. }
  1372. else {
  1373. //如果第一个子元素为 内容是'null'的span则判定为版权限制
  1374. let contentMain = document.querySelector('#contentmain');
  1375. if ("SPAN" == contentMain.firstElementChild.tagName
  1376. && contentMain.firstElementChild.innerText.trim() == 'null') {
  1377. let content = document.getElementById("content");
  1378. let appApi = {
  1379. appApiDomain: "app.wenku8.com",//app接口域名
  1380. appApiPath: "/android.php",//app接口路径
  1381. targetEncoding: targetEncoding,
  1382. appApiLangDis: true,//禁用app接口请求繁体内容。由页面自行转换。
  1383. appApiLang: () => {
  1384. if (appApi.appApiLangDis) {
  1385. return "0";
  1386. }
  1387. //0 simplified Chinese;1 traditional Chinese
  1388. let rst = "0";
  1389. if ("1" == appApi.targetEncoding) {
  1390. rst = "1";
  1391. }
  1392. else if ("2" == appApi.targetEncoding) {
  1393. rst = "0";
  1394. }
  1395. return rst;
  1396. },//语言选择
  1397. appApiGetEncrypted: (body) => {
  1398. return `appver=1.0&timetoken=${Number(new Date())}&request=${btoa(body)}`;
  1399. },//编码请求内容
  1400. appApiLoadChapter: (xhr) => {
  1401. xhr.start = true;
  1402. let lang = appApi.appApiLang(xhr.bookInfo);
  1403. let body = `action=book&do=text&aid=${xhr.bookInfo.aid}&cid=${xhr.data.cid}&t=${lang}`;
  1404. body = appApi.appApiGetEncrypted(body);
  1405. let msg = `${xhr.data.cName} 下载失败,重新下载;`;
  1406. GM_xmlhttpRequest({
  1407. method: 'POST',
  1408. url: xhr.url,
  1409. headers: { "content-type": "application/x-www-form-urlencoded;charset=utf-8" },
  1410. data: body,
  1411. onload: function (response) {
  1412. if (response.status == 200) {
  1413. let rspRaw = response.responseText;
  1414. rspRaw = rspRaw.replace(/ {2}\S+.*/, "");
  1415. //换行
  1416. rspRaw = rspRaw.replace(/\r\n/g, "<br />\r\n");
  1417. //替换插图
  1418. if (-1 < rspRaw.indexOf('<!--image-->http')) {
  1419. rspRaw = rspRaw.replaceAll(/<!--image-->(http[\w:/\.?@#&=%]+)<!--image-->/g, (m, p1) => `<div class="divimage"><a href="${p1}" target="_blank"><img src="${p1}" border="0" class="imagecontent"></a></div>`);
  1420. }
  1421. rspRaw += `</div>`;
  1422. content.innerHTML = rspRaw;
  1423. appApi.translateBody(content);
  1424. xhr.done = true;
  1425. } else {
  1426. //重新下载
  1427. xhr.XHRRetryFun(xhr, msg);
  1428. }
  1429. },
  1430. onerror: () => {
  1431. //重新下载
  1432. xhr.XHRRetryFun(xhr, msg);
  1433. }
  1434. });
  1435. },//下载章节,全部完成后拼成分卷格式
  1436. XHRAdd: null,
  1437. refreshProgress: (info, err) => {
  1438. if (err) { content.innerHTML = err + content.innerHTML; }
  1439. },
  1440. translateBody: translateBody,
  1441. };
  1442. let bookInfo = { aid: article_id, refreshProgress: appApi.refreshProgress };
  1443. [bookInfo.XHRAdd] = XHRDownloader();
  1444. let dlink = `http://${appApi.appApiDomain}${appApi.appApiPath}`;
  1445. let xhr = { start: false, done: false, url: dlink, loadFun: appApi.appApiLoadChapter, data: { cid: chapter_id }, bookInfo: bookInfo };
  1446. xhr.bookInfo.XHRAdd(xhr);
  1447. content.innerHTML = '正在下载,请稍候...';
  1448. }
  1449. }
  1450. }
  1451.  
  1452. //评论页面
  1453. let articleReg = /\/modules\/article\//;
  1454. if (articleReg.test(window.location.href)) {
  1455. let rid = hrefUrl.searchParams.get('rid');
  1456. let page = hrefUrl.searchParams.get('page');
  1457. let codeEleArr = document.querySelectorAll(".jieqiCode");
  1458. let yidSet = new Set();
  1459. for (let code of codeEleArr) {
  1460. let yidDivEle = code.parentElement.parentElement;
  1461. let yid;
  1462. for (let aEle of yidDivEle.getElementsByTagName('a')) {
  1463. yid = aEle.getAttribute("name");
  1464. if (yid) { break; }
  1465. }
  1466. if (rid && yid) {
  1467. let codeJson = code.innerText.replace(/\s/g,'');
  1468. let locCfg
  1469. try {
  1470. locCfg = JSON.parse(codeJson);
  1471. }
  1472. catch (e) {
  1473. console.log(e);
  1474. continue;
  1475. }
  1476. if (locCfg
  1477. && ePubEidterCfgUID == locCfg.UID
  1478. && locCfg.aid
  1479. && locCfg.pathname
  1480. && (locCfg.ImgLocationBase64 ||(locCfg.ImgLocation && 0 < locCfg.ImgLocation.length))
  1481. && (!yidSet.has(yid))
  1482. ) {
  1483. yidSet.add(yid);
  1484. let titleDivEle = yidDivEle.firstElementChild;
  1485. let epubRefEle = document.createElement('a');
  1486. epubRefEle.innerText = '[使用配置生成ePub]';
  1487. epubRefEle.style.color = "fuchsia";
  1488. epubRefEle.href = `${locCfg.pathname}?rid=${rid}&page=${page ? page : "1"}&yid=${yid}&CfgRef=1`;
  1489. titleDivEle.insertBefore(epubRefEle, titleDivEle.firstElementChild);
  1490. }
  1491. }
  1492. }
  1493. }
  1494.  
  1495. //读取到的配置
  1496. let ImgLocationCfgRef = [];
  1497. ///modules/article/reviewshow.php?rid=270583
  1498. if ("1" == hrefUrl.searchParams.get('CfgRef')) {
  1499. const ridCfg = hrefUrl.searchParams.get('rid');
  1500. const pageCfg = hrefUrl.searchParams.get('page');
  1501. const yidCfg = hrefUrl.searchParams.get('yid');
  1502. if (ridCfg && yidCfg) {
  1503. let articleUrl = `${hrefUrl.origin}/modules/article/reviewshow.php?rid=${ridCfg}&page=${pageCfg}`;
  1504. GM_xmlhttpRequest({
  1505. method: 'GET',
  1506. url: articleUrl,
  1507. onload: function (response) {
  1508. if (response.status == 200) {
  1509. let domParser = new DOMParser();
  1510. let rspHtml = domParser.parseFromString(response.responseText.replaceAll(xmlIllegalCharacters, ''), "text/html");
  1511. let codeEleArr = rspHtml.querySelectorAll(".jieqiCode");
  1512. for (let code of codeEleArr) {
  1513. let yidDivEle = code.parentElement.parentElement;
  1514. let yid;
  1515. for (let aEle of yidDivEle.getElementsByTagName('a')) {
  1516. yid = aEle.getAttribute("name");
  1517. if (yid) { break; }
  1518. }
  1519. if (yid && yidCfg == yid) {
  1520. let codeJson = code.innerText.replace(/\s/g, '');
  1521. let locCfg
  1522. try {
  1523. locCfg = JSON.parse(codeJson);
  1524. }
  1525. catch (e) {
  1526. console.log(e);
  1527. continue;
  1528. }
  1529. //解压
  1530. if (locCfg.ImgLocationBase64) {
  1531. let zip = new JSZip();
  1532. let textDec = new TextDecoder();
  1533. zip.load(locCfg.ImgLocationBase64, { base64: true });
  1534. let fileArry = zip.file(ImgLocationFile)._data.getContent();
  1535. let imgLocJson = textDec.decode(fileArry);
  1536. let ImgLocation = JSON.parse(imgLocJson);
  1537. locCfg.ImgLocation = ImgLocation;
  1538. }
  1539. if (locCfg
  1540. && ePubEidterCfgUID == locCfg.UID
  1541. && locCfg.aid
  1542. && locCfg.pathname
  1543. && locCfg.ImgLocation
  1544. && 0 < locCfg.ImgLocation.length
  1545. ) {
  1546. ImgLocationCfgRef.push(locCfg);
  1547. }
  1548. }
  1549. }
  1550. }
  1551. else {
  1552. console.log(articleUrl);
  1553. console.log("配置下载失败");
  1554. }
  1555. },
  1556. onerror: () => {
  1557. console.log(articleUrl);
  1558. console.log("配置下载失败");
  1559. }
  1560. });
  1561. }
  1562. }
  1563.  
  1564. const defaultCSS_content = `
  1565. nav#landmarks {
  1566. display:none;
  1567. }
  1568.  
  1569. nav#page-list {
  1570. display:none;
  1571. }
  1572.  
  1573. ol {
  1574. list-style-type: none;
  1575. }
  1576.  
  1577. .volumetitle ,
  1578. .chaptertitle {
  1579. text-align: center;
  1580. }
  1581.  
  1582. `;
  1583. const nav_xhtml_content = `
  1584. <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="en" xml:lang="en">
  1585. <head>
  1586. <title>ePub NAV</title>
  1587. <meta charset="utf-8"/>
  1588. <link href="../Styles/default.css" rel="stylesheet" type="text/css"/>
  1589. </head>
  1590. <body epub:type="frontmatter">
  1591. <nav epub:type="toc" id="toc" role="doc-toc">
  1592. <h2><a href= "#toc">目录</a></h2>
  1593. </nav>
  1594. </body>
  1595. </html>`;
  1596. const container_xml = `<?xml version="1.0" ?>
  1597. <container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
  1598. <rootfiles>
  1599. <rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml" />
  1600. </rootfiles>
  1601. </container>`;
  1602. const ePubEidterHtml = `
  1603. <div class="main" style="width: 1200px;">
  1604. <!--左 章节-->
  1605. <div id="left">
  1606. <div class="block" style="min-height: 230px;">
  1607. <div class="blocktitle">
  1608. <span class="txt">操作设置</span>
  1609. <span class="txtr"></span>
  1610. </div>
  1611. <div class="blockcontent">
  1612. <div style="padding-left:10px">
  1613. <ul class="ulrow">
  1614. <li>
  1615. <label for="SendArticle">将配置发送到书评:</label>
  1616. <input type="checkbox" id="SendArticle" />
  1617. </li>
  1618. <li>
  1619. <label for="ePubEditerClose">生成后自动关闭:</label>
  1620. <input type="checkbox" id="ePubEditerClose" checked="true" />
  1621. </li>
  1622. <li>配置内容:</li>
  1623. <li>
  1624. <textarea id="CfgArea" class="textarea"></textarea>
  1625. </li>
  1626. <li><input type="button" id="EidterImportBtn" class="button" value="导入配置" /></li>
  1627. <li><input type="button" id="EidterBuildBtn" class="button" value="生成ePub" /></li>
  1628. </ul>
  1629. <div class="cb"></div>
  1630. </div>
  1631. </div>
  1632. </div>
  1633. <div class="block" style="min-height: 230px;">
  1634. <div class="blocktitle">
  1635. <span class="txt">分卷</span>
  1636. <span class="txtr"></span>
  1637. </div>
  1638. <div class="blockcontent">
  1639. <div style="padding-left:10px">
  1640. <ul id="VolumeUL" class="ulrow">
  1641.  
  1642. </ul>
  1643. <div class="cb"></div>
  1644. </div>
  1645. </div>
  1646. </div>
  1647. </div>
  1648. <!--左 章节-->
  1649. <div id="left">
  1650. <div class="block" style="min-height: 230px;">
  1651. <div class="blocktitle">
  1652. <span class="txt">推测插图位置</span>
  1653. <span class="txtr"></span>
  1654. </div>
  1655. <div class="blockcontent">
  1656. <div style="padding-left:10px">
  1657. <ul id="ImgUL" class="ulrow">
  1658.  
  1659. </ul>
  1660. <div class="cb"></div>
  1661. </div>
  1662. </div>
  1663. </div>
  1664. <div class="block" style="min-height: 230px;">
  1665. <div class="blocktitle">
  1666. <span class="txt">章节</span>
  1667. <span class="txtr"></span>
  1668. </div>
  1669. <div class="blockcontent">
  1670. <div style="padding-left:10px">
  1671. <ul id="ChapterUL" class="ulrow">
  1672.  
  1673. </ul>
  1674. <div class="cb"></div>
  1675. </div>
  1676. </div>
  1677. </div>
  1678. </div>
  1679. <!--右 内容-->
  1680. <div id="centerm">
  1681. <!--内容-->
  1682. <div id="content">
  1683. <table class="grid" width="100%" align="center">
  1684. <tbody>
  1685. <tr>
  1686. <td width="4%" align="center"><span style="font-size:16px;">分<br>卷<br>插<br>图</span></td>
  1687. <td>
  1688. <div ondragover="return false" id="VolumeImg" style="height:155px;overflow:auto">
  1689.  
  1690. </div>
  1691. </td>
  1692. </tr>
  1693. </tbody>
  1694. </table>
  1695. <table class="grid" width="100%" align="center">
  1696. <caption>分卷内容</caption>
  1697. <tbody>
  1698. <tr>
  1699. <td>
  1700. <div id="VolumeText" style="height:500px;overflow: hidden scroll ;max-width: 900px;">
  1701. </div>
  1702. </td>
  1703. </tr>
  1704. </tbody>
  1705. </table>
  1706. </div>
  1707. </div>
  1708.  
  1709. </div>
  1710. `;
  1711. // Your code here...
  1712. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址