Wenku Doc Downloader

下载文档,导出PDF。支持①百度文库 ②豆丁网 ③道客巴巴 ④360doc个人图书馆 ⑤得力文库 ⑥MBA智库 ⑦爱问共享资料(新浪文档) ⑧原创力文档(前20页) ⑨读根网。在文档页面左侧中间有Wenku Doc Download按钮区,说明脚本生效了。【反馈请提供网址】。暂不支持手机端,手机端请切换为电脑UA访问。

当前为 2022-03-08 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Wenku Doc Downloader
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.5.1
  5. // @description 下载文档,导出PDF。支持①百度文库 ②豆丁网 ③道客巴巴 ④360doc个人图书馆 ⑤得力文库 ⑥MBA智库 ⑦爱问共享资料(新浪文档) ⑧原创力文档(前20页) ⑨读根网。在文档页面左侧中间有Wenku Doc Download按钮区,说明脚本生效了。【反馈请提供网址】。暂不支持手机端,手机端请切换为电脑UA访问。
  6. // @author allenlv2690@gmail.com
  7. // @match *://*.docin.com/p-*
  8. // @match *://ishare.iask.sina.com.cn/f/*
  9. // @match *://www.deliwenku.com/p-*
  10. // @match *://www.doc88.com/p-*
  11. // @match *://www.360doc.com/content/*
  12. // @match *://wenku.baidu.com/*/*
  13. // @match *://doc.mbalib.com/view/*
  14. // @match *://www.woc88.com/so-*
  15. // @match *://www.dugen.com/p-*
  16. // @match *://max.book118.com/html/*
  17. // @match *://view-cache.book118.com/pptView.html?*
  18. // @match *://*.book118.com/?readpage=*
  19. // @require https://cdn.staticfile.org/FileSaver.js/2.0.5/FileSaver.min.js
  20. // @require https://cdn.staticfile.org/jszip/3.7.1/jszip.min.js
  21. // @require https://cdn.staticfile.org/jspdf/2.5.1/jspdf.umd.min.js
  22. // @require https://cdn.staticfile.org/html2canvas/1.4.1/html2canvas.min.js
  23. // @icon https://s2.loli.net/2022/01/12/wc9je8RX7HELbYQ.png
  24. // @icon64 https://s2.loli.net/2022/01/12/tmFeSKDf8UkNMjC.png
  25. // @grant none
  26. // @license GPL-3.0-only
  27. // @create 2021-11-22
  28. // @note 1. 替换了外部引用源,使得脚本暂时可用
  29. // @note 2. 下次将外部引用增加到脚本内部,使脚本彻底稳定可用。
  30. // @note 3. 豆丁网导出的图片链接似乎有问题,下次修复
  31. // ==/UserScript==
  32.  
  33.  
  34. (function () {
  35. 'use strict';
  36.  
  37. // import "./lib/jspdf.umd.min";
  38. // import "./lib/html2canvas.min";
  39. // import "./lib/jszip.min";
  40. // import "./lib/FileSaver.min";
  41.  
  42.  
  43. let utils = {
  44. ver: (() => {
  45. // 显示版本号
  46. console.log("wk-utils: ver-1.5.0");
  47. })(),
  48.  
  49. /**
  50. * 创建并下载文件
  51. * @param {String} file_name 文件名
  52. * @param {String | Blob} content 文本或blob
  53. */
  54. createAndDownloadFile: function(file_name, content) {
  55. let aTag = document.createElement('a');
  56. let blob;
  57. if (typeof(content) === "string") {
  58. blob = new Blob([content]);
  59. }
  60. aTag.download = file_name;
  61. aTag.href = URL.createObjectURL(blob);
  62. aTag.click();
  63. URL.revokeObjectURL(blob);
  64. },
  65.  
  66. /**
  67. * 创建并下载链接资源
  68. * @param {String} file_name
  69. * @param {String} src
  70. */
  71. downloadUrlFile: function(file_name, src) {
  72. let aTag = document.createElement('a');
  73. aTag.download = file_name;
  74. aTag.href = src;
  75. aTag.click();
  76. },
  77.  
  78. /**
  79. * 添加外部js到当前页面
  80. * @param {String} url
  81. */
  82. addScripts2HTML: function(url) {
  83. let script = document.createElement("script");
  84. script.src = url;
  85. document.head.appendChild(script);
  86. },
  87.  
  88. /**
  89. * 临时禁用脚本,执行func后移除btns_section。
  90. * @param {Function} func
  91. */
  92. banSelf: function(func = () => { }) {
  93. func();
  94. document.querySelector(".btns_section").remove();
  95. },
  96.  
  97. /**
  98. * 睡眠 delay 毫秒
  99. * @param {Number} delay
  100. */
  101. sleep: function(delay) {
  102. let start = (new Date()).getTime();
  103. while ((new Date()).getTime() - start < delay) {
  104. continue;
  105. }
  106. },
  107.  
  108. /**
  109. * 允许打印页面
  110. */
  111. allowPrint: function() {
  112. let style = document.createElement("style");
  113. style.innerHTML = `
  114. @media print {
  115. body{
  116. display:block;
  117. }
  118. }
  119. `;
  120. document.head.appendChild(style);
  121. },
  122.  
  123. /**
  124. * 求main_set去除cut_set后的set
  125. * @param {Set} main_set
  126. * @param {Set} cut_set
  127. * @returns 差集
  128. */
  129. difference: function(main_set, cut_set) {
  130. let _diff = new Set(main_set);
  131. for (let elem of cut_set) {
  132. _diff.delete(elem);
  133. }
  134. return _diff;
  135. },
  136.  
  137. /**
  138. * 抛出set中的第一个元素
  139. * @param {Set} set
  140. * @returns 一个元素
  141. */
  142. setPop: function(set) {
  143. for (let item of set) {
  144. set.delete(item);
  145. return item;
  146. }
  147. },
  148.  
  149. /**
  150. * 绑定事件到指定按钮,返回按钮引用
  151. * @param {Function} event click事件
  152. * @param {Array} args 事件的参数列表
  153. * @param {String} aim_btn 按钮的变量名
  154. * @param {String} new_text 按钮的新文本,为null则不替换
  155. * @returns 按钮元素的引用
  156. */
  157. setBtnEvent: function(event, args = [], aim_btn = "btn_3", new_text = null) {
  158. let btn = document.querySelector(`.${aim_btn.replace("_", "-")}`);
  159. // 如果需要,替换按钮内文本
  160. if (new_text) {
  161. btn.textContent = new_text;
  162. }
  163. // 绑定事件,添加到页面上
  164. btn.onclick = () => {
  165. this.enhanceBtnClickReaction(aim_btn);
  166. if (args.length) {
  167. event(...args);
  168. } else {
  169. event();
  170. }
  171. };
  172. return btn;
  173. },
  174.  
  175. /**
  176. * 强制隐藏元素
  177. * @param {String} selector
  178. */
  179. forceHide: function(selector) {
  180. let style_cls = "force-hide";
  181. document.querySelectorAll(selector).forEach((elem) => {
  182. elem.className += ` ${style_cls}`;
  183. });
  184. // 判断css样式是否已经存在
  185. let style;
  186. style = document.querySelector(`style.${style_cls}`);
  187. // 如果已经存在,则无须重复创建
  188. if (style) {
  189. return;
  190. }
  191. // 否则创建
  192. style = document.createElement("style");
  193. style.innerHTML = `style.${style_cls} {
  194. visibility: hidden !important;
  195. }`;
  196. document.head.appendChild(style);
  197. },
  198.  
  199. /**
  200. * 隐藏按钮,打印页面,显示按钮
  201. */
  202. hideBtnThenPrint: function() {
  203. // 隐藏按钮,然后打印页面
  204. let section = document.getElementsByClassName("btns_section")[0];
  205. section.style.display = "none";
  206. window.print();
  207. // 打印结束,显示按钮
  208. section.style.removeProperty("display");
  209. },
  210.  
  211. /**
  212. * 返回times个倍数连接的str
  213. * @param {String} str
  214. * @param {Number} times
  215. * @returns multiplied_str
  216. */
  217. multiplyStr: function(str, times) {
  218. let str_list = [];
  219. for (let i = 0; i < times; i++) {
  220. str_list.push(str);
  221. }
  222. return str_list.join("");
  223. },
  224.  
  225. /**
  226. * 增强按钮(默认为蓝色按钮:展开文档)的点击效果
  227. * @param {String} custom_btn 按钮变量名
  228. */
  229. enhanceBtnClickReaction: function(custom_btn = null) {
  230. let aim_btn;
  231. // 如果不使用自定义按钮元素,则默认为使用蓝色展开文档按钮
  232. if (!custom_btn || custom_btn === "btn_1") {
  233. aim_btn = document.querySelector(".btn-1");
  234. } else {
  235. aim_btn = document.querySelector(`.${custom_btn.replace("_", "-")}`);
  236. }
  237.  
  238. let old_color = aim_btn.style.color; // 保存旧的颜色
  239. let old_text = aim_btn.textContent; // 保存旧的文字内容
  240. // 变黑缩小
  241. aim_btn.style.color = "black";
  242. aim_btn.style.fontWeight = "normal";
  243. aim_btn.textContent = `->${old_text}<-`;
  244. // 复原加粗
  245. let changeColorBack = function() {
  246. aim_btn.style.color = old_color;
  247. aim_btn.style.fontWeight = "bold";
  248. aim_btn.textContent = old_text;
  249. };
  250. setTimeout(changeColorBack, 1250);
  251. },
  252.  
  253. /**
  254. * 切换按钮显示/隐藏状态
  255. * @param {String} aim_btn 按钮变量名
  256. * @returns 按钮元素的引用
  257. */
  258. toggleBtnStatus: function(aim_btn) {
  259. let btn = document.querySelector(`.${aim_btn.replace("_", "-")}`);
  260. let display = getComputedStyle(btn).display;
  261. // return;
  262. if (display === "none") {
  263. btn.style.display = "block";
  264. } else {
  265. btn.style.display = "none";
  266. }
  267. return btn;
  268. },
  269.  
  270. /**
  271. * 根据canvas元素数量返回quality值
  272. * @param {Number} canvas_amount
  273. * @returns quality: Number
  274. */
  275. getQualityByCanvasAmount: function(canvas_amount) {
  276. // 如果有全局参数,优先用全局的
  277. if (window.img_quality !== undefined) {
  278. console.log(`image quality: ${window.img_quality*100}%`);
  279. return window.img_quality;
  280. }
  281. // 否则用默认的
  282. let quality;
  283. if (canvas_amount <= 25) {
  284. quality = 1.0;
  285. } else if (25 < canvas_amount <= 50) {
  286. quality = 0.9;
  287. } else {
  288. quality = 0.8;
  289. }
  290. console.log(`image quality: ${quality*100}%`);
  291. return quality;
  292. },
  293.  
  294. /**
  295. * 挂载func到全局
  296. * @param {Function} func
  297. */
  298. globalFunc: function(func) {
  299. globalThis[func.name] = func;
  300. },
  301.  
  302. /**
  303. * 用input框跳转到对应页码
  304. * @param {Element} cur_page 当前页码
  305. * @param {string} aim_page 目标页码
  306. * @param {string} event_type 键盘事件类型:"keyup" | "keypress" | "keydown"
  307. */
  308. jump2pageNo: function(cur_page, aim_page, event_type) {
  309. // 设置跳转页码为目标页码
  310. cur_page.value = aim_page;
  311. // 模拟回车事件来跳转
  312. let keyboard_event_enter = new KeyboardEvent(event_type, {
  313. bubbles: true,
  314. cancelable: true,
  315. keyCode: 13
  316. });
  317. cur_page.dispatchEvent(keyboard_event_enter);
  318. },
  319.  
  320. /**
  321. * 在新标签页打开链接
  322. * @param {String} href
  323. */
  324. openInNewTab: function(href) {
  325. let link = document.createElement("a");
  326. link.href = href;
  327. link.target = "_blank";
  328. link.click();
  329. },
  330.  
  331. /**
  332. * 滚动到页面底部
  333. */
  334. scrollToBottom: function() {
  335. window.scrollTo({
  336. top: document.body.scrollHeight,
  337. behavior: "smooth"
  338. });
  339. },
  340.  
  341. /**
  342. * 用try移除元素
  343. * @param {Element} element 要移除的元素
  344. */
  345. tryToRemoveElement: function(element) {
  346. try {
  347. element.remove();
  348. } catch (e) {
  349. }
  350. },
  351.  
  352. /**
  353. * 用try移除 [元素列表1, 元素列表2, ...] 中的元素
  354. * @param {Array} elem_list_box 要移除的元素列表构成的列表
  355. */
  356. tryToRemoveSameElem: function(elem_list_box) {
  357. for (let elem_list of elem_list_box) {
  358. if (!elem_list) {
  359. continue;
  360. }
  361. for (let elem of elem_list) {
  362. try {
  363. elem.remove();
  364. } catch (e) {
  365. console.log();
  366. }
  367. }
  368. }
  369. },
  370.  
  371. /**
  372. * 使文档在页面上居中
  373. * @param {String} selector 文档容器的css选择器
  374. * @param {String} default_offset 文档部分向右偏移的百分比(0-59)
  375. * @returns 偏移值是否合法
  376. */
  377. centerDoc: function(selector, default_offset) {
  378. let doc_main = document.querySelector(selector);
  379. let offset = window.prompt("请输入偏移百分位:", default_offset);
  380. // 如果输入的数字不在 0-59 内,提醒用户重新设置
  381. if (offset.length === 1 && offset.search(/[0-9]/) !== -1) {
  382. doc_main.style.marginLeft = offset + "%";
  383. return true;
  384. } else if (offset.length === 2 && offset.search(/[1-5][0-9]/) !== -1) {
  385. doc_main.style.marginLeft = offset + "%";
  386. return true
  387. } else {
  388. alert("请输入一个正整数,范围在0至59之间,用来使文档居中\n(不同文档偏移量不同,所以需要手动调整)");
  389. return false;
  390. }
  391. },
  392.  
  393. /**
  394. * 调整按钮内文本
  395. * @param {String} aim_btn 按钮变量名
  396. * @param {String} new_text 新的文本,null则保留旧文本
  397. * @param {Boolean} recommend_btn 是否增加"(推荐)"到按钮文本
  398. * @param {Boolean} use_hint 是否提示"文档已经完全展开,可以导出"
  399. */
  400. modifyBtnText: function(aim_btn = "btn_2", new_text = null, recommend_btn = false, use_hint = true) {
  401. // 提示文档已经展开
  402. if (use_hint) {
  403. let hint = "文档已经完全展开,可以导出";
  404. alert(hint);
  405. }
  406. let btn = document.querySelector(`.${aim_btn.replace("_", "-")}`);
  407. // 要替换的文本
  408. if (new_text) {
  409. btn.textContent = new_text;
  410. }
  411. // 推荐按钮
  412. if (recommend_btn) {
  413. btn.textContent += "(推荐)";
  414. }
  415. },
  416.  
  417. html2Canvases: async function(elem_list) {
  418. // 如果是空元素列表,返回null并终止函数
  419. if (elem_list.length === 0) {
  420. console.log("html2canvases was called, but no element is avaiable.");
  421. return null;
  422. }
  423. let tasks = []; // 存放异步任务
  424. let contents = []; // 存放canvas元素
  425. for (let elem of elem_list) {
  426. let task = html2canvas(elem).then((canvas) => {
  427. contents.push(canvas);
  428. });
  429. tasks.push(task);
  430. }
  431. // 等待全部page转化完成
  432. await Promise.all(tasks);
  433. return contents;
  434. },
  435.  
  436. /**
  437. * 将html元素转为canvas再合并到pdf中,最后下载pdf
  438. * @param {Array} elem_list html元素列表
  439. * @param {String} title 文档标题
  440. */
  441. html2PDF: async function(elem_list, title = "文档") {
  442. // 如果是空元素列表,终止函数
  443. let _contents = this.html2Canvases(elem_list);
  444. if (_contents === null) {
  445. return;
  446. }
  447. _contents.then((contents) => {
  448. // 控制台检查结果
  449. console.log("生成的canvas元素如下:");
  450. console.log(contents);
  451.  
  452. // 拿到canvas宽、高
  453. let model = elem_list[0];
  454. let width, height;
  455. width = model.offsetWidth;
  456. height = model.offsetHeight;
  457. // 打包为pdf
  458. this.saveCanvasesToPDF(contents, title, width, height);
  459. });
  460. },
  461.  
  462. /**
  463. * 下载全部图片链接,适用性:爱问共享资料、得力文库
  464. * @param {string} selector 图形元素的父级元素
  465. */
  466. savePicUrls: function(selector) {
  467. let pages = document.querySelectorAll(selector);
  468. let pic_urls = [];
  469.  
  470. for (let elem of pages) {
  471. let pic_obj = elem.children[0];
  472. let url = pic_obj.src;
  473. pic_urls.push(url);
  474. }
  475. let content = pic_urls.join("\n");
  476. // 启动下载
  477. this.createAndDownloadFile("urls.csv", content);
  478. },
  479.  
  480. /**
  481. * 存储所有canvas图形为png到一个压缩包
  482. * @param {Array} node_list canvas元素列表
  483. * @param {String} title 文档标题
  484. */
  485. saveCanvasesToZip: function(node_list, title) {
  486. // canvas元素转为png图像
  487. // 所有png合并为一个zip压缩包
  488. let zip = new JSZip();
  489. let n = node_list.length;
  490.  
  491. for (let i = 0; i < n; i++) {
  492. let canvas = node_list[i];
  493. let data_base64 = canvas.toDataURL();
  494. let blob = atob(data_base64.split(",")[1]);
  495. zip.file(`page-${i+1}.png`, blob, { binary: true });
  496. }
  497.  
  498. // 导出zip
  499. // promise.then(onCompleted, onRejected);
  500. zip.generateAsync({ type: "blob" }).then(function(content) {
  501. // see filesaver.js
  502. console.log(content);
  503. saveAs(content, `${title}.zip`);
  504. });
  505. },
  506.  
  507. /**
  508. * 将canvas转为jpeg,然后导出PDF
  509. * @param {Array} node_list canvas元素列表
  510. * @param {String} title 文档标题
  511. */
  512. saveCanvasesToPDF: function(node_list, title, width = 0, height = 0) {
  513. // 如果没有手动指定canvas的长宽,则自动检测
  514. if (!width && !height) {
  515. // 先获取第一个canvas用于判断竖向还是横向,以及得到页面长宽
  516. let first_canvas = node_list[0];
  517. // 如果style的长宽不存在,则直接用canvas的元素长宽
  518. let width_str, height_str;
  519. if (first_canvas.width && parseInt(first_canvas.width) && parseInt(first_canvas.height)) {
  520. [width_str, height_str] = [first_canvas.width, first_canvas.height];
  521. } else {
  522. [width_str, height_str] = [first_canvas.style.width.replace(/(px)|(rem)|(em)/, ""), first_canvas.style.height.replace(/(px)|(rem)|(em)/, "")];
  523. }
  524. // jsPDF的第三个参数为format,当自定义时,参数为数字数组。
  525. [width, height] = [parseFloat(width_str), parseFloat(height_str)];
  526. }
  527. console.log(`canvas数据:宽: ${width}px,高: ${height}px`);
  528. // 如果文档第一页的宽比长更大,则landscape,否则portrait
  529. let orientation = width > height ? 'l' : 'p';
  530. let pdf = new jspdf.jsPDF(orientation, 'px', [height, width]);
  531.  
  532. // 根据canvas数量确定quality
  533. let quality = this.getQualityByCanvasAmount(node_list.length);
  534.  
  535. // 保存每一页文档到每一页pdf
  536. node_list.forEach(function(canvas, index) {
  537. pdf.addImage(canvas.toDataURL("image/jpeg", quality), 'JPEG', 0, 0, width, height);
  538. // 如果当前不是文档最后一页,则需要添加下一个空白页
  539. if (index !== node_list.length - 1) {
  540. pdf.addPage();
  541. }
  542. });
  543.  
  544. // 导出文件
  545. pdf.save(`${title}.pdf`);
  546. },
  547.  
  548. /**
  549. * Image元素列表合并到一个PDF中
  550. * @param {NodeList} imgs Image元素列表
  551. * @param {String} title 文档名
  552. */
  553. imgs2pdf: function(imgs, title) {
  554. // 取得宽高
  555. let model = imgs[0];
  556. let width = model.offsetWidth;
  557. let height = model.offsetHeight;
  558.  
  559. // 创建pdf
  560. let orientation = width > height ? 'l' : 'p';
  561. let pdf = new jspdf.jsPDF(orientation, 'px', [height, width]);
  562.  
  563. // 添加图像到pdf
  564. imgs.forEach((img, index) => {
  565. pdf.addImage(img, 'PNG', 0, 0, width, height);
  566. // 如果当前不是文档最后一页,则需要添加下一个空白页
  567. if (index !== imgs.length - 1) {
  568. pdf.addPage();
  569. }
  570. });
  571.  
  572. // 导出文件
  573. pdf.save(`${title}.pdf`);
  574. },
  575.  
  576. /**
  577. * 取得elem的class为class_name的父级元素
  578. * @param {String} class_name
  579. * @param {Element} elem 起点元素
  580. * @param {object} JSobj 全局对象,需要有<iterator_count>计数器。默认为window.baiduJS。
  581. * @param {Boolean} ignore 是否忽略递归计数器。默认false。如果启用请确保不会无限递归。
  582. * @returns {null | Element} parent_element
  583. */
  584. getParentByClassName: function(class_name, elem, JSobj, ignore = false) {
  585. let parent = elem.parentElement;
  586. let iterator_count = JSobj.iterator_count;
  587. let now_name;
  588.  
  589. try {
  590. now_name = parent.className;
  591. } catch (e) {
  592. JSobj.iterator_count = 0;
  593. // 没有父级元素了
  594. return "no parent node";
  595. }
  596.  
  597. // 如果不忽略递归次数计数器
  598. if (!ignore) {
  599. if (iterator_count > 9) {
  600. // 超过最大迭代次数,认为不存在,返回null
  601. JSobj.iterator_count = 0;
  602. return "over max iterator counts limit";
  603. } else {
  604. JSobj.iterator_count += 1;
  605. }
  606. }
  607. // 如果类名匹配,返回该节点
  608. if (now_name.split(" ").includes(class_name)) {
  609. iterator_count = 0;
  610. return parent;
  611. }
  612. return this.getParentByClassName(class_name, parent, JSobj);
  613. },
  614.  
  615. /**
  616. * 将func绑定到window.onscroll,并设置触发频率
  617. * @param {Function} func scroll的监听函数
  618. * @param {Object} JSobj 全局对象,至少要有srcoll_count
  619. * @param {Number} useful_range 有效的触发范围,默认是10。即0-10次时触发函数。
  620. * @param {Number} wait_range 等待的范围,默认是110。即useful_range-110次不触发函数。
  621. * @param {String} hint 触发函数后的日志内容,默认为空字符串。
  622. * @param {Window} inner_window 特定的window对象,主要用于 iframe 情况。JSobj中必须有scrollFunc,在调用后会重新写入scrollFunc。
  623. */
  624. scrollFunc: function(func, JSobj, useful_range = 10, wait_range = 110, hint = "", inner_window = null) {
  625. let new_func = (func, JSobj, useful_range, wait_range, hint) => {
  626. JSobj.scroll_count += 1;
  627.  
  628. if (JSobj.scroll_count < useful_range) {
  629. func();
  630. console.log(hint);
  631. } else if (JSobj.scroll_count > wait_range) {
  632. JSobj.scroll_count = 0;
  633. }
  634. };
  635. // 如果没有指定的window对象,则使用默认的window
  636. if (!inner_window) {
  637. window.onscroll = () => {
  638. new_func(func, JSobj, useful_range, wait_range, hint);
  639. };
  640. return;
  641. }
  642. // 特定的window对象,一般用于iframe,追加scroll监听器
  643. let scrollFunc = () => {
  644. new_func(func, JSobj, useful_range, wait_range, hint);
  645. };
  646. JSobj.scrollFunc = scrollFunc;
  647. inner_window.addEventListener("scroll", scrollFunc, false);
  648. },
  649.  
  650. /**
  651. * 创建5个按钮:展开文档、导出图片、导出PDF、未设定4、未设定5;默认均为隐藏
  652. */
  653. createBtns: function() {
  654. // 创建按钮组
  655. let section = document.createElement("section");
  656. section.className = "btns_section";
  657. section.innerHTML = `
  658. <p class="logo_tit">Wenku Doc Downloader</p>
  659. <button class="btn-1" title="请先使内容加载完,防止出现空白页">展开文档 😈</button>
  660. <button class="btn-2">未设定2</button>
  661. <button class="btn-3">未设定3</button>
  662. <button class="btn-4">未设定4</button>
  663. <button class="btn-5">未设定5</button>`;
  664. document.body.appendChild(section);
  665.  
  666. // 设定样式
  667. let style = document.createElement("style");
  668. style.innerHTML = `
  669. .btns_section{
  670. position: fixed;
  671. width: 154px;
  672. left: 10px;
  673. top: 32%;
  674. background: #E7F1FF;
  675. border: 2px solid #1676FF;
  676. padding: 0px 0px 10px 0px;
  677. font-weight: 600;
  678. border-radius: 2px;
  679. font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB',
  680. 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif, 'Apple Color Emoji',
  681. 'Segoe UI Emoji', 'Segoe UI Symbol';
  682. z-index: 5000;
  683. }
  684. .logo_tit{
  685. width: 100%;
  686. background: #1676FF;
  687. text-align: center;
  688. font-size:12px ;
  689. color: #E7F1FF;
  690. line-height: 40px;
  691. height: 40px;
  692. margin: 0 0 16px 0;
  693. }
  694.  
  695. .btn-1{
  696. display: block;
  697. width: 128px;
  698. height: 28px;
  699. background: linear-gradient(180deg, #00E7F7 0%, #FEB800 0.01%, #FF8700 100%);
  700. border-radius: 4px;
  701. color: #fff;
  702. font-size: 12px;
  703. border: none;
  704. outline: none;
  705. margin: 8px auto;
  706. font-weight: bold;
  707. cursor: pointer;
  708. opacity: .9;
  709. }
  710. .btn-2{
  711. display: none;
  712. width: 128px;
  713. height: 28px;
  714. background: #07C160;
  715. border-radius: 4px;
  716. color: #fff;
  717. font-size: 12px;
  718. border: none;
  719. outline: none;
  720. margin: 8px auto;
  721. font-weight: bold;
  722. cursor: pointer;
  723. opacity: .9;
  724. }
  725. .btn-3{
  726. display: none;
  727. width: 128px;
  728. height: 28px;
  729. background:#FA5151;
  730. border-radius: 4px;
  731. color: #fff;
  732. font-size: 12px;
  733. border: none;
  734. outline: none;
  735. margin: 8px auto;
  736. font-weight: bold;
  737. cursor: pointer;
  738. opacity: .9;
  739. }
  740. .btn-4{
  741. display: none;
  742. width: 128px;
  743. height: 28px;
  744. background: #1676FF;
  745. border-radius: 4px;
  746. color: #fff;
  747. font-size: 12px;
  748. border: none;
  749. outline: none;
  750. margin: 8px auto;
  751. font-weight: bold;
  752. cursor: pointer;
  753. opacity: .9;
  754. }
  755. .btn-5{
  756. display: none;
  757. width: 128px;
  758. height: 28px;
  759. background: #ff6600;
  760. border-radius: 4px;
  761. color: #fff;
  762. font-size: 12px;
  763. border: none;
  764. outline: none;
  765. margin: 8px auto;
  766. font-weight: bold;
  767. cursor: pointer;
  768. opacity: .9;
  769. }
  770. .btn-1:hover,.btn-2:hover,.btn-3:hover,.btn-4,.btn-5:hover{ opacity: .8;}
  771. .btn-1:active,.btn-2:active,.btn-3:active,.btn-4,.btn-5:active{ opacity: 1;}`;
  772. document.head.appendChild(style);
  773. }
  774. };
  775.  
  776. /**
  777. * 清理百度文库页面的无关元素
  778. */
  779. function clearPage_Baidu() {
  780. let selectors = [
  781. "#hd, .aside, .reader-tools-bar-wrap, .sb-con, .bg-opacity",
  782. ".doc-tag-wrap, .doc-bottom-wrap, .ft, #ft, .crubms-wrap, .banner-ad",
  783. "#activity-tg, .top-ads-banner-wrap, .reader_ab_test, .tag-tips, .doc-value",
  784. ".owner-desc-wrap, a[title='全屏显示'], #next_doc_box"
  785. ];
  786. let elem_list = document.querySelectorAll(selectors.join(", "));
  787. for (let elem of elem_list) {
  788. utils.tryToRemoveElement(elem);
  789. }
  790. let nut_selector = ".fix-searchbar-wrap, #hd";
  791. utils.forceHide(nut_selector);
  792. // 去除页面顶部空白
  793. document.querySelector("#doc").style.paddingTop = "0";
  794. }
  795.  
  796.  
  797. /**
  798. * 判断是否收集完元素,如果没有,则给出提醒
  799. * @param {Array} msg_list 附加提示信息列表
  800. * @returns bool 是否完成元素收集/冻结
  801. */
  802. function isFinished(msg_list = []) {
  803. if (!window.baiduJS.finished) {
  804. let hint = [
  805. "仍有内容未加载完,无法使用该功能",
  806. "建议从头到尾慢速地再浏览一遍",
  807. ...msg_list
  808. ];
  809. alert(hint.join("\n"));
  810. return false;
  811. }
  812. return true;
  813. }
  814.  
  815.  
  816. /**
  817. * 提取文字,导出txt。适用于百度文库
  818. */
  819. function saveText_Baidu() {
  820. // 判断是否存在文字元素
  821. let elems = document.querySelectorAll(".reader-txt-layer");
  822. if (!elems.length) {
  823. alert("当前页面没有文字元素\n如果你看到了文字说明它的原文档就是图片,所以提取不到文字");
  824. return;
  825. }
  826. // 判断页面是否加载完成
  827. if (!isFinished()) {
  828. return;
  829. }
  830. let title = document.title.split("-")[0].trim(); // 取得文档标题
  831. let page_texts = [];
  832. for (let elem of window.baiduJS.elems_map.values()) {
  833. // 取得该页文档下的全部文字
  834. let text = elem.textContent;
  835. page_texts.push(text);
  836. }
  837. utils.createAndDownloadFile(`${title}.txt`, page_texts.join("\n"));
  838. }
  839.  
  840.  
  841. /**
  842. * 存储ppt格式的ppt图形
  843. * @param {NodeList} elems ppt元素列表
  844. */
  845. function savePPTurls_1(elems) {
  846. let urls = [];
  847. elems.forEach((elem) => {
  848. if (elem.hasAttribute("src")) {
  849. urls.push(elem.src);
  850. } else {
  851. urls.push(elem.getAttribute("data-src"));
  852. }
  853. });
  854. utils.createAndDownloadFile("urls.csv", urls.join("\n"));
  855. }
  856.  
  857.  
  858. /**
  859. * 取得未捕获pdf格式ppt元素的页码的列表
  860. * @returns {Array} not_loaded
  861. */
  862. function getNotLoaded_pdf_PPT() {
  863. if (window.baiduJS.finished) {
  864. return [];
  865. }
  866.  
  867. //取得loaded
  868. let elems_map = window.baiduJS.elems_map;
  869. elems_map = sortMapByID$2(elems_map); // id形式:"pageNo-1"
  870.  
  871. // 找出not loaded
  872. let not_loaded = [];
  873. for (let i = 1; i <= window.baiduJS.max_page; i++) {
  874. if (!elems_map.has(`pageNo-${i}`)) {
  875. not_loaded.push(i);
  876. }
  877. }
  878. return not_loaded;
  879. }
  880.  
  881.  
  882. /**
  883. * 存储pdf格式的ppt元素
  884. */
  885. function storePPTurls() {
  886. let imgs = document.querySelectorAll("img.reader-pptstyle");
  887. for (let img of imgs) {
  888. let page = utils.getParentByClassName("bd", img, window.baiduJS, true);
  889. let src = img.src;
  890. let id = page.id;
  891.  
  892. if (id === undefined) {
  893. continue;
  894. }
  895.  
  896. // 如果不存在则录入
  897. if (!window.baiduJS.elems_map.has(id)) {
  898. window.baiduJS.elems_map.set(id, src);
  899. }
  900. }
  901. // 如果全部捕获,则解绑
  902. if (window.baiduJS.elems_map.size === window.baiduJS.max_page) {
  903. window.onscroll = () => { };
  904. window.baiduJS.elems_map = sortMapByID$2(window.baiduJS.elems_map);
  905. window.baiduJS.finished = true;
  906. }
  907. }
  908.  
  909.  
  910. function savePPTurls_2() {
  911. let not_loaded = getNotLoaded_pdf_PPT();
  912. let hints = [
  913. "以下页面需要浏览并再次加载:",
  914. not_loaded.join(",")
  915. ];
  916. if (!isFinished(hints)) {
  917. return;
  918. }
  919. // 页面都加载完毕
  920. let elems_map = window.baiduJS.elems_map;
  921. let urls = Array.from(elems_map.values());
  922. utils.createAndDownloadFile("urls.csv", urls.join("\n"));
  923. }
  924.  
  925.  
  926. /**
  927. * 动态存储".ppt-image-wrap img"图形,并导出urls
  928. * @returns
  929. */
  930. function savePicUrls_Baidu() {
  931. // 尝试取得ppt元素
  932. let elems = document.querySelectorAll(".ppt-image-wrap img"); // 真正的ppt
  933. let elems_2 = document.querySelectorAll("img.reader-pptstyle"); // pdf格式的ppt
  934.  
  935. // 根据试探情况(ppt元素存在情况)做出相应决策
  936. if (!elems.length && !elems_2.length) {
  937. alert("当前页面没有PPT图形");
  938. return;
  939. } else if (elems.length && !elems_2.length) {
  940. // 真正的ppt
  941. savePPTurls_1(elems);
  942. } else if (!elems.length && elems_2.length) {
  943. // pdf格式的ppt
  944. utils.scrollFunc(storePPTurls, window.baiduJS, 10, 50, "baidu元素(ppt Ⅱ型): 收集");
  945. savePPTurls_2();
  946. } else {
  947. // 未知情况
  948. utils.banSelf();
  949. utils.createBtns();
  950. utils.setBtnEvent(() => { }, [], "btn_1", "未知情况");
  951. }
  952.  
  953. }
  954.  
  955.  
  956. /**
  957. * 动态存储".reader-pic-item"图形,并导出urls
  958. * @returns
  959. */
  960. function savePicUrls_BaiduNonPPT() {
  961. // 判断是否存在非PPT图形元素
  962. let elems = document.querySelectorAll(".reader-pic-item");
  963. if (!elems.length) {
  964. alert("当前页面没有非PPT图形");
  965. return;
  966. }
  967. // 判断是否页面都加载完成
  968. if (!isFinished()) {
  969. return;
  970. }
  971. // 找到img元素,导出urls
  972. let img_urls = [];
  973. for (let elem of window.baiduJS.elems_map.values()) {
  974. // 取得img元素
  975. elem.querySelectorAll(".reader-pic-item").forEach((img) => {
  976. // 取得img链接
  977. let url = img.style.backgroundImage.split('"')[1];
  978. img_urls.push(url);
  979. });
  980. }
  981. utils.createAndDownloadFile("urls.csv", img_urls.join("\n"));
  982. }
  983.  
  984.  
  985. /**
  986. * 找出没有收录的id,需求的id列表形如:["pageNo-4", "pageNo-5", ...]
  987. * @returns 未收录的id_list: [1, 2, 3, ...]
  988. */
  989. function getUnfrozen() {
  990. // 获取已经冻结的文档元素
  991. let _frozen_ids = new Set(window.baiduJS.elems);
  992. let frozen_ids = new Set();
  993. _frozen_ids.forEach((full_id) => {
  994. // full_id: pageNo-1
  995. let id = parseInt(full_id.split("-")[1]);
  996. frozen_ids.add(id);
  997. });
  998.  
  999. if (!frozen_ids.size) {
  1000. return ["all pages"];
  1001. }
  1002.  
  1003. // 取得基础页码
  1004. let a_id = utils.setPop(frozen_ids);
  1005. let basis = parseInt(a_id / 50) * 50;
  1006. // 获取全部文档元素
  1007. let all_ids = new Set();
  1008. let all_elems = document.querySelectorAll(".mod.reader-page.complex[class*=reader-page]");
  1009. all_elems.forEach((elem) => {
  1010. // ['mod', 'reader-page', 'complex', 'hidden-doc-banner', 'reader-page-1'] -> "1" -> 1
  1011. let id = parseInt(elem.className.split(" ").at(-1).split("-")[2]);
  1012. all_ids.add(basis + id);
  1013. });
  1014.  
  1015. // 求差集,取得未冻结的
  1016. let unfrozen_set = utils.difference(all_ids, frozen_ids);
  1017. let unfrozen_list = Array.from(unfrozen_set);
  1018. unfrozen_list.sort((prev, next) => { return prev - next; });
  1019. // 打印日志
  1020. console.log([
  1021. `all_ids: ${Array.from(all_ids)}`,
  1022. `frozen_ids: ${Array.from(frozen_ids)}`,
  1023. `unfrozen_list: ${unfrozen_list}`
  1024. ].join("\n"));
  1025. return unfrozen_list;
  1026. }
  1027.  
  1028.  
  1029. /**
  1030. * 适用于【*不*带有api=1】get参数的页面。可以打印页面。
  1031. * @returns
  1032. */
  1033. function printPage_BaiduNoArgs() {
  1034. // 判断是否页面都加载完成
  1035. let msg_list = [
  1036. "未加载的页面如下:",
  1037. getUnfrozen().join(", ")
  1038. ];
  1039. if (!isFinished(msg_list)) {
  1040. return;
  1041. }
  1042. // 清理页面
  1043. clearPage_Baidu();
  1044. // 隐藏上下页按钮
  1045. document.querySelectorAll("a[data-next-page], a[data-prev-page]").forEach((elem) => {
  1046. elem.setAttribute("style", "display: none !important;");
  1047. });
  1048. // 打印
  1049. utils.hideBtnThenPrint();
  1050. // 显示上下页按钮
  1051. document.querySelectorAll("a[data-next-page], a[data-prev-page]").forEach((elem) => {
  1052. elem.style.display = "block";
  1053. });
  1054. }
  1055.  
  1056.  
  1057. /**
  1058. * 调整页间距为 width px
  1059. * @param {Number} width 页间距
  1060. */
  1061. function adjustSpace(width) {
  1062. // 调整页间距
  1063. let space_selector = ".reader-container .reader-page, .reader-container .pay-page-mod";
  1064. document.querySelectorAll(space_selector).forEach((space) => {
  1065. space.style.margin = `0 0 ${width}px`;
  1066. });
  1067. console.log(`页间距已经调整为:${width}px`);
  1068. }
  1069.  
  1070.  
  1071. function userAdjustSpace() {
  1072. let space_selector = ".reader-container .reader-page, .reader-container .pay-page-mod";
  1073. let space = document.querySelector(space_selector);
  1074. let old_width = getComputedStyle(space).marginBottom;
  1075. let width_str = prompt(`当前页间距为:${old_width}\n请输入调整后的页间距(0-500的整数):`);
  1076. let width = parseInt(width_str);
  1077. width = Number.isInteger(width) && (0 <= width <= 500) ? width : parseInt(old_width);
  1078. adjustSpace(width);
  1079. console.log(`调整后的页间距为:${width}px`);
  1080. }
  1081.  
  1082.  
  1083. /**
  1084. * 根据键中的id数字对map排序
  1085. * @param {Map} elems_map
  1086. * @returns sorted_map
  1087. */
  1088. function sortMapByID$2(elems_map) {
  1089. // id形式:pageNo-10
  1090. let elems_arr = Array.from(elems_map);
  1091. elems_arr.sort((item1, item2) => {
  1092. // 从key中取出id
  1093. let id1 = parseInt(item1[0].split("-")[1]);
  1094. let id2 = parseInt(item2[0].split("-")[1]);
  1095. // 升序排序
  1096. return id1 - id2;
  1097. });
  1098. // 返回排序好的map
  1099. return new Map(elems_arr);
  1100. }
  1101.  
  1102.  
  1103. /**
  1104. * 存储html元素。适用于百度文库的文字型文档
  1105. */
  1106. function storeHtmlElemts_Baidu(selector = "[class*=reader-main]") {
  1107. let elems_map = window.baiduJS.elems_map;
  1108. document.querySelectorAll(selector).forEach(
  1109. (elem) => {
  1110. let origin_page_elem = utils.getParentByClassName("bd", elem, window.baiduJS);
  1111. if (typeof(origin_page_elem) === "string") {
  1112. return;
  1113. }
  1114. // 复制元素防止丢失
  1115. let page_elem = origin_page_elem.cloneNode(true);
  1116. // 移除data标签,切断vue对数据的渲染控制
  1117. page_elem.removeAttribute("data-page-no");
  1118. let id = page_elem.id; // id的形式:pageNo-10
  1119. if (!elems_map.has(id)) {
  1120. elems_map.set(id, page_elem);
  1121. }
  1122. });
  1123. if (elems_map.size === window.baiduJS.max_page) {
  1124. // 根据id排序,保证导出的图片链接不是乱序的
  1125. window.baiduJS.elems_map = sortMapByID$2(window.baiduJS.elems_map);
  1126. // 已经保存完全部文档页元素,移除滚动事件的绑定函数
  1127. window.baiduJS.finished = true;
  1128. window.onscroll = () => { };
  1129. }
  1130. }
  1131.  
  1132.  
  1133. /**
  1134. * 移除html元素上的data标签,切断vue的数据渲染控制。适用于百度文库的文字型文档
  1135. * @param {String} selector 要移除 data-page-no属性 的元素选择器
  1136. */
  1137. function freezeHtmlElemts_Baidu(selector = "[class*=reader-main]") {
  1138. let elems = window.baiduJS.elems;
  1139. document.querySelectorAll(selector).forEach(
  1140. (elem) => {
  1141. let page_elem = utils.getParentByClassName("bd", elem, window.baiduJS);
  1142. if (typeof(page_elem) === "string") {
  1143. return;
  1144. }
  1145. // 移除data标签,切断vue对数据的渲染控制
  1146. page_elem.removeAttribute("data-page-no");
  1147. // 存储已经冻结的元素id
  1148. let id = page_elem.id; // id的形式:pageNo-10
  1149. if (!elems.includes(id)) {
  1150. elems.push(id);
  1151. }
  1152. });
  1153. if (elems.length === window.baiduJS.max_page) {
  1154. // 已经冻结完全部文档页元素,移除滚动事件的绑定函数
  1155. window.baiduJS.finished = true;
  1156. window.onscroll = () => { };
  1157. }
  1158. }
  1159.  
  1160.  
  1161. /**
  1162. * 递归的展开百度文档,适用于旧版页面。
  1163. * @param {Function} extra_task 在文档展开完成后执行的追加函数
  1164. * @returns
  1165. */
  1166. function readMoreRecursively(extra_task = () => { }) {
  1167. let go_more = document.querySelector("#html-reader-go-more");
  1168. if (!go_more || go_more.style.display === "none") {
  1169. // 如果不存在继续阅读按钮,或隐藏显示,则认为完成了展开
  1170. console.log("wk: 文档展开完成");
  1171. extra_task();
  1172. return;
  1173. }
  1174. let read_more = go_more.querySelector(".moreBtn.goBtn");
  1175. read_more.click();
  1176. console.log("wk: 展开文档");
  1177. setTimeout(readMoreRecursively, 500);
  1178. }
  1179.  
  1180.  
  1181. /**
  1182. * 统计文档页的数量
  1183. * @param {String} selector 文档页元素选择器
  1184. * @returns 文档页的数量
  1185. */
  1186. function countPages$1(selector = ".mod.reader-page.complex[class*=reader-page]") {
  1187. let all_elems = document.querySelectorAll(selector);
  1188. return all_elems.length;
  1189. }
  1190.  
  1191.  
  1192. /**
  1193. * 取得当前行高
  1194. * @returns 行高,例如 192px
  1195. */
  1196. function getLineHeight() {
  1197. let line = document.querySelector("p.reader-word-layer");
  1198. if (!line) {
  1199. // 对于没有文字的文档,返回null即可
  1200. return null;
  1201. }
  1202. let height = getComputedStyle(line).lineHeight;
  1203. console.log(`get line-height: ${height}`);
  1204. return height;
  1205. }
  1206.  
  1207.  
  1208. /**
  1209. * 增大行间距到1500px,用于解决文字重叠
  1210. */
  1211. function changeLineHeight() {
  1212. let lines = document.querySelectorAll("p.reader-word-layer");
  1213. let aim_height, aim_hint;
  1214.  
  1215. if (getComputedStyle(lines[0]).lineHeight !== window.baiduJS.origin_line_height) {
  1216. // 切换回旧的行高
  1217. aim_height = window.baiduJS.origin_line_height;
  1218. aim_hint = "解决文字重叠";
  1219. console.log(`changed to original line height`);
  1220. } else {
  1221. // 切换到增大的行高
  1222. aim_height = "1500px";
  1223. aim_hint = "切回旧行高";
  1224. console.log(`changed to new line height: 1500px`);
  1225. }
  1226. // 应用新的行高
  1227. for (let line of lines) {
  1228. line.style.lineHeight = aim_height;
  1229. }
  1230. // 应用新的按钮文本
  1231. setTimeout(() => {
  1232. utils.modifyBtnText("btn_5", aim_hint, false, false);
  1233. }, 2000);
  1234. }
  1235.  
  1236.  
  1237. function baiduWenku_OldVer() {
  1238. // 为导出内容提供全局变量,便于动态收集文档页元素的存取
  1239. let real_max_page;
  1240. try {
  1241. real_max_page = parseInt(document.querySelector(".page-count").textContent.replace("/", ""));
  1242. } catch (e) {
  1243. real_max_page = countPages$1();
  1244. }
  1245.  
  1246. window.baiduJS = {
  1247. max_page: countPages$1(), // 当前文档页面数量
  1248. real_max_page: real_max_page, // 当前文档的总页数
  1249. iterator_count: 0, // getParentByClassName的最大迭代次数为9
  1250. finished: false, // 是否收集完了全部文档页元素
  1251. scroll_count: 0, // 用于统计累计触发scroll的次数
  1252. elems: [], // 存储已经冻结的元素id
  1253. elems_map: new Map(), // id: element
  1254. origin_line_height: getLineHeight() // 原始行高
  1255. };
  1256.  
  1257. if (location.href.includes("\u002f\u0073\u0068\u0061\u0072\u0065\u002f")) {
  1258. // old version for fetch doc elements
  1259. // 跟随浏览,动态收集页面元素
  1260. utils.scrollFunc(storeHtmlElemts_Baidu, window.baiduJS, 10, 50, "baidu元素: 收集");
  1261.  
  1262. // 隐藏按钮
  1263. utils.toggleBtnStatus("btn_1");
  1264. // 显示按钮
  1265. utils.toggleBtnStatus("btn_2");
  1266. utils.toggleBtnStatus("btn_3");
  1267. utils.toggleBtnStatus("btn_4");
  1268. utils.toggleBtnStatus("btn_5");
  1269. // 绑定事件到按钮
  1270. utils.setBtnEvent(saveText_Baidu, [], "btn_3", "导出纯文本");
  1271. utils.setBtnEvent(savePicUrls_Baidu, [], "btn_4", "导出图片链接(仅PPT)");
  1272. utils.setBtnEvent(savePicUrls_BaiduNonPPT, [], "btn_5", "导出图片链接(除PPT)");
  1273. // btn_2
  1274. utils.setBtnEvent(() => {
  1275. let hints = [
  1276. "最好是excel和word文档,是否继续?",
  1277. "其他类型如ppt、pdf也可,但是不推荐。",
  1278. "其他类型建议【导出图片链接】。"
  1279. ];
  1280. if (confirm(hints.join("\n"))) {
  1281. // 在无参数的旧版页面处理超过50页的文档
  1282. // 跟随浏览,动态冻结页面元素
  1283. window.baiduJS.finished = false;
  1284. utils.scrollFunc(freezeHtmlElemts_Baidu, window.baiduJS, 10, 50, "baidu元素: 冻结");
  1285. // 重新绑定按钮监听器
  1286. utils.setBtnEvent(userAdjustSpace, [], "btn_4", "调整页间距");
  1287. utils.setBtnEvent(printPage_BaiduNoArgs, [], "btn_3", "打印页面到PDF◈");
  1288. alert("正在截获文档内容,请上下浏览页面后再次点击该按钮");
  1289. utils.setBtnEvent(changeLineHeight, [], "btn_5", "解决文字重叠");
  1290.  
  1291. // 隐藏按钮
  1292. utils.toggleBtnStatus("btn_2");
  1293. }
  1294. }, [], "btn_2", "打印页面到PDF");
  1295. } else {
  1296. console.log(`无法识别的页面:${location.href}`);
  1297. }
  1298. }
  1299.  
  1300.  
  1301. /**
  1302. * 百度文档下载策略
  1303. */
  1304. function baiduWenku() {
  1305. // 允许打印页面
  1306. utils.allowPrint();
  1307. // 原文档解析到预览文档
  1308. if (location.href.includes("\u002f\u0076\u0069\u0065\u0077\u002f")) {
  1309. utils.createBtns();
  1310. let jump2sharePage_Baidu = function() {
  1311. location.href = `https://${location.host}${location.pathname.replace("\u0076\u0069\u0065\u0077", "\u0073\u0068\u0061\u0072\u0065")}`;
  1312. };
  1313. utils.setBtnEvent(jump2sharePage_Baidu, [], "btn_1");
  1314. } else {
  1315. // 在完全展开文档后启用旧版页面处理函数
  1316. // 创建按钮组
  1317. console.log("\n\nwk: 进入旧版页面\n\n\n");
  1318. utils.createBtns();
  1319. utils.setBtnEvent(readMoreRecursively, [baiduWenku_OldVer], "btn_1");
  1320. }
  1321. }
  1322.  
  1323. /**
  1324. * 展开道客巴巴的文档
  1325. */
  1326. function readAllDoc88() {
  1327. // 获取“继续阅读”按钮
  1328. let continue_btn = document.querySelector("#continueButton");
  1329. // 如果存在“继续阅读”按钮
  1330. if (continue_btn) {
  1331. // 跳转到文末(等同于展开全文)
  1332. let cur_page = document.querySelector("#pageNumInput");
  1333. // 取得最大页码
  1334. let page_max = cur_page.parentElement.textContent.replace(" / ", "");
  1335. // 跳转到尾页
  1336. utils.jump2pageNo(cur_page, page_max, "keypress");
  1337. // 返回顶部
  1338. setTimeout(utils.jump2pageNo(cur_page, "1", "keypress"), 1000);
  1339. }
  1340. // 文档展开后,显示按钮2、3
  1341. else {
  1342. // 隐藏按钮
  1343. utils.toggleBtnStatus("btn_1");
  1344. // 显示按钮
  1345. utils.toggleBtnStatus("btn_2");
  1346. utils.toggleBtnStatus("btn_3");
  1347. }
  1348. }
  1349.  
  1350. /**
  1351. * 道客巴巴文档下载策略
  1352. */
  1353. function doc88() {
  1354. // 创建脚本启动按钮1、2
  1355. utils.createBtns();
  1356.  
  1357. // 绑定主函数
  1358. let prepare = function() {
  1359. // 获取canvas元素列表
  1360. let node_list = document.querySelectorAll(".inner_page");
  1361. // 获取文档标题
  1362. let title;
  1363. if (document.querySelector(".doctopic h1")) {
  1364. title = document.querySelector(".doctopic h1").title;
  1365. } else {
  1366. title = "文档";
  1367. }
  1368. return [node_list, title];
  1369. };
  1370.  
  1371. // btn_1: 展开文档
  1372. utils.setBtnEvent(() => {
  1373. readAllDoc88();
  1374. }, [], "btn_1");
  1375. // btn_2: 导出zip
  1376. utils.setBtnEvent(() => {
  1377. if (confirm("确定每页内容都加载完成了吗?")) {
  1378. utils.saveCanvasesToZip(...prepare());
  1379. }
  1380. }, [], "btn_2", "导出图片到zip");
  1381. // btn_3: 导出PDF
  1382. utils.setBtnEvent(() => {
  1383. if (confirm("确定每页内容都加载完成了吗?")) {
  1384. utils.saveCanvasesToPDF(...prepare());
  1385. }
  1386. }, [], "btn_3", "导出图片到PDF");
  1387. }
  1388.  
  1389. // 绑定主函数
  1390. function getCanvasList() {
  1391. // 获取全部canvas元素,用于传递canvas元素列表给 btn_2 和 btn_3
  1392. let parent_node_list = document.querySelectorAll(".hkswf-content");
  1393. let node_list = [];
  1394. for (let node of parent_node_list) {
  1395. node_list.push(node.children[0]);
  1396. }
  1397. return node_list;
  1398. }
  1399.  
  1400.  
  1401. function prepare() {
  1402. // 获取canvas元素列表
  1403. let node_list = getCanvasList();
  1404. // 获取文档标题
  1405. let title;
  1406. if (document.querySelector("h1 [title=doc]")) {
  1407. title = document.querySelector("h1 [title=doc]").nextElementSibling.textContent;
  1408. } else if (document.querySelector(".doc_title")) {
  1409. title = document.querySelector(".doc_title").textContent;
  1410. } else {
  1411. title = "文档";
  1412. }
  1413. return [node_list, title];
  1414. }
  1415.  
  1416.  
  1417. // 判断是否有canvas元素
  1418. function detectCanvas() {
  1419. let haveCanvas = getCanvasList().length === 0 ? false : true;
  1420.  
  1421. // 隐藏按钮
  1422. utils.toggleBtnStatus("btn_1");
  1423. // 显示按钮
  1424. utils.toggleBtnStatus("btn_2");
  1425.  
  1426. // 如果没有canvas元素,则认为文档页面由外链图片构成
  1427. if (!haveCanvas) {
  1428. // btn_2: 导出图片链接
  1429. utils.setBtnEvent(() => {
  1430. if (confirm("确定每页内容都加载完成了吗?")) {
  1431. utils.savePicUrls("[id*=img_]");
  1432. }
  1433. }, [], "btn_2", "导出全部图片链接");
  1434. } else {
  1435. // 显示按钮3
  1436. utils.toggleBtnStatus("btn_3");
  1437. // btn_2: 导出zip
  1438. utils.setBtnEvent(() => {
  1439. if (confirm("确定每页内容都加载完成了吗?")) {
  1440. utils.saveCanvasesToZip(...prepare());
  1441. }
  1442. }, [], "btn_2", "导出图片到zip");
  1443. // btn_3: 导出PDF
  1444. utils.setBtnEvent(() => {
  1445. if (confirm("确定每页内容都加载完成了吗?")) {
  1446. utils.saveCanvasesToPDF(...prepare());
  1447. }
  1448. }, [], "btn_3", "导出图片到PDF");
  1449. }
  1450. }
  1451.  
  1452.  
  1453. /**
  1454. * 豆丁文档下载策略
  1455. */
  1456. function docin() {
  1457. // 创建脚本启动按钮
  1458. utils.createBtns();
  1459.  
  1460. // 隐藏底部工具栏
  1461. document.querySelector("#j_select").click(); // 选择指针
  1462. let tool_bar = document.querySelector(".reader_tools_bar_wrap.tools_bar_small.clear");
  1463. tool_bar.style.display = "none";
  1464.  
  1465. // btn_1: 判断文档类型
  1466. utils.setBtnEvent(() => {
  1467. utils.forceHide(".jz_watermark");
  1468. detectCanvas();
  1469. }, [], "btn_1", "判断文档类型");
  1470. }
  1471.  
  1472. /**
  1473. * 点击“展开继续阅读”,适用性:爱尚共享资料
  1474. */
  1475. function readAlliShare() {
  1476. // 获取“继续阅读”元素
  1477. let red_btn = document.getElementsByClassName("red-color")[0];
  1478. let red_text = red_btn.textContent;
  1479. // 如果可以展开,则展开
  1480. if (red_text.search("点击可继续阅读") !== -1) {
  1481. red_btn.click();
  1482. setTimeout(readAlliShare, 1000);
  1483. }
  1484. // 否则启动按钮2,准备清理页面然后打印为PDF
  1485. else {
  1486. // 隐藏按钮
  1487. utils.toggleBtnStatus("btn_1");
  1488. // 显示按钮
  1489. utils.toggleBtnStatus("btn_2");
  1490. utils.toggleBtnStatus("btn_3");
  1491.  
  1492. // 显示svg图片的链接
  1493. let page1 = document.querySelector('[data-num="1"] .data-detail embed');
  1494. if (!page1) {
  1495. // 如果不存在svg图形,终止后续代码
  1496. console.log("当前页面不存在svg图形");
  1497. return;
  1498. }
  1499. let page2 = document.querySelector('[data-num="2"] .data-detail embed');
  1500. let [svg1_src_div, svg2_src_div] = [document.createElement("div"), document.createElement("div")];
  1501. svg1_src_div.innerHTML = `<div id="src-1"
  1502. style="font-weight: bold;font-size: 20px; height: 100px; width: 100%">
  1503. 访问以下链接以复制文字:<br>${page1.src}
  1504. </div>`;
  1505. svg2_src_div.innerHTML = `<div id="src-1"
  1506. style="font-weight: bold;font-size: 20px; height: 100px; width: 100%">
  1507. 访问以下链接以复制文字:<br>${page2.src}
  1508. </div>`;
  1509. // 添加到页面上
  1510. page1.parentElement.parentElement.parentElement.append(svg1_src_div);
  1511. page2.parentElement.parentElement.parentElement.append(svg2_src_div);
  1512. }
  1513. }
  1514.  
  1515.  
  1516. /**
  1517. * 清理并打印爱问共享资料的文档页
  1518. * @returns 如果输入偏移量非法,返回空值以终止函数
  1519. */
  1520. function printPageiShare() {
  1521. // # 清理并打印爱问共享资料的文档页
  1522. // ## 移除页面上无关的元素
  1523. // ### 移除单个元素
  1524. let topbanner = document.getElementsByClassName("detail-topbanner")[0];
  1525. let header = document.getElementsByClassName("new-detail-header")[0];
  1526. let fixright = document.getElementById("fix-right");
  1527. let redpacket = document.getElementsByClassName("loginRedPacket-dialog")[0];
  1528. let fixedrightfull = document.getElementsByClassName("fixed-right-full")[0];
  1529. let footer = document.getElementsByClassName("website-footer")[0];
  1530. let guess = document.getElementsByClassName("guess-you-like-warpper")[0];
  1531. let detailtopbox = document.getElementsByClassName("detail-top-box")[0];
  1532. let fullscreen = document.getElementsByClassName("reader-fullScreen")[0];
  1533. let endhint = document.getElementsByClassName("endof-trial-reading")[0];
  1534. let crumb_arrow;
  1535. try { crumb_arrow = document.getElementsByClassName("crumb-arrow")[0].parentElement; } catch (e) { console.log(); }
  1536. let copyright = document.getElementsByClassName("copyright-container")[0];
  1537. let state_btn = document.getElementsByClassName("state-bottom")[0];
  1538. let comments = document.getElementsByClassName("user-comments-wrapper")[0];
  1539. // ### 执行移除
  1540. let elem_list = [
  1541. topbanner,
  1542. header,
  1543. fixright,
  1544. redpacket,
  1545. fixedrightfull,
  1546. footer,
  1547. guess,
  1548. detailtopbox,
  1549. fullscreen,
  1550. endhint,
  1551. crumb_arrow,
  1552. copyright,
  1553. state_btn,
  1554. comments
  1555. ];
  1556. for (let elem of elem_list) {
  1557. utils.tryToRemoveElement(elem);
  1558. }
  1559. // ### 移除全部同类元素
  1560. let elem_list_2 = document.querySelectorAll(".tui-detail, .adv-container");
  1561. for (let elem_2 of elem_list_2) {
  1562. utils.tryToRemoveElement(elem_2);
  1563. }
  1564. // 使文档居中
  1565. alert("建议使用:\n偏移量: 18\n缩放: 默认\n如果预览中有广告,就取消打印\n再点一次按钮,预览中应该就没有广告了");
  1566. if (!utils.centerDoc("doc-main", "18")) {
  1567. return; // 如果输入非法,终止函数调用
  1568. }
  1569. // 隐藏按钮,然后打印页面
  1570. utils.hideBtnThenPrint();
  1571. }
  1572.  
  1573.  
  1574. /**
  1575. * 爱问共享资料文档下载策略
  1576. */
  1577. function ishare() {
  1578. // 创建脚本启动按钮1、2
  1579. utils.createBtns();
  1580.  
  1581. // btn_1: 展开文档
  1582. utils.setBtnEvent(readAlliShare, [], "btn_1");
  1583. // btn_2: 导出图片链接
  1584. utils.setBtnEvent(() => {
  1585. utils.savePicUrls(".data-detail");
  1586. }, [], "btn_2", "导出图片链接(推荐)");
  1587. // btn_3: 打印页面到PDF
  1588. utils.setBtnEvent(printPageiShare, [], "btn_3", "打印页面到PDF");
  1589.  
  1590. // 移除底部下载条
  1591. let detailfixed = document.getElementsByClassName("detail-fixed")[0];
  1592. utils.tryToRemoveElement(detailfixed);
  1593. }
  1594.  
  1595. /**
  1596. * 清理并打印得力文库的文档页
  1597. */
  1598. function printPageDeliwenku() {
  1599. // 移除页面上的无关元素
  1600. let selector = ".hr-wrap, #readshop, .nav_uis, .bookdesc, #boxright, .QQ_S1, .QQ_S, #outer_page_more, .works-manage-box.shenshu, .works-intro, .mt10.related-pic-box, .mt10.works-comment, .foot_nav, .siteInner";
  1601. let elem_list = document.querySelectorAll(selector);
  1602. for (let elem of elem_list) {
  1603. utils.tryToRemoveElement(elem);
  1604. }
  1605. // 修改页间距
  1606. let outer_pages = document.getElementsByClassName("outer_page");
  1607. for (let page of outer_pages) {
  1608. page.style.marginBottom = "20px";
  1609. }
  1610. // 使文档居中
  1611. alert("建议使用:\n偏移量: 3\n缩放: 112\n请上下滚动页面,确保每页内容都加载完成以避免空白页\n如果预览时有空白页或文末有绿色按钮,请取消打印重试");
  1612. if (!utils.centerDoc("#boxleft", "3")) {
  1613. return; // 如果输入非法,终止函数调用
  1614. }
  1615. // 打印文档
  1616. utils.hideBtnThenPrint();
  1617. }
  1618.  
  1619.  
  1620. /**
  1621. * 点击“继续阅读”,适用性:得力文库
  1622. */
  1623. function readAllDeliwenku() {
  1624. // 点击“同意并开始预览全文”
  1625. let start_btn = document.getElementsByClassName("pre_button")[0];
  1626. let display = start_btn.parentElement.parentElement.style.display;
  1627. // 如果该按钮显示着,则点击,然后滚动至页面底部,最后终止函数
  1628. if (!display) {
  1629. start_btn.children[0].click();
  1630. setTimeout(() => {
  1631. scroll(0, document.body.scrollHeight);
  1632. }, 200);
  1633. return;
  1634. }
  1635. // 增强按钮点击效果
  1636. utils.enhanceBtnClickReaction();
  1637.  
  1638. let read_all_btn = document.getElementsByClassName("fc2e")[0];
  1639. let display2 = read_all_btn.parentElement.parentElement.style.display;
  1640. // 继续阅读
  1641. if (display2 !== "none") {
  1642. // 获取input元素
  1643. let cur_page = document.querySelector("#pageNumInput");
  1644. let page_old = cur_page.value;
  1645. let page_max = cur_page.parentElement.nextElementSibling.textContent.replace(" / ", "");
  1646. // 跳转到尾页
  1647. utils.jump2pageNo(cur_page, page_max, "keydown");
  1648. // 跳转回来
  1649. utils.jump2pageNo(cur_page, page_old, "keydown");
  1650.  
  1651. // 切换按钮准备导出
  1652. } else {
  1653. // 推荐导出图片链接
  1654. utils.modifyBtnText("btn_2", null, true);
  1655. // 隐藏按钮
  1656. utils.toggleBtnStatus("btn_1");
  1657. // 显示按钮
  1658. utils.toggleBtnStatus("btn_2");
  1659. utils.toggleBtnStatus("btn_3");
  1660. // btn_3 橙色按钮
  1661. utils.setBtnEvent(printPageDeliwenku, [], "btn_3", "打印页面到PDF");
  1662. }
  1663. }
  1664.  
  1665.  
  1666. /**
  1667. * 得力文库文档下载策略
  1668. */
  1669. function deliwenku() {
  1670. // 创建脚本启动按钮1、2
  1671. utils.createBtns();
  1672.  
  1673. // btn_1: 展开文档
  1674. utils.setBtnEvent(readAllDeliwenku, [], "btn_1");
  1675. // btn_2: 导出图片链接
  1676. utils.setBtnEvent(() => {
  1677. if (confirm("确定每页内容都加载完成了吗?")) {
  1678. utils.savePicUrls('.inner_page div');
  1679. }
  1680. }, [], "btn_2", "导出图片链接");
  1681.  
  1682. // 尝试关闭页面弹窗
  1683. try { document.querySelector("div[title=点击关闭]").click(); } catch (e) { console.log(0); }
  1684. // 解除打印限制
  1685. utils.allowPrint();
  1686. }
  1687.  
  1688. function readAll360Doc() {
  1689. // 展开文档
  1690. document.querySelector(".articleMaxH").setAttribute("class", "");
  1691. // 隐藏按钮
  1692. utils.toggleBtnStatus("btn_1");
  1693. // 显示按钮
  1694. utils.toggleBtnStatus("btn_2");
  1695. utils.toggleBtnStatus("btn_3");
  1696. }
  1697.  
  1698.  
  1699. function saveText_360Doc() {
  1700. // 捕获图片链接
  1701. let images = document.querySelectorAll("#artContent img");
  1702. let content = [];
  1703.  
  1704. for (let i = 0; i < images.length; i++) {
  1705. let src = images[i].src;
  1706. content.push(`图${i+1},链接:${src}`);
  1707. }
  1708. // 捕获文本
  1709. let text = document.querySelector("#artContent").textContent;
  1710. content.push(text);
  1711.  
  1712. // 保存纯文本文档
  1713. let title = document.querySelector("#titiletext").textContent;
  1714. utils.createAndDownloadFile(`${title}.txt`, content.join("\n"));
  1715. }
  1716.  
  1717.  
  1718. function printPage360Doc() {
  1719. // # 清理并打印360doc的文档页
  1720. // ## 移除页面上无关的元素
  1721. let selector = ".fontsize_bgcolor_controler, .atfixednav, .header, .a_right, .article_data, .prev_next, .str_border, .youlike, .new_plbox, .str_border, .ul-similar, #goTop2, #divtort, #divresaveunder, .bottom_controler, .floatqrcode";
  1722. let elem_list = document.querySelectorAll(selector);
  1723. let under_doc_1, under_doc_2;
  1724. try {
  1725. under_doc_1 = document.querySelector("#bgchange p.clearboth").nextElementSibling;
  1726. under_doc_2 = document.querySelector("#bgchange").nextElementSibling.nextElementSibling;
  1727. } catch (e) { console.log(); }
  1728. // 执行移除
  1729. for (let elem of elem_list) {
  1730. utils.tryToRemoveElement(elem);
  1731. }
  1732. utils.tryToRemoveElement(under_doc_1);
  1733. utils.tryToRemoveElement(under_doc_2);
  1734. // 执行隐藏
  1735. document.querySelector("a[title]").style.display = "none";
  1736.  
  1737. // 使文档居中
  1738. alert("建议使用:\n偏移量: 20\n缩放: 默认\n");
  1739. if (!utils.centerDoc(".a_left", "20")) {
  1740. return; // 如果输入非法,终止函数调用
  1741. }
  1742. // 隐藏按钮,然后打印页面
  1743. utils.hideBtnThenPrint();
  1744. }
  1745.  
  1746.  
  1747. /**
  1748. * 360doc个人图书馆下载策略
  1749. */
  1750. function doc360() {
  1751. // 创建按钮区
  1752. utils.createBtns();
  1753. // btn_1: 展开文档
  1754. utils.setBtnEvent(readAll360Doc, [], "btn_1");
  1755. // btn_2: 导出纯文本
  1756. utils.setBtnEvent(saveText_360Doc, [], "btn_2", "导出纯文本");
  1757. // btn_3: 打印页面到PDF
  1758. utils.setBtnEvent(() => {
  1759. if (confirm("确定每页内容都加载完成了吗?")) {
  1760. printPage360Doc();
  1761. }
  1762. }, [], "btn_3", "打印页面到PDF");
  1763. }
  1764.  
  1765. /**
  1766. * 查找出所有未被捕获的页码,并返回列表
  1767. * @returns 未捕获页码列表
  1768. */
  1769. function getMissedPages() {
  1770. let all = []; // 全部页码
  1771. for (let i = 0; i < window.mbaJS.max_page; i++) {
  1772. all[i] = i + 1;
  1773. }
  1774. let missed = []; // 未捕获页码
  1775. let possessed = Array.from(window.mbaJS.canvases_map.keys()); // 已捕获页面
  1776.  
  1777. // 排除并录入未捕获页码
  1778. for (let num of all) {
  1779. if (!possessed.includes(`page${num}`)) {
  1780. missed.push(num);
  1781. }
  1782. }
  1783. return missed;
  1784. }
  1785.  
  1786.  
  1787. /**
  1788. * 根据键中的id数字对map排序
  1789. * @param {Map} elems_map
  1790. * @returns sorted_map
  1791. */
  1792. function sortMapByID$1(elems_map) {
  1793. // id形式:page2
  1794. let elems_arr = Array.from(elems_map);
  1795. elems_arr.sort((item1, item2) => {
  1796. // 从key中取出id
  1797. let id1 = parseInt(item1[0].replace("page", ""));
  1798. let id2 = parseInt(item2[0].replace("page", ""));
  1799. // 升序排序
  1800. return id1 - id2;
  1801. });
  1802. // 返回排序好的map
  1803. return new Map(elems_arr);
  1804. }
  1805.  
  1806.  
  1807. /**
  1808. * 存储动态加载的canvas元素、textContent
  1809. */
  1810. function storeElements_MBA() {
  1811. let canvases_map = window.mbaJS.canvases_map;
  1812. let texts_map = window.mbaJS.texts_map;
  1813. let quality = window.mbaJS.quality;
  1814.  
  1815. document.querySelectorAll(".page[data-loaded=true]").forEach(
  1816. (elem) => {
  1817. let capture = (elem) => {
  1818. // (1) 存储页面为canvas图形
  1819. let canvas, data_base64;
  1820. // 导出canvas数据防止丢失
  1821. try {
  1822. // 存储canvas
  1823. canvas = elem.querySelector("canvas[id*=page]");
  1824. if (window.mbaJS.only_text) {
  1825. data_base64 = null;
  1826. } else {
  1827. data_base64 = canvas.toDataURL("image/jpeg", quality);
  1828. }
  1829. } catch (e) {
  1830. // utils.sleep(500);
  1831. return;
  1832. }
  1833. // 增量录入map
  1834. let id = canvas.id; // id的形式:page2
  1835. if (!canvases_map.has(id)) {
  1836. canvases_map.set(id, data_base64);
  1837. }
  1838. // 确定canvas长宽
  1839. if (!window.mbaJS.only_text && !window.mbaJS.width) {
  1840. window.mbaJS.width = parseInt(canvas.width);
  1841. window.mbaJS.height = parseInt(canvas.height);
  1842. }
  1843.  
  1844. // (2) 存储text
  1845. let text = elem.textContent;
  1846. if (!texts_map.has(id)) {
  1847. texts_map.set(id, text);
  1848. }
  1849. };
  1850. setTimeout(capture, 500, elem);
  1851. });
  1852. if (canvases_map.size === window.mbaJS.max_page) {
  1853. // 根据id排序
  1854. window.mbaJS.canvases_map = sortMapByID$1(window.mbaJS.canvases_map);
  1855. window.mbaJS.texts_map = sortMapByID$1(window.mbaJS.texts_map);
  1856. window.mbaJS.finished = true;
  1857. window.onscroll = null;
  1858. }
  1859. }
  1860.  
  1861.  
  1862. /**
  1863. * 将canvas转为jpeg,然后导出PDF
  1864. * @param {Array} base64_list canvas元素列表
  1865. * @param {String} title 文档标题
  1866. */
  1867. function saveCanvasesToPDF_MBA(base64_list, title) {
  1868. let width = window.mbaJS.width;
  1869. let height = window.mbaJS.height;
  1870.  
  1871. console.log(`canvas数据:宽: ${width}px,高: ${height}px`);
  1872. // 如果文档第一页的宽比长更大,则landscape,否则portrait
  1873. let orientation = width > height ? 'l' : 'p';
  1874. let pdf = new jspdf.jsPDF(orientation, 'px', [height, width]);
  1875.  
  1876. // 保存每一页文档到每一页pdf
  1877. let i = 0;
  1878. for (let base64 of base64_list) {
  1879. i += 1;
  1880. pdf.addImage(base64, 'JPEG', 0, 0, width, height);
  1881. // 如果当前不是文档最后一页,则需要添加下一个空白页
  1882. if (i < window.mbaJS.max_page) {
  1883. pdf.addPage();
  1884. }
  1885. }
  1886. // 导出文件
  1887. pdf.save(`${title}.pdf`);
  1888. }
  1889.  
  1890. /**
  1891. * 判断文档页是否收集完毕,当不行时给出提示
  1892. * @returns boolean
  1893. */
  1894. function ready2use() {
  1895. removeAds(); // 顺便清理广告
  1896. // 如果是首次点击按钮,给出提示
  1897. if (window.mbaJS.first_hint) {
  1898. let hint = [
  1899. "如果浏览速度过快,比如:",
  1900. "当前页面还没完全加载好就滚动页面去看下一页",
  1901. "那就极有可能导致导出的PDF有空白页或文本有缺漏",
  1902. "由防范技术的干扰,该功能目前很不好用,见谅"
  1903. ].join("\n");
  1904. alert(hint);
  1905. window.mbaJS.first_hint = false;
  1906. }
  1907. // 如果文档页没有收集完,给出提示
  1908. if (!window.mbaJS.finished) {
  1909. let hint = [
  1910. "仍有内容未加载完,无法使用该功能",
  1911. "建议从头到尾慢速地再浏览一遍",
  1912. "以下是没有加载完成页面的页码:",
  1913. getMissedPages().join(",")
  1914. ];
  1915. alert(hint.join("\n"));
  1916. return false;
  1917. }
  1918. return true;
  1919. }
  1920.  
  1921.  
  1922. /**
  1923. * 用捕获好的canvas转jpg,生成PDF
  1924. * @returns
  1925. */
  1926. function canvas2PDF_mba() {
  1927. if (!ready2use()) {
  1928. return;
  1929. }
  1930. let canvases = window.mbaJS.canvases_map.values();
  1931. // 导出PDF
  1932. let title = document.title.split("-")[0].trim();
  1933. saveCanvasesToPDF_MBA(canvases, title);
  1934. }
  1935.  
  1936.  
  1937. /**
  1938. * 拼合捕获好的文本,保存到txt文件
  1939. * @returns
  1940. */
  1941. function saveText_mba() {
  1942. if (!ready2use()) {
  1943. return;
  1944. }
  1945. let content = Array.from(window.mbaJS.texts_map.values());
  1946. let title = document.title.split("-")[0].trim();
  1947. utils.createAndDownloadFile(`${title}.txt`, content.join("\n"));
  1948. }
  1949.  
  1950.  
  1951. /**
  1952. * 移除广告
  1953. */
  1954. function removeAds() {
  1955. document.querySelectorAll(".doc-ad").forEach((ad_elem) => {
  1956. utils.tryToRemoveElement(ad_elem);
  1957. });
  1958. }
  1959.  
  1960.  
  1961. function mbalib_() {
  1962. // 移除广告和左侧工具栏
  1963. removeAds();
  1964. let tool_bar = document.querySelector(".tool-bar");
  1965. utils.tryToRemoveElement(tool_bar);
  1966.  
  1967. // 创建按钮
  1968. utils.createBtns();
  1969. // 隐藏按钮
  1970. utils.toggleBtnStatus("btn_1");
  1971. // 显示按钮
  1972. utils.toggleBtnStatus("btn_2");
  1973. utils.toggleBtnStatus("btn_3");
  1974. utils.toggleBtnStatus("btn_4");
  1975.  
  1976. // 取得页数
  1977. let max_page = parseInt(document.querySelector("#numPages").textContent.replace("/ ", ""));
  1978. let quality = utils.getQualityByCanvasAmount(max_page);
  1979.  
  1980. // 为导出内容提供全局变量,便于动态收集文档页元素的存取
  1981. window.mbaJS = {
  1982. max_page: max_page,
  1983. texts_map: new Map(), // id: text
  1984. canvases_map: new Map(), // id: canvas_data_base64
  1985. quality: quality, // canvas转jpg的质量
  1986. width: null, // canvas宽度(px)
  1987. height: null,
  1988. finished: false, // 是否收集完了全部文档页元素
  1989. first_hint: true,
  1990. scroll_count: 0, // 用于统计累计触发scroll的次数,
  1991. only_text: false // 是否仅捕获文本
  1992. };
  1993. // 跟随浏览,动态收集页面元素
  1994. window.onscroll = () => {
  1995. storeElements_MBA();
  1996. };
  1997. // 跟随浏览,动态收集页面元素
  1998. utils.scrollFunc(storeElements_MBA, window.mbaJS, 20, 50, "mba元素: 收集");
  1999. // 绑定事件
  2000. utils.setBtnEvent(saveText_mba, [], "btn_2", "导出纯文本(不稳定)");
  2001. utils.setBtnEvent(canvas2PDF_mba, [], "btn_3", "导出PDF(不稳定)");
  2002.  
  2003. // 根据页数决定按钮功能:<40页,导出文本+导出pdf,>40页:导出文本
  2004. let btn_text, aim_btn, hint;
  2005. if (max_page > 40) {
  2006. btn_text = "失效说明";
  2007. aim_btn = "btn_3";
  2008. hint = [
  2009. "页数超过40,脚本无效",
  2010. "只能使用导出文本功能",
  2011. "而此脚本会使页面内容加载明显变慢,建议禁用"
  2012. ];
  2013. utils.setBtnEvent(utils.banSelf, [
  2014. () => { window.onscroll = null; }
  2015. ], "btn_4", "临时禁用脚本");
  2016. } else {
  2017. btn_text = "空白页说明";
  2018. aim_btn = "btn_4";
  2019. hint = [
  2020. "导致空白页的原因如下",
  2021. "加载该页的时间超过2秒 / 明显等待",
  2022. "而此脚本会使页面内容加载明显变慢,如果影响严重请禁用"
  2023. ];
  2024. }
  2025.  
  2026. utils.setBtnEvent(() => {
  2027. alert(hint.join("\n"));
  2028. }, [], aim_btn, btn_text);
  2029. }
  2030.  
  2031.  
  2032. function mbalib() {
  2033. setTimeout(mbalib_, 2000);
  2034. }
  2035.  
  2036. // 拼接swf: https://pan.baidu.com/s/1PTM6watxNlqs-jvvX6XKzg?pwd=abe9 中的 JoinSWFFiles_setup.exe
  2037. // swf转pdf: https://youfiles.herokuapp.com/swftopdf/
  2038.  
  2039.  
  2040. /**
  2041. * 统计文档页的数量
  2042. * @param {String} inner_selector 【信息摘要】元素选择器
  2043. * @returns 文档页的数量
  2044. */
  2045. function countPages(inner_selector = ".container div[style=' margin:20px; ']") {
  2046. let abstract = inner_window.document.querySelector(inner_selector).textContent;
  2047. let page_amount_pattern = /文档页数:共 ([1-9]|[1-9][0-9]|[1-9][0-9][0-9]) 页/;
  2048. // 捕获到: ["文档页数:共 xx 页", "xx"]
  2049. let max_page = parseInt(abstract.match(page_amount_pattern)[1]);
  2050. return max_page;
  2051. }
  2052.  
  2053.  
  2054. /**
  2055. * 判断当前帮帮文档是否为高清版页面
  2056. * @returns 是否为高清
  2057. */
  2058. function isHD() {
  2059. // 取得页面左侧【高清版】切换功能区
  2060. let hd_bar = inner_window.document.querySelector("#gaoging");
  2061. // 取得功能区内提示文字
  2062. let hint = hd_bar.querySelector("#gqts").textContent;
  2063. // 如果存在如下文字,认为当前页面是模糊版,否则是高清版
  2064. if (hint.includes("点此阅读高清版")) {
  2065. return false;
  2066. }
  2067. return true;
  2068. }
  2069.  
  2070.  
  2071. /**
  2072. * 开启收集swf链接任务,隐藏按钮1,显示按钮2、3
  2073. */
  2074. function main$1() {
  2075. // 已经是高清版
  2076. // 跟随浏览,动态冻结页面元素
  2077. utils.scrollFunc(storeSWFLinks_Woc88, window.woc88JS, 10, 30, "woc88元素: 收集", inner_window);
  2078.  
  2079. // 绑定按钮的触发函数
  2080. utils.setBtnEvent(btn2SaveSwfUrls, [], "btn_2", "导出swf链接");
  2081. utils.setBtnEvent(() => {
  2082. let hints = [
  2083. "本脚本无需【flash】。",
  2084. "你的电脑上无需安装任何flash player也可以使用。",
  2085. "导出 media-urls.csv 后,",
  2086. "需要使用【资源下载器】来取得swf文档资源。",
  2087. "详细用法请访问脚本主页: ",
  2088. "https://greasyfork.org/zh-CN/scripts/435884-wenku-doc-downloader"
  2089. ];
  2090. alert(hints.join("\n"));
  2091. }, [], "btn_3", "友情提示");
  2092.  
  2093. // 隐藏按钮
  2094. utils.toggleBtnStatus("btn_1");
  2095. // 显示按钮
  2096. utils.toggleBtnStatus("btn_2");
  2097. utils.toggleBtnStatus("btn_3");
  2098. }
  2099.  
  2100.  
  2101. function switch2HD() {
  2102. // 取得页面左侧【高清版】切换功能区
  2103. let hd_bar = inner_window.document.querySelector("#gaoging");
  2104. // 取得功能区内【切换高清版】按钮
  2105. let hd_button = hd_bar.querySelector("#gqts span[onclick]");
  2106. hd_button.click();
  2107. // 开启主任务
  2108. setTimeout(main$1, 1000);
  2109. }
  2110.  
  2111.  
  2112. /**
  2113. * 根据键中的id数字对map排序
  2114. * @param {Map} elems_map
  2115. * @returns sorted_map
  2116. */
  2117. function sortMapByID(elems_map) {
  2118. // id形式:5, 类型: int
  2119. let elems_arr = Array.from(elems_map);
  2120. elems_arr.sort((id1, id2) => {
  2121. // 升序排序
  2122. return id1 - id2;
  2123. });
  2124. // 返回排序好的map
  2125. return new Map(elems_arr);
  2126. }
  2127.  
  2128.  
  2129. /**
  2130. * 存储swf链接。适用于帮帮文库。
  2131. * @param {String} inner_selector iframe#test 框架下的【swf链接所在元素】选择器
  2132. */
  2133. function storeSWFLinks_Woc88(inner_selector = "div[id*=imgcount] > div > object") {
  2134. let elems_map = window.woc88JS.elems_map;
  2135. inner_window.document.querySelectorAll(inner_selector).forEach(
  2136. (elem) => {
  2137. // 复制链接防止丢失
  2138. // 拿到id
  2139. let id_elem = elem.parentElement.parentElement.id;
  2140. let id = parseInt(id_elem.replace("imgcount", ""));
  2141. // 拿到link
  2142. let link = elem.data;
  2143. // 储存id: link
  2144. elems_map.set(id, link);
  2145. });
  2146. if (elems_map.size === window.woc88JS.max_page) {
  2147. // 根据id排序,保证导出的图片链接不是乱序的
  2148. window.woc88JS.elems_map = sortMapByID(window.woc88JS.elems_map);
  2149. // 已经保存完全部文档页元素,移除滚动事件的绑定函数
  2150. window.woc88JS.finished = true;
  2151. inner_window.removeEventListener("scroll", window.woc88JS.scrollFunc, false);
  2152. }
  2153. }
  2154.  
  2155.  
  2156. /**
  2157. * 导出swf链接到csv文件。需要配合【资源下载器】使用。
  2158. */
  2159. function saveSwfUrls_Woc88() {
  2160. let urls = [];
  2161. window.woc88JS.elems_map.forEach((url) => {
  2162. // 遍历map就是遍历value
  2163. urls.push(url);
  2164. });
  2165. utils.createAndDownloadFile("media-urls.csv", urls.join("\n"));
  2166. }
  2167.  
  2168.  
  2169. /**
  2170. * 返回一个列表,包含所有未捕获的页码
  2171. * @returns 未捕获页码列表
  2172. */
  2173. function getNotStored() {
  2174. // 取得全部页码
  2175. let max_page = window.woc88JS.max_page;
  2176. // 取得未捕获页码
  2177. let stored = new Set(window.woc88JS.elems_map.keys());
  2178. let not_stored = [];
  2179. for (let i = 1; i <= max_page; i++) {
  2180. // 如果当前页码未被捕获,则录入not_stored
  2181. if (!stored.has(i)) {
  2182. not_stored.push(i);
  2183. }
  2184. }
  2185. return not_stored;
  2186. }
  2187.  
  2188.  
  2189. function btn2SaveSwfUrls() {
  2190. if (!window.woc88JS.finished) {
  2191. let hints = [
  2192. "仍有内容未加载完,无法使用该功能",
  2193. "请再次浏览未加载出的页面,未加载的页码如下",
  2194. getNotStored().join(",")
  2195. ];
  2196. alert(hints.join("\n"));
  2197. return;
  2198. }
  2199. saveSwfUrls_Woc88();
  2200. }
  2201.  
  2202.  
  2203. /**
  2204. * 帮帮文库下载策略
  2205. */
  2206. function woc88() {
  2207. // 1 初始环境配置
  2208. // 取得iframe的window
  2209. window.inner_window = document.querySelector("#test").contentWindow;
  2210. // 设置全局变量
  2211. window.woc88JS = {
  2212. max_page: countPages(), // 当前文档页面数量
  2213. finished: false, // 是否收集完了全部文档页元素
  2214. scroll_count: 0, // 用于统计累计触发scroll的次数
  2215. elems_map: new Map(), // 存储已经捕获的元素: {id: element}
  2216. scrollFunc: null
  2217. };
  2218.  
  2219. // 2 主任务
  2220. // 创建按钮组
  2221. utils.createBtns();
  2222. // 如果不是高清版,先切换至高清版
  2223. if (!isHD()) {
  2224. utils.setBtnEvent(switch2HD, [], "btn_1", "高清版");
  2225. return;
  2226. }
  2227. main$1();
  2228. }
  2229.  
  2230. /**
  2231. * 判断是否进入预览模式
  2232. * @returns Boolean
  2233. */
  2234. function isInPreview() {
  2235. let p_elem = document.querySelector("#preview_tips");
  2236. if (p_elem.style.display === "none") {
  2237. return true;
  2238. }
  2239. return false;
  2240. }
  2241.  
  2242.  
  2243. /**
  2244. * 判断是否展开了全文
  2245. * @returns Boolean
  2246. */
  2247. function isNoMorePage() {
  2248. let read_more = document.querySelector("#ntip2");
  2249. if (read_more.style.display === "none") {
  2250. return true;
  2251. }
  2252. return false;
  2253. }
  2254.  
  2255.  
  2256. /**
  2257. * 确保进入预览模式
  2258. */
  2259. function ensureInPreview() {
  2260. if (!isInPreview()) {
  2261. // 如果没有进入预览,则先进入
  2262. document.querySelector(".pre_button a").click();
  2263. utils.sleep(500);
  2264. }
  2265. }
  2266.  
  2267.  
  2268. /**
  2269. * 展开全文预览,当展开完成后再次调用时,返回true
  2270. * @returns
  2271. */
  2272. function unfoldAll() {
  2273. ensureInPreview();
  2274. if (isNoMorePage()) {
  2275. // 如果全文展开了,则切换按钮,然后退出
  2276. utils.toggleBtnStatus("btn_1");
  2277. utils.toggleBtnStatus("btn_2");
  2278. return true;
  2279. }
  2280. // 跳转到最后一页,以展开全文
  2281. let cur_page = document.querySelector("#pageNumInput");
  2282. utils.jump2pageNo(cur_page, "999", "keydown");
  2283. }
  2284.  
  2285.  
  2286. /**
  2287. * 取得最大页码(最大20)
  2288. * @returns {Number} 页码int
  2289. */
  2290. function getPageCounts$1() {
  2291. let counts_str = document.querySelector(".counts").textContent;
  2292. let counts = counts_str.match(/[0-9]{1,3}/)[0];
  2293. if (counts > 20) {
  2294. counts = 20; // 最多免费预览20页,所以设置最大页码20
  2295. }
  2296. return parseInt(counts);
  2297. }
  2298.  
  2299.  
  2300. /**
  2301. * 取得全部文档页面的链接,返回urls;如果有页面未加载,则返回null
  2302. * @returns Array | null
  2303. */
  2304. function getImgUrls() {
  2305. let pages = document.querySelectorAll("[id*=pageflash_]");
  2306. // 尚未浏览完全部页面,返回null
  2307. if (pages.length < window.dugenJS.page_counts) {
  2308. return null;
  2309. }
  2310. // 浏览完全部页面,返回urls
  2311. let urls = [];
  2312. pages.forEach((page) => {
  2313. let url = page.querySelector("img").src;
  2314. urls.push(url);
  2315. });
  2316. return urls;
  2317. }
  2318.  
  2319.  
  2320. /**
  2321. * 返回当前未加载页面的页码
  2322. * @returns not_loaded
  2323. */
  2324. function getNotloadedPages() {
  2325. // 已经取得的页码
  2326. let pages = document.querySelectorAll("[id*=pageflash_]");
  2327. let loaded = new Set();
  2328. pages.forEach((page) => {
  2329. let id = page.id.split("_")[1];
  2330. id = parseInt(id);
  2331. loaded.add(id);
  2332. });
  2333. // 未取得的页码
  2334. let not_loaded = [];
  2335. for (let i = 1; i <= window.dugenJS.page_counts; i++) {
  2336. if (!loaded.has(i)) {
  2337. not_loaded.push(i);
  2338. }
  2339. }
  2340. return not_loaded;
  2341. }
  2342.  
  2343.  
  2344. function WantImgUrls() {
  2345. let res = getImgUrls();
  2346. // 页面尚未加载完
  2347. if (res === null) {
  2348. let hints = [
  2349. "尚未加载完全部页面",
  2350. "以下页面需要浏览并加载:",
  2351. getNotloadedPages().join(",")
  2352. ];
  2353. alert(hints.join("\n"));
  2354. return;
  2355. }
  2356. // 页面全部加载完
  2357. utils.createAndDownloadFile("urls.csv", res.join("\n"));
  2358. }
  2359.  
  2360.  
  2361. /**
  2362. * dugen文档下载策略
  2363. */
  2364. function dugen() {
  2365. ensureInPreview();
  2366. // 全局对象
  2367. window.dugenJS = {
  2368. page_counts: getPageCounts$1() // 最大页码(int)
  2369. };
  2370.  
  2371. // 创建按钮区
  2372. utils.createBtns();
  2373.  
  2374. // 绑定监听器
  2375. // 按钮1:展开文档
  2376. utils.setBtnEvent(unfoldAll, [], "btn_1");
  2377. // 按钮2:导出图片链接
  2378. utils.setBtnEvent(WantImgUrls, [], "btn_2", "导出图片链接");
  2379. }
  2380.  
  2381. /**
  2382. * 取得文档类型
  2383. * @returns {String} 文档类型str
  2384. */
  2385. function getDocType() {
  2386. let type_elem = document.querySelector(".title .icon.icon-format");
  2387. // ["icon", "icon-format", "icon-format-doc"]
  2388. let cls_str = type_elem.classList[2];
  2389. // "icon-format-doc"
  2390. let type = cls_str.split("-")[2];
  2391. return type;
  2392. }
  2393.  
  2394.  
  2395. /**
  2396. * 判断文档类型是否为type_list其中之一
  2397. * @returns 是否为type
  2398. */
  2399. function isTypeof(type_list) {
  2400. let type = getDocType();
  2401. if (type_list.includes(type)) {
  2402. return true;
  2403. }
  2404. return false;
  2405. }
  2406.  
  2407.  
  2408. /**
  2409. * 判断文档类型是否为PPT
  2410. * @returns 是否为PPT
  2411. */
  2412. function isPPT() {
  2413. return isTypeof(["ppt", "pptx"]);
  2414. }
  2415.  
  2416.  
  2417. /**
  2418. * 判断文档类型是否为Excel
  2419. * @returns 是否为Excel
  2420. */
  2421. function isEXCEL() {
  2422. return isTypeof(["xls", "xlsm", "xlsx"]);
  2423. }
  2424.  
  2425.  
  2426. /**
  2427. * 取得最大页码
  2428. * @returns 最大页码int
  2429. */
  2430. function getPageCounts() {
  2431. let page_counts_str = document.querySelector(".intro-list").children[3].textContent;
  2432. let page_counts = parseInt(page_counts_str.match(/[0-9]{1,3}(?=页)/)[0]);
  2433. return page_counts;
  2434. }
  2435.  
  2436.  
  2437. /**
  2438. * 取得未加载页面的页码
  2439. * @param {Set} loaded 已加载的页码集合
  2440. * @returns {Array} not_loaded 未加载页码列表
  2441. */
  2442. function getNotLoaded(loaded) {
  2443. let not_loaded = [];
  2444. let page_counts = window.book118JS.page_counts;
  2445. for (let i = 1; i <= page_counts; i++) {
  2446. if (!loaded.has(i)) {
  2447. not_loaded.push(i);
  2448. }
  2449. }
  2450. return not_loaded;
  2451. }
  2452.  
  2453.  
  2454. /**
  2455. * 取得全部文档页的url
  2456. * @returns [<是否全部加载>, <未加载页码列表>|<urls列表>]
  2457. */
  2458. function getUrls() {
  2459. let loaded = new Set(); // 存储已加载页面的页码
  2460. let urls = []; // 存储已加载页面的图形src
  2461. // 收集已加载页面的url
  2462. document.querySelectorAll("div[data-id]").forEach((div) => {
  2463. let src = div.querySelector("img").src;
  2464. if (src) {
  2465. // "1": "https://view-cache.book118.com/..."
  2466. loaded.add(parseInt(div.getAttribute("data-id")));
  2467. urls.push(src);
  2468. }
  2469. });
  2470. // 如果所有页面加载完毕
  2471. if (loaded.size === window.book118JS.page_counts) {
  2472. return [true, urls];
  2473. }
  2474. // 否则收集未加载页面的url
  2475. return [false, getNotLoaded(loaded)];
  2476. }
  2477.  
  2478.  
  2479. /**
  2480. * 展开全文
  2481. */
  2482. function readAll() {
  2483. window.preview.jump(999);
  2484. }
  2485.  
  2486.  
  2487. /**
  2488. * btn_2: 导出图片链接
  2489. */
  2490. function wantUrls() {
  2491. let [flag, res] = getUrls();
  2492. // 页面都加载完毕,下载urls
  2493. if (flag) {
  2494. utils.createAndDownloadFile("urls.csv", res.join("\n"));
  2495. return;
  2496. }
  2497. // 没有加载完,提示出未加载好的页码
  2498. let hints = [
  2499. "仍有页面没有加载",
  2500. "请浏览并加载如下页面:",
  2501. res.join(",")
  2502. ];
  2503. alert(hints.join("\n"));
  2504. }
  2505.  
  2506.  
  2507. /**
  2508. * 打开PPT预览页面
  2509. */
  2510. function openPPTpage() {
  2511. window.preview.getSrc();
  2512. let openPPT = () => {
  2513. let ppt_src = document.querySelector("iframe.preview-iframe").src;
  2514. utils.openInNewTab(ppt_src);
  2515. window.preview.close();
  2516. };
  2517. setTimeout(openPPT, 1000);
  2518. }
  2519.  
  2520.  
  2521. /**
  2522. * 原创力文档(非PPT或Excel)下载策略
  2523. */
  2524. function book118_CommonDoc() {
  2525. // 创建全局对象
  2526. window.book118JS = {
  2527. doc_type: getDocType(),
  2528. page_counts: getPageCounts()
  2529. };
  2530.  
  2531. // 处理非PPT文档
  2532. // 创建按钮组
  2533. utils.createBtns();
  2534. // 绑定监听器到按钮
  2535. // 按钮1:展开文档
  2536. utils.setBtnEvent(() => {
  2537. readAll();
  2538. utils.toggleBtnStatus("btn_1");
  2539. utils.toggleBtnStatus("btn_2");
  2540. }, [], "btn_1");
  2541. // 按钮2:导出图片链接
  2542. utils.setBtnEvent(wantUrls, [], "btn_2", "导出图片链接");
  2543. }
  2544.  
  2545.  
  2546. /**
  2547. * 取得PPT文档最大页码
  2548. * @returns PPT文档最大页码int
  2549. */
  2550. function getPageCountsPPT() {
  2551. let counts_str = document.querySelector("#PageCount").textContent;
  2552. let counts = parseInt(counts_str);
  2553. console.log(`get page counts: ${counts}`);
  2554. return counts;
  2555. }
  2556.  
  2557.  
  2558. /**
  2559. * 转换当前视图为canvas,添加到book118JS.canvases中。在递归终止时显示btn_2。
  2560. * @param {Number} max 转换执行次数,或者说,页面数量
  2561. */
  2562. function docView2Canvas(max, i = 0) {
  2563. // 取得页码
  2564. let cur_page = document.querySelector("#PageIndex").textContent;
  2565. cur_page = parseInt(cur_page);
  2566. // 取得视图元素,计数从0开始
  2567. let doc_view = document.querySelector(`#view${cur_page-1}`);
  2568. // 转化为canvas
  2569. let canvas_promise = html2canvas(doc_view);
  2570. console.log(canvas_promise); // 打印信息以检查状况
  2571. canvas_promise.then((canvas) => {
  2572. // 保存canvas到全局对象
  2573. window.book118JS.canvases.push(canvas);
  2574. // 打印日志
  2575. i += 1;
  2576. console.log(`wk: ${cur_page} complete`);
  2577.  
  2578. // 下一页
  2579. document.querySelector(".btmRight").click();
  2580. utils.sleep(500); // 等待页面加载
  2581. // 如果递归未达到最大次数
  2582. if (i < max) {
  2583. // 则下一次递归
  2584. docView2Canvas(max, i);
  2585. } else {
  2586. // 否则终止递归,并且显示导出PDF按钮
  2587. utils.toggleBtnStatus("btn_2");
  2588. }
  2589. });
  2590. }
  2591.  
  2592.  
  2593. /**
  2594. * 将捕获的canvases合并并导出为pdf
  2595. * @returns
  2596. */
  2597. function canvases2pdf() {
  2598. // 已经捕获的页面数量
  2599. let stored_amount = window.book118JS.canvases.length;
  2600. // 总页面数量
  2601. let page_counts = window.book118JS.page_counts;
  2602. // 校验数量
  2603. let diff = page_counts - stored_amount;
  2604. if (diff > 0) {
  2605. alert(`缺失了 ${diff} 页,可以过一会再点击该按钮试试。`);
  2606. if (!confirm("是否仍要导出PDF?")) {
  2607. // 不坚持导出PDF的情况
  2608. return;
  2609. }
  2610. }
  2611. // 导出PDF
  2612. let canvases = window.book118JS.canvases;
  2613. // 取得宽高
  2614. let model = canvases[0];
  2615. let width = model.width;
  2616. let height = model.height;
  2617. // 取得标题然后导出pdf
  2618. utils.saveCanvasesToPDF(canvases, "原创力PPT文档", width, height);
  2619. }
  2620.  
  2621.  
  2622. /**
  2623. * 原创力文档(PPT)下载策略
  2624. */
  2625. function book118_PPT() {
  2626. // 创建全局对象
  2627. window.book118JS = {
  2628. page_counts: getPageCountsPPT(),
  2629. canvases: [] // 存储每页文档转化的canvas
  2630. };
  2631.  
  2632. // 创建按钮区
  2633. utils.createBtns();
  2634. // 绑定监听器到按钮1
  2635. utils.setBtnEvent(() => {
  2636. let hints = [
  2637. "正在为文档“截图”,请耐心等待过程完成,不要操作",
  2638. "“截图”会有额外一层黑边,原因未知,暂无法处理,烦请谅解"
  2639. ];
  2640. alert(hints.join("\n"));
  2641. // 隐藏按钮1
  2642. utils.toggleBtnStatus("btn_1");
  2643. // 开始捕获页面
  2644. docView2Canvas(window.book118JS.page_counts);
  2645. }, [], "btn_1", "捕获页面");
  2646. // 为按钮2绑定监听器
  2647. utils.setBtnEvent(canvases2pdf, [], "btn_2", "导出PDF");
  2648. }
  2649.  
  2650.  
  2651. /**
  2652. * 取得当前页面的excel,返回csv string
  2653. * @returns {String} csv
  2654. */
  2655. function excel2CSV() {
  2656. let table = [];
  2657. let rows = document.querySelectorAll("tr[id]");
  2658.  
  2659. // 遍历行
  2660. for (let row of rows) {
  2661. let csv_row = [];
  2662. // 遍历列(单元格)
  2663. for (let cell of row.querySelectorAll("td[class*=fi], td.tdrl")) {
  2664. // 判断单元格是否存储图片
  2665. let img = cell.querySelector("img");
  2666. if (img) {
  2667. // 如果是图片,保存图片链接
  2668. csv_row.push(img.src);
  2669. } else {
  2670. // 否则保存单元格文本
  2671. csv_row.push(cell.textContent);
  2672. }
  2673. }
  2674. table.push(csv_row.join(","));
  2675. }
  2676.  
  2677. let csv = table.join("\n");
  2678. csv = csv.replace(/\n{2,}/g, "\n");
  2679. return csv;
  2680. }
  2681.  
  2682.  
  2683. /**
  2684. * 下载当前表格内容,保存为csv(utf-8编码)
  2685. */
  2686. function wantEXCEL() {
  2687. let file_name = "原创力表格_UTF-8.csv";
  2688. utils.createAndDownloadFile(file_name, excel2CSV());
  2689. }
  2690.  
  2691.  
  2692. /**
  2693. * 在Excel预览页面给出操作提示
  2694. */
  2695. function help() {
  2696. let hints = [
  2697. "【导出表格到CSV】只能导出当前sheet,",
  2698. "如果有多张sheet请在每个sheet上用按钮分别导出CSV。",
  2699. "CSV是一种简单的表格格式,可以被Excel打开,",
  2700. "并转为 xls 或 xlsx 格式存储,",
  2701. "但CSV本身不能存储图片,所以用图片链接代替,请自行下载图片",
  2702. "",
  2703. "本功能导出的CSV文件无法直接用Excel打开,因为中文会乱码。",
  2704. "有两个办法:",
  2705. "1. 打开Excel,选择【数据】,选择【从文本/CSV】,",
  2706. " 选择文件,【文件原始格式】选择【65001: Unicode(UTF-8)】,选择【加载】。",
  2707. "2. 用【记事本】打开CSV文件,【文件】->【另存为】->",
  2708. " 【编码】选择【ANSI】->【保存】。现在可以用Excel直接打开它了。"
  2709. ];
  2710. alert(hints.join("\n"));
  2711. }
  2712.  
  2713.  
  2714. /**
  2715. * 原创力文档(EXCEL)下载策略
  2716. */
  2717. function book118_EXCEL() {
  2718. // 创建按钮区
  2719. utils.createBtns();
  2720. // 绑定监听器到按钮
  2721. utils.setBtnEvent(wantEXCEL, [], "btn_1", "导出表格到CSV");
  2722. utils.setBtnEvent(help, [], "btn_2", "使用说明");
  2723. // 显示按钮
  2724. utils.toggleBtnStatus("btn_2");
  2725. }
  2726.  
  2727.  
  2728. /**
  2729. * 打开Excel预览页面
  2730. */
  2731. function openEXCELpage() {
  2732. openPPTpage();
  2733. }
  2734.  
  2735.  
  2736. /**
  2737. * 原创力文档下载策略
  2738. */
  2739. function book118() {
  2740. let host = window.location.hostname;
  2741. if (host === 'max.book118.com') {
  2742. if (isEXCEL()) {
  2743. utils.createBtns();
  2744. utils.setBtnEvent(openEXCELpage, [], "btn_1", "导出EXCEL");
  2745. } else if (isPPT()) {
  2746. utils.createBtns();
  2747. utils.setBtnEvent(openPPTpage, [], "btn_1", "导出PPT");
  2748. } else {
  2749. book118_CommonDoc();
  2750. }
  2751. } else if (host === "view-cache.book118.com") {
  2752. book118_PPT();
  2753. } else if (host.match(/view[0-9]{1,3}.book118.com/)) {
  2754. book118_EXCEL();
  2755. } else {
  2756. console.log(`wk: Unknown host: ${host}`);
  2757. }
  2758. }
  2759.  
  2760. /**
  2761. * 设置图像质量为100
  2762. */
  2763. function HD() {
  2764. window.img_quality = 1;
  2765. console.log("图像质量已经设置为100%");
  2766. console.log("如果刷新页面或跳转页面则需要再次使用该命令");
  2767. }
  2768.  
  2769. /**
  2770. * 主函数:识别网站,执行对应文档下载策略
  2771. */
  2772. function main() {
  2773. // 显示当前位置
  2774. let host = window.location.host;
  2775. console.log(`当前host: ${host}`);
  2776. // 挂载工具包到全局
  2777. window.user_utils = utils;
  2778. console.log("wk: user_utils已经挂载到全局");
  2779.  
  2780. // 附加任务
  2781. utils.globalFunc(HD); // 全局设置高清图片的函数
  2782.  
  2783. // 主任务
  2784. if (host.includes("docin.com")) {
  2785. docin();
  2786. } else if (host === "ishare.iask.sina.com.cn") {
  2787. ishare();
  2788. } else if (host === "www.deliwenku.com") {
  2789. deliwenku();
  2790. } else if (host === "www.doc88.com") {
  2791. doc88();
  2792. } else if (host === "www.360doc.com") {
  2793. doc360();
  2794. } else if (host === "wenku.baidu.com") {
  2795. baiduWenku();
  2796. } else if (host === "doc.mbalib.com") {
  2797. mbalib();
  2798. } else if (host === "www.woc88.com") {
  2799. woc88();
  2800. } else if (host === "www.dugen.com") {
  2801. dugen();
  2802. } else if (host.includes("book118.com")) {
  2803. book118();
  2804. } else {
  2805. console.log("匹配到了无效网页");
  2806. }
  2807. }
  2808.  
  2809. let options = {
  2810. fast_mode: false,
  2811. activation_test: false
  2812. };
  2813. if (options.cli_mode) {
  2814. (() => {
  2815. loadExternalScripts();
  2816. setTimeout(main, 2000);
  2817. return;
  2818. })();
  2819. }
  2820. if (options.activation_test) {
  2821. alert(`Wenku Doc Downloader 已经生效!\n当前网址:\n${window.location.host}`);
  2822. }
  2823. if (options.fast_mode) {
  2824. main();
  2825. } else {
  2826. window.onload = main;
  2827. }
  2828.  
  2829. })();