Wenku Doc Downloader

下载文档,导出PDF或图片压缩包。支持①百度文库②豆丁网③爱问共享资料(新浪文档)④得力文库⑤道客巴巴⑥360doc个人图书馆,文档导出pdf。在文档页面左侧中间有Wenku Doc Download按钮区,说明脚本生效了。

Ekde 2022/01/08. Vidu La ĝisdata versio.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Wenku Doc Downloader
// @namespace    http://tampermonkey.net/
// @version      1.4.4
// @description  下载文档,导出PDF或图片压缩包。支持①百度文库②豆丁网③爱问共享资料(新浪文档)④得力文库⑤道客巴巴⑥360doc个人图书馆,文档导出pdf。在文档页面左侧中间有Wenku Doc Download按钮区,说明脚本生效了。
// @author       [email protected]
// @match        *://*.docin.com/p-*
// @match        *://ishare.iask.sina.com.cn/f/*
// @match        *://www.deliwenku.com/p-*
// @match        *://www.doc88.com/p-*
// @match        *://www.360doc.com/content/*
// @match        *://wenku.baidu.com/*/*
// @require      https://cdn.bootcdn.net/ajax/libs/FileSaver.js/2.0.5/FileSaver.js
// @require      https://cdn.bootcdn.net/ajax/libs/jszip/3.6.0/jszip.js
// @require      https://cdn.bootcdn.net/ajax/libs/jspdf/2.3.1/jspdf.umd.min.js
// @require      https://cdn.bootcdn.net/ajax/libs/html2canvas/0.5.0-beta4/html2canvas.min.js
// @icon         https://www.google.com/s2/favicons?domain=limestart.cn
// @grant        none
// @license      GPL-3.0-only
// @create       2021-11-22
// ==/UserScript==

"use strict";

// 用户选项
let options = {
    fast_mode: false,
    activation_test: false
};

// 导入jsPDF
const jsPDF = jspdf.jsPDF;

/*
 *  附属功能函数部分
 */

/**
 * 创建并下载文件
 * @param {string} fileName 
 * @param {string} content 
 */
function createAndDownloadFile(fileName, content) {
    let aTag = document.createElement('a');
    let blob = new Blob([content]);
    aTag.download = fileName;
    aTag.href = URL.createObjectURL(blob);
    aTag.click();
    URL.revokeObjectURL(blob);
}

/**
 * 允许打印页面
 */
function allowPrint() {
    let style = document.createElement("style");
    style.innerHTML = `@media print {
        body{
            display:block;
        }
    }`;
    document.getElementsByTagName("head")[0].appendChild(style);
}

/**
 * 强制隐藏元素
 * @param {String} selector 
 */
function forceHide(selector) {
    document.querySelectorAll(selector).forEach((elem) => {
        elem.className += " force_hide";
    });
    let style = document.createElement("style");
    style.innerHTML = `.force_hide {
        visibility: hidden !important;
    }`;
    document.getElementsByTagName("head")[0].appendChild(style);
}

/**
 * 隐藏按钮,打印页面,显示按钮
 */
function hideBtnThenPrint() {
    // 隐藏按钮,然后打印页面
    let section = document.getElementsByClassName("btns_section")[0];
    section.style.display = "none";
    window.print();
    // 打印结束,显示按钮
    section.style.removeProperty("display");
}

/**
 * 返回times个倍数连接的str
 * @param {String} str 
 * @param {Number} times 
 * @returns multiplied_str
 */
function multiplyStr(str, times) {
    let str_list = [];
    for (let i = 0; i < times; i++) {
        str_list.push(str);
    }
    return str_list.join("");
}

/**
 * 增强按钮(默认为蓝色按钮:展开文档)的点击效果
 * @param {String} custom_btn 按钮变量名
 */
function enhanceBtnClickReaction(custom_btn = null) {
    let aim_btn;
    // 如果不使用自定义按钮元素,则默认为使用蓝色展开文档按钮
    if (!custom_btn || custom_btn === "btn_1") {
        aim_btn = document.querySelector(".btn-1");
    } else {
        aim_btn = document.querySelector(`.${custom_btn.replace("_", "-")}`);
    }

    let old_color = aim_btn.style.color; // 保存旧的颜色
    let old_text = aim_btn.textContent; // 保存旧的文字内容
    // 变黑缩小
    aim_btn.style.color = "black";
    aim_btn.style.fontWeight = "normal";
    aim_btn.textContent = `->${old_text}<-`;
    // 复原加粗
    let changeColorBack = function() {
        aim_btn.style.color = old_color;
        aim_btn.style.fontWeight = "bold";
        aim_btn.textContent = old_text;
    };
    setTimeout(changeColorBack, 1250);
}

/**
 * 切换按钮显示/隐藏状态
 * @param {String} aim_btn 按钮变量名
 * @returns 按钮元素的引用
 */
function toggleBtnStatus(aim_btn) {
    let btn = document.querySelector(`.${aim_btn.replace("_", "-")}`);
    let display = getComputedStyle(btn).display;
    // return;
    if (display === "none") {
        btn.style.display = "block";
    } else {
        btn.style.display = "none";
    }
    return btn;
}

/**
 * 根据canvas元素数量返回quality值
 * @param {Number} canvas_amount
 * @returns quality: Number
 */
