怠惰小说下载器

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

安装此脚本
作者推荐脚本

您可能也喜欢东方永页机

安装此脚本
  1. // ==UserScript==
  2. // @name DownloadAllContent
  3. // @name:zh-CN 怠惰小说下载器
  4. // @name:zh-TW 怠惰小説下載器
  5. // @name:ja 怠惰小説ダウンローダー
  6. // @namespace hoothin
  7. // @version 2.8.3.18
  8. // @description Lightweight web scraping script. Fetch and download main textual content from the current page, provide special support for novels
  9. // @description:zh-CN 通用网站内容爬虫抓取工具,可批量抓取任意站点的小说、论坛内容等并保存为TXT文档
  10. // @description:zh-TW 通用網站內容爬蟲抓取工具,可批量抓取任意站點的小說、論壇內容等並保存為TXT文檔
  11. // @description:ja 軽量なWebスクレイピングスクリプト。ユニバーサルサイトコンテンツクロールツール、クロール、フォーラム内容など
  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 GM_addStyle
  23. // @grant unsafeWindow
  24. // @license MIT License
  25. // @compatible chrome
  26. // @compatible firefox
  27. // @compatible opera 未测试
  28. // @compatible safari 未测试
  29. // @contributionURL https://ko-fi.com/hoothin
  30. // @contributionAmount 1
  31. // ==/UserScript==
  32.  
  33. if (window.top != window.self) {
  34. try {
  35. if (window.self.innerWidth < 250 || window.self.innerHeight < 250) {
  36. return;
  37. }
  38. } catch(e) {
  39. return;
  40. }
  41. }
  42.  
  43. (function (global, factory) {
  44. if (typeof define === "function" && define.amd) {
  45. define([], factory);
  46. } else if (typeof exports !== "undefined") {
  47. factory();
  48. } else {
  49. var mod = {
  50. exports: {}
  51. };
  52. factory();
  53. global.FileSaver = mod.exports;
  54. }
  55. })(this, function () {
  56. "use strict";
  57.  
  58. /*
  59. * FileSaver.js
  60. * A saveAs() FileSaver implementation.
  61. *
  62. * By Eli Grey, http://eligrey.com
  63. *
  64. * License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
  65. * source : http://purl.eligrey.com/github/FileSaver.js
  66. */
  67. 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;
  68.  
  69. function bom(blob, opts) {
  70. if (typeof opts === 'undefined') opts = {
  71. autoBom: false
  72. };else if (typeof opts !== 'object') {
  73. console.warn('Deprecated: Expected third argument to be a object');
  74. opts = {
  75. autoBom: !opts
  76. };
  77. }
  78.  
  79. if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
  80. return new Blob([String.fromCharCode(0xFEFF), blob], {
  81. type: blob.type
  82. });
  83. }
  84.  
  85. return blob;
  86. }
  87.  
  88. function download(url, name, opts) {
  89. var xhr = new XMLHttpRequest();
  90. xhr.open('GET', url);
  91. xhr.responseType = 'blob';
  92.  
  93. xhr.onload = function () {
  94. saveAs(xhr.response, name, opts);
  95. };
  96.  
  97. xhr.onerror = function () {
  98. console.error('could not download file');
  99. };
  100.  
  101. xhr.send();
  102. }
  103.  
  104. function corsEnabled(url) {
  105. var xhr = new XMLHttpRequest();
  106.  
  107. xhr.open('HEAD', url, false);
  108.  
  109. try {
  110. xhr.send();
  111. } catch (e) {}
  112.  
  113. return xhr.status >= 200 && xhr.status <= 299;
  114. }
  115.  
  116.  
  117. function click(node) {
  118. try {
  119. node.dispatchEvent(new MouseEvent('click'));
  120. } catch (e) {
  121. var evt = document.createEvent('MouseEvents');
  122. evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
  123. node.dispatchEvent(evt);
  124. }
  125. }
  126.  
  127.  
  128. var isMacOSWebView = _global.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent);
  129. var saveAs = _global.saveAs || (
  130. typeof window !== 'object' || window !== _global ? function saveAs() {}
  131.  
  132. : 'download' in HTMLAnchorElement.prototype && !isMacOSWebView ? function saveAs(blob, name, opts) {
  133. var URL = _global.URL || _global.webkitURL;
  134. var a = document.createElement('a');
  135. name = name || blob.name || 'download';
  136. a.download = name;
  137. a.rel = 'noopener';
  138.  
  139. if (typeof blob === 'string') {
  140. a.href = blob;
  141.  
  142. if (a.origin !== location.origin) {
  143. corsEnabled(a.href) ? download(blob, name, opts) : click(a, a.target = '_blank');
  144. } else {
  145. click(a);
  146. }
  147. } else {
  148. a.href = URL.createObjectURL(blob);
  149. setTimeout(function () {
  150. URL.revokeObjectURL(a.href);
  151. }, 4E4);
  152.  
  153. setTimeout(function () {
  154. click(a);
  155. }, 0);
  156. }
  157. }
  158. : 'msSaveOrOpenBlob' in navigator ? function saveAs(blob, name, opts) {
  159. name = name || blob.name || 'download';
  160.  
  161. if (typeof blob === 'string') {
  162. if (corsEnabled(blob)) {
  163. download(blob, name, opts);
  164. } else {
  165. var a = document.createElement('a');
  166. a.href = blob;
  167. a.target = '_blank';
  168. setTimeout(function () {
  169. click(a);
  170. });
  171. }
  172. } else {
  173. navigator.msSaveOrOpenBlob(bom(blob, opts), name);
  174. }
  175. }
  176. : function saveAs(blob, name, opts, popup) {
  177. popup = popup || open('', '_blank');
  178.  
  179. if (popup) {
  180. popup.document.title = popup.document.body.innerText = 'downloading...';
  181. }
  182.  
  183. if (typeof blob === 'string') return download(blob, name, opts);
  184. var force = blob.type === 'application/octet-stream';
  185.  
  186. var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari;
  187.  
  188. var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent);
  189.  
  190. if ((isChromeIOS || force && isSafari || isMacOSWebView) && typeof FileReader !== 'undefined') {
  191. var reader = new FileReader();
  192.  
  193. reader.onloadend = function () {
  194. var url = reader.result;
  195. url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;');
  196. if (popup) popup.location.href = url;else location = url;
  197. popup = null;
  198. };
  199.  
  200. reader.readAsDataURL(blob);
  201. } else {
  202. var URL = _global.URL || _global.webkitURL;
  203. var url = URL.createObjectURL(blob);
  204. if (popup) popup.location = url;else location.href = url;
  205. popup = null;
  206.  
  207. setTimeout(function () {
  208. URL.revokeObjectURL(url);
  209. }, 4E4);
  210. }
  211. });
  212. _global.saveAs = saveAs.saveAs = saveAs;
  213.  
  214. if (typeof module !== 'undefined') {
  215. module.exports = saveAs;
  216. }
  217. });
  218.  
  219. (function() {
  220. 'use strict';
  221. var indexReg=/^(\w.*)?PART\b|^Prologue|^(\w.*)?Chapter\s*[\-_]?\d+|分卷|^序$|^序\s*[·言章]|^前\s*言|^附\s*[录錄]|^引\s*[言子]|^摘\s*要|^[楔契]\s*子|^后\s*记|^後\s*記|^附\s*言|^结\s*语|^結\s*語|^尾\s*[声聲]|^最終話|^最终话|^番\s*外|^\d+[\s\.、,,)\-_::][^\d#\.]|^(\d|\s|\.)*[第(]?\s*[\d〇零一二两三四五六七八九十百千万萬-]+\s*[、)章节節回卷折篇幕集话話]/i;
  222. var innerNextPage=/^\s*(下一[页頁张張]|next\s*page|次のページ)/i;
  223. var lang=navigator.appName=="Netscape"?navigator.language:navigator.userLanguage;
  224. var i18n={};
  225. var rCats=[];
  226. var processFunc, nextPageFunc;
  227. const AsyncFunction=Object.getPrototypeOf(async function(){}).constructor;
  228. var win=(typeof unsafeWindow=='undefined'?window:unsafeWindow);
  229. switch (lang){
  230. case "zh-CN":
  231. case "zh-SG":
  232. i18n={
  233. fetch:"开始下载小说",
  234. info:"来源:#t#\n本文是使用怠惰小说下载器(DownloadAllContent)下载的",
  235. error:"该段内容获取失败",
  236. downloading:"已下载完成 %s 段,剩余 %s 段<br>正在下载 %s",
  237. complete:"已全部下载完成,共 %s 段",
  238. del:"设置文本干扰码的CSS选择器",
  239. custom:"自定规则下载",
  240. customInfo:"输入网址或者章节CSS选择器",
  241. reSort:"按标题名重新排序章节",
  242. reSortUrl:"按网址重新排序章节",
  243. setting:"选项参数设置",
  244. searchRule:"搜索网站规则",
  245. abort:"跳过此章",
  246. save:"保存当前",
  247. saveAsMd:"存为 Markdown",
  248. saveAsJSON: "存为 JSON",
  249. downThreadNum:"设置同时下载的线程数,负数为单线程下载间隔",
  250. enableTouch:"在移动端按→↓←↑的方向滑动屏幕画正方形立即开始下载",
  251. customTitle:"自定义章节标题,输入内页文字对应选择器",
  252. saveUrl: "储存 URL",
  253. disableAutoStartSave: "禁用自动保存",
  254. maxDlPerMin:"每分钟最大下载数",
  255. reSortDefault:"默认按页面中位置排序章节",
  256. reverseOrder:"反转章节排序",
  257. saveBtn:"保存设置",
  258. saveOk:"保存成功",
  259. nextPage:"嗅探章节内分页",
  260. nextPageReg:"自定义分页正则",
  261. retainImage:"保留正文中图片的网址",
  262. minTxtLength:"当检测到的正文字数小于此数,则尝试重新抓取",
  263. showFilterList:"下载前显示章节筛选排序窗口",
  264. ok:"确定",
  265. close:"关闭",
  266. dacSortByPos:"按页内位置排序",
  267. dacSortByUrl:"按网址排序",
  268. dacSortByName:"按章节名排序",
  269. reverse:"反选",
  270. dacUseIframe:"使用 iframe 后台加载内容(慢速)",
  271. dacSaveAsZip:"下载为 zip",
  272. dacSetCustomRule:"修改规则",
  273. dacAddUrl:"添加章节",
  274. prefix:"给章节名称添加前缀",
  275. dacStartDownload:"下载选中",
  276. downloadShortcut:"下载章节快捷键",
  277. downloadSingleShortcut:"下载单页快捷键",
  278. downloadCustomShortcut:"自定义下载快捷键"
  279. };
  280. break;
  281. case "zh":
  282. case "zh-TW":
  283. case "zh-HK":
  284. i18n={
  285. fetch:"開始下載小說",
  286. info:"來源:#t#\n本文是使用怠惰小說下載器(DownloadAllContent)下載的",
  287. error:"該段內容獲取失敗",
  288. downloading:"已下載完成 %s 段,剩餘 %s 段<br>正在下載 %s",
  289. complete:"已全部下載完成,共 %s 段",
  290. del:"設置文本干擾碼的CSS選擇器",
  291. custom:"自訂規則下載",
  292. customInfo:"輸入網址或者章節CSS選擇器",
  293. reSort:"按標題名重新排序章節",
  294. reSortUrl:"按網址重新排序章節",
  295. setting:"選項參數設定",
  296. searchRule:"搜尋網站規則",
  297. abort:"跳過此章",
  298. save:"保存當前",
  299. saveAsMd:"存爲 Markdown",
  300. saveAsJSON: "存爲 JSON",
  301. downThreadNum:"設置同時下載的綫程數,負數為單線程下載間隔",
  302. enableTouch:"在行動端按→↓←↑的方向滑動螢幕畫方立即開始下載",
  303. customTitle:"自訂章節標題,輸入內頁文字對應選擇器",
  304. saveUrl: "儲存 URL",
  305. disableAutoStartSave: "禁用自動保存",
  306. maxDlPerMin:"每分鐘最大下載數",
  307. reSortDefault:"預設依頁面中位置排序章節",
  308. reverseOrder:"反轉章節排序",
  309. saveBtn:"儲存設定",
  310. saveOk:"儲存成功",
  311. nextPage:"嗅探章節內分頁",
  312. nextPageReg:"自訂分頁正規",
  313. retainImage:"保留內文圖片的網址",
  314. minTxtLength:"當偵測到的正文字數小於此數,則嘗試重新抓取",
  315. showFilterList:"下載前顯示章節篩選排序視窗",
  316. ok:"確定",
  317. close:"關閉",
  318. dacSortByPos:"依頁內位置排序",
  319. dacSortByUrl:"依網址排序",
  320. dacSortByName:"依章節名排序",
  321. reverse:"反選",
  322. dacUseIframe:"使用 iframe 背景載入內容(慢速)",
  323. dacSaveAsZip:"下載為 zip",
  324. dacSetCustomRule:"修改規則",
  325. dacAddUrl:"新增章節",
  326. prefix:"為章節名稱加上前綴",
  327. dacStartDownload:"下載選取",
  328. downloadShortcut:"下載章節快速鍵",
  329. downloadSingleShortcut:"下載單頁快速鍵",
  330. downloadCustomShortcut:"自設下載快速鍵"
  331. };
  332. break;
  333. case "ar":
  334. case "ar-AE":
  335. case "ar-BH":
  336. case "ar-DZ":
  337. case "ar-EG":
  338. case "ar-IQ":
  339. case "ar-JO":
  340. case "ar-KW":
  341. case "ar-LB":
  342. case "ar-LY":
  343. case "ar-MA":
  344. case "ar-OM":
  345. case "ar-QA":
  346. case "ar-SA":
  347. case "ar-SY":
  348. case "ar-TN":
  349. case "ar-YE":
  350. i18n={
  351. encode: true,
  352. fetch: "%D8%AA%D8%AD%D9%85%D9%8A%D9%84",
  353. info: "%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1:%20#t#%0A%D8%AA%D9%85%20%D8%AA%D9%86%D8%B2%D9%8A%D9%84%20%D8%A7%D9%84%D9%80%20TXT%20%D8%A8%D9%88%D8%A7%D8%B3%D8%B7%D8%A9%20'DownloadAllContent'",
  354. error: "%D9%81%D8%B4%D9%84%20%D9%81%D9%8A%20%D8%AA%D8%AD%D9%85%D9%8A%D9%84%20%D8%A7%D9%84%D9%81%D8%B5%D9%84%20%D8%A7%D9%84%D8%AD%D8%A7%D9%84%D9%8A",
  355. downloading: "......%25s%20%D8%AA%D8%AD%D9%85%D9%8A%D9%84%3Cbr%3E%D8%B5%D9%81%D8%AD%D8%A7%D8%AA%20%D9%85%D8%AA%D8%A8%D9%82%D9%8A%D8%A9%20%25s%20%D8%B5%D9%81%D8%AD%D8%A7%D8%AA%20%D8%AA%D9%85%20%D8%AA%D8%AD%D9%85%D9%8A%D9%84%D9%87%D8%A7%D8%8C%20%D9%87%D9%86%D8%A7%D9%83%20%25s",
  356. complete: "%D8%B5%D9%81%D8%AD%D8%A7%D8%AA%20%D9%81%D9%8A%20%D8%A7%D9%84%D9%85%D8%AC%D9%85%D9%88%D8%B9%20%25s%20%D8%A7%D9%83%D8%AA%D9%85%D9%84!%20%D8%AD%D8%B5%D9%84%D8%AA%20%D8%B9%D9%84%D9%89",
  357. del: "%D9%84%D8%AA%D8%AC%D8%A7%D9%87%D9%84%20CSS%20%D8%AA%D8%B9%D9%8A%D9%8A%D9%86%20%D9%85%D8%AD%D8%AF%D8%AF%D8%A7%D8%AA",
  358. custom: "%D8%AA%D8%AD%D9%85%D9%8A%D9%84%20%D9%85%D8%AE%D8%B5%D8%B5",
  359. customInfo: "%D9%84%D8%B1%D9%88%D8%A7%D8%A8%D8%B7%20%D8%A7%D9%84%D9%81%D8%B5%D9%88%D9%84%20sss%20%D8%A5%D8%AF%D8%AE%D8%A7%D9%84%20%D8%A7%D9%84%D8%B1%D9%88%D8%A7%D8%A8%D8%B7%20%D8%A3%D9%88%20%D9%85%D8%AD%D8%AF%D8%AF%D8%A7%D8%AA",
  360. reSort: "%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9%20%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8%20%D8%AD%D8%B3%D8%A8%20%D8%A7%D9%84%D8%B9%D9%86%D9%88%D8%A7%D9%86",
  361. reSortUrl: "%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9%20%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8%20%D8%AD%D8%B3%D8%A8%20%D8%A7%D9%84%D8%B1%D9%88%D8%A7%D8%A8%D8%B7",
  362. setting: "%D9%81%D8%AA%D8%AD%20%D8%A7%D9%84%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA",
  363. searchRule: "%D9%82%D8%A7%D8%B9%D8%AF%D8%A9%20%D8%A7%D9%84%D8%A8%D8%AD%D8%AB",
  364. abort: "%D8%A5%D9%8A%D9%82%D8%A7%D9%81",
  365. save: "%D8%AD%D9%81%D8%B8",
  366. saveAsMd: "Markdown%20%D8%AD%D9%81%D8%B8%20%D9%83%D9%80",
  367. saveAsJSON: "JSON%20%D8%AD%D9%81%D8%B8%20%D9%83%D9%80",
  368. downThreadNum: "%D8%AA%D8%B9%D9%8A%D9%8A%D9%86%20%D8%B9%D8%AF%D8%AF%20%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7%20%D9%84%D9%84%D8%AA%D8%AD%D9%85%D9%8A%D9%84",
  369. enableTouch: "On%20the%20mobile%20device,%20slide%20the%20screen%20in%20the%20direction%20of%20%E2%86%92%E2%86%93%E2%86%90%E2%86%91%20to%20draw%20a%20square%20will%20start%20downloading%20immediately",
  370. customTitle: "%D8%AA%D8%AE%D8%B5%D9%8A%D8%B5%20%D8%B9%D9%86%D9%88%D8%A7%D9%86%20%D8%A7%D9%84%D9%81%D8%B5%D9%84%D8%8C%20%D8%A5%D8%AF%D8%AE%D8%A7%D9%84%20%D8%A7%D9%84%D9%85%D8%AD%D8%AF%D8%AF%20%D9%81%D9%8A%20%D8%A7%D9%84%D8%B5%D9%81%D8%AD%D8%A9%20%D8%A7%D9%84%D8%AF%D8%A7%D8%AE%D9%84%D9%8A%D8%A9",
  371. saveUrl: "%D8%AD%D9%81%D8%B8%20URL",
  372. disableAutoStartSave: "%D8%AA%D8%B9%D8%B7%D9%8A%D9%84%20%D8%A7%D9%84%D8%AD%D9%81%D8%B8%20%D8%A7%D9%84%D8%AA%D9%84%D9%82%D8%A7%D8%A6%D9%8A",
  373. maxDlPerMin: "%D8%A7%D9%84%D8%AD%D8%AF%20%D8%A7%D9%84%D8%A3%D9%82%D8%B5%D9%89%20%D9%84%D8%B9%D8%AF%D8%AF%20%D8%A7%D9%84%D8%AA%D9%86%D8%B2%D9%8A%D9%84%D8%A7%D8%AA%20%D9%81%D9%8A%20%D8%A7%D9%84%D8%AF%D9%82%D9%8A%D9%82%D8%A9",
  374. reSortDefault: "%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8%20%D8%A7%D9%84%D8%A7%D9%81%D8%AA%D8%B1%D8%A7%D8%B6%D9%8A%20%D8%AD%D8%B3%D8%A8%20%D8%A7%D9%84%D9%85%D9%88%D9%82%D8%B9%20%D9%81%D9%8A%20%D8%A7%D9%84%D8%B5%D9%81%D8%AD%D8%A9",
  375. reverseOrder: "%D8%B9%D9%83%D8%B3%20%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8%20%D8%A7%D9%84%D9%81%D8%B5%D9%88%D9%84",
  376. saveBtn: "%D8%AD%D9%81%D8%B8%20%D8%A7%D9%84%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA",
  377. saveOk: "%D8%AA%D9%85%20%D8%A7%D9%84%D8%AD%D9%81%D8%B8",
  378. nextPage: "%D8%A7%D9%84%D8%AA%D8%AD%D9%82%D9%82%20%D9%85%D9%86%20%D8%A7%D9%84%D8%B5%D9%81%D8%AD%D8%A9%20%D8%A7%D9%84%D8%AA%D8%A7%D9%84%D9%8A%D8%A9%20%D9%81%D9%8A%20%D8%A7%D9%84%D9%81%D8%B5%D9%84",
  379. nextPageReg: "%D9%85%D8%AE%D8%B5%D8%B5%20%D9%84%D9%84%D8%B5%D9%81%D8%AD%D8%A9%20%D8%A7%D9%84%D8%AA%D8%A7%D9%84%D9%8A%D8%A9%20RegExp",
  380. retainImage: "%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%81%D8%A7%D8%B8%20%D8%A8%D8%B9%D9%86%D9%88%D8%A7%D9%86%20%D8%A7%D9%84%D8%B5%D9%88%D8%B1%D8%A9%20%D8%A5%D8%B0%D8%A7%20%D9%83%D8%A7%D9%86%D8%AA%20%D9%87%D9%86%D8%A7%D9%83%20%D8%B5%D9%88%D8%B1%20%D9%81%D9%8A%20%D8%A7%D9%84%D9%86%D8%B5",
  381. minTxtLength: "%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D9%88%D9%84%D8%A9%20%D9%85%D8%B1%D8%A9%20%D8%A3%D8%AE%D8%B1%D9%89%20%D8%B9%D9%86%D8%AF%D9%85%D8%A7%20%D9%8A%D9%83%D9%88%D9%86%20%D8%B7%D9%88%D9%84%20%D8%A7%D9%84%D9%85%D8%AD%D8%AA%D9%88%D9%89%20%D8%A3%D9%82%D9%84%20%D9%85%D9%86%20%D9%87%D8%B0%D8%A7",
  382. showFilterList: "%D8%B9%D8%B1%D8%B6%20%D9%86%D8%A7%D9%81%D8%B0%D8%A9%20%D8%A7%D9%84%D8%AA%D8%B5%D9%81%D9%8A%D8%A9%20%D9%88%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8%20%D9%82%D8%A8%D9%84%20%D8%A7%D9%84%D8%AA%D8%AD%D9%85%D9%8A%D9%84",
  383. ok: "%D9%85%D9%88%D8%A7%D9%81%D9%82",
  384. close: "%D8%A5%D8%BA%D9%84%D8%A7%D9%82",
  385. dacSortByPos: "%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8%20%D8%AD%D8%B3%D8%A8%20%D8%A7%D9%84%D9%85%D9%88%D9%82%D8%B9",
  386. dacSortByUrl: "%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8%20%D8%AD%D8%B3%D8%A8%20%D8%A7%D9%84%D8%B1%D8%A7%D8%A8%D8%B7",
  387. dacSortByName: "%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8%20%D8%AD%D8%B3%D8%A8%20%D8%A7%D9%84%D8%A7%D8%B3%D9%85",
  388. reverse: "%D8%B9%D9%83%D8%B3%20%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1",
  389. dacUseIframe: "%D9%84%D8%AA%D8%AD%D9%85%D9%8A%D9%84%20%D8%A7%D9%84%D9%85%D8%AD%D8%AA%D9%88%D9%89%20(%D8%A8%D8%B7%D9%8A%D8%A1)%20iframe%20%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85",
  390. dacSaveAsZip: "zip%20%D8%AD%D9%81%D8%B8%20%D9%83%D9%80",
  391. dacSetCustomRule: "%D8%AA%D8%B9%D8%AF%D9%8A%D9%84%20%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%B9%D8%AF",
  392. dacAddUrl: "%D8%A5%D8%B6%D8%A7%D9%81%D8%A9%20%D9%81%D8%B5%D9%84",
  393. prefix:"Prefix%20of%20chapter%20name",
  394. dacStartDownload: "%D8%AA%D8%AD%D9%85%D9%8A%D9%84%20%D8%A7%D9%84%D9%85%D8%AD%D8%AF%D8%AF",
  395. downloadShortcut: "%D8%AA%D8%AD%D9%85%D9%8A%D9%84%20%D8%A7%D9%84%D9%81%D8%B5%D9%84",
  396. downloadSingleShortcut: "%D8%AA%D8%AD%D9%85%D9%8A%D9%84%20%D8%B5%D9%81%D8%AD%D8%A9%20%D9%88%D8%A7%D8%AD%D8%AF%D8%A9",
  397. downloadCustomShortcut: "%D8%AA%D8%AD%D9%85%D9%8A%D9%84%20%D9%85%D8%AE%D8%B5%D8%B5"
  398. };
  399. break;
  400. case "ja":
  401. case "ja-JP":
  402. i18n={
  403. fetch:"小説のダウンロードを開始",
  404. info:"提供元:#t#\nこの作品は、怠惰小説ダウンローダー(DownloadAllContent)を使ってダウンロードされました。",
  405. error:"このコンテンツの取得に失敗しました",
  406. downloading:"%s/%sエピソードをダウンロード済み<br>%sをダウンロード中",
  407. complete:"全%sエピソードのダウンロードが完了しました",
  408. del:"テキストの乱れを削除するCSSセレクターを設定",
  409. custom:"カスタムルールでダウンロード",
  410. customInfo:"URLまたは章のCSSセレクターを入力してください",
  411. reSort:"章をタイトル順で並べ替え",
  412. reSortUrl:"章をURL順で並べ替え",
  413. setting:"オプション設定",
  414. searchRule:"サイトの検索ルール",
  415. abort:"この章をスキップ",
  416. save:"現在の設定を保存",
  417. saveAsMd:"Markdown形式で保存",
  418. saveAsJSON: "JSON形式で保存",
  419. downThreadNum:"同時ダウンロードスレッド数を設定(負の値はシングルスレッドでのダウンロード間隔)",
  420. enableTouch:"モバイルで画面をスライドして正方形を描くとダウンロードを開始",
  421. customTitle:"カスタム章タイトル(ページ内のテキストに対応するセレクターを入力)",
  422. saveUrl: "URLを保存",
  423. disableAutoStartSave: "自動保存を無効にする",
  424. maxDlPerMin:"1分あたりの最大ダウンロード数",
  425. reSortDefault:"デフォルト(ページ内の位置)で章を並べ替え",
  426. reverseOrder:"章の並び順を反転",
  427. saveBtn:"設定を保存",
  428. saveOk:"保存しました",
  429. nextPage:"章内のページを検出",
  430. nextPageReg:"カスタムページネーション正規表現",
  431. retainImage:"本文中の画像のURLを保持",
  432. minTxtLength:"検出された本文の文字数がこの数より少ない場合、再取得を試行",
  433. showFilterList:"ダウンロード前に章のフィルタリング・並べ替えウィンドウを表示",
  434. ok:"OK",
  435. close:"閉じる",
  436. dacSortByPos:"ページ内の位置で並べ替え",
  437. dacSortByUrl:"URLで並べ替え",
  438. dacSortByName:"章の名前で並べ替え",
  439. reverse:"選択を反転",
  440. dacUseIframe:"iframeを使ってコンテンツをバックグラウンドで読み込む(低速)",
  441. dacSaveAsZip:"Zip形式でダウンロード",
  442. dacSetCustomRule:"ルールを編集",
  443. dacAddUrl:"章を追加",
  444. prefix:"章のタイトルにプレフィックスを追加",
  445. dacStartDownload:"選択した章をダウンロード",
  446. downloadShortcut:"章ダウンロードのショートカット",
  447. downloadSingleShortcut:"単一ページダウンロードのショートカット",
  448. downloadCustomShortcut:"カスタムダウンロードのショートカット"
  449. };
  450. break;
  451. default:
  452. i18n={
  453. fetch:"Download",
  454. info:"Source: #t#\nThe TXT is downloaded by 'DownloadAllContent'",
  455. error:"Failed in downloading current chapter",
  456. downloading:"%s pages are downloaded, there are still %s pages left<br>Downloading %s ......",
  457. complete:"Completed! Get %s pages in total",
  458. del:"Set css selectors for ignore",
  459. custom:"Custom to download",
  460. customInfo:"Input urls OR sss selectors for chapter links",
  461. reSort:"ReSort by title",
  462. reSortUrl:"Resort by URLs",
  463. setting:"Open Setting",
  464. searchRule:"Search rule",
  465. abort:"Abort",
  466. save:"Save",
  467. saveAsMd:"Save as Markdown",
  468. saveAsJSON: "Save as JSON",
  469. downThreadNum:"Set threadNum for download, negative means interval of single thread",
  470. enableTouch:"On the mobile device, slide the screen in the direction of →↓←↑ to draw a square will start downloading immediately",
  471. customTitle: "Customize the chapter title, enter the selector on inner page",
  472. saveUrl: "Save URL",
  473. disableAutoStartSave: "Disable auto save",
  474. maxDlPerMin:"Maximum number of downloads per minute",
  475. reSortDefault: "Default sort by position in the page",
  476. reverseOrder:"Reverse chapter ordering",
  477. saveBtn:"Save Setting",
  478. saveOk:"Save Over",
  479. nextPage:"Check next page in chapter",
  480. nextPageReg:"Custom RegExp of next page",
  481. retainImage:"Keep the URL of image if there are images in the text",
  482. minTxtLength:"Try to crawl again when the length of content is less than this",
  483. showFilterList: "Show chapter filtering and sorting window before downloading",
  484. ok:"OK",
  485. close:"Close",
  486. dacSortByPos:"Sort by position",
  487. dacSortByUrl:"Sort by URL",
  488. dacSortByName:"Sort by name",
  489. reverse:"Reverse selection",
  490. dacUseIframe: "Use iframe to load content (slow)",
  491. dacSaveAsZip: "Save as zip",
  492. dacSetCustomRule:"Modify rules",
  493. dacAddUrl:"Add Chapter",
  494. prefix:"Prefix of chapter name",
  495. dacStartDownload:"Download selected",
  496. downloadShortcut:"Download chapter Shortcut",
  497. downloadSingleShortcut:"Download single page Shortcut",
  498. downloadCustomShortcut:"Custom download Shortcut"
  499. };
  500. break;
  501. }
  502. if (i18n.encode) {
  503. for (let k in i18n) {
  504. if (k != "encode") {
  505. i18n[k] = decodeURI(i18n[k]);
  506. }
  507. }
  508. }
  509. var firefox=navigator.userAgent.toLowerCase().indexOf('firefox')!=-1,curRequests=[],useIframe=false,iframeSandbox=false,iframeInit=false;
  510. var filterListContainer,txtDownContent,txtDownWords,txtDownQuit,dacLinksCon,dacUseIframe,shadowContainer,downTxtShadowContainer;
  511.  
  512. const escapeHTMLPolicy = (win.trustedTypes && win.trustedTypes.createPolicy) ? win.trustedTypes.createPolicy('dac_default', {
  513. createHTML: (string, sink) => string
  514. }) : null;
  515.  
  516. function createHTML(html) {
  517. return escapeHTMLPolicy ? escapeHTMLPolicy.createHTML(html) : html;
  518. }
  519.  
  520. function str2Num(str) {
  521. str = str.replace(/^番\s*外/, "99999+").replace(/[一①Ⅰ壹]/g, "1").replace(/[二②Ⅱ贰]/g, "2").replace(/[三③Ⅲ叁]/g, "3").replace(/[四④Ⅳ肆]/g, "4").replace(/[五⑤Ⅴ伍]/g, "5").replace(/[六⑥Ⅵ陆]/g, "6").replace(/[七⑦Ⅶ柒]/g, "7").replace(/[八⑧Ⅷ捌]/g, "8").replace(/[九⑨Ⅸ玖]/g, "9").replace(/[十⑩Ⅹ拾]/g, "*10+").replace(/[百佰]/g, "*100+").replace(/[千仟]/g, "*1000+").replace(/[万萬]/g, "*10000+").replace(/\s/g, "").match(/[\d\*\+]+/);
  522. if (!str) return 0;
  523. str = str[0];
  524. let mul = str.match(/(\d*)\*(\d+)/);
  525. while(mul) {
  526. let result = parseInt(mul[1] || 1) * parseInt(mul[2]);
  527. str = str.replace(mul[0], result);
  528. mul = str.match(/(\d+)\*(\d+)/);
  529. }
  530. let plus = str.match(/(\d+)\+(\d+)/);
  531. while(plus) {
  532. let result = parseInt(plus[1]) + parseInt(plus[2]);
  533. str = str.replace(plus[0], result);
  534. plus = str.match(/(\d+)\+(\d+)/);
  535. }
  536. return parseInt(str);
  537. }
  538.  
  539. var dragOverItem, dragFrom, linkDict;
  540. function createLinkItem(aEle) {
  541. let item = document.createElement("div");
  542. item.innerHTML = createHTML(`
  543. <input type="checkbox" checked>
  544. <a class="dacLink" draggable="false" target="_blank" href="${aEle.href}">${aEle.innerText || "📄"}</a>
  545. <span>🖱️</span>
  546. `);
  547. item.title = aEle.innerText;
  548. item.setAttribute("draggable", "true");
  549. item.addEventListener("dragover", e => {
  550. e.preventDefault();
  551. });
  552. item.addEventListener("dragenter", e => {
  553. if (dragOverItem) dragOverItem.style.opacity = "";
  554. item.style.opacity = 0.3;
  555. dragOverItem = item;
  556. });
  557. item.addEventListener('dragstart', e => {
  558. dragFrom = item;
  559. });
  560. item.addEventListener('drop', e => {
  561. if (!dragFrom) return;
  562. if (e.clientX < item.getBoundingClientRect().left + 142) {
  563. dacLinksCon.insertBefore(dragFrom, item);
  564. } else {
  565. if (item.nextElementSibling) {
  566. dacLinksCon.insertBefore(dragFrom, item.nextElementSibling);
  567. } else {
  568. dacLinksCon.appendChild(dragFrom);
  569. }
  570. }
  571. e.preventDefault();
  572. });
  573. linkDict[aEle.href] = item;
  574. dacLinksCon.appendChild(item);
  575. }
  576.  
  577. var saveAsZip = true;
  578. function filterList(list) {
  579. if (!GM_getValue("showFilterList")) {
  580. indexDownload(list);
  581. return;
  582. }
  583. if (txtDownContent) {
  584. txtDownContent.style.display = "none";
  585. }
  586. if (filterListContainer) {
  587. filterListContainer.style.display = "";
  588. filterListContainer.classList.remove("customRule");
  589. dacLinksCon.innerHTML = createHTML("");
  590. } else {
  591. document.addEventListener('dragend', e => {
  592. if (dragOverItem) dragOverItem.style.opacity = "";
  593. }, true);
  594. filterListContainer = document.createElement("div");
  595. filterListContainer.id = "filterListContainer";
  596. filterListContainer.innerHTML = createHTML(`
  597. <div id="dacFilterBg" style="height: 100%; width: 100%; position: fixed; top: 0; z-index: 2147483646; opacity: 0.3; filter: alpha(opacity=30); background-color: #000;"></div>
  598. <div id="filterListBody">
  599. <div class="dacCustomRule">
  600. ${i18n.custom}
  601. <textarea id="dacCustomInput"></textarea>
  602. <div class="fun">
  603. <input id="dacConfirmRule" value="${i18n.ok}" type="button"/>
  604. <input id="dacCustomClose" value="${i18n.close}" type="button"/>
  605. </div>
  606. </div>
  607. <div class="sort">
  608. <input id="dacSortByPos" value="${i18n.dacSortByPos}" type="button"/>
  609. <input id="dacSortByUrl" value="${i18n.dacSortByUrl}" type="button"/>
  610. <input id="dacSortByName" value="${i18n.dacSortByName}" type="button"/>
  611. <input id="reverse" value="${i18n.reverse}" type="button"/>
  612. </div>
  613. <div id="dacLinksCon" style="max-height: calc(80vh - 100px); min-height: 100px; display: grid; grid-template-columns: auto auto; width: 100%; overflow: auto; white-space: nowrap;"></div>
  614. <p style="margin: 5px; text-align: center; font-size: 14px; height: 20px;"><span><input id="dacUseIframe" type="checkbox"/><label for="dacUseIframe"> ${i18n.dacUseIframe}</label></span> <span style="display:${win.downloadAllContentSaveAsZip ? "inline" : "none"}"><input id="dacSaveAsZip" type="checkbox" checked="checked"/><label for="dacSaveAsZip"> ${i18n.dacSaveAsZip}</label></span></p>
  615. <div class="fun">
  616. <input id="dacSetCustomRule" value="${i18n.dacSetCustomRule}" type="button"/>
  617. <input id="dacAddUrl" value="${i18n.dacAddUrl}" type="button"/>
  618. <input id="dacStartDownload" value="${i18n.dacStartDownload}" type="button"/>
  619. <input id="dacLinksClose" value="${i18n.close}" type="button"/>
  620. </div>
  621. </div>`);
  622. let dacSortByPos = filterListContainer.querySelector("#dacSortByPos");
  623. let dacSortByUrl = filterListContainer.querySelector("#dacSortByUrl");
  624. let dacSortByName = filterListContainer.querySelector("#dacSortByName");
  625. let reverse = filterListContainer.querySelector("#reverse");
  626. let dacSetCustomRule = filterListContainer.querySelector("#dacSetCustomRule");
  627. let dacCustomInput = filterListContainer.querySelector("#dacCustomInput");
  628. let dacConfirmRule = filterListContainer.querySelector("#dacConfirmRule");
  629. let dacCustomClose = filterListContainer.querySelector("#dacCustomClose");
  630. let dacAddUrl = filterListContainer.querySelector("#dacAddUrl");
  631. let dacStartDownload = filterListContainer.querySelector("#dacStartDownload");
  632. let dacLinksClose = filterListContainer.querySelector("#dacLinksClose");
  633. let dacFilterBg = filterListContainer.querySelector("#dacFilterBg");
  634. let dacSaveAsZip = filterListContainer.querySelector("#dacSaveAsZip");
  635. dacUseIframe = filterListContainer.querySelector("#dacUseIframe");
  636. dacSaveAsZip.onchange = e => {
  637. saveAsZip = dacSaveAsZip.checked;
  638. };
  639. dacSortByPos.onclick = e => {
  640. let linkList = [].slice.call(dacLinksCon.children);
  641. if (linkList[0].children[1].href != list[0].href) {
  642. list.reverse().forEach(a => {
  643. let link = linkDict[a.href];
  644. if (!link) return;
  645. dacLinksCon.insertBefore(link, dacLinksCon.children[0]);
  646. });
  647. } else {
  648. list.forEach(a => {
  649. let link = linkDict[a.href];
  650. if (!link) return;
  651. dacLinksCon.insertBefore(link, dacLinksCon.children[0]);
  652. });
  653. }
  654. };
  655. dacSortByUrl.onclick = e => {
  656. let linkList = [].slice.call(dacLinksCon.children);
  657. linkList.sort((a, b) => {
  658. const nameA = a.children[1].href.toUpperCase();
  659. const nameB = b.children[1].href.toUpperCase();
  660. if (nameA < nameB) {
  661. return -1;
  662. }
  663. if (nameA > nameB) {
  664. return 1;
  665. }
  666. return 0;
  667. });
  668. if (linkList[0] == dacLinksCon.children[0]) {
  669. linkList = linkList.reverse();
  670. }
  671. linkList.forEach(link => {
  672. dacLinksCon.appendChild(link);
  673. });
  674. };
  675. dacSortByName.onclick = e => {
  676. let linkList = [].slice.call(dacLinksCon.children);
  677. linkList.sort((a, b) => {
  678. return str2Num(a.innerText) - str2Num(b.innerText);
  679. });
  680. if (linkList[0] == dacLinksCon.children[0]) {
  681. linkList = linkList.reverse();
  682. }
  683. linkList.forEach(link => {
  684. dacLinksCon.appendChild(link);
  685. });
  686. };
  687. reverse.onclick = e => {
  688. let linkList = [].slice.call(dacLinksCon.children);
  689. linkList.forEach(link => {
  690. link.children[0].checked=!link.children[0].checked;
  691. });
  692. };
  693. dacSetCustomRule.onclick = e => {
  694. filterListContainer.classList.add("customRule");
  695. dacCustomInput.value = GM_getValue("DACrules_" + document.domain) || "";
  696. };
  697. dacConfirmRule.onclick = e => {
  698. if (dacCustomInput.value) {
  699. customDown(dacCustomInput.value);
  700. }
  701. };
  702. dacCustomClose.onclick = e => {
  703. filterListContainer.classList.remove("customRule");
  704. };
  705. dacAddUrl.onclick = e => {
  706. let addUrls = window.prompt(i18n.customInfo, "https://xxx.xxx/book-[20-99].html, https://xxx.xxx/book-[01-10].html");
  707. if (!addUrls || !/^http|^ftp/.test(addUrls)) return;
  708. let index = 1;
  709. [].forEach.call(addUrls.split(","), function(i) {
  710. var curEle;
  711. var varNum = /\[\d+\-\d+\]/.exec(i);
  712. if (varNum) {
  713. varNum = varNum[0].trim();
  714. } else {
  715. curEle = document.createElement("a");
  716. curEle.href = i;
  717. curEle.innerText = "Added Url";
  718. createLinkItem(curEle);
  719. return;
  720. }
  721. var num1 = /\[(\d+)/.exec(varNum)[1].trim();
  722. var num2 = /(\d+)\]/.exec(varNum)[1].trim();
  723. var num1Int = parseInt(num1);
  724. var num2Int = parseInt(num2);
  725. var numLen = num1.length;
  726. var needAdd = num1.charAt(0) == "0";
  727. if (num1Int >= num2Int) return;
  728. for (var j = num1Int; j <= num2Int; j++) {
  729. var urlIndex = j.toString();
  730. if (needAdd) {
  731. while(urlIndex.length < numLen) urlIndex = "0" + urlIndex;
  732. }
  733. var curUrl = i.replace(/\[\d+\-\d+\]/, urlIndex).trim();
  734. curEle = document.createElement("a");
  735. curEle.href = curUrl;
  736. curEle.innerText = "Added Url " + index++;
  737. createLinkItem(curEle);
  738. }
  739. });
  740. };
  741. dacStartDownload.onclick = e => {
  742. let linkList = [].slice.call(dacLinksCon.querySelectorAll("input:checked+.dacLink"));
  743. useIframe = !!dacUseIframe.checked;
  744. indexDownload(linkList, true);
  745. };
  746. dacLinksClose.onclick = e => {
  747. filterListContainer.style.display = "none";
  748. };
  749. dacFilterBg.onclick = e => {
  750. filterListContainer.style.display = "none";
  751. };
  752. let listStyle = GM_addStyle(`
  753. #filterListContainer * {
  754. font-size: 13px;
  755. float: initial;
  756. background-image: initial;
  757. height: fit-content;
  758. color: black;
  759. }
  760. #filterListContainer.customRule .dacCustomRule {
  761. display: flex;
  762. }
  763. #filterListContainer .dacCustomRule>textarea {
  764. height: 300px;
  765. width: 100%;
  766. border: 1px #DADADA solid;
  767. background: #ededed70;
  768. margin: 5px;
  769. }
  770. #filterListContainer.customRule .dacCustomRule~* {
  771. display: none!important;
  772. }
  773. #dacLinksCon>div {
  774. padding: 5px 0;
  775. display: flex;
  776. }
  777. #dacLinksCon>div>a {
  778. max-width: 245px;
  779. display: inline-block;
  780. text-overflow: ellipsis;
  781. overflow: hidden;
  782. }
  783. #dacLinksCon>div>input {
  784. margin-right: 5px;
  785. }
  786. #filterListContainer .dacCustomRule {
  787. border-radius: 8px;
  788. font-weight: bold;
  789. font-size: 16px;
  790. outline: none;
  791. align-items: center;
  792. flex-wrap: nowrap;
  793. white-space: nowrap;
  794. flex-direction: column;
  795. display: none;
  796. }
  797. #filterListContainer input {
  798. border-width: 2px;
  799. border-style: outset;
  800. border-color: buttonface;
  801. border-image: initial;
  802. border: 1px #DADADA solid;
  803. padding: 5px;
  804. border-radius: 8px;
  805. font-weight: bold;
  806. font-size: 9pt;
  807. outline: none;
  808. cursor: pointer;
  809. line-height: initial;
  810. width: initial;
  811. min-width: initial;
  812. max-width: initial;
  813. height: initial;
  814. min-height: initial;
  815. max-height: initial;
  816. }
  817. #dacLinksCon>div:nth-of-type(4n),
  818. #dacLinksCon>div:nth-of-type(4n+1) {
  819. background: #ffffff;
  820. }
  821. #dacLinksCon>div:nth-of-type(4n+2),
  822. #dacLinksCon>div:nth-of-type(4n+3) {
  823. background: #f5f5f5;
  824. }
  825. #filterListContainer .fun,#filterListContainer .sort {
  826. display: flex;
  827. justify-content: space-around;
  828. flex-wrap: nowrap;
  829. width: 100%;
  830. height: 28px;
  831. }
  832. #filterListContainer input[type=button]:hover {
  833. border: 1px #C6C6C6 solid;
  834. box-shadow: 1px 1px 1px #EAEAEA;
  835. color: #333333;
  836. background: #F7F7F7;
  837. }
  838. #filterListContainer input[type=button]:active {
  839. box-shadow: inset 1px 1px 1px #DFDFDF;
  840. }
  841. #filterListBody {
  842. padding: 5px;
  843. box-sizing: border-box;
  844. overflow: hidden;
  845. width: 600px;
  846. height: auto;
  847. max-height: 80vh;
  848. min-height: 200px;
  849. position: fixed;
  850. left: 50%;
  851. top: 10%;
  852. margin-left: -300px;
  853. z-index: 2147483646;
  854. background-color: #ffffff;
  855. border: 1px solid #afb3b6;
  856. border-radius: 10px;
  857. opacity: 0.95;
  858. filter: alpha(opacity=95);
  859. box-shadow: 5px 5px 20px 0px #000;
  860. }
  861. @media screen and (max-width: 800px) {
  862. #filterListBody {
  863. width: 90%;
  864. margin-left: -45%;
  865. }
  866. }
  867. `);
  868. dacLinksCon = filterListContainer.querySelector("#dacLinksCon");
  869. shadowContainer = document.createElement("div");
  870. shadowContainer.id = "download-all-content";
  871. document.body.appendChild(shadowContainer);
  872. let shadow = shadowContainer.attachShadow({ mode: "open" });
  873. shadow.appendChild(listStyle);
  874. shadow.appendChild(filterListContainer);
  875. }
  876. if (shadowContainer.parentNode) shadowContainer.parentNode.removeChild(shadowContainer);
  877. linkDict = {};
  878. list.forEach(a => {
  879. createLinkItem(a);
  880. });
  881. dacUseIframe.checked = useIframe;
  882. document.body.appendChild(shadowContainer);
  883. }
  884.  
  885. function initTxtDownDiv() {
  886. if (txtDownContent) {
  887. txtDownContent.style.display = "";
  888. document.body.appendChild(downTxtShadowContainer);
  889. return;
  890. }
  891. txtDownContent = document.createElement("div");
  892. txtDownContent.id = "txtDownContent";
  893. downTxtShadowContainer = document.createElement("div");
  894. document.body.appendChild(downTxtShadowContainer);
  895. let shadow = downTxtShadowContainer.attachShadow({ mode: "open" });
  896. shadow.appendChild(txtDownContent);
  897. txtDownContent.innerHTML = createHTML(`
  898. <style>
  899. #txtDownContent>div{
  900. font-size:16px;
  901. color:#333333;
  902. width:342px;
  903. height:110px;
  904. position:fixed;
  905. left:50%;
  906. top:50%;
  907. margin-top:-25px;
  908. margin-left:-171px;
  909. z-index:2147483647;
  910. background-color:#ffffff;
  911. border:1px solid #afb3b6;
  912. border-radius:10px;
  913. opacity:0.95;
  914. filter:alpha(opacity=95);
  915. box-shadow:5px 5px 20px 0px #000;
  916. }
  917. #txtDownWords{
  918. position:absolute;
  919. width:275px;
  920. height: 90px;
  921. max-height: 90%;
  922. border: 1px solid #f3f1f1;
  923. padding: 8px;
  924. border-radius: 10px;
  925. overflow: auto;
  926. }
  927. #txtDownQuit{
  928. width: 30px;height: 30px;border-radius: 30px;position:absolute;right:2px;top:2px;cursor: pointer;background-color:#ff5a5a;
  929. }
  930. #txtDownQuit>span{
  931. 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;
  932. }
  933. #txtDownQuit+div{
  934. position:absolute;right:0px;bottom:2px;cursor: pointer;max-width:85px;
  935. }
  936. #txtDownQuit+div>button{
  937. background: #008aff;border: 0;padding: 5px;border-radius: 6px;color: white;float: right;margin: 1px;height: 25px;line-height: 16px;cursor: pointer;overflow: hidden;
  938. }
  939. </style>
  940. <div>
  941. <div id="txtDownWords">
  942. Analysing......
  943. </div>
  944. <div id="txtDownQuit">
  945. <span>╳</span>
  946. </div>
  947. <div>
  948. <button id="abortRequest" style="display:none;">${getI18n('abort')}</button>
  949. <button id="tempSaveTxt">${getI18n('save')}</button>
  950. <button id="saveAsMd" title="${getI18n('saveAsMd')}">Markdown</button>
  951. <button id="saveAsJSON" title="${getI18n('saveAsJSON')}">JSON</button>
  952. </div>
  953. </div>`);
  954. txtDownWords=txtDownContent.querySelector("#txtDownWords");
  955. txtDownQuit=txtDownContent.querySelector("#txtDownQuit");
  956. txtDownQuit.onclick=function(){
  957. txtDownContent.style.display="none";
  958. };
  959. initTempSave(txtDownContent);
  960. win.txtDownWords = txtDownWords;
  961. }
  962.  
  963. function saveContent() {
  964. if (win.downloadAllContentSaveAsZip && saveAsZip) {
  965. win.downloadAllContentSaveAsZip(rCats, i18n.info.replace("#t#", location.href), content => {
  966. saveAs(content, document.title.replace(/[\*\/:<>\?\\\|\r\n,]/g, "_") + ".zip");
  967. });
  968. } else {
  969. var blob = new Blob([i18n.info.replace("#t#", location.href) + "\r\n\r\n" + rCats.join("\r\n\r\n")], {type: "text/plain;charset=utf-8"});
  970. saveAs(blob, document.title.replace(/[\*\/:<>\?\\\|\r\n,]/g, "_") + ".txt");
  971. }
  972. }
  973.  
  974. function initTempSave(txtDownContent){
  975. var tempSavebtn = txtDownContent.querySelector('#tempSaveTxt');
  976. var abortbtn = txtDownContent.querySelector('#abortRequest');
  977. var saveAsMd = txtDownContent.querySelector('#saveAsMd');
  978. var saveAsJSON = txtDownContent.querySelector('#saveAsJSON');
  979.  
  980. tempSavebtn.onclick = function(){
  981. saveContent();
  982. console.log(curRequests);
  983. }
  984. abortbtn.onclick = function(){
  985. let curRequest = curRequests.pop();
  986. if(curRequest)curRequest[1].abort();
  987. }
  988. saveAsMd.onclick = function(){
  989. let txt = i18n.info.replace("#t#", location.href)+"\n\n---\n"+document.title+"\n===\n";
  990. rCats.forEach(cat => {
  991. cat = cat.replace("\r\n", "\n---").replace(/(\r\n|\n\r)+/g, "\n\n").replace(/[\n\r]\t+/g, "\n");
  992. txt += '\n\n'+cat;
  993. });
  994. var blob = new Blob([txt], {type: "text/plain;charset=utf-8"});
  995. saveAs(blob, document.title.replace(/[\*\/:<>\?\\\|\r\n,]/g, "_") + ".md");
  996. }
  997. saveAsJSON.onclick = function(){
  998. let txt = [];
  999. rCats.forEach(cat => {
  1000. let catArr = cat.split("\r\n", 3);
  1001. let saveUrl = GM_getValue("saveUrl");
  1002. let catJson = {
  1003. title: catArr[0].trim(),
  1004. content: catArr[1].trim()
  1005. };
  1006. if (saveUrl){
  1007. catJson = {
  1008. title: catArr[0].trim(),
  1009. url: catArr[1].trim(),
  1010. content: catArr[2].trim()
  1011. };
  1012. }
  1013. txt.push(catJson);
  1014. });
  1015. txt = JSON.stringify(txt, null, 2);
  1016. var blob = new Blob([txt], {type: "text/plain;charset=utf-8"});
  1017. saveAs(blob, document.title.replace(/[\*\/:<>\?\\\|\r\n,]/g, "_") + ".json");
  1018. }
  1019. }
  1020.  
  1021. let charset = (document.characterSet || document.charset || document.inputEncoding);
  1022. let equiv = document.querySelector('[http-equiv="Content-Type"]'), charsetValid = true;
  1023. if (equiv && equiv.content) {
  1024. let innerCharSet = equiv.content.match(/charset\=([^;]+)/);
  1025. if (!innerCharSet) {
  1026. charsetValid = false;
  1027. } else if (innerCharSet[1].replace("-", "").toLowerCase() != charset.replace("-", "").toLowerCase()) {
  1028. charsetValid = false;
  1029. }
  1030. } else charsetValid = false;
  1031. var iframePool = [];
  1032. function indexDownload(aEles, noSort){
  1033. if(aEles.length<1)return;
  1034. initTxtDownDiv();
  1035. if(!noSort) {
  1036. if(GM_getValue("contentSort")){
  1037. aEles.sort((a, b) => {
  1038. return str2Num(a.innerText) - str2Num(b.innerText);
  1039. });
  1040. }
  1041. if(GM_getValue("contentSortUrl")){
  1042. aEles.sort((a, b) => {
  1043. const nameA = a.href.toUpperCase();
  1044. const nameB = b.href.toUpperCase();
  1045. if (nameA < nameB) {
  1046. return -1;
  1047. }
  1048. if (nameA > nameB) {
  1049. return 1;
  1050. }
  1051. return 0;
  1052. });
  1053. }
  1054. if(GM_getValue("reverse")){
  1055. aEles=aEles.reverse();
  1056. }
  1057. }
  1058. rCats=[];
  1059. const minute=60000;
  1060. var minTxtLength=GM_getValue("minTxtLength") || 100;
  1061. var customTitle=GM_getValue("customTitle");
  1062. var prefix=GM_getValue("prefix");
  1063. var disableNextPage=!!GM_getValue("disableNextPage");
  1064. var customNextPageReg=GM_getValue("nextPageReg");
  1065. var maxDlPerMin=GM_getValue("maxDlPerMin") || 0;
  1066. var dlCount=0;
  1067. if (customNextPageReg) {
  1068. try {
  1069. innerNextPage = new RegExp(customNextPageReg);
  1070. } catch(e) {
  1071. console.warn(e);
  1072. }
  1073. }
  1074. function packLink(doc, item, curIndex) {
  1075. if (customTitle) {
  1076. try {
  1077. let title = doc.querySelector(customTitle);
  1078. if (title && title.innerText) {
  1079. item.innerText = title.innerText;
  1080. }
  1081. } catch(e) {
  1082. console.warn(e);
  1083. }
  1084. }
  1085. if (prefix) {
  1086. item.innerText = prefix.replace(/\$i/g, ++curIndex) + item.innerText;
  1087. }
  1088. }
  1089. function getIframe() {
  1090. if (iframePool && iframePool.length) return iframePool.shift();
  1091. let iframe = document.createElement('iframe');
  1092. iframe.name = 'pagetual-iframe';
  1093. iframe.width = '100%';
  1094. iframe.height = '1000';
  1095. iframe.frameBorder = '0';
  1096. iframe.style.cssText = 'margin:0!important;padding:0!important;visibility:hidden!important;flex:0;opacity:0!important;pointer-events:none!important;position:fixed;top:0px;left:0px;z-index:-2147483647;';
  1097. return iframe;
  1098. }
  1099. var insertSigns=[];
  1100. // var j=0,rCats=[];
  1101. var downIndex=0,downNum=0,downOnce=function(wait){
  1102. if(downNum>=aEles.length)return;
  1103. if(maxDlPerMin){
  1104. if(dlCount===-1){
  1105. setTimeout(() => {
  1106. downOnce(wait);
  1107. }, minute);
  1108. return;
  1109. }else if(dlCount>=maxDlPerMin){
  1110. dlCount=-1;
  1111. setTimeout(() => {
  1112. dlCount=0;
  1113. downOnce(wait);
  1114. }, minute);
  1115. return;
  1116. }else dlCount++;
  1117. }
  1118. let curIndex=downIndex;
  1119. let aTag=aEles[curIndex];
  1120. let request=(aTag, curIndex)=>{
  1121. if (aTag && aTag.cloneNode) {
  1122. aTag = aTag.cloneNode(true);
  1123. }
  1124. let tryTimes=0;
  1125. let validTimes=0;
  1126. function requestDoc(_charset) {
  1127. if (!_charset) _charset = charset;
  1128. return GM_xmlhttpRequest({
  1129. method: 'GET',
  1130. url: aTag.href,
  1131. headers:{
  1132. referer:aTag.href,
  1133. "Content-Type":"text/html;charset="+_charset
  1134. },
  1135. timeout:10000,
  1136. overrideMimeType:"text/html;charset="+_charset,
  1137. onload: async function(result) {
  1138. let doc = getDocEle(result.responseText);
  1139. if (charsetValid) {
  1140. let equiv = doc.querySelector('[http-equiv="Content-Type"]');
  1141. if (equiv && equiv.content) {
  1142. let innerCharSet = equiv.content.match(/charset\=([^;]+)/);
  1143. if (innerCharSet && innerCharSet[1].replace("-", "").toLowerCase() != _charset.replace("-", "").toLowerCase()) {
  1144. charset = innerCharSet[1];
  1145. return requestDoc(charset);
  1146. }
  1147. }
  1148. }
  1149. downIndex++;
  1150. downNum++;
  1151. if (/^{/.test(result.responseText)) {
  1152. doc.json = () => {
  1153. try {
  1154. return JSON.parse(result.responseText);
  1155. } catch(e) {}
  1156. return {};
  1157. }
  1158. }
  1159. let base = doc.querySelector("base");
  1160. let nextPages = !disableNextPage && (!processFunc || nextPageFunc) && await checkNextPage(doc, base ? base.href : aTag.href);
  1161. if (nextPages) {
  1162. if (!nextPages.length) nextPages = [nextPages];
  1163. nextPages.forEach(nextPage => {
  1164. var inArr=false;
  1165. for(var ai=0;ai<aEles.length;ai++){
  1166. if(aEles[ai].href==nextPage.href){
  1167. inArr=true;
  1168. break;
  1169. }
  1170. }
  1171. if(!inArr){
  1172. nextPage.innerText=aTag.innerText+"\t>>";
  1173. aEles.push(nextPage);
  1174. let targetIndex = curIndex;
  1175. for(let a=0;a<insertSigns.length;a++){
  1176. let signs=insertSigns[a],breakSign=false;
  1177. if(signs){
  1178. for(let b=0;b<signs.length;b++){
  1179. let sign=signs[b];
  1180. if(sign==curIndex){
  1181. targetIndex=a;
  1182. breakSign=true;
  1183. break;
  1184. }
  1185. }
  1186. }
  1187. if(breakSign)break;
  1188. }
  1189. let insertSign = insertSigns[targetIndex];
  1190. if(!insertSign)insertSigns[targetIndex] = [];
  1191. insertSigns[targetIndex].push(aEles.length-1);
  1192. }
  1193. });
  1194. }
  1195. if (result.status >= 400) {
  1196. console.warn("error:", `status: ${result.status} from: ${aTag.href}`);
  1197. } else {
  1198. console.log(result.status);
  1199. }
  1200. let validData = processDoc(curIndex, aTag, doc, (result.status>=400?` status: ${result.status} from: ${aTag.href} `:""), validTimes < 5);
  1201. if (!validData && validTimes++ < 5) {
  1202. downIndex--;
  1203. downNum--;
  1204. setTimeout(() => {
  1205. requestDoc();
  1206. }, Math.random() * 500 + validTimes * 1000);
  1207. return;
  1208. }
  1209. if (wait) {
  1210. setTimeout(() => {
  1211. downOnce(wait);
  1212. }, wait);
  1213. } else downOnce();
  1214. },
  1215. onerror: function(e) {
  1216. console.warn("error:", e, aTag.href);
  1217. if(tryTimes++ < 5){
  1218. setTimeout(() => {
  1219. requestDoc();
  1220. }, Math.random() * 500 + tryTimes * 1000);
  1221. return;
  1222. }
  1223. downIndex++;
  1224. downNum++;
  1225. processDoc(curIndex, aTag, null, ` NETWORK ERROR: ${(e.response||e.responseText)} from: ${aTag.href} `);
  1226. if (wait) {
  1227. setTimeout(() => {
  1228. downOnce(wait);
  1229. }, wait);
  1230. } else downOnce();
  1231. },
  1232. ontimeout: function(e) {
  1233. console.warn("timeout: times="+(tryTimes+1)+" url="+aTag.href);
  1234. //console.log(e);
  1235. if(tryTimes++ < 5){
  1236. setTimeout(() => {
  1237. requestDoc();
  1238. }, Math.random() * 500 + tryTimes * 1000);
  1239. return;
  1240. }
  1241. downIndex++;
  1242. downNum++;
  1243. processDoc(curIndex, aTag, null, ` TIMEOUT: ${aTag.href} `);
  1244. if (wait) {
  1245. setTimeout(() => {
  1246. downOnce(wait);
  1247. }, wait);
  1248. } else downOnce();
  1249. }
  1250. });
  1251. };
  1252. if (useIframe) {
  1253. let iframe = getIframe(), inited = false, failedTimes = 0;
  1254. let loadtimeout;
  1255. let loadIframe = src => {
  1256. iframe.src = src;
  1257. clearTimeout(loadtimeout);
  1258. loadtimeout = setTimeout(() => {
  1259. iframe.src = src;
  1260. }, 20000);
  1261. };
  1262. iframe.sandbox = iframeSandbox || "allow-same-origin allow-scripts allow-popups allow-forms";
  1263. iframe.addEventListener('load', e => {
  1264. if (e.data != 'pagetual-iframe:DOMLoaded' && e.type != 'load') return;
  1265. if (inited) return;
  1266. inited = true;
  1267. clearTimeout(loadtimeout);
  1268. async function checkIframe() {
  1269. try {
  1270. let doc = iframe.contentDocument || iframe.contentWindow.document;
  1271. if (!doc || !doc.body) {
  1272. setTimeout(() => {
  1273. checkIframe();
  1274. }, 1000);
  1275. return;
  1276. }
  1277. doc.body.scrollTop = 9999999;
  1278. doc.documentElement.scrollTop = 9999999;
  1279. if (!processFunc && validTimes++ > 5 && failedTimes++ < 2) {
  1280. loadIframe(iframe.src);
  1281. validTimes = 0;
  1282. inited = false;
  1283. return;
  1284. }
  1285. let base = doc.querySelector("base");
  1286. let nextPages = !disableNextPage && !processFunc && await checkNextPage(doc, base ? base.href : aTag.href);
  1287. if (nextPages) {
  1288. if (!nextPages.length) nextPages = [nextPages];
  1289. nextPages.forEach(nextPage => {
  1290. var inArr=false;
  1291. for(var ai=0;ai<aEles.length;ai++){
  1292. if(aEles[ai].href==nextPage.href){
  1293. inArr=true;
  1294. break;
  1295. }
  1296. }
  1297. if(!inArr){
  1298. nextPage.innerText=aTag.innerText+"\t>>";
  1299. aEles.push(nextPage);
  1300. let targetIndex = curIndex;
  1301. for(let a=0;a<insertSigns.length;a++){
  1302. let signs=insertSigns[a],breakSign=false;
  1303. if(signs){
  1304. for(let b=0;b<signs.length;b++){
  1305. let sign=signs[b];
  1306. if(sign==curIndex){
  1307. targetIndex=a;
  1308. breakSign=true;
  1309. break;
  1310. }
  1311. }
  1312. }
  1313. if(breakSign)break;
  1314. }
  1315. let insertSign = insertSigns[targetIndex];
  1316. if(!insertSign)insertSigns[targetIndex] = [];
  1317. insertSigns[targetIndex].push(aEles.length-1);
  1318. }
  1319. });
  1320. }
  1321. downIndex++;
  1322. downNum++;
  1323. let validData = processDoc(curIndex, aTag, doc, "", failedTimes < 2);
  1324. if (!validData) {
  1325. downIndex--;
  1326. downNum--;
  1327. setTimeout(() => {
  1328. checkIframe();
  1329. }, 1000);
  1330. return;
  1331. }
  1332. if (wait) {
  1333. setTimeout(() => {
  1334. downOnce(wait);
  1335. }, wait);
  1336. } else downOnce();
  1337. } catch(e) {
  1338. console.debug("Stop as cors");
  1339. }
  1340. if (iframe && iframe.parentNode) {
  1341. try {
  1342. iframe.src = 'about:blank';
  1343. } catch (e) {
  1344. console.error("Error clearing iframe src:", e);
  1345. }
  1346. iframe.parentNode.removeChild(iframe);
  1347. iframePool.push(iframe);
  1348. }
  1349. }
  1350. setTimeout(() => {
  1351. checkIframe();
  1352. }, 500);
  1353. }, false);
  1354. let checkReady = setInterval(() => {
  1355. let doc;
  1356. try {
  1357. doc = iframe.contentDocument || (iframe.contentWindow && iframe.contentWindow.document);
  1358. } catch(e) {
  1359. clearInterval(checkReady);
  1360. return;
  1361. }
  1362. if (doc) {
  1363. try {
  1364. Function('win', 'iframe', '"use strict";' + (iframeInit || "win.self=win.top;"))(iframe.contentWindow, iframe);
  1365. clearInterval(checkReady);
  1366. } catch(e) {
  1367. console.debug(e);
  1368. }
  1369. }
  1370. }, 50);
  1371. loadIframe(aTag.href);
  1372. document.body.appendChild(iframe);
  1373. return [curIndex, null, aTag.href];
  1374. } else {
  1375. return [curIndex, requestDoc(), aTag.href];
  1376. }
  1377. }
  1378. if(!aTag){
  1379. let waitAtagReadyInterval=setInterval(function(){
  1380. if(downNum>=aEles.length)clearInterval(waitAtagReadyInterval);
  1381. aTag=aEles[curIndex];
  1382. if(aTag){
  1383. clearInterval(waitAtagReadyInterval);
  1384. request(aTag, curIndex);
  1385. }
  1386. },1000);
  1387. return null;
  1388. }
  1389. let result = request(aTag, curIndex);
  1390. if (result) curRequests.push(result);
  1391. return result;
  1392. };
  1393. function getDocEle(str){
  1394. var doc = null;
  1395. try {
  1396. doc = document.implementation.createHTMLDocument('');
  1397. doc.documentElement.innerHTML = str;
  1398. }
  1399. catch (e) {
  1400. console.log('parse error');
  1401. }
  1402. return doc;
  1403. }
  1404. function sortInnerPage(){
  1405. var pageArrs=[],maxIndex=0,i,j;
  1406. for(i=0;i<insertSigns.length;i++){
  1407. var signs=insertSigns[i];
  1408. if(signs){
  1409. for(j=0;j<signs.length;j++){
  1410. var sign=signs[j];
  1411. var cat=rCats[sign];
  1412. rCats[sign]=null;
  1413. if(!pageArrs[i])pageArrs[i]=[];
  1414. pageArrs[i].push(cat);
  1415. }
  1416. }
  1417. }
  1418. for(i=pageArrs.length-1;i>=0;i--){
  1419. let pageArr=pageArrs[i];
  1420. if(pageArr){
  1421. for(j=pageArr.length-1;j>=0;j--){
  1422. rCats.splice(i+1, 0, pageArr[j]);
  1423. }
  1424. }
  1425. }
  1426. rCats = rCats.filter(function(e){return e!=null});
  1427. }
  1428. var waitForComplete;
  1429. function processDoc(i, aTag, doc, cause, check){
  1430. let cbFunc=content=>{
  1431. packLink(doc, aTag, i);
  1432. let isHref = "";
  1433. let saveUrl = GM_getValue("saveUrl");
  1434. if (saveUrl){
  1435. isHref = aTag.href + '\r\n';
  1436. }
  1437. rCats[i]=(aTag.innerText.replace(/[\r\n\t]/g, "") + "\r\n" + isHref + (cause || '') + content.replace(/\s*$/, ""));
  1438. curRequests = curRequests.filter(function(e){return e[0]!=i});
  1439. txtDownContent.style.display="block";
  1440. txtDownWords.innerHTML=getI18n("downloading",[downNum,(aEles.length-downNum),aTag.innerText]);
  1441. if(downNum==aEles.length){
  1442. if(waitForComplete) clearTimeout(waitForComplete);
  1443. waitForComplete=setTimeout(()=>{
  1444. if(downNum==aEles.length){
  1445. txtDownWords.innerHTML=getI18n("complete",[downNum]);
  1446. sortInnerPage();
  1447. var disableAutoStartSave = GM_getValue("disableAutoStartSave");
  1448. if(!disableAutoStartSave){
  1449. saveContent();
  1450. }
  1451. }
  1452. },3000);
  1453. }
  1454. };
  1455. let contentResult=getPageContent(doc, content=>{
  1456. cbFunc(content);
  1457. }, aTag.href);
  1458. if(contentResult!==false){
  1459. if(check && contentResult && contentResult.replace(/\s/g, "").length<minTxtLength){
  1460. return false;
  1461. }
  1462. cbFunc(contentResult);
  1463. }
  1464. return true;
  1465. }
  1466. var downThreadNum = parseInt(GM_getValue("downThreadNum"));
  1467. downThreadNum = downThreadNum || 20;
  1468. if (useIframe && downThreadNum > 5) {
  1469. downThreadNum = 5;
  1470. }
  1471. if (downThreadNum > 0) {
  1472. for (var i = 0; i < downThreadNum; i++) {
  1473. downOnce();
  1474. if (downIndex >= aEles.length - 1 || downIndex >= downThreadNum - 1) break;
  1475. else downIndex++;
  1476. }
  1477. } else {
  1478. downOnce(-downThreadNum * 1000);
  1479. }
  1480. }
  1481.  
  1482. function canonicalUri(src, baseUrl) {
  1483. if (!src) {
  1484. return "";
  1485. }
  1486. if (src.charAt(0) == "#") return baseUrl + src;
  1487. if (src.charAt(0) == "?") return baseUrl.replace(/^([^\?#]+).*/, "$1" + src);
  1488. let origin = location.protocol + '//' + location.host;
  1489. let url = baseUrl || origin;
  1490. url = url.replace(/(\?|#).*/, "");
  1491. if (/https?:\/\/[^\/]+$/.test(url)) url = url + '/';
  1492. if (url.indexOf("http") !== 0) url = origin + url;
  1493. var root_page = /^[^\?#]*\//.exec(url)[0],
  1494. root_domain = /^\w+\:\/\/\/?[^\/]+/.exec(root_page)[0],
  1495. absolute_regex = /^\w+\:\/\//;
  1496. while (src.indexOf("../") === 0) {
  1497. src = src.substr(3);
  1498. root_page = root_page.replace(/\/[^\/]+\/$/, "/");
  1499. }
  1500. src = src.replace(/\.\//, "");
  1501. if (/^\/\/\/?/.test(src)) {
  1502. src = location.protocol + src;
  1503. }
  1504. return (absolute_regex.test(src) ? src : ((src.charAt(0) == "/" ? root_domain : root_page) + src));
  1505. }
  1506.  
  1507. async function checkNextPage(doc, baseUrl) {
  1508. let nextPage = null;
  1509. if (nextPageFunc) {
  1510. nextPage = await nextPageFunc(doc, baseUrl);
  1511. if (nextPage && nextPage.length === 0) nextPage = null;
  1512. } else {
  1513. let aTags = doc.querySelectorAll("a");
  1514. for (var i = 0; i < aTags.length; i++) {
  1515. let aTag = aTags[i];
  1516. if (innerNextPage.test(aTag.innerText) && aTag.href && !/javascript:|#/.test(aTag.href)) {
  1517. let nextPageHref = canonicalUri(aTag.getAttribute("href"), baseUrl || location.href);
  1518. if (nextPageHref != location.href) {
  1519. nextPage = aTag;
  1520. nextPage.href = nextPageHref;
  1521. break;
  1522. }
  1523. }
  1524. }
  1525. }
  1526. return nextPage;
  1527. }
  1528.  
  1529. function textNodesUnder(el){
  1530. var n, a=[], walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false);
  1531. while(n=walk.nextNode()) a.push(n);
  1532. return a;
  1533. }
  1534.  
  1535. function getPageContent(doc, cb, url){
  1536. if(!doc)return i18n.error;
  1537. if(doc.body && !doc.body.children.length)return doc.body.innerText;
  1538. if(processFunc){
  1539. return processFunc(doc, cb, url);
  1540. }
  1541. [].forEach.call(doc.querySelectorAll("span,div,ul"),function(item){
  1542. var thisStyle=doc.defaultView?doc.defaultView.getComputedStyle(item):item.style;
  1543. if(thisStyle && (thisStyle.display=="none" || (item.nodeName=="SPAN" && thisStyle.fontSize=="0px"))){
  1544. item.innerHTML="";
  1545. }
  1546. });
  1547. var i,j,k,rStr="",pageData=(doc.body?doc.body:doc).cloneNode(true);
  1548. pageData.innerHTML=pageData.innerHTML.replace(/\<\!\-\-((.|[\n|\r|\r\n])*?)\-\-\>/g,"");
  1549. [].forEach.call(pageData.querySelectorAll("font.jammer"),function(item){
  1550. item.innerHTML="";
  1551. });
  1552. var selectors=GM_getValue("selectors");
  1553. if(selectors){
  1554. [].forEach.call(pageData.querySelectorAll(selectors),function(item){
  1555. item.innerHTML="";
  1556. });
  1557. }
  1558. [].forEach.call(pageData.querySelectorAll("script,style,link,noscript,iframe"),function(item){
  1559. if (item && item.parentNode) {
  1560. item.parentNode.removeChild(item);
  1561. }
  1562. });
  1563. var endEle = ele => {
  1564. return /^(I|STRONG|B|FONT|P|DL|DD|H\d)$/.test(ele.nodeName) && ele.children.length <= 1;
  1565. };
  1566. var largestContent,contents=pageData.querySelectorAll("span,div,article,p,td,pre"),largestNum=0;
  1567. for(i=0;i<contents.length;i++){
  1568. let content=contents[i],hasText=false,allSingle=true,item,curNum=0;
  1569. if(/footer/.test(content.className))continue;
  1570. for(j=content.childNodes.length-1;j>=0;j--){
  1571. item=content.childNodes[j];
  1572. if(item.nodeType==3){
  1573. if(/^\s*$/.test(item.data)){
  1574. item.innerHTML="";
  1575. }else hasText=true;
  1576. }else if(/^(I|A|STRONG|B|FONT|P|DL|DD|H\d)$/.test(item.nodeName)){
  1577. hasText=true;
  1578. }else if(item.nodeType==1&&item.children.length==1&&/^(I|A|STRONG|B|FONT|P|DL|DD|H\d)$/.test(item.children[0].nodeName)){
  1579. hasText=true;
  1580. }
  1581. }
  1582. for(j=content.childNodes.length-1;j>=0;j--){
  1583. item=content.childNodes[j];
  1584. if(item.nodeType==1 && !/^(I|A|STRONG|B|FONT|BR)$/.test(item.nodeName) && /^[\s\-\_\?\>\|]*$/.test(item.innerHTML)){
  1585. item.innerHTML="";
  1586. }
  1587. }
  1588. if(content.childNodes.length>1){
  1589. let indexItem=0;
  1590. for(j=0;j<content.childNodes.length;j++){
  1591. item=content.childNodes[j];
  1592. if(item.nodeType==1){
  1593. if(item.innerText && item.innerText.length<50 && indexReg.test(item.innerText))indexItem++;
  1594. for(k=0;k<item.childNodes.length;k++){
  1595. var childNode=item.childNodes[k];
  1596. if(childNode.nodeType!=3 && !/^(I|A|STRONG|B|FONT|BR)$/.test(childNode.nodeName)){
  1597. allSingle=false;
  1598. break;
  1599. }
  1600. }
  1601. if(!allSingle)break;
  1602. }
  1603. }
  1604. if(indexItem>=5)continue;
  1605. }else{
  1606. allSingle=false;
  1607. }
  1608. if(!allSingle && !hasText){
  1609. continue;
  1610. }else {
  1611. if(pageData==document && content.offsetWidth<=0 && content.offsetHeight<=0){
  1612. continue;
  1613. }
  1614. [].forEach.call(content.childNodes,function(item){
  1615. if(item.nodeType==3)curNum+=item.data.trim().length;
  1616. 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);
  1617. });
  1618. }
  1619. if(curNum>largestNum){
  1620. largestNum=curNum;
  1621. largestContent=content;
  1622. }
  1623. }
  1624. if(!largestContent)return i18n.error+" : NO TEXT CONTENT";
  1625. var retainImage=!!GM_getValue("retainImage");
  1626. function getContentByLargest() {
  1627. var childlist=pageData.querySelectorAll(largestContent.nodeName);//+(largestContent.className?"."+largestContent.className.replace(/(^\s*)|(\s*$)/g, '').replace(/\s+/g, '.'):""));
  1628. function getRightStr(ele, noTextEnable){
  1629. [].forEach.call(ele.querySelectorAll("a[href]"), a => {
  1630. a.parentNode && a.parentNode.removeChild(a);
  1631. });
  1632. if(retainImage){
  1633. [].forEach.call(ele.querySelectorAll("img[src]"), img => {
  1634. let imgTxtNode=document.createTextNode(`![img](${canonicalUri(img.getAttribute("src"), url || location.href)})`);
  1635. img.parentNode.replaceChild(imgTxtNode, img);
  1636. });
  1637. }
  1638. let childNodes=ele.childNodes,cStr="\r\n",hasText=false;
  1639. for(let j=0;j<childNodes.length;j++){
  1640. let childNode=childNodes[j];
  1641. if(childNode.nodeType==3 && childNode.data && !/^[\s\-\_\?\>\|]*$/.test(childNode.data))hasText=true;
  1642. if(childNode.innerHTML){
  1643. childNode.innerHTML=childNode.innerHTML.replace(/\<\s*br\s*\>/gi,"\r\n").replace(/\n+/gi,"\n").replace(/\r+/gi,"\r");
  1644. }
  1645. let content=childNode.textContent;
  1646. if(content){
  1647. if(!content.trim())continue;
  1648. cStr+=content.replace(/[\uFEFF\xA0 ]+/g," ").replace(/([^\r]|^)\n([^\r]|$)/gi,"$1\r\n$2");
  1649. }
  1650. if(childNode.nodeType!=3 && !/^(I|A|STRONG|B|FONT|IMG)$/.test(childNode.nodeName))cStr+="\r\n";
  1651. else if(childNode.nextSibling && /^P$/.test(childNode.nextSibling.nodeName))cStr+="\r\n";
  1652. }
  1653. if(hasText || noTextEnable || ele==largestContent)rStr+=cStr+"\r\n";
  1654. }
  1655. var sameDepthChildren=[];
  1656. for(i=0;i<childlist.length;i++){
  1657. var child=childlist[i];
  1658. if(getDepth(child)==getDepth(largestContent)){
  1659. if(largestContent.className != child.className)continue;
  1660. sameDepthChildren.push(child);
  1661. }
  1662. }
  1663. var minLength = largestNum>>2;
  1664. var tooShort = sameDepthChildren.length <= 3;
  1665. sameDepthChildren.forEach(child => {
  1666. if(tooShort && child.innerText.length < minLength) return;
  1667. if((largestContent.className && largestContent.className == child.className) || largestContent.parentNode == child.parentNode){
  1668. getRightStr(child, true);
  1669. }else {
  1670. getRightStr(child, false);
  1671. }
  1672. });
  1673. rStr = rStr.replace(/[\n\r]+/g,"\n\r");
  1674. }
  1675. getContentByLargest();
  1676. if (rStr.length < 100) {
  1677. let articles = pageData.querySelectorAll("article,.content,#content");
  1678. if (articles && articles.length == 1) {
  1679. largestContent = articles[0];
  1680. largestNum = largestContent.innerText.length;
  1681. if (largestNum > 100) {
  1682. rStr = "";
  1683. getContentByLargest();
  1684. }
  1685. }
  1686. }
  1687. return rStr;
  1688. }
  1689.  
  1690. function getI18n(key, args){
  1691. var resultStr=i18n[key];
  1692. if(args && args.length>0){
  1693. args.forEach(function(item){
  1694. resultStr=resultStr.replace(/%s/,item);
  1695. });
  1696. }
  1697. return resultStr;
  1698. }
  1699.  
  1700. function getDepth(dom){
  1701. var pa=dom,i=0;
  1702. while(pa.parentNode){
  1703. pa=pa.parentNode;
  1704. i++;
  1705. }
  1706. return i;
  1707. }
  1708.  
  1709. async function sleep(time) {
  1710. await new Promise((resolve) => {
  1711. setTimeout(() => {
  1712. resolve();
  1713. }, time);
  1714. })
  1715. }
  1716.  
  1717. async function fetch(forceSingle){
  1718. forceSingle=forceSingle===true;
  1719. processFunc=null;
  1720. initTxtDownDiv();
  1721. var aEles=document.body.querySelectorAll("a"),list=[];
  1722. txtDownWords.innerHTML=`Analysing ( 1/${aEles.length} )......`;
  1723. txtDownContent.style.pointerEvents="none";
  1724. for(var i=0;i<aEles.length;i++){
  1725. if (i % 100 == 0) {
  1726. await sleep(1);
  1727. }
  1728. txtDownWords.innerHTML=`Analysing ( ${i + 1}/${aEles.length} )......`;
  1729. var aEle=aEles[i],has=false;
  1730. if(aEle.dataset.href && (!aEle.href || aEle.href.indexOf("javascript")!=-1)){
  1731. aEle.href=aEle.dataset.href;
  1732. }
  1733. if(aEle.href==location.href)continue;
  1734. for(var j=0;j<list.length;j++){
  1735. if(list[j].href==aEle.href){
  1736. aEle=list[j];
  1737. list.splice(j,1);
  1738. list.push(aEle);
  1739. has=true;
  1740. break;
  1741. }
  1742. }
  1743. if(!has && aEle.href && /^http/i.test(aEle.href) && ((aEle.innerText.trim()!="" && indexReg.test(aEle.innerText.trim())) || /chapter[\-_]?\d/.test(aEle.href))){
  1744. list.push(aEle);
  1745. }
  1746. }
  1747. txtDownContent.style.display="none";
  1748. txtDownContent.style.pointerEvents="";
  1749. txtDownWords.innerHTML="Analysing......";
  1750. if(list.length>2 && !forceSingle){
  1751. useIframe = false;
  1752. filterList(list);
  1753. }else{
  1754. var blob = new Blob([i18n.info.replace("#t#", location.href)+"\r\n\r\n"+document.title+"\r\n\r\n"+getPageContent(document)], {type: "text/plain;charset=utf-8"});
  1755. saveAs(blob, document.title+".txt");
  1756. }
  1757. }
  1758.  
  1759. function customDown(urls){
  1760. processFunc = null;
  1761. useIframe = false;
  1762. if(urls){
  1763. urls=decodeURIComponent(urls.replace(/%/g,'%25'));
  1764. GM_setValue("DACrules_"+document.domain, urls);
  1765. var processEles=[];
  1766. let urlsArr=urls.split("@@"),eles=[];
  1767. if(/^http|^ftp/.test(urlsArr[0])){
  1768. [].forEach.call(urlsArr[0].split(","),function(i){
  1769. var curEle;
  1770. var varNum=/\[\d+\-\d+\]/.exec(i);
  1771. if(varNum){
  1772. varNum=varNum[0].trim();
  1773. }else{
  1774. curEle=document.createElement("a");
  1775. curEle.href=i;
  1776. curEle.innerText="Added Url";
  1777. processEles.push(curEle);
  1778. return;
  1779. }
  1780. var num1=/\[(\d+)/.exec(varNum)[1].trim();
  1781. var num2=/(\d+)\]/.exec(varNum)[1].trim();
  1782. var num1Int=parseInt(num1);
  1783. var num2Int=parseInt(num2);
  1784. var numLen=num1.length;
  1785. var needAdd=num1.charAt(0)=="0";
  1786. if(num1Int>=num2Int)return;
  1787. for(var j=num1Int;j<=num2Int;j++){
  1788. var urlIndex=j.toString();
  1789. if(needAdd){
  1790. while(urlIndex.length<numLen)urlIndex="0"+urlIndex;
  1791. }
  1792. var curUrl=i.replace(/\[\d+\-\d+\]/,urlIndex).trim();
  1793. curEle=document.createElement("a");
  1794. curEle.href=curUrl;
  1795. curEle.innerText="Added Url " + processEles.length.toString();
  1796. processEles.push(curEle);
  1797. }
  1798. });
  1799. }else{
  1800. let urlSel=urlsArr[0].split(">>");
  1801. try{
  1802. eles=document.querySelectorAll(urlSel[0]);
  1803. eles=[].filter.call(eles, ele=>{
  1804. return ele.nodeName=='BODY'||(!!ele.offsetParent&&getComputedStyle(ele).display!=='none');
  1805. })
  1806. }catch(e){}
  1807. if(eles.length==0){
  1808. eles=[];
  1809. var eleTxts=urlsArr[0].split(/(?<=[^\\])[,,]/),exmpEles=[],excludeTxts={};
  1810. [].forEach.call(document.querySelectorAll("a"),function(item){
  1811. if(!item.offsetParent)return;
  1812. eleTxts.forEach(txt=>{
  1813. var txtArr=txt.split("!");
  1814. if(item.innerText.indexOf(txtArr[0])!=-1){
  1815. exmpEles.push(item);
  1816. excludeTxts[item]=txtArr.splice(1);
  1817. }
  1818. });
  1819. })
  1820. exmpEles.forEach(e=>{
  1821. var cssSelStr="a",pa=e.parentNode,excludeTxt=excludeTxts[e];
  1822. if(e.className)cssSelStr+="."+CSS.escape(e.className.replace(/\s+/g, ".")).replace(/\\\./g, '.');
  1823. while(pa && pa.nodeName!="BODY"){
  1824. cssSelStr=pa.nodeName+">"+cssSelStr;
  1825. pa=pa.parentNode;
  1826. }
  1827. cssSelStr="body>"+cssSelStr;;
  1828. [].forEach.call(document.querySelectorAll(cssSelStr),function(item){
  1829. if(!item.offsetParent)return;
  1830. var isExclude=false;
  1831. for(var t in excludeTxt){
  1832. if(item.innerText.indexOf(excludeTxt[t])!=-1){
  1833. isExclude=true;
  1834. break;
  1835. }
  1836. }
  1837. if(!isExclude && eles.indexOf(item)==-1){
  1838. eles.push(item);
  1839. }
  1840. });
  1841. });
  1842. }
  1843. function addItem(item) {
  1844. let has=false;
  1845. for(var j=0;j<processEles.length;j++){
  1846. if(processEles[j].href==item.href){
  1847. processEles.splice(j,1);
  1848. processEles.push(item);
  1849. has=true;
  1850. break;
  1851. }
  1852. }
  1853. if((!item.href || item.href.indexOf("javascript")!=-1) && item.dataset.href){
  1854. item.href=item.dataset.href;
  1855. }
  1856. if(!has && item.href && /^http/i.test(item.href)){
  1857. processEles.push(item.cloneNode(1));
  1858. }
  1859. }
  1860. [].forEach.call(eles,function(item){
  1861. if(urlSel[1]){
  1862. item=Function("item",urlSel[1])(item);
  1863. let items;
  1864. if (Array.isArray(item)) {
  1865. items = item;
  1866. } else items = [item];
  1867. items.forEach(item => {
  1868. if(!item || !item.href)return;
  1869. if(!item.nodeName || item.nodeName!="A"){
  1870. let href=item.href;
  1871. let innerText=item.innerText;
  1872. item=document.createElement("a");
  1873. item.href=href;
  1874. item.innerText=innerText;
  1875. }
  1876. addItem(item);
  1877. });
  1878. } else {
  1879. addItem(item);
  1880. }
  1881. });
  1882. }
  1883. if(urlsArr[1]){
  1884. processEles.forEach(ele=>{
  1885. ele.href=ele.href.replace(new RegExp(urlsArr[1]), urlsArr[2]);
  1886. });
  1887. }
  1888. var retainImage=!!GM_getValue("retainImage");
  1889. var evalCode = urlsArr[3];
  1890. if (evalCode) {
  1891. evalCode = evalCode.trim();
  1892. if (/^iframe:/.test(evalCode)) {
  1893. evalCode = evalCode.replace("iframe:", "");
  1894. useIframe = true;
  1895. iframeSandbox = false;
  1896. iframeInit = false;
  1897. while (/^(sandbox|init):/.test(evalCode)) {
  1898. iframeSandbox = evalCode.match(/^sandbox:\{(.*?)\}/);
  1899. if (iframeSandbox) {
  1900. evalCode = evalCode.replace(iframeSandbox[0], "");
  1901. iframeSandbox = iframeSandbox[1];
  1902. }
  1903. iframeInit = evalCode.match(/^init:\{(.*?)\}/);
  1904. if (iframeInit) {
  1905. evalCode = evalCode.replace(iframeInit[0], "");
  1906. iframeInit = iframeInit[1];
  1907. }
  1908. }
  1909. }
  1910. let charsetMatch = evalCode.match(/^charset:{(.+?)}/);
  1911. if (charsetMatch) {
  1912. charset = charsetMatch[1];
  1913. evalCode = evalCode.replace(charsetMatch[0], "");
  1914. }
  1915. let nextMatch = evalCode.match(/^next:(\{+)/);
  1916. if (nextMatch) {
  1917. let splitLen = nextMatch[1].length;
  1918. nextMatch = evalCode.match(new RegExp(`^next:\\{{${splitLen}}(.*?)\\}{${splitLen}}`));
  1919. if (nextMatch) {
  1920. let nextCode = nextMatch[1];
  1921. evalCode = evalCode.replace(nextMatch[0], "");
  1922. nextPageFunc = async (doc, url) => {
  1923. let result;
  1924. if (/\breturn\b/.test(nextCode)) {
  1925. result = await new AsyncFunction('doc', 'url', '"use strict";' + nextCode)(doc, url);
  1926. } else {
  1927. try {
  1928. result = doc.querySelectorAll(nextCode);
  1929. if (result && result.length) {
  1930. [].forEach.call(result, ele => {
  1931. ele.href = canonicalUri(ele.getAttribute("href"), url || location.href);
  1932. });
  1933. } else result = null;
  1934. } catch(e) {}
  1935. }
  1936. return result;
  1937. }
  1938. }
  1939. }
  1940. }
  1941. if(evalCode){
  1942. processFunc=(data, cb, url)=>{
  1943. let doc=data;
  1944. if(evalCode.indexOf("return ")==-1){
  1945. if(evalCode.indexOf("@")==0){
  1946. let content="";
  1947. var selectors=GM_getValue("selectors");
  1948. if(selectors){
  1949. [].forEach.call(data.querySelectorAll(selectors),function(item){
  1950. item.innerHTML="";
  1951. });
  1952. }
  1953. [].forEach.call(data.querySelectorAll("script,style,link,noscript,iframe"),function(item){
  1954. if (item && item.parentNode) {
  1955. item.parentNode.removeChild(item);
  1956. }
  1957. });
  1958. if(retainImage){
  1959. [].forEach.call(data.querySelectorAll("img[src]"), img => {
  1960. let imgTxt=`![img](${canonicalUri(img.getAttribute("src"), location.href)})`;
  1961. let imgTxtNode=document.createTextNode(imgTxt);
  1962. img.parentNode.replaceChild(imgTxtNode, img);
  1963. });
  1964. }
  1965. [].forEach.call(data.querySelectorAll(evalCode.slice(1)), ele=>{
  1966. [].forEach.call(ele.childNodes, child=>{
  1967. if(child.innerHTML){
  1968. child.innerHTML=child.innerHTML.replace(/\<\s*br\s*\>/gi,"\r\n").replace(/\n+/gi,"\n").replace(/\r+/gi,"\r");
  1969. }
  1970. if(child.textContent){
  1971. content+=(child.textContent.replace(/ +/g," ").replace(/([^\r]|^)\n([^\r]|$)/gi,"$1\r\n$2")+"\r\n");
  1972. }
  1973. });
  1974. content+="\r\n";
  1975. });
  1976. return content;
  1977. }else return eval(evalCode);
  1978. }else{
  1979. return Function("data", "doc", "cb", "url", evalCode)(data, doc, cb, url);
  1980. }
  1981. };
  1982. }else{
  1983. if(win.dacProcess){
  1984. processFunc=win.dacProcess;
  1985. }
  1986. }
  1987. filterList(processEles);
  1988. }
  1989. }
  1990. const configPage = "https://hoothin.github.io/UserScripts/DownloadAllContent/";
  1991. 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>';
  1992. function searchRule(){
  1993. GM_openInTab(configPage + "#@" + location.hostname, {active: true});
  1994. }
  1995. var downloadShortcut = GM_getValue("downloadShortcut") || {ctrlKey: true, shiftKey: false, altKey: false, metaKey: false, key: 'F9'};
  1996. var downloadSingleShortcut = GM_getValue("downloadSingleShortcut") || {ctrlKey: true, shiftKey: true, altKey: false, metaKey: false, key: 'F9'};
  1997. var downloadCustomShortcut = GM_getValue("downloadCustomShortcut") || {ctrlKey: true, shiftKey: false, altKey: true, metaKey: false, key: 'F9'};
  1998.  
  1999. var enableTouch = GM_getValue("enableTouch");
  2000. if (enableTouch) {
  2001. const minLength = 256, tg = 0.5, atg = 2;
  2002. var lastX, lastY, signs, lastSign;
  2003. function tracer(e) {
  2004. let curX = e.changedTouches[0].clientX, curY = e.changedTouches[0].clientY;
  2005. let distanceX = curX - lastX, distanceY = curY - lastY;
  2006. let distance = distanceX * distanceX + distanceY * distanceY;
  2007. if (distance > minLength) {
  2008. lastX = curX;
  2009. lastY = curY;
  2010. let direction = "";
  2011. let slope = Math.abs(distanceY / distanceX);
  2012. if (slope > atg) {
  2013. if (distanceY > 0) {
  2014. direction = "↓";
  2015. } else {
  2016. direction = "↑";
  2017. }
  2018. } else if (slope < tg) {
  2019. if (distanceX > 0) {
  2020. direction = "→";
  2021. } else {
  2022. direction = "←";
  2023. }
  2024. }
  2025. if (direction && lastSign != direction) {
  2026. signs += direction;
  2027. lastSign = direction;
  2028. }
  2029. }
  2030. }
  2031. document.addEventListener("touchstart", function(e) {
  2032. lastX = e.changedTouches[0].clientX;
  2033. lastY = e.changedTouches[0].clientY;
  2034. lastSign = signs = "";
  2035. document.addEventListener("touchmove", tracer, false);
  2036. }, false);
  2037. document.addEventListener("touchend", function(e) {
  2038. document.removeEventListener("touchmove", tracer, false);
  2039. if (signs == "→↓←↑") {
  2040. e.stopPropagation();
  2041. e.preventDefault();
  2042. startCustom();
  2043. }
  2044. }, false);
  2045. }
  2046.  
  2047. if (location.origin + location.pathname == configPage) {
  2048. let exampleNode = document.getElementById("example");
  2049. if (!exampleNode) return;
  2050.  
  2051. exampleNode = exampleNode.parentNode;
  2052. let ruleList = exampleNode.nextElementSibling.nextElementSibling;
  2053. let searchInput = document.createElement("input");
  2054. let inputTimer;
  2055. function searchByInput() {
  2056. clearTimeout(inputTimer);
  2057. inputTimer = setTimeout(() => {
  2058. let curValue = searchInput.value;
  2059. let matchRules = [];
  2060. let dontMatchRules = [];
  2061. if (curValue) {
  2062. for (let i = 0; i < ruleList.children.length; i++) {
  2063. let curRule = ruleList.children[i];
  2064. let aHref = curRule.firstChild.href;
  2065. if (aHref.indexOf(curValue) == -1) {
  2066. dontMatchRules.push(curRule);
  2067. } else {
  2068. matchRules.push(curRule);
  2069. }
  2070. }
  2071. } else {
  2072. dontMatchRules = ruleList.children;
  2073. }
  2074. if (matchRules.length) {
  2075. for (let i = 0; i < dontMatchRules.length; i++) {
  2076. let curRule = dontMatchRules[i];
  2077. curRule.style.display = "none";
  2078. }
  2079. for (let i = 0; i < matchRules.length; i++) {
  2080. let curRule = matchRules[i];
  2081. curRule.style.display = "";
  2082. }
  2083. } else {
  2084. for (let i = 0; i < dontMatchRules.length; i++) {
  2085. let curRule = dontMatchRules[i];
  2086. curRule.style.display = "";
  2087. }
  2088. }
  2089. }, 500);
  2090. }
  2091. searchInput.style.margin = "10px";
  2092. searchInput.style.width = "100%";
  2093. searchInput.placeholder = i18n.searchRule;
  2094. searchInput.addEventListener("input", function(e) {
  2095. searchByInput();
  2096. });
  2097. if (location.hash) {
  2098. let hash = location.hash.slice(1);
  2099. if (hash.indexOf("@") == 0) {
  2100. setTimeout(() => {
  2101. exampleNode.scrollIntoView();
  2102. }, 500);
  2103. searchInput.value = hash.slice(1);
  2104. searchByInput();
  2105. }
  2106. }
  2107. [].forEach.call(ruleList.querySelectorAll("div.highlight"), highlight => {
  2108. highlight.style.position = "relative";
  2109. highlight.innerHTML = highlight.innerHTML + copySvg;
  2110. let svg = highlight.children[1];
  2111. svg.addEventListener("click", function(e) {
  2112. GM_setClipboard(highlight.children[0].innerText);
  2113. svg.style.opacity = 0;
  2114. setTimeout(() => {
  2115. svg.style.opacity = 1;
  2116. }, 1000);
  2117. });
  2118. });
  2119. exampleNode.parentNode.insertBefore(searchInput, ruleList);
  2120.  
  2121.  
  2122. let donateNode = document.querySelector("[alt='donate']");
  2123. if (!donateNode) return;
  2124. let insertPos = donateNode.parentNode.nextElementSibling;
  2125. let radioIndex = 0;
  2126. function createOption(_name, _value, _type) {
  2127. if (!_type) _type = "input";
  2128. let con = document.createElement("div");
  2129. let option = document.createElement("input");
  2130. let cap = document.createElement("b");
  2131. option.type = _type;
  2132. option.value = _value;
  2133. option.checked = _value;
  2134. cap.style.margin = "0px 10px 0px 0px";
  2135. if (_type == "radio") {
  2136. let label = document.createElement("label");
  2137. label.innerText = _name;
  2138. radioIndex++;
  2139. option.id = "radio" + radioIndex;
  2140. label.setAttribute("for", option.id);
  2141. cap.appendChild(label);
  2142. } else {
  2143. if (_type == "input") {
  2144. option.style.flexGrow = "1";
  2145. }
  2146. cap.innerText = _name;
  2147. }
  2148. con.style.margin = "10px 0";
  2149. con.style.display = "flex";
  2150. con.style.alignItems = "center";
  2151. con.appendChild(cap);
  2152. con.appendChild(option);
  2153. insertPos.parentNode.insertBefore(con, insertPos);
  2154. return option;
  2155. }
  2156. function formatShortcut(e) {
  2157. let result = [];
  2158. if (e.ctrlKey) {
  2159. result.push("Ctrl");
  2160. }
  2161. if (e.shiftKey) {
  2162. result.push("Shift");
  2163. }
  2164. if (e.altKey) {
  2165. result.push("Alt");
  2166. }
  2167. if (e.metaKey) {
  2168. result.push("Meta");
  2169. }
  2170. result.push(e.key);
  2171. return result.join(" + ");
  2172. }
  2173. function geneShortcutData(str) {
  2174. if (!str) return "";
  2175. let result = {ctrlKey: false, shiftKey: false, altKey: false, metaKey: false, key: ''};
  2176. str.split(" + ").forEach(item => {
  2177. switch(item) {
  2178. case "Ctrl":
  2179. result.ctrlKey = true;
  2180. break;
  2181. case "Shift":
  2182. result.shiftKey = true;
  2183. break;
  2184. case "Alt":
  2185. result.altKey = true;
  2186. break;
  2187. case "Meta":
  2188. result.metaKey = true;
  2189. break;
  2190. default:
  2191. result.key = item;
  2192. break;
  2193. }
  2194. });
  2195. return result;
  2196. }
  2197. let showFilterList = createOption(i18n.showFilterList, !!GM_getValue("showFilterList"), "checkbox");
  2198. let downloadShortcutInput = createOption(i18n.downloadShortcut, formatShortcut(downloadShortcut) || "");
  2199. let downloadSingleShortcutInput = createOption(i18n.downloadSingleShortcut, formatShortcut(downloadSingleShortcut) || "");
  2200. let downloadCustomShortcutInput = createOption(i18n.downloadCustomShortcut, formatShortcut(downloadCustomShortcut) || "");
  2201. downloadShortcutInput.setAttribute("readonly", "true");
  2202. downloadSingleShortcutInput.setAttribute("readonly", "true");
  2203. downloadCustomShortcutInput.setAttribute("readonly", "true");
  2204. downloadShortcutInput.style.cursor = "cell";
  2205. downloadSingleShortcutInput.style.cursor = "cell";
  2206. downloadCustomShortcutInput.style.cursor = "cell";
  2207. let keydonwHandler = e => {
  2208. if (e.key) {
  2209. if (e.key == "Backspace") {
  2210. e.target.value = "";
  2211. } else if (e.key != "Control" && e.key != "Shift" && e.key != "Alt" && e.key != "Meta") {
  2212. e.target.value = formatShortcut(e);
  2213. }
  2214. }
  2215. e.preventDefault();
  2216. e.stopPropagation();
  2217. };
  2218. downloadShortcutInput.addEventListener("keydown", keydonwHandler);
  2219. downloadSingleShortcutInput.addEventListener("keydown", keydonwHandler);
  2220. downloadCustomShortcutInput.addEventListener("keydown", keydonwHandler);
  2221. let enableTouchInput = createOption(i18n.enableTouch, !!enableTouch, "checkbox");
  2222.  
  2223. let delSelector = createOption(i18n.del, GM_getValue("selectors") || "");
  2224. delSelector.setAttribute("placeHolder", ".mask,.ksam");
  2225. let downThreadNum = createOption(i18n.downThreadNum, GM_getValue("downThreadNum") || "20", "number");
  2226. let maxDlPerMin = createOption(i18n.maxDlPerMin, GM_getValue("maxDlPerMin") || "0", "number");
  2227. let customTitle = createOption(i18n.customTitle, GM_getValue("customTitle") || "");
  2228. let saveUrl = createOption(i18n.saveUrl, !!GM_getValue("saveUrl"), "checkbox");
  2229. let disableAutoStartSave = createOption(i18n.disableAutoStartSave, !!GM_getValue("disableAutoStartSave"), "checkbox");
  2230. customTitle.setAttribute("placeHolder", "title");
  2231. let minTxtLength = createOption(i18n.minTxtLength, GM_getValue("minTxtLength") || "100", "number");
  2232. let contentSortUrlValue = GM_getValue("contentSortUrl") || false;
  2233. let contentSortValue = GM_getValue("contentSort") || false;
  2234. let reSortDefault = createOption(i18n.reSortDefault, !contentSortUrlValue && !contentSortValue, "radio");
  2235. let reSortUrl = createOption(i18n.reSortUrl, contentSortUrlValue || false, "radio");
  2236. let contentSort = createOption(i18n.reSort, contentSortValue || false, "radio");
  2237. reSortDefault.name = "sort";
  2238. reSortUrl.name = "sort";
  2239. contentSort.name = "sort";
  2240. let reverse = createOption(i18n.reverseOrder, !!GM_getValue("reverse"), "checkbox");
  2241. let prefix = createOption(i18n.prefix, GM_getValue("prefix") || "");
  2242. let disableNextPage = !!GM_getValue("disableNextPage");
  2243. let nextPage = createOption(i18n.nextPage, !disableNextPage, "checkbox");
  2244. let nextPageReg = createOption(i18n.nextPageReg, GM_getValue("nextPageReg") || "");
  2245. let retainImage = createOption(i18n.retainImage, !!GM_getValue("retainImage"), "checkbox");
  2246. prefix.setAttribute("placeHolder", "第 $i 章:");
  2247. nextPageReg.setAttribute("placeHolder", "^\\s*(下一[页頁张張]|next\\s*page|次のページ)");
  2248. if (disableNextPage) {
  2249. nextPageReg.parentNode.style.display = "none";
  2250. }
  2251. nextPage.onclick = e => {
  2252. nextPageReg.parentNode.style.display = nextPage.checked ? "flex" : "none";
  2253. }
  2254. let saveBtn = document.createElement("button");
  2255. saveBtn.innerText = i18n.saveBtn;
  2256. saveBtn.style.margin = "0 0 20px 0";
  2257. insertPos.parentNode.insertBefore(saveBtn, insertPos);
  2258. saveBtn.onclick = e => {
  2259. GM_setValue("selectors", delSelector.value || "");
  2260. GM_setValue("downThreadNum", parseInt(downThreadNum.value || 20));
  2261. GM_setValue("maxDlPerMin", parseInt(maxDlPerMin.value || 20));
  2262. GM_setValue("minTxtLength", parseInt(minTxtLength.value || 100));
  2263. GM_setValue("customTitle", customTitle.value || "");
  2264. GM_setValue("disableAutoStartSave", disableAutoStartSave.checked);
  2265. GM_setValue("saveUrl", saveUrl.checked);
  2266. if (reSortUrl.checked) {
  2267. GM_setValue("contentSortUrl", true);
  2268. GM_setValue("contentSort", false);
  2269. } else if (contentSort.checked) {
  2270. GM_setValue("contentSortUrl", false);
  2271. GM_setValue("contentSort", true);
  2272. } else {
  2273. GM_setValue("contentSortUrl", false);
  2274. GM_setValue("contentSort", false);
  2275. }
  2276. GM_setValue("reverse", reverse.checked);
  2277. GM_setValue("enableTouch", enableTouchInput.checked);
  2278. GM_setValue("retainImage", retainImage.checked);
  2279. GM_setValue("showFilterList", showFilterList.checked);
  2280. GM_setValue("disableNextPage", !nextPage.checked);
  2281. GM_setValue("nextPageReg", nextPageReg.value || "");
  2282. GM_setValue("prefix", prefix.value || "");
  2283. GM_setValue("downloadShortcut", geneShortcutData(downloadShortcutInput.value) || "");
  2284. GM_setValue("downloadSingleShortcut", geneShortcutData(downloadSingleShortcutInput.value) || "");
  2285. GM_setValue("downloadCustomShortcut", geneShortcutData(downloadCustomShortcutInput.value) || "");
  2286. alert(i18n.saveOk);
  2287. };
  2288. return;
  2289. }
  2290.  
  2291. function setDel(){
  2292. GM_openInTab(configPage + "#操作說明", {active: true});
  2293. }
  2294.  
  2295. function checkKey(shortcut1, shortcut2) {
  2296. return shortcut1.ctrlKey == shortcut2.ctrlKey && shortcut1.shiftKey == shortcut2.shiftKey && shortcut1.altKey == shortcut2.altKey && shortcut1.metaKey == shortcut2.metaKey && shortcut1.key == shortcut2.key;
  2297. }
  2298.  
  2299. function startCustom() {
  2300. var customRules = GM_getValue("DACrules_" + document.domain);
  2301. var urls = window.prompt(i18n.customInfo + ":\nhttps://xxx.xxx/book-[20-99].html, https://xxx.xxx/book-[01-10].html", customRules || "");
  2302. if (urls) {
  2303. customDown(urls);
  2304. }
  2305. }
  2306.  
  2307. document.addEventListener("keydown", function(e) {
  2308. if (checkKey(downloadCustomShortcut, e)) {
  2309. startCustom();
  2310. } else if (checkKey(downloadSingleShortcut, e)) {
  2311. fetch(true);
  2312. } else if (checkKey(downloadShortcut, e)) {
  2313. fetch(false);
  2314. }
  2315. });
  2316. GM_registerMenuCommand(i18n.custom, () => {
  2317. startCustom();
  2318. });
  2319. GM_registerMenuCommand(i18n.fetch, fetch);
  2320. GM_registerMenuCommand(i18n.setting, setDel);
  2321. GM_registerMenuCommand(i18n.searchRule, searchRule);
  2322. })();

QingJ © 2025

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