WELearn英语网课答案显示

悬浮窗显示选择题、填空题答案,口语参考文本,支持单元测试

当前为 2020-03-27 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         WELearn英语网课答案显示
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  悬浮窗显示选择题、填空题答案,口语参考文本,支持单元测试
// @author       SSmJaE
// @match        https://course.sflep.com/*
// @match        https://centercourseware.sflep.com/*
// @match        https://welearn.sflep.com/*
// @grant        GM_xmlhttpRequest
// @connect      *
// @license      MIT
// ==/UserScript==
(function () {
    'use strict';

    var container, title, bufferUrl;
    var parser = new DOMParser();
    const manifest = [
        'new%20century%20college%20english%20secedition%20integration%202',
        'an%20integrated%20skills%20course%20(2nd%20edition)%202%20for%20vocational%20college%20english',
        'an%20integrated%20skills%20course%202',
    ];
    const origin = [
        'new%20target%20college%20english%20integrated%20course%202',
        'new%20progressive%20college%20english%20integrated%20course%202',
    ]

    function createContainer() {
        container = document.createElement('div');
        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: #fff; min-width: 250px;max-width:400px; max-height: 500px; min-height: 200px;overflow:auto;")
        container.style.visibility = 'hidden';
        document.body.appendChild(container);

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

    function makeDraggable(elem) {
        document.mouseState = 'up'
        elem.mouseState = 'up'
        elem.lastMousePosY = null
        elem.lastMousePosX = null
        elem.proposedNewPosY = parseInt(elem.style.top, 10)
        elem.proposedNewPosX = parseInt(elem.style.left, 10)
        document.onmousedown = _ => {
            document.mouseState = 'down'
        }

        document.onmouseup = _ => {
            document.mouseState = 'up'
            elem.mouseState = 'up'
        }
        elem.onmousedown = e => {
            elem.lastMousePosY = e.pageY
            elem.lastMousePosX = e.pageX
            elem.mouseState = 'down'
            document.mouseState = 'down'
            document.onselectstart = e => {
                e.preventDefault()
                return false
            }
        }
        elem.onmouseup = e => {
            elem.mouseState = 'up'
            document.mouseState = 'up'
            document.onselectstart = null
        }
        const getAtInt = (obj, attrib) => parseInt(obj.style[attrib], 10)
        document.onmousemove = e => {
            if ((document.mouseState === 'down') && (elem.mouseState === 'down')) {
                elem.proposedNewPosY = getAtInt(elem.parentElement, 'top') + e.pageY - elem.lastMousePosY
                elem.proposedNewPosX = getAtInt(elem.parentElement, 'left') + e.pageX - elem.lastMousePosX
                if (elem.proposedNewPosY < 0) {
                    elem.parentElement.style.top = "0px"
                } else if (elem.proposedNewPosY > window.innerHeight - getAtInt(elem.parentElement, 'height')) {
                    elem.parentElement.style.top = window.innerHeight - getAtInt(elem.parentElement, 'height') + 'px'
                } else {
                    elem.parentElement.style.top = elem.proposedNewPosY + 'px'
                }
                if (elem.proposedNewPosX < 0) {
                    elem.parentElement.style.left = "0px"
                } else if (elem.proposedNewPosX > window.innerWidth - getAtInt(elem.parentElement, 'width')) {
                    elem.parentElement.style.left = window.innerWidth - getAtInt(elem.parentElement, 'width') + 'px'
                } else {
                    elem.parentElement.style.left = elem.proposedNewPosX + 'px'
                }
                elem.lastMousePosY = e.pageY
                elem.lastMousePosX = e.pageX
            }
        }
    }

    function addToContainer(htmlDOM, answers) {
        if (answers.length > 0) {
            for (let i = 0; i < answers.length; i++) {

                let content = document.createElement('div');
                let tag = answers[i].tagName;

                if (tag == 'ET-BLANK') {
                    content.textContent = answers[i].textContent;
                }
                if (tag == 'ET-CHOICE' || tag == 'ET-MATCHING') {
                    content.textContent = answers[i].getAttribute('key');
                }
                if (tag == 'ET-REFERENCE') {
                    content.innerHTML = answers[i].innerHTML;
                }
                if (tag == 'VALUE') {
                    let identifier = answers[i].textContent
                    if (identifier.length == 36) {
                        let selector = '[identifier="' + identifier + '"]';
                        content.textContent = htmlDOM.querySelector(selector).textContent;

                    } else {

                        content.textContent = answers[i].textContent;
                    }
                }
                if (answers[i].hasAttribute('data-solution')) {
                    let answer = answers[i].getAttribute('data-solution');
                    if (!answer.length) {
                        content.textContent = answers[i].firstElementChild.textContent;
                    } else {
                        content.textContent = answer;
                    }
                }
                content.setAttribute('style', "margin: 10px 10px; color: orange; font-size: medium; ")
                container.appendChild(content);
            }
        }
    }

    function isShow() {
        if (container.childNodes.length > 1) {
            container.style.visibility = 'visible';
        } else {
            container.style.visibility = 'hidden';
        }
    }

    function parseAjax(htmlDOM) {
        container.innerHTML = '';
        container.appendChild(title);
        let blank = htmlDOM.querySelectorAll('et-blank'); //问答题+填空题
        let choice = htmlDOM.querySelectorAll('et-choice'); //选择题(二选一,多选)
        let matching = htmlDOM.querySelectorAll('et-matching'); //连线题
        let reference = htmlDOM.querySelectorAll('et-reference'); //口语参考
        let identifier = htmlDOM.querySelectorAll('correctresponse value'); //identifier类型
        addToContainer(htmlDOM, blank);
        addToContainer(htmlDOM, choice);
        addToContainer(htmlDOM, matching);
        addToContainer(htmlDOM, reference);
        addToContainer(htmlDOM, identifier);
        isShow();
    }




    function getManifest(manifestUrl, identifier, courseInfo) {
        let answerUrl;
        GM_xmlhttpRequest({
            method: "GET",
            url: manifestUrl,
            onload: answerUrl = 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');

                let answerUrl = 'https://centercourseware.sflep.com/' + courseInfo + '/' + resource;
                getAjax(answerUrl);
                return answerUrl;
            }
        });
        return answerUrl;
    }


    function getCurrentUrl() {
        let answerUrl = document.querySelector('div.courseware_main_1').firstElementChild.src;
        return answerUrl;
    }

    function getAjax(answerUrl) {
        GM_xmlhttpRequest({
            method: "GET",
            url: answerUrl,
            onload: response => {
                let html = response.response;
                let htmlDOM = parser.parseFromString(html, 'text/html');
                // console.log(htmlDOM);
                parseAjax(htmlDOM);
            }
        });
    }

    function getResource(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';
            let answerUrl = getManifest(manifestUrl, identifier, courseInfo);
        } else if (origin.includes(courseInfo)) {
            setTimeout(() => {
                let answers = top.frames[0].document.querySelectorAll('[data-solution]');
                addToContainer('', answers);
                isShow();
            }, 2000);
        } else {
            answerUrl = 'https://centercourseware.sflep.com/' + courseInfo + '/data' + identifier + '.html';
            getAjax(answerUrl);
        }
    }

    function isChange() {
        let currentUrl = getCurrentUrl();
        if (currentUrl != bufferUrl) {
            container.innerHTML = '';
            container.appendChild(title);
            getResource(currentUrl);
        }
        bufferUrl = currentUrl;
    }

    createContainer();
    makeDraggable(title);
    isChange();

    setInterval(isChange, 5000);
})();