function getQualityByCanvasAmount(canvas_amount) {
    let quality;
    if (canvas_amount <= 25) {
        quality = 1.0;
    } else if (25 < canvas_amount <= 50) {
        quality = 0.85;
    } else {
        quality = 0.7;
    }
    return quality;
}

/**
 * 用input框跳转到对应页码
 * @param {Element} cur_page 当前页码
 * @param {string} aim_page 目标页码
 * @param {string} event_type 键盘事件类型:"keyup" | "keypress" | "keydown"
 */
function jump2pageNo(cur_page, aim_page, event_type) {
    // 设置跳转页码为目标页码
    cur_page.value = aim_page;
    // 模拟回车事件来跳转
    let keyboard_event_enter = new KeyboardEvent(event_type, {
        bubbles: true,
        cancelable: true,
        keyCode: 13
    });
    cur_page.dispatchEvent(keyboard_event_enter);
}

/**
 * 滚动到页面底部
 */
function scrollToBottom() {
    window.scrollTo({
        top: document.body.scrollHeight,
        behavior: "smooth"
    });
}

/**
 * 用try移除元素
 * @param {Element} element 要移除的元素
 */
function tryToRemoveElement(element) {
    try {
        element.remove();
    } catch (e) {
        console.log();
    }
}

/**
 * 用try移除 [元素列表1, 元素列表2, ...] 中的元素
 * @param {Array} elem_list_box 要移除的元素列表构成的列表
 */
function tryToRemoveSameElem(elem_list_box) {
    for (let elem_list of elem_list_box) {
        if (!elem_list) {
            continue;
        }
        for (let elem of elem_list) {
            try {
                elem.remove();
            } catch (e) {
                console.log();
            }
        }
    }
}

/**
 * 使文档在页面上居中
 * @param {String} class_name 文档元素的class
 * @param {String} default_offset 文档部分向右偏移的百分比(0-59)
 * @returns 偏移值是否合法
 */
function centerDoc(class_name, default_offset) {
    let doc_main = document.getElementsByClassName(class_name)[0];
    let offset = window.prompt("请输入偏移百分位:", default_offset);
    // 如果输入的数字不在 0-59 内,提醒用户重新设置
    if (offset.length === 1 && offset.search(/[0-9]/) !== -1) {
        doc_main.style.marginLeft = offset + "%";
        return true;
    } else if (offset.length === 2 && offset.search(/[1-5][0-9]/) !== -1) {
        doc_main.style.marginLeft = offset + "%";
        return true
    } else {
        alert("请输入一个正整数,范围在0至59之间,用来使文档居中\n(不同文档偏移量不同,所以需要手动调整)");
        return false;
    }
}

/**
 * 调整按钮内文本
 * @param {String} aim_btn 按钮变量名
 * @param {String} new_text 新的文本,null则保留旧文本
 * @param {Boolean} recommend_btn 是否增加"(推荐)"到按钮文本
 * @param {Boolean} use_hint 是否提示"文档已经完全展开,可以导出"
 */
function modifyBtnText(aim_btn = "btn_2", new_text = null, recommend_btn = false, use_hint = true) {
    // 提示文档已经展开
    if (use_hint) {
        let hint = "文档已经完全展开,可以导出";
        alert(hint);
    }
    let btn = document.querySelector(`.${aim_btn.replace("_", "-")}`);
    // 要替换的文本
    if (new_text) {
        btn.textContent = new_text;
    }
    // 推荐按钮
    if (recommend_btn) {
        btn_2.textContent += "(推荐)";
    }
}

/*
 *  主要功能函数部分
 */

/**
 * 清理并打印得力文库的文档页
 */
function printPageDeliwenku() {
    // 移除页面上的无关元素
    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";
    let elem_list = document.querySelectorAll(selector);
    for (let elem of elem_list) {
        tryToRemoveElement(elem);
    }
    // 修改页间距
    let outer_pages = document.getElementsByClassName("outer_page");
    for (let page of outer_pages) {
        page.style.marginBottom = "20px";
    }
    // 使文档居中
    alert("建议使用:\n偏移量: 3\n缩放: 112\n请上下滚动页面,确保每页内容都加载完成以避免空白页\n如果预览时有空白页或文末有绿色按钮,请取消打印重试");
    if (!centerDoc("boxleft", "3")) {
        return; // 如果输入非法,终止函数调用
    }
    // 打印文档
    hideBtnThenPrint();
}

/**
 * 清理并打印爱问共享资料的文档页
 * @returns 如果输入偏移量非法,返回空值以终止函数
 */
