Greasy Fork

Greasy Fork is available in English.

WELearn英语网课答案显示

悬浮窗显示选择题、填空题、判断题、连线题答案,口语参考;

当前为 2020-05-13 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         WELearn英语网课答案显示
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description  悬浮窗显示选择题、填空题、判断题、连线题答案,口语参考;
// @author       SSmJaE
// @match        https://course.sflep.com/*
// @match        https://welearn.sflep.com/*
// @match        https://centercourseware.sflep.com/*
// @grant        GM_xmlhttpRequest
// @connect      *
// @license      MIT
// @compatible   chrome
// ==/UserScript==
(function () {
    'use strict';
    const USER_SETTINGS = {
        // autoSolve: false, //自动答题开关,还未完成
        // solveInterval: 3000, //自动答题间隔,单位毫秒
        checkInterval: 3000, //答案查询间隔,单位毫秒;多久检测一次页面是否改变
        showReference: true, //是否显示听力、口语参考
        containerColor: 'rgba(255,255,255,0.95)', //悬浮窗背景色
        debugMode: false, //调试用,正常使用不用开
    };
    var container, title, bufferUrl, bufferTag, realOrder = 1;
    const PARSER = new DOMParser();
    const MANIFEST = [
        'new%20century%20college%20english%20secedition%20integration%201',
        'new%20century%20college%20english%20secedition%20integration%202',
        'new%20century%20college%20english%20secedition%20integration%203',
        'new%20century%20college%20english%20secedition%20integration%204',
        'an%20integrated%20skills%20course%20(2nd%20edition)%201%20for%20vocational%20college%20english',
        'an%20integrated%20skills%20course%20(2nd%20edition)%202%20for%20vocational%20college%20english',
        'an%20integrated%20skills%20course%20(2nd%20edition)%203%20for%20vocational%20college%20english',
        'an%20integrated%20skills%20course%20(2nd%20edition)%204%20for%20vocational%20college%20english',
        'an%20integrated%20skills%20course%201',
        'an%20integrated%20skills%20course%202',
    ];
    const ORIGIN = [
        'new%20target%20college%20english%20integrated%20course%201',
        'new%20target%20college%20english%20integrated%20course%202',
        'new%20target%20college%20english%20integrated%20course%203',
        'new%20target%20college%20english%20integrated%20course%204',
        'new%20progressive%20college%20english%20integrated%20course%201',
        'new%20progressive%20college%20english%20integrated%20course%202',
        'new%20progressive%20college%20english%20integrated%20course%203',
        'new%20progressive%20college%20english%20integrated%20course%204',
    ]
    const ANSWER_TYPES = [
        'et-tof', //判断题
        'et-blank', //问答题+填空题
        'et-select', //下拉选择题
        'et-choice', //选择题(二选一,多选)
        'et-matching', //连线题
        'et-reference', //口语参考
        'wordDeclaration', //高职单词测试
        'correctresponse value', //identifier类型
    ];

    function create_container() {
        container = document.createElement('div');
        container.id = 'container';
        container.setAttribute('style', "top: 100px; left: 100px; margin: 0 auto; z-index: 99; border-radius: 8px;" +
            " box-shadow: 0 11px 15px -7px rgba(0,0,0,.2), 0 24px 38px 3px rgba(0,0,0,.14), 0 9px 46px 8px rgba(0,0,0,.12);" +
            " position: absolute; background:" + USER_SETTINGS.containerColor + "; width=400px;min-width: 150px;max-width:400px; max-height: 500px; min-height: 100px;overflow:auto;")
        container.style.visibility = 'hidden';
        if (!top.document.querySelector('#container')) {
            top.document.body.appendChild(container);
        }

        title = document.createElement('div');
        title.textContent = '参考答案';
        title.setAttribute("style", "background: rgba(0,0,0,0); height: 25px; margin-top: 10px; text-align: center; font-size: x-large;cursor:move;");
        container.appendChild(title);
    }

    function drag_box(drag, wrap) {
        function getCss(ele, prop) {
            return parseInt(window.getComputedStyle(ele)[prop]);
        }

        var initX, initY,
            dragable = false,
            wrapLeft = getCss(wrap, "left"),
            wrapRight = getCss(wrap, "top");

        drag.addEventListener("mousedown", function (e) {
            dragable = true;
            initX = e.clientX;
            initY = e.clientY;
        }, false);

        document.addEventListener("mousemove", function (e) {
            if (dragable === true) {
                var nowX = e.clientX,
                    nowY = e.clientY,
                    disX = nowX - initX,
                    disY = nowY - initY;
                wrap.style.left = wrapLeft + disX + "px";
                wrap.style.top = wrapRight + disY + "px";
            }
        });

        drag.addEventListener("mouseup", function (e) {
            dragable = false;
            wrapLeft = getCss(wrap, "left");
            wrapRight = getCss(wrap, "top");
        }, false);

    };

    function empty_container() {
        container.innerHTML = '';
        container.appendChild(title);
        is_show();
    }

    function is_show() {
        container.childNodes.length > 1 ? container.style.visibility = 'visible' : container.style.visibility = 'hidden';
        realOrder = 1;
        bufferTag = undefined;
    }

    function get_current_url() {
        let currentUrl;
        try {
            currentUrl = document.querySelector('div.courseware_main_1').firstElementChild.src;
        } catch (error) {
            currentUrl = top.frames[0].location.href;
        }
        return currentUrl;
    }

    function is_change() {
        let currentUrl = get_current_url();
        if (currentUrl != bufferUrl) {
            empty_container();
            determine_course_type(currentUrl);
        }
        bufferUrl = currentUrl;
    }

    function determine_course_type(answerUrl) {
        let courseInfo = /com\/(.*?)\//.exec(answerUrl)[1];
        let identifier;
        try {
            identifier = /#(.*)\?/.exec(answerUrl)[1];
        } catch (error) {}

        if (MANIFEST.includes(courseInfo)) { //需要查询名单
            let manifestUrl = 'https://centercourseware.sflep.com/' + courseInfo + '/resource/manifest.xml';
            query_manifest(manifestUrl, identifier, courseInfo);
        } else if (ORIGIN.includes(courseInfo)) { //直接在原始页面查找
            setTimeout(() => {
                let answers = top.frames[0].document.querySelectorAll('[data-solution]');
                add_to_container('', answers);
                is_show();
            }, 2000);
        } else { //默认(视听说)
            answerUrl = 'https://centercourseware.sflep.com/' + courseInfo + '/data' + identifier + '.html';
            send_ajax_request(answerUrl);
        }
    }

    function query_manifest(manifestUrl, identifier, courseInfo) {
        GM_xmlhttpRequest({
            method: "GET",
            url: manifestUrl,
            onload: response => {
                let html = response.response;
                let htmlDOM = PARSER.parseFromString(html, 'text/html');
                let selector = 'resource[identifier="' + identifier + '"] file';
                let resource = htmlDOM.querySelector(selector).getAttribute('href').replace('.html', '.xml').replace('.htm', '');
                let answerUrl = 'https://centercourseware.sflep.com/' + courseInfo + '/' + resource;
                send_ajax_request(answerUrl);
            }
        });
    }

    function send_ajax_request(answerUrl) {
        GM_xmlhttpRequest({
            method: "GET",
            url: answerUrl,
            onload: response => {
                let html = response.response;
                let htmlDOM = PARSER.parseFromString(html, 'text/html');
                if (USER_SETTINGS.debugMode) {
                    console.log(htmlDOM);
                }
                parse_ajax_response(htmlDOM);
            }
        });
    }

    function parse_ajax_response(htmlDOM) {
        empty_container();
        ANSWER_TYPES.map(answerType => htmlDOM.querySelectorAll(answerType)).forEach(answers => add_to_container(htmlDOM, answers));
        is_show();
    }

    function isRepeat(answerNode) {
        let parentElement = answerNode,
            parentTag;
        let webFlag = false;
        let mobileFlag = false;
        try {

            for (let i = 0; i < 7; i++) {
                if (i > 0) {
                    parentElement = parentElement.parentElement;
                }
                parentTag = parentElement.tagName;
                if (USER_SETTINGS.debugMode) console.log(parentTag);
                if (parentTag == 'ET-MOBILE-ONLY') {
                    mobileFlag = true;
                }
                if (parentTag == 'ET-WEB-ONLY') {
                    webFlag = true;
                }
            }
        } catch (error) {
            if (USER_SETTINGS.debugMode) console.log(error);
        } finally {

            if (webFlag && mobileFlag) { //针对web下嵌套mobile的题目,如视听说2的3-2-3
                return false;
            } else if (webFlag) { //web和mobile只留其一,这里保留mobile,丢弃web
                return true;
            } else {
                return false;
            }
        }
    }

    function add_to_container(htmlDOM, answers) {
        if (answers.length > 0) {
            for (let i = 0; i < answers.length; i++) {
                if (USER_SETTINGS.debugMode) {
                    console.log(answers[i]);
                }
                let content = document.createElement('div');
                let hr = document.createElement('hr');
                // let br = document.createElement('br')
                let parentTag, web = false;
                let tag = answers[i].tagName;
                switch (tag) {
                    case 'ET-BLANK':
                        if (isRepeat(answers[i])) continue;
                        content.textContent = answers[i].textContent.split("|")[0];
                        if (USER_SETTINGS.autoSolve) {
                            //et-blank span span[style].value填空题
                            //
                            //et-blank span.blank
                            //et-blank textarea.blank问答题
                            //对于et类型,都先清空在输入,因为可能重复显示
                            //是否有必要实现逐个单词录入效果?
                            //是否有必要实现打字音效
                        }
                        break;
                    case 'ET-TOF':
                        //et-tof span.controls
                    case 'ET-SELECT':
                    case 'ET-CHOICE':
                        if (isRepeat(answers[i])) //针对有只有inline的情况(视听说2 4-2),也就是说,不能跳
                            if (answers[i].hasAttribute('inline')) continue; //针对视听说2 7-1重复,



                        //et-choice li.textContent
                        //多选,需要分块解决tbody et-choice div span
                    case 'ET-MATCHING':
                        if (USER_SETTINGS.debugMode) console.log(isRepeat(answers[i]))
                        if (isRepeat(answers[i])) {
                            continue;
                        }
                        content.textContent = answers[i].getAttribute('key');
                        try {

                            if (!content.textContent.length) content.textContent = answers[i].firstElementChild.textContent;
                        } catch (error) {
                            content.textContent = '无固定答案';
                        }
                        break;
                    case 'ET-REFERENCE':
                        if (!USER_SETTINGS.showReference) continue;
                    case 'WORDDECLARATION':
                        content.innerHTML = answers[i].innerHTML;
                        break;
                    case 'VALUE':
                        (() => {
                            let identifier = answers[i].textContent;
                            if (identifier.length == 36) { //选择题
                                if (answers[i].textContent.length == 36) {
                                    let selector = '[identifier="' + identifier + '"]';
                                    try {
                                        content.textContent = htmlDOM.querySelector(selector).textContent;
                                    } catch (error) {
                                        content.textContent = answers[i].textContent; //高职第七八单元填空
                                    }
                                } else { //高职,非精编,综合,单元测试
                                    content.textContent = answers[i].textContent;
                                }

                            } else if (identifier.length > 200) { //纠错题
                                let selectors = identifier.split(',');
                                for (let i = 0; i < selectors.length; i++) {
                                    let selector = '[identifier="' + selectors[i] + '"]';
                                    content.innerHTML += htmlDOM.querySelector(selector).textContent + "<br>";
                                }
                            } else { //填空题
                                content.textContent = answers[i].textContent;
                            }
                            //input[onfocus].value填空
                            //label[for].textContent选择
                            //document.querySelectorAll('input[style]')直接与identifier对应选择
                            //textarea[onchange]
                        })();
                        break;
                    default:
                        (() => {
                            if (answers[i].hasAttribute('data-solution')) {
                                let answer = answers[i].getAttribute('data-solution');
                                if (!answer.length) {
                                    try {
                                        content.textContent = answers[i].firstElementChild.textContent;
                                    } catch (error) {
                                        content.textContent = answers[i].textContent;
                                    }
                                } else {
                                    content.textContent = answer;
                                }
                            }
                        })();
                        //ul[data-itemtype] li 选择题
                        //input[data-itemtype]填空题
                        //最好有通解
                        break;
                }
                if (content.textContent.length) {
                    let order; //控制序号的宽度一致
                    if (realOrder < 10) {
                        order = '  ' + String(realOrder);
                    } else {
                        order = String(realOrder);
                    }
                    content.textContent = order + '、' + content.textContent;
                    realOrder += 1;
                } else {
                    continue;
                }
                content.setAttribute('style', "margin: 10px 10px; color: orange; font-size: medium;" +
                    "font-family:Georgia, 'Times New Roman', Times, serif;white-space:pre-wrap; "); //
                if ((bufferTag !== tag) && (bufferTag !== undefined)) {
                    // content.innerHTML = '<hr>' += content.innerHTML;
                    container.appendChild(hr);
                }
                container.appendChild(content);

                content.offsetWidth; //强制浏览器刷新悬浮窗宽度
                bufferTag = tag;
            }
        }
    }

    create_container();
    drag_box(title, container);
    is_change();
    setInterval(is_change, USER_SETTINGS.checkInterval);
})();