计蒜客助手 Jisuanke Helper

1. 取消复制限制; 2. 选择题显示序号(一般情况最小的几个是答案); 3. 跳过强制等待; 4. 双击单行或块代码区域复制代码; 5. 跳过点击直接复制提示内容

// ==UserScript==
// @name         计蒜客助手 Jisuanke Helper
// @namespace    http://tampermonkey.net/
// @version      1.2.2
// @description  1. 取消复制限制; 2. 选择题显示序号(一般情况最小的几个是答案); 3. 跳过强制等待; 4. 双击单行或块代码区域复制代码; 5. 跳过点击直接复制提示内容
// @author       yusanshi
// @source       https://gist.github.com/yusanshi/981b5926851d4cde3d67536b279cbf34
// @match        http://www.jisuanke.com/course/*
// @match        https://www.jisuanke.com/course/*
// @grant        none
// @run-at       document-start
// @require      https://cdn.jsdelivr.net/gh/colxi/getEventListeners/src/getEventListeners.min.js
// ==/UserScript==

/* eslint-disable no-use-before-define */
/* eslint-disable strict */

(function () {
  'use strict';

  // Run continually
  setInterval(() => {
    // Remove disabled and oncopy attribute
    removeCls('jsk-disabled');
    ['disabled', 'oncopy', 'oncut'].forEach((elem) => {
      removeAttr(elem);
    });

    // Delete line numbers to avoid its being copied
    ['#guide', '#container-content'].forEach((selector) => {
      document.querySelectorAll(
        `${selector} .CodeMirror-linenumber`,
      ).forEach((elem) => elem.remove());
    });


    // Remove mousedown event handler
    ['#guide', '#container-content'].forEach((selector) => {
      document.querySelectorAll(
        `${selector} .CodeMirror-scroll`,
      ).forEach((elem) => { customRemoveEventLister(elem, 'mousedown'); });
    });


    // Remove selectstart event handler
    ['#guide', '#container-content'].forEach((selector) => {
      document.querySelectorAll(
        `${selector} .CodeMirror-lines`,
      ).forEach((elem) => {
        customRemoveEventLister(elem.querySelector('div'), 'selectstart');
      });
    });


    // Double click to copy
    ['#guide', '#container-content'].forEach((i) => {
      ['code', '.CodeMirror-scroll'].forEach((j) => {
        document.querySelectorAll(`${i} ${j}`).forEach((elem) => {
          elem.ondblclick = () => {
            copyTextWithFeedback(elem.innerText);
          };
        });
      });
    });

    // Skip waiting
    document.querySelectorAll('[data-unlocked]').forEach((elem) => {
      elem.setAttribute('data-unlocked', '999');
    });
  }, 500);

  window.onload = () => {
    // Add number prompts for multiple choice problem
    document.querySelectorAll('[num]').forEach((elem) => {
      const span = document.createElement('span');
      span.style.color = 'green';
      span.innerText = elem.getAttribute('num');
      elem.insertAdjacentElement('afterbegin', span);
    });

    // Skip clicking hint to copy its text directly
    const hint = document.querySelector('#hint');
    if (hint) {
      const button = document.createElement('button');
      button.innerText = 'Copy hint directly';
      button.className = 'jsk-btn jsk-btn-success hint-btn';
      button.style.marginBottom = '0.5rem';
      button.onclick = () => {
        // Simulate clicking and closing the popup window, in order to
        // make hint.querySelector('.CodeMirror-scroll') fullfilled.
        // Looks argly, a better approach may exist
        document.querySelector('#hint-btn').click();
        hint.querySelector('.jsk-close').click();
        hint.querySelectorAll('.CodeMirror-linenumber').forEach((elem) => {
          elem.remove();
        });
        // Copy last code area if multiple code areas in #hint are present
        const allCode = hint.querySelectorAll('.CodeMirror-scroll');
        copyTextWithFeedback(allCode[allCode.length - 1].innerText);
      };
      hint.insertAdjacentElement('afterbegin', button);
    }
  };

  function customRemoveEventLister(target, listenerType) {
    const listeners = target.getEventListeners(listenerType);
    if (typeof listeners !== 'undefined') {
      listeners.forEach((event) => {
        target.removeEventListener(
          event.type,
          event.listener,
          event.useCapture,
        );
      });
    }
  }


  function removeCls(cls) {
    document.querySelectorAll(`.${cls}`).forEach((elem) => {
      elem.classList.remove(cls);
    });
  }

  function removeAttr(attr) {
    document.querySelectorAll(`[${attr}]`).forEach((elem) => {
      elem.removeAttribute(attr);
    });
  }

  function copyTextWithFeedback(text) {
    copyTextToClipboardAsync(text).then(
      () => {
        // window.alertSuccess('Copied successfully');
      },
      () => {
        window.alertError('Failed to copy');
      },
    );
  }

  // Based on https://stackoverflow.com/questions/400212/how-do-i-copy-to-the-clipboard-in-javascript
  function copyTextToClipboardAsync(text) {
    if (navigator.clipboard) {
      return navigator.clipboard.writeText(text);
    }
    // Async copying is not available, turn to sync copying
    return fallbackCopyTextToClipboard(text)
      ? Promise.resolve()
      : Promise.reject();
  }

  function fallbackCopyTextToClipboard(text) {
    const textArea = document.createElement('textarea');
    textArea.value = text;
    textArea.style.position = 'fixed'; // avoid scrolling to bottom
    document.body.appendChild(textArea);
    textArea.focus();
    textArea.select();
    let returnValue;
    try {
      document.execCommand('copy');
      returnValue = true;
    } catch (err) {
      returnValue = false;
    }
    document.body.removeChild(textArea);
    return returnValue;
  }
}());

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址