function printPageiShare() {
    // # 清理并打印爱问共享资料的文档页
    // ## 移除页面上无关的元素
    // ### 移除单个元素
    let topbanner = document.getElementsByClassName("detail-topbanner")[0];
    let header = document.getElementsByClassName("new-detail-header")[0];
    let fixright = document.getElementById("fix-right");
    let redpacket = document.getElementsByClassName("loginRedPacket-dialog")[0];
    let fixedrightfull = document.getElementsByClassName("fixed-right-full")[0];
    let footer = document.getElementsByClassName("website-footer")[0];
    let guess = document.getElementsByClassName("guess-you-like-warpper")[0];
    let detailtopbox = document.getElementsByClassName("detail-top-box")[0];
    let fullscreen = document.getElementsByClassName("reader-fullScreen")[0];
    let endhint = document.getElementsByClassName("endof-trial-reading")[0];
    let crumb_arrow;
    try { crumb_arrow = document.getElementsByClassName("crumb-arrow")[0].parentElement; } catch (e) { console.log(); }
    let copyright = document.getElementsByClassName("copyright-container")[0];
    let state_btn = document.getElementsByClassName("state-bottom")[0];
    let comments = document.getElementsByClassName("user-comments-wrapper")[0];
    // ### 执行移除
    let elem_list = [
        topbanner,
        header,
        fixright,
        redpacket,
        fixedrightfull,
        footer,
        guess,
        detailtopbox,
        fullscreen,
        endhint,
        crumb_arrow,
        copyright,
        state_btn,
        comments
    ];
    for (let elem of elem_list) {
        tryToRemoveElement(elem);
    }
    // ### 移除全部同类元素
    let elem_list_2 = document.querySelectorAll(".tui-detail, .adv-container")
    for (let elem_2 of elem_list_2) {
        tryToRemoveElement(elem_2);
    }
    // 使文档居中
    alert("建议使用:\n偏移量: 18\n缩放: 默认\n如果预览中有广告,就取消打印\n再点一次按钮,预览中应该就没有广告了");
    if (!centerDoc("doc-main", "18")) {
        return; // 如果输入非法,终止函数调用
    }
    // 隐藏按钮,然后打印页面
    hideBtnThenPrint();
}

function printPage360Doc() {
    // # 清理并打印360doc的文档页
    // ## 移除页面上无关的元素
    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";
    let elem_list = document.querySelectorAll(selector);
    let under_doc_1, under_doc_2;
    try {
        under_doc_1 = document.querySelector("#bgchange p.clearboth").nextElementSibling;
        under_doc_2 = document.querySelector("#bgchange").nextElementSibling.nextElementSibling;
    } catch (e) { console.log(); }
    // 执行移除
    for (let elem of elem_list) {
        tryToRemoveElement(elem);
    }
    tryToRemoveElement(under_doc_1);
    tryToRemoveElement(under_doc_2);
    // 执行隐藏
    document.querySelector("a[title]").style.display = "none";

    // 使文档居中
    alert("建议使用:\n偏移量: 20\n缩放: 默认\n");
    if (!centerDoc("a_left", "20")) {
        return; // 如果输入非法,终止函数调用
    }
    // 隐藏按钮,然后打印页面
    hideBtnThenPrint();
}

function clearPage_Baidu() {
    let selectors = [
        "#hd, .aside, .reader-tools-bar-wrap, .sb-con, .bg-opacity",
        ".doc-tag-wrap, .doc-bottom-wrap, .ft, #ft, .crubms-wrap, .banner-ad",
        "#activity-tg, .top-ads-banner-wrap, .reader_ab_test, .tag-tips, .doc-value, .owner-desc-wrap"
    ];
    let elem_list = document.querySelectorAll(selectors.join(", "));
    for (let elem of elem_list) {
        tryToRemoveElement(elem);
    }
    let nut_selector = ".fix-searchbar-wrap, #hd";
    forceHide(nut_selector);
}

/**
 * 绑定事件到指定按钮,返回按钮引用
 * @param {Function} event click事件
 * @param {Array} args 事件的参数列表 
 * @param {String} aim_btn 按钮的变量名
 * @param {String} new_text 按钮的新文本,为null则不替换
 * @returns 按钮元素的引用
 */
function setBtnEvent(event, args = [], aim_btn = "btn_3", new_text = null) {
    let btn = document.querySelector(`.${aim_btn.replace("_", "-")}`);
    // 如果需要,替换按钮内文本
    if (new_text) {
        btn.textContent = new_text;
    }
    // 绑定事件,添加到页面上
    btn.onclick = () => {
        enhanceBtnClickReaction(aim_btn);
        if (args.length) {
            event(...args);
        } else {
            event();
        }
    };
    return btn;
}

/**
 * 点击“继续阅读”,适用性:得力文库
 */
function readAllDeliwenku() {
    // 点击“同意并开始预览全文”
    let start_btn = document.getElementsByClassName("pre_button")[0];
    let display = start_btn.parentElement.parentElement.style.display;
    // 如果该按钮显示着,则点击,然后滚动至页面底部,最后终止函数
    if (!display) {
        start_btn.children[0].click();
        setTimeout("scroll(0, document.body.scrollHeight)", 200);
        return;
    }
    // 增强按钮点击效果
    enhanceBtnClickReaction();

    let read_all_btn = document.getElementsByClassName("fc2e")[0];
    let display2 = read_all_btn.parentElement.parentElement.style.display
        // 继续阅读
    if (display2 !== "none") {
        // 获取input元素
        let cur_page = document.querySelector("#pageNumInput");
        let page_old = cur_page.value;
        let page_max = cur_page.parentElement.nextElementSibling.textContent.replace(" / ", "");
        // 跳转到尾页
        jump2pageNo(cur_page, page_max, "keydown");
        // 跳转回来
        jump2pageNo(cur_page, page_old, "keydown");

        // 切换按钮准备导出
    } else {
        // 推荐导出全部图片链接
        modifyBtnText(recommend_btn = true);
        // 隐藏按钮
        toggleBtnStatus("btn_1");
        // 显示按钮
        toggleBtnStatus("btn_2");
        toggleBtnStatus("btn_3");
        // btn_3 橙色按钮
        setBtnEvent(printPageDeliwenku);
    }
}

