您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
文泉书局
当前为
- // ==UserScript==
- // @name WQ
- // @namespace http://tampermonkey.net/
- // @homepage https://github.com/systemmin/kill-doc
- // @version 1.0.2
- // @description 文泉书局
- // @author Mr.Fang
- // @match https://*.wqxuetang.com/deep/read/pdf*
- // @require https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/jspdf/2.4.0/jspdf.umd.min.js
- // @require https://unpkg.com/@zip.js/zip.js@2.7.34/dist/zip.min.js
- // @icon https://dtking.cn/favicon.ico
- // @run-at document-idle
- // @grant GM_getValue
- // @grant GM_deleteValue
- // @grant GM_setValue
- // @grant GM_download
- // @grant GM_notification
- // @grant unsafeWindow
- // @license Apache-2.0
- // ==/UserScript==
- (function() {
- 'use strict';
- let MF =
- '#MF_fixed{position:fixed;top:50%;transform:translateY(-50%);right:20px;gap:20px;flex-direction:column;z-index:2147483647;display:flex}';
- MF +=
- '.MF_box{padding:10px;cursor:pointer;border-color:rgb(0,102,255);border-radius:5px;background-color:white;color:rgb(0,102,255);margin-right:10px;box-shadow:rgb(207,207,207) 1px 1px 9px 3px}.MF_active{color: green}#MF_size,#MF_speed{color: red;}';
- MF +=
- '@media print{html{height:auto !important}body{display:block !important}#app-left{display:none !important}#app-right{display:none !important}#MF_fixed{display:none !important}.menubar{display:none !important}.top-bar-right{display:none !important}.user-guide{display:none !important}#app-reader-editor-below{display:none !important}.no-full-screen{display:none !important}.comp-vip-pop{display:none !important}.center-wrapper{width:auto !important}.reader-thumb,.related-doc-list,.fold-page-content,.try-end-fold-page,.lazy-load,#MF_textarea,#nav-menu-wrap{display:none !important}}'
- const prefix = "MF_";
- // canvas 禁止重写 drawImage
- const canvasRenderingContext2DPrototype = CanvasRenderingContext2D.prototype;
- const originalDrawImage = canvasRenderingContext2DPrototype.drawImage;
- Object.defineProperty(canvasRenderingContext2DPrototype, 'drawImage', {
- value: originalDrawImage,
- writable: false,
- configurable: false
- });
- class Box {
- id = ""; // id
- label = ""; // 按钮文本
- fun = ""; // 执行方法
- constructor(id, label, fun) {
- this.id = id;
- this.label = label;
- this.fun = fun;
- }
- }
- class Utility {
- debug = true;
- /**
- * 添加 css 样式
- * @param e 节点
- * @param data JSON 格式样式
- */
- style(e, data) {
- Object.keys(data).forEach(key => {
- e.style[key] = data[key]
- })
- }
- attr(e, key, val) {
- if (!val) {
- return e.getAttribute(key);
- } else {
- e.setAttribute(key, val);
- }
- }
- /**
- * 追加样式
- * @param css 格式样式
- */
- appendStyle(css) {
- let style = this.createEl('', 'style');
- style.textContent = css;
- style.type = 'text/css';
- let dom = document.head || document.documentElement;
- dom.appendChild(style);
- }
- /**
- * @description 创建 dom
- * @param id 必填
- * @param elType
- * @param data
- */
- createEl(id, elType, data) {
- const el = document.createElement(elType);
- el.id = id || '';
- if (data) {
- this.style(el, data);
- }
- return el;
- }
- query(el) {
- return document.querySelector(el);
- }
- queryAll(el) {
- return document.querySelectorAll(el);
- }
- update(el, text) {
- const elNode = this.query(el);
- if (!elNode) {
- console.log('节点不存在');
- } else {
- elNode.innerHTML = text;
- }
- }
- /**
- * 进度
- * @param current 当前数量 -1预览结束
- * @param total 总数量
- * @param content 内容
- */
- preview(current, total, content) {
- return new Promise(async (resolve, reject) => {
- if (current === -1) {
- this.update('#' + prefix + 'text', content ? content : "已完成");
- } else {
- let p = (current / total) * 100;
- let ps = p.toFixed(0) > 100 ? 100 : p.toFixed(0);
- console.log('当前进度', ps)
- this.update('#' + prefix + 'text', '进度' + ps + '%');
- await this.sleep(500);
- resolve();
- }
- })
- }
- preText(content) {
- this.update('#' + prefix + 'text', content);
- }
- gui(boxs) {
- const box = this.createEl(prefix + "fixed", 'div');
- for (let x in boxs) {
- let item = boxs[x];
- if (!item.id) continue;
- let el = this.createEl(prefix + item.id, 'button');
- el.append(new Text(item.label));
- if (x === '0') {
- el.classList = prefix + 'box ' + prefix + "active";
- } else {
- el.className = prefix + "box";
- }
- if (item.fun) {
- el.onclick = function() {
- eval(item.fun);
- }
- }
- if (item.id === 'speed') {
- this.attr(el, 'contenteditable', true)
- }
- if (item.id === 'size') {
- this.attr(el, 'contenteditable', true)
- }
- box.append(el);
- }
- document.body.append(box);
- }
- sleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
- log(msg) {
- if (this.debug) {
- console.log(msg);
- }
- }
- logt(msg) {
- if (this.debug) {
- console.table(msg);
- }
- }
- }
- const u = new Utility();
- u.appendStyle(MF);
- const btns = [
- new Box('text', '状态 0 %'),
- new Box('speed', '1'),
- new Box('size', '100'),
- new Box('startHandle', '开始执行', 'startHandle()'),
- new Box('clearHandle', '结束执行', 'clearHandle()'),
- new Box('start', '继续预览', 'autoPreview()'),
- new Box('stop', '停止预览', 'stopPreview()'),
- new Box('pdf', '下载PDF', 'executeDownload(1)')
- ]
- const domain = {
- wqxuetang: 'wqxuetang.com'
- };
- const {
- host,
- href,
- origin
- } = window.location;
- const jsPDF = jspdf.jsPDF;
- let zipWriter; // 声明全局变量
- zipWriter = new zip.ZipWriter(new zip.BlobWriter("application/zip"), {
- bufferedWrite: true,
- useCompressionStream: false
- });
- const doc = new jsPDF({
- orientation: 'p',
- unit: 'px',
- compress: true
- });
- // 794 x 1123 px
- let pdf_w = 446,
- pdf_h = 631,
- loading = 500, // 毫秒
- pdf_ratio = 0.56,
- title = document.title,
- fileType = '',
- downType = 1, // 下载文件类型
- select = null,
- selectBox = null,
- dom = null,
- beforeFun = null,
- interval = null,
- BASE_URL = 'https://wkretype.bdimg.com/retype',
- readerInfoBai = null, // 百度文档参数
- intervalBai = null; // 百度定时任务
- if (host.includes(domain.taodocs)) {
- iscopy = 'TRUE'; // taodocs copy flag
- }
- let size = 0; // 页面容量
- let count = 0; // 计数
- let times = 0; // 计次
- const params = new URLSearchParams(document.location.search.substring(1));
- if (params.size && params.get('custom')) {
- window.parent.postMessage({
- type: "onload",
- value: 'success'
- }, "*")
- u.log('子页面加载完成!');
- }
- // 监听页面卸载,移除百度定时删除广告等 DOM 定时器
- window.onunload = function() {
- if (intervalBai) {
- clearInterval(intervalBai);
- intervalBai = null;
- }
- }
- /**
- * @description 前置方法
- * @author Mr.Fang
- * @time 2024年2月2日
- */
- const before = () => {
- if (beforeFun) {
- u.log('---------->beforeFun');
- eval(beforeFun)
- }
- }
- /**
- * @description 初始化方法
- * @author Mr.Fang
- * @time 2024年2月2日
- */
- const init = () => {
- console.table({
- host,
- href,
- origin
- })
- dom = document.documentElement || document.body;
- if (host.includes(domain.wqxuetang)) {
- fileType = "pdf";
- select = "#pagebox .page-lmg";
- dom = u.query('#scroll');
- btns.splice(1, 0, );
- }
- u.gui(btns);
- console.log('文件名称:', title);
- console.log('文件类型:', fileType);
- }
- // load 事件
- document.onreadystatechange = function() {
- if (document.readyState === "complete") {
- console.log('readyState:', document.readyState);
- // 在这里执行渲染完成后的操作
- console.log('HTML 渲染完成!');
- init()
- const start = GM_getValue('start');
- times = Number(GM_getValue('times')) || 0;
- size = Number(GM_getValue('size')) || 0;
- if (start) {
- console.log('自动开始')
- setTimeout(() => {
- autoPreview();
- console.log('1 ms')
- }, 1000)
- }
- loginfo()
- }
- };
- const startHandle = () => {
- // 重新设置页面容量参数
- if (GM_getValue('size')) {
- size = Number(GM_getValue('size'));
- } else {
- let MF_size = Number(u.query('#MF_size').innerText);
- if (MF_size > 0) {
- size = MF_size
- GM_setValue('size', size)
- } else {
- u.update('#MF_size', size)
- GM_setValue('size', size)
- }
- }
- // 重新设置页码参数
- let MF_page = Number(u.query('#MF_speed').innerText) - 1;
- if (MF_page > 0) {
- GM_setValue('page', MF_page)
- localStorage.setItem('WQ_index', MF_page)
- }
- GM_setValue('start', 1);
- console.log('startHandle')
- autoPreview();
- }
- const clearHandle = () => {
- console.log('clearHandle')
- stopPreview();
- localStorage.removeItem('start')
- localStorage.removeItem('WQ_index')
- GM_deleteValue('page')
- GM_deleteValue('start')
- GM_deleteValue('size')
- GM_deleteValue('times')
- }
- const loginfo = () => {
- console.log('start', localStorage.getItem('start'))
- console.log('WQ_index', localStorage.getItem('WQ_index'))
- console.log('GM_page', GM_getValue('page'))
- console.log('GM_start', GM_getValue('start'))
- console.log('size', size)
- console.log('count', count)
- console.log('times', times)
- }
- /**
- * @description 开始方法,自动预览
- * @author Mr.Fang
- * @time 2024年2月2日
- */
- const autoPreview = async () => {
- localStorage.setItem('start', '1');
- if (GM_getValue('page')) {
- localStorage.setItem('WQ_index', GM_getValue('page'))
- } else {
- let pages = u.query('.page-head-tol').innerText.split('/');
- let index = Number(pages[0]) - 1 || 0;
- localStorage.setItem('WQ_index', index)
- }
- await scrollWQxuetang()
- return false;
- }
- /**
- * @description 结束方法,停止预览
- * @author Mr.Fang
- * @time 2024年2月2日
- */
- const stopPreview = async () => {
- console.log('---------->stopPreview');
- if (interval) {
- clearInterval(interval);
- interval = null;
- }
- localStorage.removeItem('start')
- }
- /**
- * @description 执行文件下载
- * @author Mr.Fang
- * @time 2024年2月20日
- * @param type 文件类型
- */
- const executeDownload = async (type) => {
- downType = type;
- const down = localStorage.getItem('down');
- console.log('down', down)
- console.log('down', host)
- if (!down) {
- if (host.includes(domain.wqxuetang)) {
- title = u.query('.read-header-title').innerText;
- conditionDownload();
- }
- } else {
- conditionDownload();
- }
- }
- /**
- * 根据指定条件下载文件
- */
- const conditionDownload = () => {
- if (downType === 1) {
- downpdf()
- localStorage.setItem('down', '1')
- } else if (downType === 2) {
- downzip()
- }
- u.preText('下载完成')
- }
- /**
- * 判断 dom 是否在可视范围内
- */
- const isElementInViewport = (el) => {
- const rect = el.getBoundingClientRect();
- return (
- rect.top >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight)
- );
- }
- // wq 保存图片
- const saveWQImage = async (els, i) => {
- let canvas = await MF_ImageJoinToBlob(els);
- doc.addPage();
- doc.addImage(canvas, 'JPEG', 0, 0, pdf_w, pdf_h, i, 'FAST')
- if (doc.internal.pages[1].length === 2) {
- doc.deletePage(1); // 删除空白页
- }
- count++;
- localStorage.setItem('WQ_index', i + 1);
- GM_setValue('page', i + 1)
- // 更新dom
- u.update('#MF_size', size)
- u.update('#MF_speed', i + 1)
- // 处理分页
- if (size === count && count != 0) {
- let res = await downpdf();
- console.log(res);
- GM_setValue('times', times + 1);
- await u.sleep(500);
- console.log('重载');
- window.location.reload()
- }
- }
- /**
- * wq 边预览边下载
- */
- const scrollWQxuetang = async () => {
- if (!localStorage.getItem("start")) {
- u.preview(-1, null, "已终止");
- return;
- }
- if (u.query('.reload_image')) {
- console.log('重新加载')
- u.query('.reload_image').click();
- }
- // 判断图片是否加载完成
- function isImageLoaded(img) {
- return img.complete && img.naturalWidth > 0 && img.naturalHeight > 0;
- }
- function isAllLoaded(childrens) {
- if (!childrens.length) {
- return false;
- }
- for (let i = 0; i < childrens.length; i++) {
- if (!isImageLoaded(childrens[i])) {
- return false;
- }
- }
- return true;
- }
- let i = Number(localStorage.getItem('WQ_index')) || 0;
- let children = u.queryAll(select)
- let pages = u.query('.page-head-tol').innerText.split('/');
- let index = Number(pages[1]);
- if (i === index) {
- console.log('执行结束');
- u.preview(-1);
- clearHandle()
- if (size !== count && count != 0) {
- let res = await downpdf();
- console.log(res);
- }
- return;
- }
- let current = children[i];
- if (isAllLoaded(current.children)) {
- await saveWQImage(current, i)
- // 滚动到下一个范围
- if (i !== children.length - 1) {
- children[i + 1].scrollIntoView({
- behavior: "smooth"
- });
- }
- } else {
- children[i].scrollIntoView({
- behavior: "smooth"
- });
- }
- u.preview(i, children.length);
- if (i !== children.length) {
- setTimeout(() => {
- console.log(loading, 'ms 后执行');
- scrollWQxuetang()
- }, loading)
- }
- }
- /**
- * @description 下载压缩包,包含图片
- * @author Mr.Fang
- * @time 2024年2月2日
- */
- const downzip = () => {
- zipWriter.close().then(blob => {
- GM_download(URL.createObjectURL(blob), `${title}.zip`);
- URL.revokeObjectURL(blob);
- // 在关闭旧的 ZipWriter 后,创建新的 ZipWriter
- zipWriter = new zip.ZipWriter(new zip.BlobWriter("application/zip"), {
- bufferedWrite: true,
- useCompressionStream: false
- });
- }).catch(error => {
- console.error(error);
- });
- }
- /**
- * @description 下载 PDF
- * @author Mr.Fang
- * @time 2024年2月2日
- */
- const downpdf = async () => {
- title = u.query('.read-header-title').innerText;
- // 下载 PDF 文件
- return doc.save(`${title}_${times}.pdf`, {
- returnPromise: true
- });
- }
- // document.querySelector('.reload_image')
- // const event = new EventTarget()
- // event.dispatchEvent(document.querySelector("#pageImgBox1 > div.page-m-mark"))
- // event.onclick()
- /**
- * @description 图片拼接转 blob
- * @author Mr.Fang
- * @time 2024年6月5日
- * @param el 节点对象
- * @returns {Promise<blob>}
- */
- const MF_ImageJoinToBlob = (el) => {
- return new Promise((resolve, reject) => {
- const children = el.children;
- const {
- naturalWidth,
- naturalHeight
- } = children[0];
- // 1、创建画布
- let canvas = u.createEl('', 'canvas');
- canvas.width = naturalWidth * 6;
- canvas.height = naturalHeight;
- const ctx = canvas.getContext('2d');
- // 2、获取所有图片节点
- const listData = []
- for (var i = 0; i < children.length; i++) {
- const img = children[i];
- const left = img.style.left.replace('px', '')
- listData.push({
- index: i,
- left: Number(left)
- })
- }
- listData.sort((a, b) => a.left - b.left);
- // 3、遍历绘制画布
- for (var i = 0; i < listData.length; i++) {
- const img = children[listData[i].index];
- ctx.drawImage(img, i * naturalWidth, 0, naturalWidth, naturalHeight);
- }
- resolve(canvas)
- })
- }
- /**
- * @description 将 blob 对象转 uint8Array
- * @author Mr.Fang
- * @time 2024年5月27日
- * @param {Object} blob 图片对象
- * @returns {Promise<Uint8Array>}
- */
- const MF_BlobToUint8Array = (blob) => {
- return new Promise((resolve, reject) => {
- const fileReader = new FileReader();
- fileReader.onload = function() {
- resolve(new Uint8Array(this.result));
- };
- fileReader.onerror = function(error) {
- reject(error);
- };
- fileReader.readAsArrayBuffer(blob);
- });
- }
- /**
- * @description 画布输出 blob 对象
- * @author Mr.Fang
- * @time 2024年1月20日18:05:49
- * @param src 图片地址
- * @returns {Promise<Object>}
- */
- const MF_CanvasToBase64 = (canvas) => {
- return new Promise((resolve, reject) => {
- const {
- width,
- height
- } = canvas;
- canvas.toBlob(
- (blob) => {
- resolve({
- blob,
- width,
- height
- });
- },
- "image/png",
- 1,
- );
- })
- }
- })();