Greasy Fork is available in English.

文章导出成pdf

将一些主流的网站的文章,去除掉一些无关部分直接启动浏览器自带打印功能

// ==UserScript==
// @name         文章导出成pdf
// @namespace    https://github.com/Vanisper/web-article-to-pdf
// @version      1.3.2
// @author       Vanisper
// @license      MIT
// @icon         https://vitejs.dev/logo.svg
// @defaulticon  将一些主流的网站的文章,去除掉一些无关部分直接启动浏览器自带打印功能
// @match        https://www.bilibili.com/read/cv*
// @match        https://www.cnblogs.com/*/p/*
// @match        https://www.cnblogs.com/*/archive/*
// @match        https://blog.csdn.net/*/article/details/*
// @match        https://www.jianshu.com/p/*
// @match        https://juejin.cn/post/*
// @match        https://segmentfault.com/a/*
// @match        https://mp.weixin.qq.com/s/*
// @match        https://mp.weixin.qq.com/s?*
// @match        https://zhuanlan.zhihu.com/p/*
// @match        https://www.zhihu.com/question/*/answer/*
// @match        https://www.zhihu.com/question/*
// @require      https://cdn.jsdelivr.net/npm/vue@3.2.47/dist/vue.global.prod.js
// @description 将一些主流的网站的文章,去除掉一些无关部分直接启动浏览器自带打印功能
// ==/UserScript==

(e=>{const t=document.createElement("style");t.dataset.source="vite-plugin-monkey",t.textContent=e,document.head.append(t)})(" *{padding:0;margin:0}button[data-v-13bc6f6b]{font-weight:700;color:#fff;border-radius:2rem;width:95.02px;height:42.66px;border:none;background-color:#3653f8;display:flex;justify-content:center;align-items:center}button .span-mother[data-v-13bc6f6b]{display:flex;overflow:hidden}button .span-mother span[data-v-13bc6f6b]{display:flex;justify-content:center;align-items:center}button .span-mother2[data-v-13bc6f6b]{display:flex;position:absolute;overflow:hidden}button .span-mother2 span[data-v-13bc6f6b]{transform:translateY(-1.2em);display:flex;justify-content:center;align-items:center}button:hover .span-mother[data-v-13bc6f6b]{position:absolute}button:hover .span-mother span[data-v-13bc6f6b]{transform:translateY(1.2em)}button:hover .span-mother2 span[data-v-13bc6f6b]{transform:translateY(0)}@keyframes spin-19ff1008{to{transform:rotate(360deg)}}#loading[data-v-19ff1008]{position:fixed;display:flex;top:0;left:0;width:100%;height:100%;background-color:#8ae79d82;z-index:9999}#loading .spinner[data-v-19ff1008]{margin:auto;width:40px;height:40px;border-radius:50%;border:3px solid transparent;border-top-color:#fff;animation:spin-19ff1008 .8s ease infinite}.setpdf[data-v-af70e9ff]{position:fixed;top:100px;right:24px;box-sizing:border-box;cursor:pointer;user-select:none;transition:opacity .2s ease .1s;z-index:9998} ");