/**
 * 点击“展开继续阅读”,适用性:爱尚共享资料
 */
function readAlliShare() {
    // 获取“继续阅读”元素
    let red_btn = document.getElementsByClassName("red-color")[0];
    let red_text = red_btn.textContent;
    // 如果可以展开,则展开
    if (red_text.search("点击可继续阅读") !== -1) {
        red_btn.click();
        setTimeout(readAlliShare, 1000);
    }
    // 否则启动按钮2,准备清理页面然后打印为PDF
    else {
        // 隐藏按钮
        toggleBtnStatus("btn_1");
        // 显示按钮
        toggleBtnStatus("btn_2");
        toggleBtnStatus("btn_3");

        // 显示svg图片的链接
        let page1 = document.querySelector('[data-num="1"] .data-detail embed');
        if (!page1) {
            // 如果不存在svg图形,终止后续代码
            console.log("当前页面不存在svg图形");
            return;
        }
        let page2 = document.querySelector('[data-num="2"] .data-detail embed');
        let [svg1_src_div, svg2_src_div] = [document.createElement("div"), document.createElement("div")];
        svg1_src_div.innerHTML = `<div id="src-1"
                                    style="font-weight: bold;font-size: 20px; height: 100px; width: 100%">
                                        访问以下链接以复制文字:<br>${page1.src}
                                    </div>`;
        svg2_src_div.innerHTML = `<div id="src-1"
                                    style="font-weight: bold;font-size: 20px; height: 100px; width: 100%">
                                    访问以下链接以复制文字:<br>${page2.src}
                                    </div>`;
        // 添加到页面上
        page1.parentElement.parentElement.parentElement.append(svg1_src_div);
        page2.parentElement.parentElement.parentElement.append(svg2_src_div);
    }
}

function readAllDoc88() {
    // 获取“继续阅读”按钮
    let continue_btn = document.querySelector("#continueButton");
    // 如果存在“继续阅读”按钮
    if (continue_btn) {
        // 跳转到文末(等同于展开全文)
        let cur_page = document.querySelector("#pageNumInput");
        // 取得最大页码
        let page_max = cur_page.parentElement.textContent.replace(" / ", "");
        // 跳转到尾页
        jump2pageNo(cur_page, page_max, "keypress");
        // 返回顶部
        setTimeout(jump2pageNo(cur_page, "1", "keypress"), 1000);
    }
    // 文档展开后,显示按钮2、3
    else {
        // 隐藏按钮
        toggleBtnStatus("btn_1");
        // 显示按钮
        toggleBtnStatus("btn_2");
        toggleBtnStatus("btn_3");
    }
}

function readAll360Doc() {
    // 展开文档
    document.querySelector(".articleMaxH").setAttribute("class", "");
    // 隐藏按钮
    toggleBtnStatus("btn_1");
    // 显示按钮
    toggleBtnStatus("btn_2");
    toggleBtnStatus("btn_3");
}

// function storeCanvases(selector) {
//     // 获取最新全部canvas
//     let canvas_list = document.querySelectorAll(selector);
//     // 获取全局canvas_map
//     let canvas_map = window.canvas_map;

//     for (let canvas of canvas_list) {
//         let width, height;
//         width = canvas.width;
//         height = canvas.height;

//         // 找出长宽均不为0的、可捕获的canvas
//         if (width && height) {
//             // 找出并非已有的canvas
//             if (!canvas_map.has(canvas.id)) {
//                 // 捕获展开的canvas
//                 let canvas_copy = canvas.toDataURL();
//                 canvas_map.set(canvas.id, canvas_copy);
//             }
//         }
//     }
//     console.log(canvas_map.size);

//     if (`${canvas_map.size}` === window.page_max) {
//         let title = document.querySelector(".doc-title").textContent;
//         let quality = getQualityByCanvasAmount(`${canvas_map.size}`);
//         // btn_3: 到pdf
//         setBtnEvent(saveCanvasesToPDF, [
//             [...canvas_map.values()], title, quality
//         ], "btn_3");
//         // btn_2: 到zip
//         setBtnEvent(saveCanvasesToZip, [
//             [...canvas_map.values()], title
//         ], "btn_2", "导出全部图片");
//         // 移除监听器
//         console.log("scroll监听器已经移除");
//         window.onscroll = () => { console.log() };
//     }
// }

/**
 * 将html元素转为canvas再合并到pdf中,最后下载pdf
 * @param {Array} elem_list html元素列表
 * @param {String} title 文档标题
 */
