怠惰小说下载器

通用网站内容抓取工具,可批量抓取任意站点的小说、论坛内容等并保存为TXT文档

当前为 2023-10-24 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name DownloadAllContent
  3. // @name:zh-CN 怠惰小说下载器
  4. // @name:zh-TW 怠惰小説下載器
  5. // @name:ja 怠惰者小説ダウンロードツール
  6. // @namespace hoothin
  7. // @version 2.7.4.4
  8. // @description Fetch and download main content on current page, provide special support for novel
  9. // @description:zh-CN 通用网站内容抓取工具,可批量抓取任意站点的小说、论坛内容等并保存为TXT文档
  10. // @description:zh-TW 通用網站內容抓取工具,可批量抓取任意站點的小說、論壇內容等並保存為TXT文檔
  11. // @description:ja ユニバーサルサイトコンテンツクロールツール、クロール、フォーラム内容など
  12. // @author hoothin
  13. // @match http://*/*
  14. // @match https://*/*
  15. // @match ftp://*/*
  16. // @grant GM_xmlhttpRequest
  17. // @grant GM_registerMenuCommand
  18. // @grant GM_setValue
  19. // @grant GM_getValue
  20. // @grant GM_openInTab
  21. // @grant GM_setClipboard
  22. // @grant unsafeWindow
  23. // @license MIT License
  24. // @compatible chrome
  25. // @compatible firefox
  26. // @compatible opera 未测试
  27. // @compatible safari 未测试
  28. // @contributionURL https://ko-fi.com/hoothin
  29. // @contributionAmount 1
  30. // ==/UserScript==
  31.  
  32. if (window.top != window.self) {
  33. try {
  34. if (window.self.innerWidth < 250 || window.self.innerHeight < 250) {
  35. return;
  36. }
  37. } catch(e) {
  38. return;
  39. }
  40. }
  41.  
  42. (function (global, factory) {
  43. if (typeof define === "function" && define.amd) {
  44. define([], factory);
  45. } else if (typeof exports !== "undefined") {
  46. factory();
  47. } else {
  48. var mod = {
  49. exports: {}
  50. };
  51. factory();
  52. global.FileSaver = mod.exports;
  53. }
  54. })(this, function () {
  55. "use strict";
  56.  
  57. /*
  58. * FileSaver.js
  59. * A saveAs() FileSaver implementation.
  60. *
  61. * By Eli Grey, http://eligrey.com
  62. *
  63. * License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
  64. * source : http://purl.eligrey.com/github/FileSaver.js
  65. */
  66. var _global = typeof window === 'object' && window.window === window ? window : typeof self === 'object' && self.self === self ? self : typeof global === 'object' && global.global === global ? global : void 0;
  67.  
  68. function bom(blob, opts) {
  69. if (typeof opts === 'undefined') opts = {
  70. autoBom: false
  71. };else if (typeof opts !== 'object') {
  72. console.warn('Deprecated: Expected third argument to be a object');
  73. opts = {
  74. autoBom: !opts
  75. };
  76. }
  77.  
  78. if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
  79. return new Blob([String.fromCharCode(0xFEFF), blob], {
  80. type: blob.type
  81. });
  82. }
  83.  
  84. return blob;
  85. }
  86.  
  87. function download(url, name, opts) {
  88. var xhr = new XMLHttpRequest();
  89. xhr.open('GET', url);
  90. xhr.responseType = 'blob';
  91.  
  92. xhr.onload = function () {
  93. saveAs(xhr.response, name, opts);
  94. };
  95.  
  96. xhr.onerror = function () {
  97. console.error('could not download file');
  98. };
  99.  
  100. xhr.send();
  101. }
  102.  
  103. function corsEnabled(url) {
  104. var xhr = new XMLHttpRequest();
  105.  
  106. xhr.open('HEAD', url, false);
  107.  
  108. try {
  109. xhr.send();
  110. } catch (e) {}
  111.  
  112. return xhr.status >= 200 && xhr.status <= 299;
  113. }
  114.  
  115.  
  116. function click(node) {
  117. try {
  118. node.dispatchEvent(new MouseEvent('click'));
  119. } catch (e) {
  120. var evt = document.createEvent('MouseEvents');
  121. evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
  122. node.dispatchEvent(evt);
  123. }
  124. }
  125.  
  126.  
  127. var isMacOSWebView = _global.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent);
  128. var saveAs = _global.saveAs || (
  129. typeof window !== 'object' || window !== _global ? function saveAs() {}
  130.  
  131. : 'download' in HTMLAnchorElement.prototype && !isMacOSWebView ? function saveAs(blob, name, opts) {
  132. var URL = _global.URL || _global.webkitURL;
  133. var a = document.createElement('a');
  134. name = name || blob.name || 'download';
  135. a.download = name;
  136. a.rel = 'noopener';
  137.  
  138. if (typeof blob === 'string') {
  139. a.href = blob;
  140.  
  141. if (a.origin !== location.origin) {
  142. corsEnabled(a.href) ? download(blob, name, opts) : click(a, a.target = '_blank');
  143. } else {
  144. click(a);
  145. }
  146. } else {
  147. a.href = URL.createObjectURL(blob);
  148. setTimeout(function () {
  149. URL.revokeObjectURL(a.href);
  150. }, 4E4);
  151.  
  152. setTimeout(function () {
  153. click(a);
  154. }, 0);
  155. }
  156. }
  157. : 'msSaveOrOpenBlob' in navigator ? function saveAs(blob, name, opts) {
  158. name = name || blob.name || 'download';
  159.  
  160. if (typeof blob === 'string') {
  161. if (corsEnabled(blob)) {
  162. download(blob, name, opts);
  163. } else {
  164. var a = document.createElement('a');
  165. a.href = blob;
  166. a.target = '_blank';
  167. setTimeout(function () {
  168. click(a);
  169. });
  170. }
  171. } else {
  172. navigator.msSaveOrOpenBlob(bom(blob, opts), name);
  173. }
  174. }
  175. : function saveAs(blob, name, opts, popup) {
  176. popup = popup || open('', '_blank');
  177.  
  178. if (popup) {
  179. popup.document.title = popup.document.body.innerText = 'downloading...';
  180. }
  181.  
  182. if (typeof blob === 'string') return download(blob, name, opts);
  183. var force = blob.type === 'application/octet-stream';
  184.  
  185. var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari;
  186.  
  187. var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent);
  188.  
  189. if ((isChromeIOS || force && isSafari || isMacOSWebView) && typeof FileReader !== 'undefined') {
  190. var reader = new FileReader();
  191.  
  192. reader.onloadend = function () {
  193. var url = reader.result;
  194. url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;');
  195. if (popup) popup.location.href = url;else location = url;
  196. popup = null;
  197. };
  198.  
  199. reader.readAsDataURL(blob);
  200. } else {
  201. var URL = _global.URL || _global.webkitURL;
  202. var url = URL.createObjectURL(blob);
  203. if (popup) popup.location = url;else location.href = url;
  204. popup = null;
  205.  
  206. setTimeout(function () {
  207. URL.revokeObjectURL(url);
  208. }, 4E4);
  209. }
  210. });
  211. _global.saveAs = saveAs.saveAs = saveAs;
  212.  
  213. if (typeof module !== 'undefined') {
  214. module.exports = saveAs;
  215. }
  216. });
  217.  
  218. (function() {
  219. 'use strict';
  220. var indexReg=/PART\b|^Prologue|Chapter\s*[\-_]?\d+|分卷|^序$|^序\s*言|^序\s*章|^前\s*言|^附\s*[录錄]|^引\s*[言子]|^摘\s*要|^[楔契]\s*子|^后\s*记|^後\s*記|^附\s*言|^结\s*语|^結\s*語|^尾\s*[声聲]|^最終話|^最终话|^番\s*外|^\d+[\s\.、,,)\-_::][^\d#\.]+$|^[第(]?\s*[\d〇零一二三四五六七八九十百千万萬-]+\s*[、)章节節回卷折篇幕集话話]/i;
  221. var innerNextPage=/^\s*(下一[页頁张張]|next\s*page|次のページ)/i;
  222. var lang = navigator.appName=="Netscape"?navigator.language:navigator.userLanguage;
  223. var i18n={};
  224. var rCats=[];
  225. var processFunc;
  226. var win=(typeof unsafeWindow=='undefined'? window : unsafeWindow);
  227. switch (lang){
  228. case "zh-CN":
  229. case "zh-SG":
  230. i18n={
  231. fetch:"开始下载小说【Ctrl+F9】",
  232. info:"本文是使用怠惰小说下载器(DownloadAllContent)下载的",
  233. error:"该段内容获取失败",
  234. downloading:"已下载完成 %s 段,剩余 %s 段<br>正在下载 %s",
  235. complete:"已全部下载完成,共 %s 段",
  236. del:"设置文本干扰码的CSS选择器",
  237. custom:"自定规则下载",
  238. customInfo:"输入网址或者章节CSS选择器",
  239. reSort:"按标题名重新排序章节",
  240. reSortUrl:"按网址重新排序章节",
  241. setting:"选项参数设置",
  242. searchRule:"搜索网站规则",
  243. abort:"跳过此章",
  244. save:"保存当前",
  245. saveAsMd:"存为 Markdown",
  246. downThreadNum:"设置同时下载的线程数",
  247. customTitle:"自定义章节标题,输入内页文字对应选择器",
  248. reSortDefault:"默认按页面中位置排序章节",
  249. reverse:"反转章节排序",
  250. saveBtn:"保存设置",
  251. saveOk:"保存成功",
  252. nextPage:"嗅探章节内分页",
  253. nextPageReg:"自定义分页正则",
  254. retainImage:"保留正文中图片的网址",
  255. minTxtLength:"当检测到的正文字数小于此数,则尝试重新抓取"
  256. };
  257. break;
  258. case "zh-TW":
  259. case "zh-HK":
  260. i18n={
  261. fetch:"開始下載小說【Ctrl+F9】",
  262. info:"本文是使用怠惰小說下載器(DownloadAllContent)下載的",
  263. error:"該段內容獲取失敗",
  264. downloading:"已下載完成 %s 段,剩餘 %s 段<br>正在下載 %s",
  265. complete:"已全部下載完成,共 %s 段",
  266. del:"設置文本干擾碼的CSS選擇器",
  267. custom:"自訂規則下載",
  268. customInfo:"輸入網址或者章節CSS選擇器",
  269. reSort:"按標題名重新排序章節",
  270. reSortUrl:"按網址重新排序章節",
  271. setting:"選項參數設定",
  272. searchRule:"搜尋網站規則",
  273. abort:"跳過此章",
  274. save:"保存當前",
  275. saveAsMd:"存爲 Markdown",
  276. downThreadNum:"設置同時下載的綫程數",
  277. customTitle:"自訂章節標題,輸入內頁文字對應選擇器",
  278. reSortDefault:"預設依頁面中位置排序章節",
  279. reverse:"反轉章節排序",
  280. saveBtn:"儲存設定",
  281. saveOk:"儲存成功",
  282. nextPage:"嗅探章節內分頁",
  283. nextPageReg:"自訂分頁正規",
  284. retainImage:"保留內文圖片的網址",
  285. minTxtLength:"當偵測到的正文字數小於此數,則嘗試重新抓取"
  286. };
  287. break;
  288. default:
  289. i18n={
  290. fetch:"Download [Ctrl+F9]",
  291. info:"The TXT is downloaded by 'DownloadAllContent'",
  292. error:"Failed in downloading current chapter",
  293. downloading:"%s pages are downloaded, there are still %s pages left<br>Downloading %s ......",
  294. complete:"Completed! Get %s pages in total",
  295. del:"Set css selectors for ignore",
  296. custom:"Custom to download",
  297. customInfo:"Input urls OR sss selectors for chapter links",
  298. reSort:"ReSort by title",
  299. reSortUrl:"Resort by URLs",
  300. setting:"Open Setting",
  301. searchRule:"Search rule",
  302. abort:"Abort",
  303. save:"Save",
  304. saveAsMd:"Save as Markdown",
  305. downThreadNum:"Set threadNum for download",
  306. customTitle: "Customize the chapter title, enter the selector on inner page",
  307. reSortDefault: "Default sort by position in the page",
  308. reverse:"Reverse chapter ordering",
  309. saveBtn:"Save Setting",
  310. saveOk:"Save Over",
  311. nextPage:"Check next page in chapter",
  312. nextPageReg:"Custom RegExp of next page",
  313. retainImage:"Keep the URL of image if there are images in the text",
  314. minTxtLength:"Try to crawl again when the length of content is less than this"
  315. };
  316. break;
  317. }
  318. var firefox=navigator.userAgent.toLowerCase().indexOf('firefox')!=-1,curRequests=[];
  319. var rocketContent,txtDownContent,txtDownWords,txtDownQuit,txtDownDivInited=false;
  320.  
  321. function initTxtDownDiv(){
  322. if(txtDownDivInited)return;
  323. txtDownDivInited=true;
  324. rocketContent=document.createElement("div");
  325. document.body.appendChild(rocketContent);
  326. rocketContent.outerHTML=`
  327. <div id="txtDownContent">
  328. <div style="font-size:16px;color:#333333;width:362px;height:110px;position:fixed;left:50%;top:50%;margin-top:-25px;margin-left:-150px;z-index:100000;background-color:#ffffff;border:1px solid #afb3b6;border-radius:10px;opacity:0.95;filter:alpha(opacity=95);box-shadow:5px 5px 20px 0px #000;">
  329. <div id="txtDownWords" style="position:absolute;width:275px;height: 90px;max-height: 90%;border: 1px solid #f3f1f1;padding: 8px;border-radius: 10px;overflow: auto;">
  330. Analysing......
  331. </div>
  332. <div id="txtDownQuit" style="width: 30px;height: 30px;border-radius: 30px;position:absolute;right:2px;top:2px;cursor: pointer;background-color:#ff5a5a;">
  333. <span style="height: 30px;line-height: 30px;display:block;color:#FFF;text-align:center;font-size: 12px;font-weight: bold;font-family: arial;background: initial; float: initial;">╳</span>
  334. </div>
  335. <div style="position:absolute;right:0px;bottom:2px;cursor: pointer;max-width:85px">
  336. <button id="abortRequest" style="background: #008aff;border: 0;padding: 5px;border-radius: 6px;color: white;float: right;margin: 1px;height: 25px;display:none;line-height: 16px;">${getI18n('abort')}</button>
  337. <button id="tempSaveTxt" style="background: #008aff;border: 0;padding: 5px;border-radius: 6px;color: white;float: right;margin: 1px;height: 25px;line-height: 16px;cursor: pointer;">${getI18n('save')}</button>
  338. <button id="saveAsMd" style="background: #008aff;border: 0;padding: 5px;border-radius: 6px;color: white;float: right;margin: 1px;height: 25px;line-height: 16px;cursor: pointer;overflow: hidden;" title="${getI18n('saveAsMd')}">Markdown</button>
  339. </div>
  340. </div>
  341. </div>`;
  342. txtDownContent=document.querySelector("#txtDownContent");
  343. txtDownWords=document.querySelector("#txtDownWords");
  344. txtDownQuit=document.querySelector("#txtDownQuit");
  345. txtDownQuit.onclick=function(){
  346. txtDownContent.style.display="none";
  347. txtDownContent.parentNode.removeChild(txtDownContent);
  348. };
  349. initTempSave();
  350. }
  351.  
  352. function saveContent() {
  353. if (win.downloadAllContentSaveAsZip) {
  354. win.downloadAllContentSaveAsZip(rCats, i18n.info, content => {
  355. saveAs(content, document.title + ".zip");
  356. });
  357. } else {
  358. var blob = new Blob([i18n.info + "\r\n\r\n" + document.title + "\r\n\r\n" + rCats.join("\r\n\r\n")], {type: "text/plain;charset=utf-8"});
  359. saveAs(blob, document.title + ".txt");
  360. }
  361. }
  362.  
  363. function initTempSave(){
  364. var tempSavebtn = document.getElementById('tempSaveTxt');
  365. var abortbtn = document.getElementById('abortRequest');
  366. var saveAsMd = document.getElementById('saveAsMd');
  367. tempSavebtn.onclick = function(){
  368. saveContent();
  369. console.log(curRequests);
  370. }
  371. abortbtn.onclick = function(){
  372. let curRequest = curRequests.pop();
  373. if(curRequest)curRequest[1].abort();
  374. }
  375. saveAsMd.onclick = function(){
  376. let txt = i18n.info+"\n\n---\n"+document.title+"\n===\n";
  377. rCats.forEach(cat => {
  378. cat = cat.replace("\r\n", "\n---").replace(/(\r\n|\n\r)+/g, "\n\n").replace(/[\n\r]\t+/g, "\n");
  379. txt += '\n\n'+cat;
  380. });
  381. var blob = new Blob([txt], {type: "text/plain;charset=utf-8"});
  382. saveAs(blob, document.title+".md");
  383. }
  384. }
  385.  
  386. function indexDownload(aEles){
  387. if(aEles.length<1)return;
  388. initTxtDownDiv();
  389. if(GM_getValue("contentSort")){
  390. aEles.sort(function(a,b){
  391. return parseInt(a.innerText.replace(/[^0-9]/ig,"")) - parseInt(b.innerText.replace(/[^0-9]/ig,""));
  392. });
  393. }
  394. if(GM_getValue("contentSortUrl")){
  395. aEles.sort(function(a,b){
  396. return parseInt(a.href.replace(/[^0-9]/ig,"")) - parseInt(b.href.replace(/[^0-9]/ig,""));
  397. });
  398. }
  399. if(GM_getValue("reverse")){
  400. aEles=aEles.reverse();
  401. }
  402. rCats=[];
  403. var minTxtLength=GM_getValue("minTxtLength") || 100;
  404. var customTitle=GM_getValue("customTitle");
  405. var disableNextPage=!!GM_getValue("disableNextPage");
  406. var customNextPageReg=GM_getValue("nextPageReg");
  407. if (customNextPageReg) {
  408. try {
  409. innerNextPage = new RegExp(customNextPageReg);
  410. } catch(e) {
  411. console.warn(e);
  412. }
  413. }
  414. var insertSigns=[];
  415. // var j=0,rCats=[];
  416. var downIndex=0,downNum=0,downOnce=function(wait){
  417. if(downNum>=aEles.length)return;
  418. let curIndex=downIndex;
  419. let aTag=aEles[curIndex];
  420. let validTimes=0;
  421. let request=(aTag, curIndex)=>{
  422. let tryTimes=0;
  423. let requestBody={
  424. method: 'GET',
  425. url: aTag.href,
  426. headers:{
  427. referer:aTag.href,
  428. "Content-Type":"text/html;charset="+document.charset
  429. },
  430. timeout:10000,
  431. overrideMimeType:"text/html;charset="+document.charset,
  432. onload: function(result) {
  433. downIndex++;
  434. downNum++;
  435. let doc = getDocEle(result.responseText);
  436. let base = doc.querySelector("base");
  437. let nextPage = !disableNextPage && !processFunc && checkNextPage(doc, base ? base.href : aTag.href);
  438. if(nextPage){
  439. var inArr=false;
  440. for(var ai=0;ai<aEles.length;ai++){
  441. if(aEles[ai].href==nextPage.href){
  442. inArr=true;
  443. break;
  444. }
  445. }
  446. if(!inArr){
  447. nextPage.innerText=aTag.innerText+"\t>>";
  448. aEles.push(nextPage);
  449. let targetIndex = curIndex;
  450. for(let a=0;a<insertSigns.length;a++){
  451. let signs=insertSigns[a],breakSign=false;
  452. if(signs){
  453. for(let b=0;b<signs.length;b++){
  454. let sign=signs[b];
  455. if(sign==curIndex){
  456. targetIndex=a;
  457. breakSign=true;
  458. break;
  459. }
  460. }
  461. }
  462. if(breakSign)break;
  463. }
  464. let insertSign = insertSigns[targetIndex];
  465. if(!insertSign)insertSigns[targetIndex] = [];
  466. insertSigns[targetIndex].push(aEles.length-1);
  467. }
  468. }
  469. if (result.status >= 400) {
  470. console.warn("error:", `status: ${result.status} from: ${aTag.href}`);
  471. } else {
  472. console.log(result.status);
  473. }
  474. if (customTitle) {
  475. try {
  476. let title = doc.querySelector(customTitle);
  477. if (title && title.innerText) {
  478. aTag.innerText = title.innerText;
  479. }
  480. } catch(e) {
  481. console.warn(e);
  482. }
  483. }
  484. let validData = processDoc(curIndex, aTag, doc, (result.status>=400?` status: ${result.status} from: ${aTag.href} `:""), validTimes < 3);
  485. if (!validData && validTimes++ < 3) {
  486. downIndex--;
  487. downNum--;
  488. return GM_xmlhttpRequest(requestBody);
  489. }
  490. if (wait) {
  491. setTimeout(() => {
  492. downOnce(wait);
  493. }, wait);
  494. } else downOnce();
  495. },
  496. onerror: function(e) {
  497. console.warn("error:", e);
  498. if(++tryTimes<3){
  499. return GM_xmlhttpRequest(requestBody);
  500. }
  501. downIndex++;
  502. downNum++;
  503. processDoc(curIndex, aTag, null, ` NETWORK ERROR: '+${(e.response||e.responseText)} from: ${aTag.href} `);
  504. if (wait) {
  505. setTimeout(() => {
  506. downOnce(wait);
  507. }, wait);
  508. } else downOnce();
  509. },
  510. ontimeout: function(e) {
  511. console.warn("timeout: times="+tryTimes+" url="+aTag.href);
  512. //console.log(e);
  513. if(++tryTimes<3){
  514. return GM_xmlhttpRequest(requestBody);
  515. }
  516. downIndex++;
  517. downNum++;
  518. processDoc(curIndex, aTag, null, ` TIMEOUT: '+${aTag.href} `);
  519. if (wait) {
  520. setTimeout(() => {
  521. downOnce(wait);
  522. }, wait);
  523. } else downOnce();
  524. }
  525. };
  526. return [curIndex, GM_xmlhttpRequest(requestBody), aTag.href];
  527. }
  528. if(!aTag){
  529. let waitAtagReadyInterval=setInterval(function(){
  530. if(downNum>=aEles.length)clearInterval(waitAtagReadyInterval);
  531. aTag=aEles[curIndex];
  532. if(aTag){
  533. clearInterval(waitAtagReadyInterval);
  534. request(aTag, curIndex);
  535. }
  536. },1000);
  537. return null;
  538. }
  539. let result = request(aTag, curIndex);
  540. if (result) curRequests.push(result);
  541. return result;
  542. };
  543. function getDocEle(str){
  544. var doc = null;
  545. try {
  546. doc = document.implementation.createHTMLDocument('');
  547. doc.documentElement.innerHTML = str;
  548. }
  549. catch (e) {
  550. console.log('parse error');
  551. }
  552. return doc;
  553. }
  554. function sortInnerPage(){
  555. var pageArrs=[],maxIndex=0,i,j;
  556. for(i=0;i<insertSigns.length;i++){
  557. var signs=insertSigns[i];
  558. if(signs){
  559. for(j=0;j<signs.length;j++){
  560. var sign=signs[j];
  561. var cat=rCats[sign];
  562. rCats[sign]=null;
  563. if(!pageArrs[i])pageArrs[i]=[];
  564. pageArrs[i].push(cat);
  565. }
  566. }
  567. }
  568. for(i=pageArrs.length-1;i>=0;i--){
  569. let pageArr=pageArrs[i];
  570. if(pageArr){
  571. for(j=pageArr.length-1;j>=0;j--){
  572. rCats.splice(i+1, 0, pageArr[j]);
  573. }
  574. }
  575. }
  576. rCats = rCats.filter(function(e){return e!=null});
  577. }
  578. var waitForComplete;
  579. function processDoc(i, aTag, doc, cause, check){
  580. let cbFunc=content=>{
  581. rCats[i]=(aTag.innerText.replace(/[\r\n\t]/g, "") + "\r\n" + (cause || '') + content.replace(/\s*$/, ""));
  582. curRequests = curRequests.filter(function(e){return e[0]!=i});
  583. txtDownContent.style.display="block";
  584. txtDownWords.innerHTML=getI18n("downloading",[downNum,(aEles.length-downNum),aTag.innerText]);
  585. if(downNum==aEles.length){
  586. if(waitForComplete) clearTimeout(waitForComplete);
  587. waitForComplete=setTimeout(()=>{
  588. if(downNum==aEles.length){
  589. txtDownWords.innerHTML=getI18n("complete",[downNum]);
  590. sortInnerPage();
  591. saveContent();
  592. }
  593. },3000);
  594. }
  595. };
  596. let contentResult=getPageContent(doc, content=>{
  597. cbFunc(content);
  598. }, aTag.href);
  599. if(contentResult!==false){
  600. if(check && contentResult.length<minTxtLength){
  601. return false;
  602. }
  603. cbFunc(contentResult);
  604. }
  605. return true;
  606. }
  607. var downThreadNum = parseInt(GM_getValue("downThreadNum"));
  608. downThreadNum = downThreadNum || 20;
  609. if (downThreadNum > 0) {
  610. for (var i = 0; i < downThreadNum; i++) {
  611. downOnce();
  612. if (downIndex >= aEles.length - 1 || downIndex >= downThreadNum - 1) break;
  613. else downIndex++;
  614. }
  615. } else {
  616. downOnce(-downThreadNum * 1000);
  617. if (downIndex < aEles.length - 1 && downIndex < downThreadNum - 1) downIndex++;
  618. }
  619.  
  620. /*for(let i=0;i<aEles.length;i++){
  621. let aTag=aEles[i];
  622. GM_xmlhttpRequest({
  623. method: 'GET',
  624. url: aTag.href,
  625. overrideMimeType:"text/html;charset="+document.charset,
  626. onload: function(result) {
  627. var doc = getDocEle(result.responseText);
  628. processDoc(i, aTag, doc);
  629. }
  630. });
  631. }*/
  632. }
  633.  
  634. function canonicalUri(src, baseUrl) {
  635. if (!src) {
  636. return "";
  637. }
  638. if (src.charAt(0) == "#") return baseUrl + src;
  639. if (src.charAt(0) == "?") return baseUrl.replace(/^([^\?#]+).*/, "$1" + src);
  640. let origin = location.protocol + '//' + location.host;
  641. let url = baseUrl || origin;
  642. url = url.replace(/(\?|#).*/, "");
  643. if (/https?:\/\/[^\/]+$/.test(url)) url = url + '/';
  644. if (url.indexOf("http") !== 0) url = origin + url;
  645. var root_page = /^[^\?#]*\//.exec(url)[0],
  646. root_domain = /^\w+\:\/\/\/?[^\/]+/.exec(root_page)[0],
  647. absolute_regex = /^\w+\:\/\//;
  648. while (src.indexOf("../") === 0) {
  649. src = src.substr(3);
  650. root_page = root_page.replace(/\/[^\/]+\/$/, "/");
  651. }
  652. src = src.replace(/\.\//, "");
  653. if (/^\/\/\/?/.test(src)) {
  654. src = location.protocol + src;
  655. }
  656. return (absolute_regex.test(src) ? src : ((src.charAt(0) == "/" ? root_domain : root_page) + src));
  657. }
  658.  
  659. function checkNextPage(doc, baseUrl) {
  660. let aTags = doc.querySelectorAll("a"), nextPage = null;
  661. for (var i = 0; i < aTags.length; i++) {
  662. let aTag = aTags[i];
  663. if (innerNextPage.test(aTag.innerText) && aTag.href && !/javascript:|#/.test(aTag.href)) {
  664. let nextPageHref = canonicalUri(aTag.getAttribute("href"), baseUrl || location.href);
  665. if (nextPageHref != location.href) {
  666. nextPage = aTag;
  667. nextPage.href = nextPageHref;
  668. break;
  669. }
  670. }
  671. }
  672. return nextPage;
  673. }
  674.  
  675. function textNodesUnder(el){
  676. var n, a=[], walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false);
  677. while(n=walk.nextNode()) a.push(n);
  678. return a;
  679. }
  680.  
  681. function getPageContent(doc, cb, url){
  682. if(!doc)return i18n.error;
  683. if(processFunc){
  684. return processFunc(doc, cb, url);
  685. }
  686. [].forEach.call(doc.querySelectorAll("span,div,ul"),function(item){
  687. var thisStyle=doc.defaultView?doc.defaultView.getComputedStyle(item):item.style;
  688. if(thisStyle && (thisStyle.display=="none" || (item.nodeName=="SPAN" && thisStyle.fontSize=="0px"))){
  689. item.innerHTML="";
  690. }
  691. });
  692. var i,j,k,rStr="",pageData=(doc.body?doc.body:doc).cloneNode(true),delList=[];
  693. pageData.innerHTML=pageData.innerHTML.replace(/\<\!\-\-((.|[\n|\r|\r\n])*?)\-\-\>/g,"");
  694. [].forEach.call(pageData.querySelectorAll("font.jammer"),function(item){
  695. item.innerHTML="";
  696. });
  697. var selectors=GM_getValue("selectors");
  698. if(selectors){
  699. [].forEach.call(pageData.querySelectorAll(selectors),function(item){
  700. item.innerHTML="";
  701. });
  702. }
  703. [].forEach.call(pageData.querySelectorAll("script,style,link,img,noscript,iframe"),function(item){delList.push(item);});
  704. [].forEach.call(delList,function(item){item.innerHTML="";});
  705. var endEle = ele => {
  706. return /^(I|STRONG|B|FONT|P|DL|DD|H\d)$/.test(ele.nodeName) && ele.children.length == 0;
  707. };
  708. var largestContent,contents=pageData.querySelectorAll("span,div,article,p,td"),largestNum=0;
  709. for(i=0;i<contents.length;i++){
  710. let content=contents[i],hasText=false,allSingle=true,item,curNum=0;
  711. if(/footer/.test(content.className))continue;
  712. for(j=content.childNodes.length-1;j>=0;j--){
  713. item=content.childNodes[j];
  714. if(item.nodeType==3){
  715. if(/^\s*$/.test(item.data)){
  716. item.innerHTML="";
  717. }else hasText=true;
  718. }else if(/^(I|A|STRONG|B|FONT|P|DL|DD|H\d)$/.test(item.nodeName)){
  719. hasText=true;
  720. }else if(item.nodeType==1&&item.children.length==1&&/^(I|A|STRONG|B|FONT|P|DL|DD|H\d)$/.test(item.children[0].nodeName)){
  721. hasText=true;
  722. }
  723. }
  724. for(j=content.childNodes.length-1;j>=0;j--){
  725. item=content.childNodes[j];
  726. if(item.nodeType==1 && !/^(I|A|STRONG|B|FONT|BR)$/.test(item.nodeName) && /^[\s\-\_\?\>\|]*$/.test(item.innerHTML)){
  727. item.innerHTML="";
  728. }
  729. }
  730. if(content.childNodes.length>1){
  731. let indexItem=0;
  732. for(j=0;j<content.childNodes.length;j++){
  733. item=content.childNodes[j];
  734. if(item.nodeType==1){
  735. if(item.innerText && item.innerText.length<50 && indexReg.test(item.innerText))indexItem++;
  736. for(k=0;k<item.childNodes.length;k++){
  737. var childNode=item.childNodes[k];
  738. if(childNode.nodeType!=3 && !/^(I|A|STRONG|B|FONT|BR)$/.test(childNode.nodeName)){
  739. allSingle=false;
  740. break;
  741. }
  742. }
  743. if(!allSingle)break;
  744. }
  745. }
  746. if(indexItem>=5)continue;
  747. }else{
  748. allSingle=false;
  749. }
  750. if(!allSingle && !hasText){
  751. continue;
  752. }else {
  753. if(pageData==document && content.offsetWidth<=0 && content.offsetHeight<=0){
  754. continue;
  755. }
  756. [].forEach.call(content.childNodes,function(item){
  757. if(item.nodeType==3)curNum+=item.data.trim().length;
  758. else if(endEle(item) || (item.nodeType == 1 && item.children.length == 1 && endEle(item.children[0]))) curNum += (firefox ? item.textContent.trim().length : item.innerText.trim().length);
  759. });
  760. }
  761. if(curNum>largestNum){
  762. largestNum=curNum;
  763. largestContent=content;
  764. }
  765. }
  766. if(!largestContent)return i18n.error+" : NO TEXT CONTENT";
  767. var retainImage=!!GM_getValue("retainImage");
  768. var childlist=pageData.querySelectorAll(largestContent.nodeName);//+(largestContent.className?"."+largestContent.className.replace(/(^\s*)|(\s*$)/g, '').replace(/\s+/g, '.'):""));
  769. function getRightStr(ele, noTextEnable){
  770. if(retainImage){
  771. [].forEach.call(ele.querySelectorAll("img[src]"), img => {
  772. let imgTxtNode=document.createTextNode(`![img](${canonicalUri(img.getAttribute("src"), url || location.href)})`);
  773. img.parentNode.replaceChild(imgTxtNode, img);
  774. });
  775. }
  776. let childNodes=ele.childNodes,cStr="\r\n",hasText=false;
  777. [].forEach.call(ele.querySelectorAll("a[href]"), a => {
  778. a.parentNode && a.parentNode.removeChild(a);
  779. });
  780. for(let j=0;j<childNodes.length;j++){
  781. let childNode=childNodes[j];
  782. if(childNode.nodeType==3 && childNode.data && !/^[\s\-\_\?\>\|]*$/.test(childNode.data))hasText=true;
  783. if(childNode.innerHTML){
  784. childNode.innerHTML=childNode.innerHTML.replace(/\<\s*br\s*\>/gi,"\r\n").replace(/\n+/gi,"\n").replace(/\r+/gi,"\r");
  785. }
  786. let content=childNode.textContent;
  787. if(content){
  788. if(!content.trim())continue;
  789. cStr+=content.replace(/ +/g," ").replace(/([^\r]|^)\n([^\r]|$)/gi,"$1\r\n$2");
  790. }
  791. if(childNode.nodeType!=3 && !/^(I|A|STRONG|B|FONT|IMG)$/.test(childNode.nodeName))cStr+="\r\n";
  792. }
  793. if(hasText || noTextEnable || ele==largestContent)rStr+=cStr+"\r\n";
  794. }
  795. for(i=0;i<childlist.length;i++){
  796. var child=childlist[i];
  797. if(getDepth(child)==getDepth(largestContent)){
  798. if(largestContent.className != child.className)continue;
  799. if((largestContent.className && largestContent.className == child.className) || largestContent.parentNode == child.parentNode){
  800. getRightStr(child, true);
  801. }else {
  802. getRightStr(child, false);
  803. }
  804. }
  805. }
  806. return rStr.replace(/[\n\r]+/g,"\n\r");
  807. }
  808.  
  809. function getI18n(key, args){
  810. var resultStr=i18n[key];
  811. if(args && args.length>0){
  812. args.forEach(function(item){
  813. resultStr=resultStr.replace(/%s/,item);
  814. });
  815. }
  816. return resultStr;
  817. }
  818.  
  819. function getDepth(dom){
  820. var pa=dom,i=0;
  821. while(pa.parentNode){
  822. pa=pa.parentNode;
  823. i++;
  824. }
  825. return i;
  826. }
  827.  
  828. function fetch(forceSingle){
  829. forceSingle=forceSingle===true;
  830. processFunc=null;
  831. var aEles=document.body.querySelectorAll("a"),list=[];
  832. for(var i=0;i<aEles.length;i++){
  833. var aEle=aEles[i],has=false;
  834. if((!aEle.href || aEle.href.indexOf("javascript")!=-1) && aEle.dataset.href){
  835. aEle.href=aEle.dataset.href;
  836. }
  837. if(aEle.href==location.href)continue;
  838. for(var j=0;j<list.length;j++){
  839. if(list[j].href==aEle.href){
  840. aEle=list[j];
  841. list.splice(j,1);
  842. list.push(aEle);
  843. has=true;
  844. break;
  845. }
  846. }
  847. if(!has && aEle.href && /^http/i.test(aEle.href) && ((aEle.innerText.trim()!="" && indexReg.test(aEle.innerText.trim())) || /chapter[\-_]?\d/.test(aEle.href))){
  848. list.push(aEle);
  849. }
  850. }
  851. if(list.length>2 && !forceSingle){
  852. indexDownload(list);
  853. }else{
  854. var blob = new Blob([i18n.info+"\r\n\r\n"+document.title+"\r\n\r\n"+getPageContent(document)], {type: "text/plain;charset=utf-8"});
  855. saveAs(blob, document.title+".txt");
  856. }
  857. }
  858.  
  859. function customDown(){
  860. processFunc=null;
  861. var customRules=GM_getValue("DACrules_"+document.domain);
  862. var urls=window.prompt(i18n.customInfo,customRules?customRules:"https://xxx.xxx/book-[20-99].html, https://xxx.xxx/book-[01-10].html");
  863. if(urls){
  864. urls=decodeURIComponent(urls.replace(/%/g,'%25'));
  865. GM_setValue("DACrules_"+document.domain, urls);
  866. var processEles=[];
  867. let urlsArr=urls.split("@@"),eles=[];
  868. if(/^http|^ftp/.test(urlsArr[0])){
  869. [].forEach.call(urlsArr[0].split(","),function(i){
  870. var curEle;
  871. var varNum=/\[\d+\-\d+\]/.exec(i);
  872. if(varNum){
  873. varNum=varNum[0].trim();
  874. }else{
  875. curEle=document.createElement("a");
  876. curEle.href=i;
  877. processEles.push(curEle);
  878. return;
  879. }
  880. var num1=/\[(\d+)/.exec(varNum)[1].trim();
  881. var num2=/(\d+)\]/.exec(varNum)[1].trim();
  882. var num1Int=parseInt(num1);
  883. var num2Int=parseInt(num2);
  884. var numLen=num1.length;
  885. var needAdd=num1.charAt(0)=="0";
  886. if(num1Int>=num2Int)return;
  887. for(var j=num1Int;j<=num2Int;j++){
  888. var urlIndex=j.toString();
  889. if(needAdd){
  890. while(urlIndex.length<numLen)urlIndex="0"+urlIndex;
  891. }
  892. var curUrl=i.replace(/\[\d+\-\d+\]/,urlIndex).trim();
  893. curEle=document.createElement("a");
  894. curEle.href=curUrl;
  895. curEle.innerText=processEles.length.toString();
  896. processEles.push(curEle);
  897. }
  898. });
  899. }else{
  900. let urlSel=urlsArr[0].split(">>");
  901. try{
  902. eles=document.querySelectorAll(urlSel[0]);
  903. eles=[].filter.call(eles, ele=>{
  904. return ele.nodeName=='BODY'||(!!ele.offsetParent&&getComputedStyle(ele).display!=='none');
  905. })
  906. }catch(e){}
  907. if(eles.length==0){
  908. eles=[];
  909. var eleTxts=urlsArr[0].split(/(?<=[^\\])[,,]/),exmpEles=[],excludeTxts={};
  910. [].forEach.call(document.querySelectorAll("a"),function(item){
  911. if(!item.offsetParent)return;
  912. eleTxts.forEach(txt=>{
  913. var txtArr=txt.split("!");
  914. if(item.innerText.indexOf(txtArr[0])!=-1){
  915. exmpEles.push(item);
  916. excludeTxts[item]=txtArr.splice(1);
  917. }
  918. });
  919. })
  920. exmpEles.forEach(e=>{
  921. var cssSelStr="a",pa=e.parentNode,excludeTxt=excludeTxts[e];
  922. if(e.className)cssSelStr+="."+CSS.escape(e.className);
  923. while(pa && pa.nodeName!="BODY"){
  924. cssSelStr=pa.nodeName+">"+cssSelStr;
  925. pa=pa.parentNode;
  926. }
  927. cssSelStr="body>"+cssSelStr;;
  928. [].forEach.call(document.querySelectorAll(cssSelStr),function(item){
  929. if(!item.offsetParent)return;
  930. var isExclude=false;
  931. for(var t in excludeTxt){
  932. if(item.innerText.indexOf(excludeTxt[t])!=-1){
  933. isExclude=true;
  934. break;
  935. }
  936. }
  937. if(!isExclude && eles.indexOf(item)==-1){
  938. eles.push(item);
  939. }
  940. });
  941. });
  942. }
  943. function addItem(item) {
  944. let has=false;
  945. for(var j=0;j<processEles.length;j++){
  946. if(processEles[j].href==item.href){
  947. processEles.splice(j,1);
  948. processEles.push(item);
  949. has=true;
  950. break;
  951. }
  952. }
  953. if((!item.href || item.href.indexOf("javascript")!=-1) && item.dataset.href){
  954. item.href=item.dataset.href;
  955. }
  956. if(!has && item.href && /^http/i.test(item.href)){
  957. processEles.push(item.cloneNode(1));
  958. }
  959. }
  960. [].forEach.call(eles,function(item){
  961. if(urlSel[1]){
  962. item=Function("item",urlSel[1])(item);
  963. let items;
  964. if (Array.isArray(item)) {
  965. items = item;
  966. } else items = [item];
  967. items.forEach(item => {
  968. if(!item || !item.href)return;
  969. if(!item.nodeName || item.nodeName!="A"){
  970. let href=item.href;
  971. let innerText=item.innerText;
  972. item=document.createElement("a");
  973. item.href=href;
  974. item.innerText=innerText;
  975. }
  976. addItem(item);
  977. });
  978. } else {
  979. addItem(item);
  980. }
  981. });
  982. }
  983. if(urlsArr[1]){
  984. processEles.forEach(ele=>{
  985. ele.href=ele.href.replace(new RegExp(urlsArr[1]), urlsArr[2]);
  986. });
  987. }
  988. var retainImage=!!GM_getValue("retainImage");
  989. if(urlsArr[3]){
  990. processFunc=(data, cb, url)=>{
  991. let doc=data;
  992. if(urlsArr[3].indexOf("return ")==-1){
  993. if(urlsArr[3].indexOf("@")==0){
  994. let content="";
  995. if(retainImage){
  996. [].forEach.call(data.querySelectorAll("img[src]"), img => {
  997. let imgTxt=`![img](${canonicalUri(img.getAttribute("src"), location.href)})`;
  998. let imgTxtNode=document.createTextNode(imgTxt);
  999. img.parentNode.replaceChild(imgTxtNode, img);
  1000. });
  1001. }
  1002. [].forEach.call(data.querySelectorAll(urlsArr[3].slice(1)), ele=>{
  1003. [].forEach.call(ele.childNodes, child=>{
  1004. if(child.innerHTML){
  1005. child.innerHTML=child.innerHTML.replace(/\<\s*br\s*\>/gi,"\r\n").replace(/\n+/gi,"\n").replace(/\r+/gi,"\r");
  1006. }
  1007. if(child.textContent){
  1008. content+=(child.textContent.replace(/ +/g," ").replace(/([^\r]|^)\n([^\r]|$)/gi,"$1\r\n$2")+"\r\n");
  1009. }
  1010. });
  1011. content+="\r\n";
  1012. });
  1013. return content;
  1014. }else return eval(urlsArr[3]);
  1015. }else{
  1016. return Function("data", "doc", "cb", "url",urlsArr[3])(data, doc, cb, url);
  1017. }
  1018. };
  1019. }else{
  1020. if(win.dacProcess){
  1021. processFunc=win.dacProcess;
  1022. }
  1023. }
  1024. indexDownload(processEles);
  1025. }
  1026. }
  1027. const configPage = "https://hoothin.github.io/UserScripts/DownloadAllContent/";
  1028. const copySvg = '<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" style="transition: all ease 0.5s;top: 5px;right: 5px;position: absolute;cursor: pointer;"><title>Copy</title><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg>';
  1029. function searchRule(){
  1030. GM_openInTab(configPage + "#@" + location.hostname, {active: true});
  1031. }
  1032. if (location.origin + location.pathname == configPage) {
  1033. let exampleNode = document.getElementById("example");
  1034. if (!exampleNode) return;
  1035.  
  1036. exampleNode = exampleNode.parentNode;
  1037. let ruleList = exampleNode.nextElementSibling.nextElementSibling;
  1038. let searchInput = document.createElement("input");
  1039. let inputTimer;
  1040. function searchByInput() {
  1041. clearTimeout(inputTimer);
  1042. inputTimer = setTimeout(() => {
  1043. let curValue = searchInput.value;
  1044. let matchRules = [];
  1045. let dontMatchRules = [];
  1046. if (curValue) {
  1047. for (let i = 0; i < ruleList.children.length; i++) {
  1048. let curRule = ruleList.children[i];
  1049. let aHref = curRule.firstChild.href;
  1050. if (aHref.indexOf(curValue) == -1) {
  1051. dontMatchRules.push(curRule);
  1052. } else {
  1053. matchRules.push(curRule);
  1054. }
  1055. }
  1056. } else {
  1057. dontMatchRules = ruleList.children;
  1058. }
  1059. if (matchRules.length) {
  1060. for (let i = 0; i < dontMatchRules.length; i++) {
  1061. let curRule = dontMatchRules[i];
  1062. curRule.style.display = "none";
  1063. }
  1064. for (let i = 0; i < matchRules.length; i++) {
  1065. let curRule = matchRules[i];
  1066. curRule.style.display = "";
  1067. }
  1068. } else {
  1069. for (let i = 0; i < dontMatchRules.length; i++) {
  1070. let curRule = dontMatchRules[i];
  1071. curRule.style.display = "";
  1072. }
  1073. }
  1074. }, 500);
  1075. }
  1076. searchInput.style.margin = "10px";
  1077. searchInput.style.width = "100%";
  1078. searchInput.placeholder = i18n.searchRule;
  1079. searchInput.addEventListener("input", function(e) {
  1080. searchByInput();
  1081. });
  1082. if (location.hash) {
  1083. let hash = location.hash.slice(1);
  1084. if (hash.indexOf("@") == 0) {
  1085. setTimeout(() => {
  1086. exampleNode.scrollIntoView();
  1087. }, 500);
  1088. searchInput.value = hash.slice(1);
  1089. searchByInput();
  1090. }
  1091. }
  1092. [].forEach.call(ruleList.querySelectorAll("div.highlight"), highlight => {
  1093. highlight.style.position = "relative";
  1094. highlight.innerHTML = highlight.innerHTML + copySvg;
  1095. let svg = highlight.children[1];
  1096. svg.addEventListener("click", function(e) {
  1097. GM_setClipboard(highlight.children[0].innerText);
  1098. svg.style.opacity = 0;
  1099. setTimeout(() => {
  1100. svg.style.opacity = 1;
  1101. }, 1000);
  1102. });
  1103. });
  1104. exampleNode.parentNode.insertBefore(searchInput, ruleList);
  1105.  
  1106.  
  1107. let donateNode = document.querySelector("[alt='donate']");
  1108. if (!donateNode) return;
  1109. let insertPos = donateNode.parentNode.nextElementSibling;
  1110. let radioIndex = 0;
  1111. function createOption(_name, _value, _type) {
  1112. if (!_type) _type = "input";
  1113. let con = document.createElement("div");
  1114. let option = document.createElement("input");
  1115. let cap = document.createElement("b");
  1116. option.type = _type;
  1117. option.value = _value;
  1118. option.checked = _value;
  1119. cap.style.margin = "0px 10px 0px 0px";
  1120. if (_type == "radio") {
  1121. let label = document.createElement("label");
  1122. label.innerText = _name;
  1123. radioIndex++;
  1124. option.id = "radio" + radioIndex;
  1125. label.setAttribute("for", option.id);
  1126. cap.appendChild(label);
  1127. } else {
  1128. if (_type == "input") {
  1129. option.style.flexGrow = "1";
  1130. }
  1131. cap.innerText = _name;
  1132. }
  1133. con.style.margin = "10px 0";
  1134. con.style.display = "flex";
  1135. con.style.alignItems = "center";
  1136. con.appendChild(cap);
  1137. con.appendChild(option);
  1138. insertPos.parentNode.insertBefore(con, insertPos);
  1139. return option;
  1140. }
  1141. let delSelector = createOption(i18n.del, GM_getValue("selectors") || "");
  1142. delSelector.setAttribute("placeHolder", ".mask,.ksam");
  1143. let downThreadNum = createOption(i18n.downThreadNum, GM_getValue("downThreadNum") || "20", "number");
  1144. let customTitle = createOption(i18n.customTitle, GM_getValue("customTitle") || "");
  1145. customTitle.setAttribute("placeHolder", "title");
  1146. let minTxtLength = createOption(i18n.minTxtLength, GM_getValue("minTxtLength") || "100", "number");
  1147. let contentSortUrlValue = GM_getValue("contentSortUrl") || false;
  1148. let contentSortValue = GM_getValue("contentSort") || false;
  1149. let reSortDefault = createOption(i18n.reSortDefault, !contentSortUrlValue && !contentSortValue, "radio");
  1150. let reSortUrl = createOption(i18n.reSortUrl, contentSortUrlValue || false, "radio");
  1151. let contentSort = createOption(i18n.reSort, contentSortValue || false, "radio");
  1152. reSortDefault.name = "sort";
  1153. reSortUrl.name = "sort";
  1154. contentSort.name = "sort";
  1155. let reverse = createOption(i18n.reverse, !!GM_getValue("reverse"), "checkbox");
  1156. let retainImage = createOption(i18n.retainImage, !!GM_getValue("retainImage"), "checkbox");
  1157. let disableNextPage = !!GM_getValue("disableNextPage");
  1158. let nextPage = createOption(i18n.nextPage, !disableNextPage, "checkbox");
  1159. let nextPageReg = createOption(i18n.nextPageReg, GM_getValue("nextPageReg") || "");
  1160. nextPageReg.setAttribute("placeHolder", "^\\s*(下一[页頁张張]|next\\s*page|次のページ)");
  1161. if (disableNextPage) {
  1162. nextPageReg.parentNode.style.display = "none";
  1163. }
  1164. nextPage.onclick = e => {
  1165. nextPageReg.parentNode.style.display = nextPage.checked ? "flex" : "none";
  1166. }
  1167. let saveBtn = document.createElement("button");
  1168. saveBtn.innerText = i18n.saveBtn;
  1169. saveBtn.style.margin = "0 0 20px 0";
  1170. insertPos.parentNode.insertBefore(saveBtn, insertPos);
  1171. saveBtn.onclick = e => {
  1172. GM_setValue("selectors", delSelector.value || "");
  1173. GM_setValue("downThreadNum", downThreadNum.value || 20);
  1174. GM_setValue("minTxtLength", minTxtLength.value || 100);
  1175. GM_setValue("customTitle", customTitle.value || "");
  1176. if (reSortUrl.checked) {
  1177. GM_setValue("contentSortUrl", true);
  1178. GM_setValue("contentSort", false);
  1179. } else if (contentSort.checked) {
  1180. GM_setValue("contentSortUrl", false);
  1181. GM_setValue("contentSort", true);
  1182. } else {
  1183. GM_setValue("contentSortUrl", false);
  1184. GM_setValue("contentSort", false);
  1185. }
  1186. GM_setValue("reverse", reverse.checked);
  1187. GM_setValue("retainImage", retainImage.checked);
  1188. GM_setValue("disableNextPage", !nextPage.checked);
  1189. GM_setValue("nextPageReg", nextPageReg.value || "");
  1190. alert(i18n.saveOk);
  1191. };
  1192. return;
  1193. }
  1194.  
  1195. function setDel(){
  1196. GM_openInTab(configPage + "#操作說明", {active: true});
  1197. return;
  1198. /*var selValue=GM_getValue("selectors");
  1199. var selectors=prompt(i18n.del,selValue?selValue:"");
  1200. GM_setValue("selectors",selectors);
  1201. selValue=GM_getValue("downThreadNum");
  1202. var downThreadNum=prompt(i18n.downThreadNum,selValue?selValue:"20");
  1203. GM_setValue("downThreadNum",downThreadNum);
  1204. var sortByUrl=window.confirm(i18n.reSortUrl);
  1205. GM_setValue("contentSortUrl",sortByUrl);
  1206. if(!sortByUrl)GM_setValue("contentSort",window.confirm(i18n.reSort));*/
  1207. }
  1208.  
  1209. document.addEventListener("keydown", function(e) {
  1210. if(e.keyCode == 120 && e.ctrlKey) {
  1211. fetch(e.shiftKey);
  1212. }
  1213. });
  1214. GM_registerMenuCommand(i18n.fetch, fetch);
  1215. GM_registerMenuCommand(i18n.custom, customDown);
  1216. GM_registerMenuCommand(i18n.setting, setDel);
  1217. GM_registerMenuCommand(i18n.searchRule, searchRule);
  1218. })();