(function (vue) {
  'use strict';

  var __defProp = Object.defineProperty;
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  var __publicField = (obj, key, value) => {
    __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
    return value;
  };
  class useDraggable {
    // dom初始top
    constructor(element) {
      __publicField(this, "draggableElement");
      __publicField(this, "isDragging");
      __publicField(this, "isDraggable");
      __publicField(this, "startPosition");
      __publicField(this, "currentX");
      // dom初始left
      __publicField(this, "currentY");
      __publicField(this, "startDragging", (event) => {
        const elementRect = this.draggableElement.getBoundingClientRect();
        this.startPosition = { x: elementRect.x, y: elementRect.y };
        this.currentX = elementRect.left;
        this.currentY = elementRect.top;
        this.isDragging = true;
        this.startPosition = {
          x: event.clientX,
          y: event.clientY
        };
        window.addEventListener("mousemove", this.dragging);
        window.addEventListener("mouseup", this.stopDragging);
      });
      __publicField(this, "dragging", (event) => {
        if (this.isDragging && this.draggableElement) {
          const maxLeft = document.documentElement.clientWidth - this.draggableElement.offsetWidth;
          const maxTop = document.documentElement.clientHeight - this.draggableElement.offsetHeight;
          const offsetX = event.clientX - this.startPosition.x;
          const offsetY = event.clientY - this.startPosition.y;
          let left = this.currentX + offsetX;
          let top = this.currentY + offsetY;
          if (offsetX < 0) {
            left = Math.max(0, left);
          } else {
            left = Math.min(maxLeft, left);
          }
          if (offsetY < 0) {
            top = Math.max(0, top);
          } else {
            top = Math.min(maxTop, top);
          }
          this.draggableElement.style.position = "fixed";
          this.draggableElement.style.top = top + "px";
          this.draggableElement.style.left = left + "px";
        }
      });
      __publicField(this, "stopDragging", (event) => {
        this.isDragging = false;
        window.removeEventListener("mousemove", this.dragging, false);
        window.removeEventListener("mouseup", this.stopDragging, false);
      });
      __publicField(this, "resetPosition", () => {
        const elementRect = this.draggableElement.getBoundingClientRect();
        this.draggableElement.style.position = "fixed";
        if (window.innerWidth < elementRect.right) {
          this.draggableElement.style.left = window.innerWidth - elementRect.width + "px";
        }
        if (window.innerHeight < elementRect.bottom) {
          this.draggableElement.style.top = window.innerHeight - elementRect.height + "px";
        }
        if (elementRect.top < 0) {
          this.draggableElement.style.top = "0px";
        }
      });
      this.draggableElement = element;
      this.isDraggable = element.getAttribute("draggable") === "true" || element.getAttribute("draggable") === "" ? true : false;
      this.isDragging = false;
      const elementRect = element.getBoundingClientRect();
      this.startPosition = { x: elementRect.x, y: elementRect.y };
      this.currentX = elementRect.left;
      this.currentY = elementRect.top;
      this.init();
    }
    init() {
      var _a;
      this.unDraggable();
      const elementRect = this.draggableElement.getBoundingClientRect();
      this.draggableElement.style.position = "fixed";
      this.draggableElement.style.left = elementRect.left + "px";
      this.draggableElement.style.top = elementRect.top + "px";
      this.draggableElement.style.right = "unset";
      window.addEventListener("resize", this.resetPosition);
      (_a = this.draggableElement) == null ? void 0 : _a.addEventListener("mousedown", this.startDragging);
    }
    draggable() {
      var _a;
      (_a = this.draggableElement) == null ? void 0 : _a.setAttribute("draggable", "true");
      this.isDraggable = true;
    }
    unDraggable() {
      var _a;
      (_a = this.draggableElement) == null ? void 0 : _a.setAttribute("draggable", "false");
      this.isDraggable = false;
    }
    preventDefault(event) {
      event.preventDefault();
    }
    destroy() {
      var _a;
      (_a = this.draggableElement) == null ? void 0 : _a.removeEventListener("mousedown", this.startDragging);
      window.removeEventListener("resize", this.resetPosition);
    }
  }
  const _hoisted_1$1 = { class: "span-mother" };
  const _hoisted_2$1 = { class: "span-mother2" };
  const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
    __name: "button1",
    props: {
      text: {
        type: String,
        default: ""
      }
    },
    setup(__props) {
      const props = __props;
      vue.ref(props.text.length);
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock("button", null, [
          vue.createElementVNode("span", _hoisted_1$1, [
            (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(__props.text, (item, index) => {
              return vue.openBlock(), vue.createElementBlock("span", {
                key: item,
                style: vue.normalizeStyle({ transition: `${0.1 * index + 0.1}s` })
              }, vue.toDisplayString(item), 5);
            }), 128))
          ]),
          vue.createElementVNode("span", _hoisted_2$1, [
            (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(__props.text, (item, index) => {
              return vue.openBlock(), vue.createElementBlock("span", {
                key: item,
                style: vue.normalizeStyle({ transition: `${0.1 * index + 0.1}s` })
              }, vue.toDisplayString(item), 5);
            }), 128))
          ])
        ]);
      };
    }
  });
  const _export_sfc = (sfc, props) => {
    const target = sfc.__vccOpts || sfc;
    for (const [key, val] of props) {
      target[key] = val;
    }
    return target;
  };
  const button1 = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-13bc6f6b"]]);
  const _sfc_main$1 = {};
  const _withScopeId = (n) => (vue.pushScopeId("data-v-19ff1008"), n = n(), vue.popScopeId(), n);
  const _hoisted_1 = { id: "loading" };
  const _hoisted_2 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "spinner" }, null, -1));
  const _hoisted_3 = [
    _hoisted_2
  ];
  function _sfc_render(_ctx, _cache) {
    return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, _hoisted_3);
  }
  const loading1 = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__scopeId", "data-v-19ff1008"]]);
  const bilibiliRules = [{
    url: "https?://www.bilibili.com/read/cv\\w+",
    name: "bilibili",
    class: "#bili-header-container, div.article-breadcrumb, div.right-side-bar.on.is-mini-page, #comment-wrapper, div.fixed-top-header,#readRecommendInfo, div.interaction-info",
    style: `@media print {
            #app > div > div.article-container {}
    }`
  }];
  const cnblogsRules = [
    {
      url: "https?://www\\.cnblogs\\.com/\\w+/(p|archive)/\\w+",
      name: "cnblogs",
      class: "#top_nav, #header, #sideBar,#blog_post_info,#post_next_prev,#topics > div > div.postDesc,#comment_form,#footer,#top_nav,#header,#mylinks,#mytopmenu,body > div.footer,#leftcontent,#blog_post_info_block,#comment_form",
      style: `@media print {
                #main {
                    display: flex !important;
            }
                #mainContent {
                    max-width: 1080px!important;
                    min-width: 720px!important;
                    width: 100%!important;
            }
                #post_detail {
                    display: flex !important;
                    justify-content: center;
            }
        }`
    }
  ];
  const csdnRules = [
    {
      url: "https?://blog\\.csdn\\.net/\\w+/article/details/\\w+",
      name: "csdn",
      class: "#mainBox > aside, #toolbarBox #csdn-toolbar, body > div:nth-child(49) > div, #toolBarBox, #mainBox > main > div.recommend-box, #recommendNps, #copyright-box, #treeSkill > div, .csdn-side-toolbar, .left-toolbox",
      style: `
        @media print {
            #mainBox > main > div.blog-content-box {
                position: absolute; top: 0; left: 0; max-width: 1080px; z-index: 999;
            }
            main div.blog-content-box pre.set-code-hide {
                height: auto; overflow-y: auto;
            }
            main div.blog-content-box pre.set-code-hide .hide-preCode-box {
                display: none;
            }
            #article_content .markdown_views pre.prettyprint * {
                white-space: pre-wrap; word-break: break-word; word-wrap: normal;
            }
            #toolBarBox, #csdn-toolbar, .recommend-right, .recommend-right1, .blog_container_aside {
                display: none !important;
            }
        }`,
      copyrightTarget: ".blog-content-box"
    }
  ];
  const jianshuRules = [{
    url: "https?://www.jianshu.com/p/\\w+",
    name: "jianshu",
    class: "#__next > header, #__next aside,#__next > div._3Pnjry, #__next > footer, #__next > div > div > div._gp-ck > section:nth-child(1) > div._13lIbp, #__next > div > div > div._gp-ck > section:nth-child(2), #__next > div._21bLU4._3kbg6I > div > div._gp-ck > section:nth-child(5),#note-page-comment",
    style: `@media print {
            #__next > div._21bLU4._3kbg6I > div > div._gp-ck > section:nth-child(1) {
                position: absolute; top: 0; left: 0;z-index: 999;
                max-width: 1080px;
                min-width: 1080px;
                width: 1080px;
        }
    }`
  }];
  const juejinRules = [
    {
      url: "https?://juejin.cn/post/\\w+",
      name: "juejin",
      class: "#juejin > div.view-container > div, #juejin > div.view-container > main > div > div.article-suspended-panel.dynamic-data-ready,#juejin > div.view-container > main > div > div.main-area.article-area > div.wrap.category-course-recommend,#comment-box,#juejin > div.view-container > main > div > div.main-area.recommended-area.shadow,#juejin > div.view-container > main > div > div.recommended-links.main-area,#juejin > div.view-container > main > div > div.sidebar.sidebar,#juejin > div.global-component-box, #juejin > div.view-container > main > div > div.main-area.article-area > div.article-end > div.column-container,#juejin > div.view-container > main > div > div.main-area.article-area > div.article-end > div.extension-banner,#juejin > div.recommend-box,#juejin > div.view-container > main > div > div.main-area.article-area > div.action-box.action-bar",
      style: `@media print {
                article {
                    position: absolute; top: 0; left: 0;z-index: 999;
                    max-width: 1080px;
                    min-width: 1080px;
                    width: 1080px;
            }
        }`
    }
  ];
  const segmentfaultRules = [
    {
      url: "https?://segmentfault.com/a/\\w+",
      name: "segmentfault",
      class: ".fix-bottom-action-wrap, nav, .right-side, .sticky-wrap, #comment-area, div.article-content div.card.mt-4",
      style: `@media print {
                .fmt pre {max-height: unset !important;
            }
        }`,
      copyright: ""
    }
  ];
  const weixinRules = [
    {
      url: "https?://mp.weixin.qq.com/s(\\?|/)\\w+",
      name: "weixin",
      class: "#js_base_container > div.rich_media_area_extra, #js_pc_qr_code",
      style: `@media print {}`
    }
  ];
  const zhihuRules = [
    {
      url: "https?://zhuanlan.zhihu.com/p/\\w+",
      name: "zhihu",
      class: ".Catalog, .ColumnPageHeader-Wrapper, .RichContent-actions, .RichContent-actions, .Post-NormalSub, .Post-SideActions, .complementary, .CornerAnimayedFlex",
      style: `@media print {
                article > div, article > header {
                    max-width: 1080px;
                    min-width: 1080px;
                    width: 1080px;
            }
        }`
    },
    // 知乎提问板块-指定某个回答
    {
      url: "https?://www.zhihu.com/question/\\d+/answer/\\d+",
      name: "zhihu",
      class: ".AppHeader, .Question-sideColumn, .ContentItem-actions, .CornerButtons, .MoreAnswers, .ViewAll",
      style: `
        @media print {
            .ListShortcut{
                width: 100%;
            }
            .Question-mainColumn {
                width: 100%;
            }
        }`
    },
    {
      url: "https?://www.zhihu.com/question/\\d+",
      name: "zhihu",
      class: ".AppHeader, .Question-sideColumn, .ContentItem-actions, .CornerButtons, .MoreAnswers, .ViewAll",
      style: `
        @media print {
            .ListShortcut{
                width: 100%;
            }
            .Question-mainColumn {
                width: 100%;
            }
        }`,
      hideDefault: true,
      javascript: () => {
        var _a;
        const listItem = (_a = document.querySelector(".AnswersNavWrapper")) == null ? void 0 : _a.querySelectorAll(".List-item");
        if (!listItem)
          return false;
        const printItem = (doc, url) => {
          if (!doc)
            return false;
          window.open(
            url,
            "_blank",
            "width=1080,height=800,menubar=yes,scrollbars=yes,resizable=yes"
          );
        };
        listItem.forEach((item, index) => {
          var _a2, _b;
          const box = item.querySelector(".AnswerItem-authorInfo");
          const name = (_b = (_a2 = item.querySelector(".AnswerItem")) == null ? void 0 : _a2.attributes.getNamedItem("name")) == null ? void 0 : _b.value;
          if (!box || !name)
            return false;
          const printBtn = document.createElement("button");
          printBtn.innerHTML = "打印当前回答";
          printBtn.className = "print-btn_" + index;
          const url = window.location.href + `/answer/${name}`;
          printBtn.onclick = () => {
            printItem(item, url);
          };
          box.append(printBtn);
        });
      }
    }
  ];
  const rules = [
    ...bilibiliRules,
    ...cnblogsRules,
    ...csdnRules,
    ...jianshuRules,
    ...juejinRules,
    ...segmentfaultRules,
    ...weixinRules,
    ...zhihuRules
  ];
  const _sfc_main = /* @__PURE__ */ vue.defineComponent({
    __name: "App",
    setup(__props) {
      const dragDomRef = vue.ref();
      let draggableInstance;
      const isShow = vue.ref(false);
      function preventDefault(e) {
        e.preventDefault();
      }
      const action = () => {
        stopScroll.value = false;
        isShow.value = true;
        window.addEventListener("wheel", preventDefault, { passive: false });
        window.scrollTo(0, 0);
        smoothScrollToBottom();
      };
      const stopScroll = vue.ref(false);
      const stop = () => {
        stopScroll.value = true;
      };
      function smoothScrollToBottom() {
        var scrollHeight = document.body.scrollHeight;
        var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
        var clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
        if (stopScroll.value) {
          isShow.value = false;
          window.removeEventListener("wheel", preventDefault);
          return;
        }
        if (scrollTop + clientHeight >= scrollHeight) {
          isShow.value = false;
          window.removeEventListener("wheel", preventDefault);
          setTimeout(() => {
            window.print();
          }, 200);
          return;
        } else {
          window.scrollTo({
            top: scrollTop + clientHeight,
            behavior: "auto"
            // 可以设置成光滑的
          });
          setTimeout(smoothScrollToBottom, 1e3);
        }
      }
      const currentUrl = window.location.href;
      let curr = {
        url: "",
        name: "",
        class: "",
        style: "",
        /** 版权信息将插入的css选择器 */
        target: "body"
      };
      const flag = vue.ref(false);
      vue.onMounted(() => {
        flag.value = rules.some((e) => {
          var reg = new RegExp(e.url);
          if (reg.test(currentUrl)) {
            curr.url = currentUrl;
            curr.name = e.name;
            curr.class = e.class;
            curr.style = e.style;
            curr.target = e.copyrightTarget || "body";
            curr.js = e.javascript;
            curr.hideDefault = e.hideDefault;
            return true;
          }
        });
        if (flag.value) {
          var style2 = document.createElement("style");
          style2.innerHTML = `@media print {${curr.class}, .mod, button.setpdf, .web-article-to-pdf { display: none!important;} div.setpdf-copyright { display: block!important; }} ${curr.style}`;
          document.head.appendChild(style2);
          const target = document.querySelector(curr.target);
          const copyright = document.createElement("div");
          copyright.setAttribute("style", "display:none");
          copyright.setAttribute("class", "setpdf-copyright");
          copyright.appendChild(document.createTextNode("来源:"));
          const link = document.createElement("a");
          link.textContent = currentUrl;
          link.setAttribute("href", currentUrl);
          copyright.appendChild(link);
          target.appendChild(copyright);
          setTimeout(() => {
            draggableInstance = new useDraggable(dragDomRef.value.$el);
            curr.js && (() => {
              curr.js();
            })();
          }, 200);
          curr.hideDefault && (flag.value = false);
        }
      });
      vue.onUnmounted(() => {
        draggableInstance.destroy();
      });
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
          vue.withDirectives(vue.createVNode(button1, {
            onClick: action,
            class: "setpdf",
            text: "导出PDF",
            draggable: "true",
            ref_key: "dragDomRef",
            ref: dragDomRef
          }, null, 512), [
            [vue.vShow, flag.value]
          ]),
          vue.withDirectives(vue.createVNode(loading1, { onDblclick: stop }, null, 512), [
            [vue.vShow, flag.value && isShow.value]
          ])
        ], 64);
      };
    }
  });
  const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-af70e9ff"]]);
  vue.createApp(App).mount(
    (() => {
      const app = document.createElement("div");
      app.classList.add("web-article-to-pdf");
      document.body.append(app);
      return app;
    })()
  );

})(Vue);