async function html2PDF(elem_list, title = "文档", quality) {
    // 如果是空元素列表,返回null并终止函数
    if (elem_list.length === 0) {
        console.log("html2PDF was called, but no canvas element avaiable.");
        return null;
    }
    let tasks = []; //  存放异步任务
    let contents = []; //  存放canvas元素
    for (let elem of elem_list) {
        let task = html2canvas(elem).then((canvas) => {
            contents.push(canvas);
        });
        tasks.push(task);
    }
    // 等待全部page转化完成
    await Promise.all(tasks);
    // 控制台检查结果
    console.log("生成的canvas元素如下:");
    console.log(contents);

    // 拿到canvas宽、高:如果第二页存在,就用第二页的宽高,如果不存在就用第一页的
    let model_page = document.querySelector("#pageNo-2") ? document.querySelector("#pageNo-2") : document.querySelector("#pageNo-1");
    let width, height;
    width = model_page.offsetWidth;
    height = model_page.offsetHeight;
    // 打包为pdf
    saveCanvasesToPDF(contents, title, quality, width, height);
}

/**
 * 下载全部图片链接,适用性:爱问共享资料、得力文库
 * @param {string} selector 图形元素的父级元素
 */
function savePicUrls(selector) {
    let pages = document.querySelectorAll(selector);
    let pic_urls = [];

    for (let elem of pages) {
        let pic_obj = elem.children[0];
        let url = pic_obj.src;
        pic_urls.push(url);
    }
    let content = pic_urls.join("\n");
    // 启动下载
    createAndDownloadFile("urls.csv", content);
}

/**
 * 存储所有canvas图形为png到一个压缩包
 * @param {Array} node_list canvas元素列表
 * @param {String} title 文档标题
 */
function saveCanvasesToZip(node_list, title) {
    // canvas元素转为png图像
    // 所有png合并为一个zip压缩包
    let zip = new JSZip();
    let n = node_list.length;

    for (let i = 0; i < n; i++) {
        let canvas = node_list[i];
        let data_base64 = canvas.toDataURL();
        let blob = atob(data_base64.split(",")[1]);
        zip.file(`page-${i+1}.png`, blob, { binary: true });
    }

    // 导出zip
    // promise.then(onCompleted, onRejected);
    zip.generateAsync({ type: "blob" }).then(function(content) {
        // see filesaver.js
        console.log(content);
        saveAs(content, `${title}.zip`);
    });
}

/**
 * 将canvas转为jpeg,然后导出PDF
 * @param {Array} node_list canvas元素列表
 * @param {String} title 文档标题
 * @param {Number} quality 图片质量,浮点数,范围 (0,1],默认值0.92
 */
function saveCanvasesToPDF(node_list, title, width = 0, height = 0) {
    // 如果没有手动指定canvas的长宽,则自动检测
    if (!width && !height) {
        // 先获取第一个canvas用于判断竖向还是横向,以及得到页面长宽
        let first_canvas = node_list[0];
        // 如果style的长宽不存在,则直接用canvas的元素长宽
        let width_str, height_str;
        if (first_canvas.width && parseInt(first_canvas.width) && parseInt(first_canvas.height)) {
            [width_str, height_str] = [first_canvas.width, first_canvas.height];
        } else {
            [width_str, height_str] = [first_canvas.style.width.replace(/(px)|(rem)|(em)/, ""), first_canvas.style.height.replace(/(px)|(rem)|(em)/, "")];
        }
        // jsPDF的第三个参数为format,当自定义时,参数为数字数组。
        [width, height] = [parseFloat(width_str), parseFloat(height_str)];
    }
    console.log(`canvas数据:宽: ${width}px,高: ${height}px`);
    // 如果文档第一页的宽比长更大,则landscape,否则portrait
    let orientation = width > height ? 'l' : 'p';
    let pdf = new jsPDF(orientation, 'px', [height, width]);

    // 根据canvas数量确定quality
    let quality = getQualityByCanvasAmount(node_list.length);

    // 保存每一页文档到每一页pdf
    node_list.forEach(function(canvas, index) {
        pdf.addImage(canvas.toDataURL("image/jpeg", quality), 'JPEG', 0, 0, width, height);
        // 如果当前不是文档最后一页,则需要添加下一个空白页
        if (index !== node_list.length - 1) {
            pdf.addPage();
        }
    });

    // 导出文件
    pdf.save(`${title}.pdf`);
}

function saveText_360Doc() {
    let text_selectors = [
        "#artContent .blogzz_acon p",
        `#artContent section section span ${multiplyStr(" section", 5)} p span`,
        `#artContent section section span ${multiplyStr(" section", 6)}`,
        "#artContent span > strong",
        "#artContent > section > span",
        "#artContent > div > p > font",
        "#artContent > div > p > font > strong",
        `#artContent${multiplyStr(' section', 17)}`,
        "#artContent > section > section > p > span",
        "#artContent > div > section > span",
        `#artContent ${multiplyStr(' section', 4)} p`,
        "#artContent > section > p"
    ];

    let paragraphs = document.querySelectorAll(text_selectors.join(", "));
    let images = document.querySelectorAll("#artContent img");
    let contents = [];

    for (let i = 0; i < images.length; i++) {
        let src = images[i].src;
        contents.push(`图${i+1},链接:${src}`);
    }
    for (let p of paragraphs) {
        contents.push(p.textContent);
    }
    // 保存纯文本文档
    let title = document.querySelector("#titiletext").textContent;
    createAndDownloadFile(`${title}.txt`, contents.join("\n"));
}

/**
 * 取得elem的class为class_name的父级元素
 * @param {String} class_name 
 * @param {Element} elem 
 */
