计蒜客助手 Jisuanke Helper

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

当前为 2020-02-22 提交的版本,查看 最新版本

// ==UserScript==
// @name         计蒜客助手 Jisuanke Helper
// @namespace    http://tampermonkey.net/
// @version      1.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==

(function () {
    'use strict';

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

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

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

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

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

        // Skip waiting
        for (let elem of document.querySelectorAll('[data-unlocked]')) {
            elem.setAttribute('data-unlocked', '999');
        }

    }, 500);


    window.onload = () => {
        // Add number prompts for multiple choice problem
        for (let elem of document.querySelectorAll('[num]')) {
            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();
                for (let elem of hint.querySelectorAll('.CodeMirror-linenumber')) {
                    elem.remove();
                }
                // Copy last code area if multiple code areas in #lint 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') {
            for (let event of listeners) {
                target.removeEventListener(event.type, event.listener, event.useCapture);
            }
        }
    }

    function removeCls(cls) {
        for (let elem of document.querySelectorAll(`.${cls}`)) {
            elem.classList.remove(cls);
        }
    }

    function removeAttr(attr) {
        for (let elem of document.querySelectorAll(`[${attr}]`)) {
            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);
        } else {
            // 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;
    }

})();