Greasy Fork is available in English.

juejin掘金小帮手

掘金小帮手:掘金纯净复制、掘金纯净小册阅读、添加掘金快捷键(cmd+e/esc/p]进入编辑模式/返回上一页/发布文章)、hover自动拉出头像菜单

// ==UserScript==
// @name         juejin掘金小帮手
// @name:zh-CN   掘金小帮手:掘金纯净复制、掘金纯净小册阅读、添加掘金快捷键(cmd+e/esc/p]进入编辑模式/返回上一页/发布文章)、hover自动拉出头像菜单
// @namespace    http://tampermonkey.net/
// @version      0.6.0
// @description  掘金小帮手:掘金纯净复制、掘金纯净小册阅读、添加掘金快捷键(cmd+e/esc/p]进入编辑模式/返回上一页/发布文章)、hover自动拉出头像菜单
// @author       zzailianlian
// @require      https://code.jquery.com/jquery-3.5.1.min.js
// @match        *://juejin.cn/*
// @match        *://juejin.im/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=meitun-test.com
// @license      MIT
// @run-at       document-idle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addElement
// ==/UserScript==

(function () {
  'use strict';

  // 沉浸式小册阅读
  const openJuejinPamphlethelper = () => {
    if (/juejin\.[cnim]{2}.+\/section/.test(window.location.href)) {
      // 沉浸式处理
      function immersion(type = 'active') {
        // 处理沉浸式时要处理的dom列表
        const displayDoms = [
          {
            observer: () => document.querySelector('.book-summary'),
            action: () => {
              document.querySelector('.book-summary').style.display = 'none';
            },
            unset: () => {
              document.querySelector('.book-summary').style = '';
            },
          },
          {
            observer: () => document.querySelector('.book-content__header'),
            action: () => {
              document.querySelector('.book-content__header').style.display = 'none';
            },
            unset: () => {
              document.querySelector('.book-content__header').style = '';
            },
          },
          {
            observer: () => document.querySelector('.book-comments'),
            action: () => {
              document.querySelector('.book-comments').style.display = 'none';
            },
            unset: () => {
              document.querySelector('.book-comments').style = '';
            },
          },
          {
            observer: () => document.querySelector('.book-body'),
            action: () => {
              document.querySelector('.book-body').style.paddingTop = '0';
            },
            unset: () => {
              document.querySelector('.book-body').style = '';
            },
          },
          {
            observer: () => document.querySelector('.book-content'),
            action: () => {
              document.querySelector('.book-content').style.marginLeft = '0';
            },
            unset: () => {
              document.querySelector('.book-content').style = '';
            },
          },
          {
            observer: () => document.querySelector('.book-section-view'),
            action: () => {
              document.querySelector('.book-section-view').style.maxWidth = 'unset';
            },
            unset: () => {
              document.querySelector('.book-section-view').style = '';
            },
          },
          {
            observer: () => document.querySelector('.book-handle'),
            action: () => {
              document.querySelector('.book-handle').style.maxWidth = 'unset';
              document.querySelector('.book-handle').style.marginLeft = '0';
            },
            unset: () => {
              document.querySelector('.book-handle').style = '';
            },
          },
        ];
        displayDoms.map(dom => {
          loopDom(dom, type);
        });
      }
      const openImmersion = () => immersion('active');
      const closeImmersion = () => immersion('disabled');

      // 沉浸式控制按钮
      loopDom({
        observer: () => document.querySelector('.book-handle'),
        action: () => {
          // 干掉document的title,让阅读不被others打扰
          document.title = 'LinStaMIDIAccess';

          document.querySelector('.book-handle').style.maxWidth = 'unset';
          document.querySelector('.book-handle').style.marginLeft = '0';

          const bookHandle = document.querySelector('.book-handle');
          let isTrigger = false;
          const immersionBtn = document.createElement('div');
          immersionBtn.innerHTML = '恢复';
          immersionBtn.style = `background-color:#007fff;border-radius:50%;width:50px;height:50px;display:flex;justify-content:center;align-items:center;z-index:10;cursor:pointer;color:white;position:absolute;left:10px;font-size:14px;bottom:70px;`;
          immersionBtn.onclick = function () {
            console.log('isTrigger', isTrigger);
            if (isTrigger) {
              // 沉浸阅读界面
              openImmersion();
              immersionBtn.innerHTML = '恢复';
            } else {
              // 默认展示界面
              closeImmersion('disabled');
              immersionBtn.innerHTML = '沉浸';
            }
            isTrigger = !isTrigger;
          };
          immersionBtn.classList.add('step-btn', 'step-btn--prev');
          bookHandle.insertBefore(immersionBtn, bookHandle.firstChild);
        },
      });

      // 沉浸式展示页面
      openImmersion();
    }
  };

  //

  window.onload = () => {
    console.log('我是onload');
    // 复制去除后缀
    [...document.querySelectorAll('*')].forEach(
      item =>
        (item.oncopy = function (e) {
          e.stopPropagation();
        })
    );
    // 开启沉浸式小册阅读
    openJuejinPamphlethelper();
    // 允许鼠标移动到头像后自动弹出来菜单
    avatarHoverHandle();
    // 注册掘金快捷键
    dispatchKeyCode();
  };

  function dispatchKeyCode() {
    // 键入 cmd+e 进入文章编辑页面
    if (/juejin.cn\/post/.test(window.location.href)) {
      document.onkeydown = function (event) {
        var e = event || window.event;
        const isMetaKey = e.metaKey;
        // 69是esc
        if (isMetaKey && e.keyCode === 69) {
          document.querySelector('.edit-btn').click();
        }
      };
    }
    if (/juejin.cn\/editor\/drafts/.test(window.location.href)) {
      document.onkeydown = function (event) {
        const isFocusEditor = document.activeElement.tagName === 'TEXTAREA';
        var e = event || window.event;

        const isMetaKey = e.metaKey;
        // 键入 cmd+esc 返回上一页 27是esc
        if (isMetaKey && e.keyCode === 27) {
          history.back();
        }
        // 键入 cmd+p 开始发布 80是p
        if (isMetaKey && e.keyCode === 80) {
          e.preventDefault();
          document.querySelector('.publish-popup .xitu-btn').click();
        }
      };
    }
  }

  // 重写pushState与repalceState方法来监听url变化
  var _wr = function (type) {
    var orig = history[type];
    return function () {
      var rv = orig.apply(this, arguments);
      var e = new Event(type);
      e.arguments = arguments;
      window.dispatchEvent(e);
      return rv;
    };
  };

  window.addEventListener('replaceState', function (e) {
    console.log('监听自定义replaceState', e);
    // 开启沉浸式小册阅读
    openJuejinPamphlethelper();
    // 允许鼠标移动到头像后自动弹出来菜单
    avatarHoverHandle();
  });
  window.addEventListener('pushState', function (e) {
    console.log('监听自定义pushState', e);
    // 开启沉浸式小册阅读
    openJuejinPamphlethelper();
    // 允许鼠标移动到头像后自动弹出来菜单
    avatarHoverHandle();
  });

  history.pushState = _wr('pushState');
  history.replaceState = _wr('replaceState');

  // 头像hover事件
  function avatarHoverHandle() {
    loopDom({
      observer: () => document.querySelector('.avatar') && document.querySelector('.nav-item.menu'),
      action: () => {
        const avatarImg = document.querySelector('.avatar');
        const avatarMenuItem = document.querySelector('.nav-item.menu');
        const getIsAvatarMenuShow = () => document.querySelector('.avatar-wrapper').nextElementSibling;
        function mouseenterHandle() {
          if (!getIsAvatarMenuShow()) {
            avatarImg.click();
          }
        }
        function mouseleaveHandle() {
          if (getIsAvatarMenuShow()) {
            avatarImg.click();
          }
        }
        avatarMenuItem && avatarMenuItem.addEventListener('mouseenter', mouseenterHandle);
        avatarMenuItem && avatarMenuItem.addEventListener('mouseleave', mouseleaveHandle);
      },
    });
  }

  function loopDom({ observer, action = () => {}, unset = () => {} }, type = 'active') {
    const hadnler = () => {
      if (type === 'active') {
        action();
      } else {
        unset();
      }
    };
    if (observer()) {
      hadnler();
    }
    const interval = setInterval(() => {
      if (observer()) {
        hadnler();
        clearInterval(interval);
      }
    }, 200);
  }
})();