function getParentByClassName(class_name, elem) {
    let parent = elem.parentElement;
    let now_name;
    try {
        now_name = parent.className;
    } catch (e) {
        // 没有父级元素了,返回null
        return null;
    }
    let iterator_count = window.baiduJS.iterator_count;
    if (iterator_count > 9) {
        // 超过最大迭代次数,认为不存在,返回null
        return null;
    }
    if (now_name === class_name) {
        iterator_count = 0;
        return parent;
    }
    return getParentByClassName(class_name, parent);
}

/**
 * 存储html元素。适用于百度文库的文字型文档
 */
function storeHtmlElemts_Baidu() {
    let elems_map = window.baiduJS.elems_map;
    document.querySelectorAll("[class*=reader-main]").forEach(
        (elem) => {
            let origin_page_elem = getParentByClassName("bd", elem);
            // 复制元素防止丢失
            let page_elem = origin_page_elem.cloneNode(true);
            let id = page_elem.id;
            if (!elems_map.has(id)) {
                elems_map.set(id, page_elem);
            }
        });
    if (elems_map.size === window.baiduJS.max_page) {
        window.baiduJS.finished = true;
        window.onscroll = () => { console.log(); };
    }
}

/**
 * 提取文字,导出txt。适用于百度文库
 */
function saveText_Baidu() {
    if (!window.baiduJS.finished) {
        alert("1. 可能仍有内容未加载完,无法使用该功能\n建议从头到尾慢速地再浏览一遍\n2. 可能该文档是PPT等图片型文档,没有文字内容");
        return;
    }
    let title = document.title.split("-")[0].trim(); // 取得文档标题
    let page_texts = [];
    for (let elem of window.baiduJS.elems_map.values()) {
        let text = elem.textContent;
        page_texts.push(text);
    }
    createAndDownloadFile(`${title}.txt`, page_texts.join("\n"));
}

/**
 * 存储html元素。适用于百度文库的PPT型文档
 */
function storeHtmlElemts_BaiduPPT() {
    let elems_map = window.baiduJS.elems_map;
    document.querySelectorAll(".ppt-image-wrap").forEach(
        (elem) => {
            let origin_page_elem = getParentByClassName("bd", elem);
            // 复制元素防止丢失
            let page_elem = origin_page_elem.cloneNode(true);
            let id = page_elem.id;
            if (!elems_map.has(id)) {
                elems_map.set(id, page_elem);
            }
        });
    if (elems_map.size === window.baiduJS.max_page) {
        window.baiduJS.finished = true;
        window.onscroll = () => { console.log(); };
    }
}

/**
 * 将文档页html转到pdf。适用于百度文库
 */
function savePicUrls_Baidu() {
    let urls = [];
    let elems = document.querySelectorAll(".ppt-image-wrap img");
    if (!elems.length) {
        alert("当前页面没有PPT图形元素");
        return;
    }
    elems.forEach((elem) => {
        if (elem.hasAttribute("src")) {
            urls.push(elem.src);
        } else {
            urls.push(elem.getAttribute("data-src"));
        }
    });
    createAndDownloadFile("urls.csv", urls.join("\n"));
}

/**
 * 创建4个按钮:展开文档、导出图片、导出PDF、未设定;默认均为隐藏
 * @returns [btn_1, btn_2]
 */
function createBtns() {
    // 创建按钮组
    let section = document.createElement("section");
    section.className = "btns_section";
    section.innerHTML = `
        <p class="logo_tit">Wenku Doc Downloader</p>
        <button class="btn-1" title="请先滑到底部,使内容加载完,防止出现空白页">展开文档 😈</button>
        <button class="btn-2">导出图片 🖼️</button>
        <button class="btn-3">导出PDF 🌼</button>
        <button class="btn-4">未设定</button>`;
    document.body.appendChild(section);

    // 设定样式
    let style = document.createElement("style");
    style.innerHTML = `
        .btns_section{
            position: fixed;
            width: 154px;                
            left: 10px;
            top: 32%;
            background: #E7F1FF;
            border: 2px solid #1676FF;                
            padding: 0px 0px 10px 0px;
            font-weight: 600;
            border-radius: 2px;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB',
            'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif, 'Apple Color Emoji',
            'Segoe UI Emoji', 'Segoe UI Symbol';
            z-index: 5000;
        }
        .logo_tit{
            width: 100%;
            background: #1676FF;
            text-align: center;
            font-size:12px ;
            color: #E7F1FF;
            line-height: 40px;
            height: 40px;
            margin: 0 0 16px 0;
        }

        .btn-1{
            display: block;
            width: 128px;
            height: 28px;
            background: linear-gradient(180deg, #00E7F7 0%, #FEB800 0.01%, #FF8700 100%);
            border-radius: 4px;
            color: #fff;
            font-size: 12px;
            border: none;
            outline: none;
            margin: 8px auto;
            font-weight: bold;
            cursor: pointer;
            opacity: .9;
        }
        .btn-2{
            display: none;
            width: 128px;
            height: 28px;
            background: #07C160;
            border-radius: 4px;
            color: #fff;
            font-size: 12px;
            border: none;
            outline: none;
            margin: 8px auto;
            font-weight: bold;
            cursor: pointer;
            opacity: .9;
        }
        .btn-3{
            display: none;
            width: 128px;
            height: 28px;
            background:#FA5151;
            border-radius: 4px;
            color: #fff;
            font-size: 12px;
            border: none;
            outline: none;
            margin: 8px auto;
            font-weight: bold;
            cursor: pointer;
            opacity: .9;
        }
        .btn-4{
            display: none;
            width: 128px;
            height: 28px;
            background: #1676FF;
            border-radius: 4px;
            color: #fff;
            font-size: 12px;
            border: none;
            outline: none;
            margin: 8px auto;
            font-weight: bold;
            cursor: pointer;
            opacity: .9;
        }
        .btn-1:hover,.btn-2:hover,.btn-3:hover,.btn-4:hover{ opacity: .8;}
        .btn-1:active,.btn-2:active,.btn-3:active,.btn-4:active{ opacity: 1;}`;
    document.head.appendChild(style);

    let btn_1, btn_2;
    btn_1 = document.querySelector(".btn-1");
    btn_2 = document.querySelector(".btn-2");
    // 返回元素引用
    return [btn_1, btn_2]
}

/*
 *  主函数部分
 */

/**
 * 豆丁文档下载策略
 */
function docin() {
    // 创建脚本启动按钮
    createBtns();

    // 隐藏底部工具栏
    document.querySelector("#j_select").click(); // 选择指针
    let tool_bar = document.querySelector(".reader_tools_bar_wrap.tools_bar_small.clear");
    tool_bar.style.display = "none";

    // 绑定主函数
    let getCanvasList = function() {
        // 获取全部canvas元素,用于传递canvas元素列表给 btn_2 和 btn_3
        let parent_node_list = document.querySelectorAll(".hkswf-content");
        let node_list = [];
        for (let node of parent_node_list) {
            node_list.push(node.children[0]);
        }
        return node_list;
    };

    let prepare = function() {
        // 获取canvas元素列表
        let node_list = getCanvasList();
        // 获取文档标题
        let title;
        if (document.querySelector("h1 [title=doc]")) {
            title = document.querySelector("h1 [title=doc]").nextElementSibling.textContent;
        } else if (document.querySelector(".doc_title")) {
            title = document.querySelector(".doc_title").textContent;
        } else {
            title = "文档";
        }
        return [node_list, title];
    }

    // 判断是否有canvas元素
    let detectCanvas = function() {
            let haveCanvas = getCanvasList().length === 0 ? false : true;

            // 隐藏按钮
            toggleBtnStatus("btn_1");
            // 显示按钮
            toggleBtnStatus("btn_2");

            // 如果没有canvas元素,则认为文档页面由外链图片构成
            if (!haveCanvas) {
                // btn_2: 导出图片链接
                setBtnEvent(() => {
                    if (confirm("确定每页内容都加载完成了吗?")) {
                        savePicUrls("[id*=img_]");
                    }
                }, [], "btn_2", "导出全部图片链接");
            } else {
                // 显示按钮3
                toggleBtnStatus("btn_3");
                // btn_2: 导出zip
                setBtnEvent(() => {
                    if (confirm("确定每页内容都加载完成了吗?")) {
                        saveCanvasesToZip(...prepare());
                    }
                }, [], "btn_2", "导出图片到zip");
                // btn_3: 导出PDF
                setBtnEvent(() => {
                    if (confirm("确定每页内容都加载完成了吗?")) {
                        saveCanvasesToPDF(...prepare());
                    }
                }, [], "btn_3", "导出图片到PDF");
            }
        }
        // btn_1: 判断文档类型
    setBtnEvent(() => {
        forceHide(".jz_watermark");
        detectCanvas();
    }, [], "btn_1", "判断文档类型");
}

/**
 * 爱问共享资料文档下载策略
 */
function ishare() {
    // 创建脚本启动按钮1、2
    createBtns();

    // btn_1: 展开文档
    setBtnEvent(readAlliShare, [], "btn_1");
    // btn_2: 导出图片链接
    setBtnEvent(() => {
        savePicUrls(".data-detail");
    }, [], "btn_2", "导出图片链接(推荐)");
    // btn_3: 打印页面到PDF
    setBtnEvent(printPageiShare, [], "btn_3", "打印页面到PDF");

    // 移除底部下载条
    let detailfixed = document.getElementsByClassName("detail-fixed")[0];
    tryToRemoveElement(detailfixed);
}

/**
 * 得力文库文档下载策略
 */
function deliwenku() {
    // 创建脚本启动按钮1、2
    createBtns();

    // btn_1: 展开文档
    setBtnEvent(readAllDeliwenku, [], "btn_1");
    // btn_2: 导出全部图片链接
    setBtnEvent(() => {
        if (confirm("确定每页内容都加载完成了吗?")) {
            savePicUrls('.inner_page div');
        }
    }, [], "btn_2", "导出全部图片链接");

    // 尝试关闭页面弹窗
    try { document.querySelector("div[title=点击关闭]").click(); } catch (e) { console.log(0); }
    // 解除打印限制
    allowPrint();
}

/**
 * 道客巴巴文档下载策略
 */
function doc88() {
    // 创建脚本启动按钮1、2
    createBtns();

    // 绑定主函数
    let prepare = function() {
        // 获取canvas元素列表
        let node_list = document.querySelectorAll(".inner_page");
        // 获取文档标题
        let title;
        if (document.querySelector(".doctopic h1")) {
            title = document.querySelector(".doctopic h1").title;
        } else {
            title = "文档";
        }
        return [node_list, title];
    }

    // btn_1: 展开文档
    setBtnEvent(() => {
        readAllDoc88();
    }, [], "btn_1");
    // btn_2: 导出zip
    setBtnEvent(() => {
        if (confirm("确定每页内容都加载完成了吗?")) {
            saveCanvasesToZip(...prepare());
        }
    }, [], "btn_2", "导出图片到zip");
    // btn_3: 导出PDF
    setBtnEvent(() => {
        if (confirm("确定每页内容都加载完成了吗?")) {
            saveCanvasesToPDF(...prepare());
        }
    }, [], "btn_3", "导出图片到PDF");
}

function doc360() {
    // 创建按钮区
    createBtns();
    // btn_1: 展开文档
    setBtnEvent(readAll360Doc, [], "btn_1");
    // btn_2: 导出纯文本
    setBtnEvent(saveText_360Doc, [], "btn_2", "导出纯文本");
    // btn_3: 打印页面到PDF
    setBtnEvent(() => {
        if (confirm("确定每页内容都加载完成了吗?")) {
            printPage360Doc();
        }
    }, [], "btn_3", "打印页面到PDF");
}
/**
 * 百度文档预览策略
 */
function baiduWenku() {
    // 原文档解析到预览文档
    if (location.href.includes("\u002f\u0076\u0069\u0065\u0077\u002f")) {
        createBtns();
        let jump2sharePage_Baidu = function() {
            location.href = `https://${location.host}${location.pathname.replace("\u0076\u0069\u0065\u0077", "\u0073\u0068\u0061\u0072\u0065")}`;
        }
        setBtnEvent(jump2sharePage_Baidu, [], "btn_1");
        // 预览文档清理广告
    } else if (location.href.includes("\u002f\u0073\u0068\u0061\u0072\u0065\u002f")) {
        createBtns();
        // 隐藏按钮
        toggleBtnStatus("btn_1");
        // 显示按钮
        toggleBtnStatus("btn_2");
        toggleBtnStatus("btn_3");
        toggleBtnStatus("btn_4");
        // 清理无关页面元素并打印页面
        let printPage = function() {
            let read_more_btn = document.querySelector("#html-reader-go-more");
            let unfold_all;

            if (read_more_btn) {
                unfold_all = read_more_btn.style.display === "none";
            } else {
                unfold_all = !Boolean(document.querySelector("[id*=next-pageList]"));
            }
            if (unfold_all) {
                if (confirm("文档都加载完毕了吗?")) {
                    let sentence_1 = "\u5982\u679c\u51fa\u73b0\u5927\u7247\u7a7a\u767d\u9875\uff0c\u8bf4\u660e";
                    let sentence_2 = "\u6587\u6863\u6709\u4fdd\u62a4\u673a\u5236\uff0c\u65e0\u6cd5\u6253\u5370";
                    alert(`${sentence_1}${sentence_2}`);
                    allowPrint();
                    clearPage_Baidu();
                    hideBtnThenPrint();
                }
            } else {
                alert("文档尚未完全展开,无法使用该功能。");
            }
        }
        let max_page = parseInt(document.querySelector(".page-count").textContent.replace("/", ""));
        // 为动态收集文档页元素提供全局变量
        window.baiduJS = {
            max_page: max_page,
            elems_map: new Map(), // id: element
            iterator_count: 0, // getParentByClassName的最大迭代次数为9
            finished: false // 是否收集完了全部文档页元素
        };
        // 跟随浏览,动态收集页面元素
        window.onscroll = storeHtmlElemts_Baidu;
        // 绑定事件到按钮
        setBtnEvent(printPage, [], "btn_2", "\u6253\u5370\u9875\u9762\u5230\u0050\u0044\u0046");
        setBtnEvent(saveText_Baidu, [], "btn_3", "\u5bfc\u51fa\u7eaf\u6587\u672c");
        setBtnEvent(savePicUrls_Baidu, [], "btn_4", "导出图片链接(仅PPT)");
    } else {
        console.log(`无法识别的页面:${location.href}`);
    }
}

/**
 * 主函数:识别网站,执行对应文档下载策略
 */
function main() {
    let host = window.location.host;
    console.log(`当前host: ${host}`);

    if (host.includes("docin.com")) {
        docin();
    } else if (host === "ishare.iask.sina.com.cn") {
        ishare();
    } else if (host === "www.deliwenku.com") {
        deliwenku();
    } else if (host === "www.doc88.com") {
        doc88();
    } else if (host === "www.360doc.com") {
        doc360();
    } else if (host === "wenku.baidu.com") {
        baiduWenku();
    } else {
        console.log("匹配到了无效网页");
    }
}

if (options.activation_test) {
    alert(`Wenku Doc Downloader 已经在当前页面生效!\n当前站点为:\n${window.location.host}`);
}

if (options.fast_mode) {
    main();
} else {
    window.onload = main;
}


// let url_tasks = [];
// let contents = [];
// for (let url of urls) {
//     let task = fetch(url).then(response => response.blob()).then(blob => contents.push(blob));
//     url_tasks.push(task);
// }
// await Promise.all(url_tasks);