Greasy Fork is available in English.

电信网上大学超级学习

更快、更全、更好用的电信网上大学(知学云zhixueyun)学习工具。

// ==UserScript==
// @name        电信网上大学超级学习
// @namespace   remain_true_to_our_original_aspiration
// @version     1.6.2
// @description 更快、更全、更好用的电信网上大学(知学云zhixueyun)学习工具。
// @author      Ghost River
// @match       https://*.zhixueyun.com/*
// @icon        data:image/gif;base64,R0lGODlhIAAgAPcAAAAAAAAAMwAAZgAAmQAAzAAA/wArAAArMwArZgArmQArzAAr/wBVAABVMwBVZgBVmQBVzABV/wCAAACAMwCAZgCAmQCAzACA/wCqAACqMwCqZgCqmQCqzACq/wDVAADVMwDVZgDVmQDVzADV/wD/AAD/MwD/ZgD/mQD/zAD//zMAADMAMzMAZjMAmTMAzDMA/zMrADMrMzMrZjMrmTMrzDMr/zNVADNVMzNVZjNVmTNVzDNV/zOAADOAMzOAZjOAmTOAzDOA/zOqADOqMzOqZjOqmTOqzDOq/zPVADPVMzPVZjPVmTPVzDPV/zP/ADP/MzP/ZjP/mTP/zDP//2YAAGYAM2YAZmYAmWYAzGYA/2YrAGYrM2YrZmYrmWYrzGYr/2ZVAGZVM2ZVZmZVmWZVzGZV/2aAAGaAM2aAZmaAmWaAzGaA/2aqAGaqM2aqZmaqmWaqzGaq/2bVAGbVM2bVZmbVmWbVzGbV/2b/AGb/M2b/Zmb/mWb/zGb//5kAAJkAM5kAZpkAmZkAzJkA/5krAJkrM5krZpkrmZkrzJkr/5lVAJlVM5lVZplVmZlVzJlV/5mAAJmAM5mAZpmAmZmAzJmA/5mqAJmqM5mqZpmqmZmqzJmq/5nVAJnVM5nVZpnVmZnVzJnV/5n/AJn/M5n/Zpn/mZn/zJn//8wAAMwAM8wAZswAmcwAzMwA/8wrAMwrM8wrZswrmcwrzMwr/8xVAMxVM8xVZsxVmcxVzMxV/8yAAMyAM8yAZsyAmcyAzMyA/8yqAMyqM8yqZsyqmcyqzMyq/8zVAMzVM8zVZszVmczVzMzV/8z/AMz/M8z/Zsz/mcz/zMz///8AAP8AM/8AZv8Amf8AzP8A//8rAP8rM/8rZv8rmf8rzP8r//9VAP9VM/9VZv9Vmf9VzP9V//+AAP+AM/+AZv+Amf+AzP+A//+qAP+qM/+qZv+qmf+qzP+q///VAP/VM//VZv/Vmf/VzP/V////AP//M///Zv//mf//zP///wAAAAAAAAAAAAAAACH5BAEAAPwALAAAAAAgACAAAAj/APcJHEiw4D5i9AwqXLiwnR9UD3nVY8iQXsKFxGr5eXWRIsFCfgrSs1XrFS93+2z54eVx4LBXqAqiKsSrly2QLDe23DfzIU2BtQbqQ/XqlcBXhDxqTGjrVch9fughpPfn51GjC3v5mbiPECquKmfSc5VqGEE/vRbaGuiwo621+3oRKtRxn0OGL/38cbWRZS+W+4rGLPgKbkGVtdwFDexnWDunM18VMkjvKeG1vP4E3ZjX1jCVdQf6aWfwMy/O9ZwWdKzW8EB6Md1F5UXIFkqplfcNa7x6cMFCw1wVmomqskaOm1G53pfbIC9Uvcz6Wcv5YNFCy5lbJuiu48WtApXNrSStcLtHVPqG9SpZEKHA5gYz+rFM6E9MrQV5ASbme2AviETB1RQqZg0FmECpAMbLYgM9R0haBM1kWCGpECJZZAhCSJCGZ2H13kaFIKWcdjsJRIxCnxVmVkoMltiSMuC5uNOILSVkkY0HMUfYZDthtg9La7mSEkEauWjLIhOl1Ys+xNQkWioyrhXUWrbUY5ZRp7W400XK0LMkc/QM9wd5MopE4Xy1rFgmZY6FtlBAADs=
// @license     GPLv3
// @grant       GM_addStyle
// @grant       GM_openInTab
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_deleteValue
// @grant       GM_listValues
// @grant       GM_addValueChangeListener
// @grant       GM_removeValueChangeListener
// @grant       window.close
// @grant       window.focus
// @grant       unsafeWindow
// ==/UserScript==

(function() {
    'use strict';
    if (!window.location.href.match('/train-new/class-detail/|/study/subject/detail/|/study/course/detail/|/study/course/out-detail/|classId')) return;
    //非学习页面退出。

    const ver = '1.6.0';

    const superCss = '.study_box{position:fixed;top:10px;left:20px;z-index:99999}.sbtn{height:40px;line-height:24px;transition:0.5s;outline:none;border:none;padding:6px 10px;border-radius:5px;cursor:pointer;color:white;font-size:18px;font-weight:bold;text-align:center;box-shadow:0 0 9px#666777}.setting_btn{background-color:#d6d6dc}.auto_btn{width:100px;background-color:#b3d9ff}.hide_btn{width:20px;background-color:#b3d9ff;padding:0px 0px}.progress_box{position:fixed;top:70px;left:10px;padding:8px 12px;background-color:#DBE5FF;box-shadow:0 0 9px#666777;display:none}.progress_item{margin-top:3px;height:30px;width:400px;font-size:16px;display:flex;justify-items:center;justify-content:space-between}.title{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;width:280px}.setting_box{position:fixed;top:70px;left:10px;z-index:99999;padding:6px 10px;border-radius:10px;background-color:#DBE5FF;box-shadow:0 0 9px#666777;display:none}.setting_item{margin-top:3px;height:30px;width:200px;font-size:16px;display:flex;justify-items:center;justify-content:space-between}input[type="checkbox"].setting_switch{cursor:pointer;margin:0;outline:0;appearance:none;-webkit-appearance:none;-moz-appearance:none;position:relative;width:40px;height:22px;background:#ccc;border-radius:50px;transition:border-color 0.3s,background-color 0.3s}input[type="checkbox"].setting_switch::after{content:"";display:inline-block;width:1rem;height:1rem;border-radius:50%;background:#fff;box-shadow:0,0,2px,#999;transition:0.4s;top:3px;position:absolute;left:3px}input[type="checkbox"].setting_switch:checked{background:#3399ff}input[type="checkbox"].setting_switch:checked::after{content:"";position:absolute;left:55%;top:3px}input[type="number"]{width:50px;height:25px}';
    GM_addStyle(superCss);

    const max = 1;
    //当前只能为1
    var lessons = [];
    var lessonsinfo = {};
    const isTrain = window.location.href.match('train|classId');
    var taskID = 0;
    var initID = '';
    var hasExam = [];
    var hasError = [];
    var studying = {};
    var count = 0;
    var listenerId = null
    const urls = { 'normal':`${window.location.origin}/#/study/course/detail/10&$id$/6/1`,
                'course':`${window.location.origin}/#/study/course/detail/11&$id$/5/1`}

    const isCourse = window.location.href.match("/study/course/detail/|/study/course/out-detail/")
    //是否学习页

    function downPDF() {
        let modleId = Object.keys(unsafeWindow.app._modules).find(value => value.startsWith('picker/pdf-filecloud--'));
        if(modleId) {
            const base64PDF = unsafeWindow.app._modules[modleId].store.models.downEncode.data;
            if (base64PDF.length>0 || unsafeWindow.app._modules[modleId].store.models.downFile.data.type == 'pdf') {
                const url = base64PDF.length>0? ('data:application/octe-stream;base64,' + base64PDF):unsafeWindow.app._modules[modleId].store.models.downEncode.options.url;
                const title =  $('.chapter-list-box.required[data-sectiontype=1]').length>1? $('.course-title-text').text()+'-':'';
                let filename = base64PDF.length>0? $('.chapter-list-box.required.focus').find('.text-overflow').text().split(':')[1].trim():unsafeWindow.app._modules[modleId].store.models.downFile.data.filename;
                filename = title + filename;
                console.log(filename);
                const a = document.createElement('a');
                a.href = url;
                a.download = filename.endsWith('.pdf')? filename:filename + '.pdf';
                a.click();
                return true;
            }
        }
        return false;
    }

    function checkStatus(){
        setTimeout(function () {
            const r = GM_getValue("g.r:task" + initID, 0);
            if ( r != taskID ) window.close();
            //学习任务页与控制页失联,有新的页面打开,关闭本任务页。
            if ($('.study-errors-page').length>0){
                $("[id$='goOnStudy'").click();
                checkStatus();
                return;
            }
            //有其他学习页打开造成学习暂停,继续学习。
            let requires = $(".item.pointer.item22[style$='margin-right:12px']");
            if(requires.length==0){
                $.post(`${window.location.origin}/api/v1/course-study/course-front/score`,`score=10&businessId=${initID}&businessType=1`);
                GM_deleteValue("g.r:task" + initID);
                if($('.chapter-list-box.required.focus[data-sectiontype=1]').length==1 && GM_getValue("g.r:downPDF", false)) {
                    if (downPDF()){
                        setTimeout(()=> {
                            GM_sendMessage('remain.true.to.our.original.aspiration', initID,"finished with exam is "+ ($('.chapter-list-box.required[data-sectiontype=9]').length>0));
                            window.close();
                        },3000);
                        return;
                    }
                }
                GM_sendMessage('remain.true.to.our.original.aspiration', initID,"finished with exam is "+ ($('.chapter-list-box.required[data-sectiontype=9]').length>0));
                window.close();
            }else{
                if(!document.title.endsWith('🟩')) document.title += '🟩';
                if($('.chapter-list-box.required.focus').find('span').length==1) {
                    if($('.chapter-list-box.required.focus[data-sectiontype=1]').length==1 && GM_getValue("g.r:downPDF", false)) {
                        if (downPDF()){
                            setTimeout(()=> {
                                $(".item.pointer.item22[style$='margin-right:12px']")[0].click();
                            },3000);
                            checkStatus();
                            return;
                        }
                    }
                    requires[0].click();
                }
                autoPlay();
                GM_sendMessage('remain.true.to.our.original.aspiration', initID,"studying|" + Date.now() + "|" + $('div[style$="margin-right:12px"]').find('span').text().match(/ \d+/g).reduce((accumulator, current)=>accumulator + parseInt(current),1));
                checkStatus()
            }
        }, 11000);
    }

    function autoPlay() {
        if($('video').length>0) {
            if ($('video')[0].onplay === null) {
                $('video')[0].onplay = function() {
                    $('video')[0].muted = true;
                };
            };
            if ($('video')[0].onpause === null) {
                $('video')[0].onpause = function() {
                    $('video')[0].play();
                };
            };
            $('video')[0].muted = true;
            $('video')[0].playbackRate = GM_getValue("g.r:speedup", false)? 1.5:1
            if( $('video')[0].paused) {
                $('video')[0].play();
            }
        }
    }

    function clearLostTasks() {
        const keys = GM_listValues();
        const nt = Date.now();
        for(let k in keys) {
            if (keys[k].startsWith('g.r:task')) {
                let id = GM_getValue(keys[k], nt);
                if((nt - id) > 43200000) GM_deleteValue(keys[k]);
            }
        }
    }

    //去除章节顺序限制
    function removeLearnSequence() {
        const modleId = Object.keys(unsafeWindow.app._modules).find(value => value.startsWith('study/course/detail--'));
        unsafeWindow.app._modules[modleId].store.models.course.data.courseChapters.forEach(function(Chapter){
            if(Chapter.learnSequence) Chapter.learnSequence = null;
        });
    }

    if( !isCourse ) {
        const r = sessionStorage.getItem(window.location.href.slice(-36));
        if (r) {
            const rhtml = JSON.parse(r);
            $(rhtml).appendTo("body");
			sessionStorage.removeItem(window.location.href.slice(-36));
        } else {
            GM_deleteValue('remain.true.to.our.original.aspiration');
            clearLostTasks();
            let notice='';
            if(ver>GM_getValue("g.r:version", '')) {
                notice = `<a href="https://greasyfork.org/zh-CN/scripts/472634-%E7%94%B5%E4%BF%A1%E7%BD%91%E4%B8%8A%E5%A4%A7%E5%AD%A6%E8%B6%85%E7%BA%A7%E5%AD%A6%E4%B9%A0" target="_blank"><span style="color:red;">⚠️超级学习 ${ver} 版有重要更新说明,去查看➡️</span></a>`;
                GM_setValue("g.r:version", ver);
            }
            $(`<div id="autostudydiv" class="study_box">
				<button id="btn_hide" type="button" class="sbtn hide_btn">&lt;</button>
                <button id="autostudy" type="button" class="sbtn auto_btn">自动学习</button>
                <button id="setting" type="button" class="sbtn setting_btn">配置</button>
                <div id="progress" class="progress_box">
                    <div><span id="msg">${notice}</span></div>
                    <hr style="margin: 2px 2px;">
                </div>
                <div id="settingbox" class="setting_box">
                    <div class="setting_item" >
                        <label>
                            专题/培训班
                        </label>
                    </div>
                    <div class="setting_item">
                        <label>
                            同时学习课程数
                        </label>
                        <input id="max" type="number" inputmode="numeric" disabled autocomplete="off" value="1" min="1" max="9" onkeypress="return (/[\d]/.test(String.fromCharCode(event.keyCode)))" style="ime-mode:Disabled">
                    </div>
                    <div class="setting_item">
                        <label title="直接更新URL类课程状态为已完成">
                            更新URL课程状态 ❓
                        </label>
                        <input class="setting_switch" type="checkbox" id="updateURL" ${GM_getValue("g.r:updateURL", false)?"checked":""}/>
                    </div>
                    <hr style="margin: 5px 5px;">
                    <div class="setting_item" >
                        <label>
                            课程
                        </label>
                    </div>
                    <div class="setting_item">
                        <label title="没启动自动学习的时候也去除章节顺序限制,本设置不影响自动学习。">
                            解除章节顺序限制 ❓
                        </label>
                        <input class="setting_switch" type="checkbox" id="removeLearnSequence" ${GM_getValue("g.r:removeLearnSequence", false)?"checked":""}/>
                    </div>
                    <div class="setting_item">
                        <label>
                            使用1.5倍速播放视频
                        </label>
                        <input class="setting_switch" type="checkbox" id="speedup" ${GM_getValue("g.r:speedup", false)?"checked":""}/>
                    </div>
                    <hr style="margin: 5px 5px;">
                    <div class="setting_item" >
                        <label>
                            PDF课件
                        </label>
                    </div>
                    <div class="setting_item" >
                        <label title="学习过程中遇到PDF文档课程,自动下载PDF课件。">
                            边学边下 ❓
                        </label>
                        <input class="setting_switch" type="checkbox" id="downPDF" ${GM_getValue("g.r:downPDF", false)?"checked":""}/>
                    </div>
                    <hr style="margin: 5px 5px;">
                    <div class="setting_item" >
                        <label>
                            外观
                        </label>
                    </div>
                    <div class="setting_item" >
                        <label>
                            默认折叠
                        </label>
                        <input class="setting_switch" type="checkbox" id="collapseBox" ${GM_getValue("g.r:collapseBox", false)?"checked":""}/>
                    </div>
                    <!--
                    <div class="setting_item" >
                        <label title="遍历当前课题(培训班)所有课程,下载所有PDF课件。">
                            下载全部课件 ❓
                        </label>
                        <input class="setting_switch" type="checkbox" id="getEmAll"}/>
                    </div>
                    -->
                    <hr style="margin: 5px 5px;">
                    <div id='saveSetting' style="color:#3399ff;border: solid 1px;justify-content:center;align-items: center;border-radius:10px;cursor: pointer;margin: 12px 0;font-size:14px;" class="setting_item">
                        <label style="cursor: pointer;">
                            保存配置
                        </label>
                    </div>
                </div>
            </div>`).appendTo("body");
            //课程列表页,增加学习按钮
            if( notice.length>0) $('#progress').show();
            $("#setting").click(function () {
                if($('#settingbox').is(':hidden')){
                    $("#settingbox").show();
                }else{
                    $("#settingbox").hide();
                }
            });
            if(GM_getValue("g.r:collapseBox", false)) {
                $("#btn_hide").text('>');
                $("#autostudy").hide();
                $("#setting").hide();
            }
        }
        $("#btn_hide").click(function () {
            if($('#autostudy').is(':hidden')){
                $(this).text('<');
                $("#autostudy").show();
                $("#setting").show();
                if ($('#msg').text().length>0) $("#progress").show();
            }else{
                $(this).text('>');
                $("#autostudy").hide();
                $("#setting").hide();
                $("#progress").hide();
                $("#settingbox").hide();
            }
        });
    } else {
        $(`<div id="autostudydiv" class="study_box"><a id="coursePDF" class="sbtn auto_btn" >下载PDF课件</a>
           <div id="progress" class="progress_box"><span class="progress_item_title" id="filename"></span>
           <span id="per" style="float:right;"></span></div></div>`).appendTo("body");
        $("#coursePDF").click(async function() {
            if($(this).text() != "下载PDF课件") return;
            $(this).text('下载PDF课件中......');
            const configs = unsafeWindow.app.global.fileCloudConfig.configs;
            const modleId = Object.keys(unsafeWindow.app._modules).find(value => value.startsWith('study/course/detail--'));
            const data = unsafeWindow.app._modules[modleId].store.models.course.data;
            const resources = data.courseChapters.reduce((accumulator, current)=>accumulator.concat(current.courseChapterSections.filter(item=>item.required&item.sectionType==1).map(item=> {return {id:item.resourceId,name:item.name}})),[]);
            //console.log(resources);
            if(resources.length==0) {
                alert('当前课程没有PDF课件');
                $("#autostudydiv").remove();
                return;
            }
            $('#progress').show();
            async function wait(ms) {
                return new Promise((resolve) => setTimeout(resolve, ms));
            }
            function getBlob(url) {
                return new Promise(resolve => {
                    const xhr = new XMLHttpRequest();
                    xhr.open('GET', url, true);
                    xhr.responseType = 'blob';
                    xhr.onload = () => {
                        if (xhr.status === 200) {
                            resolve(xhr.response);
                        }
                    };
                    xhr.send();
                });
            }
            const a = document.createElement('a');
            const title = resources.length>1? data.name+'-':'';
            let filename = '';
            for (let i=0; i< resources.length; i++) {
                let resource = resources[i];
                $('#per').text((i+1) + '/' + resources.length);
                $.get(`${window.location.origin}/api/v1/tools-center-v2/file-cloud/preview?id=${resource.id}&_=${Date.now()}`)
                    .done((data)=> {
                    if (data.configId != 'default') {
                        getBlob(`${configs[data.configId].viewUrl}${data.url}`)
                            .then(blob=> {
                            let reader = new FileReader();
                            reader.onloadend = function () {
                                let base64PDF = reader.result; // 获得转换后的字符串结果
                                a.href = 'data:application/octe-stream;base64,' + base64PDF;
                                a.download = title + resource.name + '.pdf';
                                $('#filename').text(resource.name);
                                a.click();
                            };
                            reader.readAsText(blob);
                        })
                    } else {
                        filename = data.filename;
                        $('#filename').text(filename);
                        a.href = `${configs[data.configId].viewUrl}${data.url}`;
                        a.download = filename.endsWith('.pdf')? title + filename:title + filename + '.pdf';
                        a.click();
                    }
                })
                await wait(3000);
            }
            alert('下载完成');
            $("#autostudydiv").remove();
        })
        initID = GM_getValue("g.r:current", 0);
        if(initID) GM_deleteValue("g.r:current");
        let r = GM_getValue("g.r:task" + initID, 0);
        if (r) {
            taskID = r;
            let waitApp = setInterval(()=>{
                if( window.location.href.match('error-page')) {
                    //资源不存在
                    GM_sendMessage('remain.true.to.our.original.aspiration', initID,"finished with error");
                    window.close();
                };
                try {
                    removeLearnSequence();
                }
                catch {
                    return;
                }
                clearInterval(waitApp);
                let requires = $(".item.pointer.item22[style$='margin-right:12px']");
                if(requires.length>0){
                    if($('.focus').find('div[style$="margin-right:12px"]').length == 0) {
                        requires[0].click();
                    }
                }
                document.title += '🟩';
                checkStatus();
            },1000);
            let c = 0;
            let waitPlay = setInterval(()=>{
                try {
                    //console.log('try mute')
                    if (!$('video')[0].muted) {
                        autoPlay();
                        clearInterval(waitPlay);
                    }
                }
                catch {
                    //console.log('no muted');
                    c++;
                    //console.log(c);
                    if(c>4) clearInterval(waitPlay);
                    return;
                }
            },1000);
        } else {
            console.log('非自动学习打开的页面不处理。');
            if (GM_getValue("g.r:removeLearnSequence", false)) {
                let waitApp = setInterval(()=>{
                    try {
                        removeLearnSequence();
                    }
                    catch {
                        return;
                    }
                    clearInterval(waitApp);
                },1000);
            }
        }
    }

    $("#saveSetting").click(function() {
        GM_setValue("g.r:downPDF", $('#downPDF').prop('checked'));
        GM_setValue("g.r:speedup", $('#speedup').prop('checked'));
        GM_setValue("g.r:removeLearnSequence", $('#removeLearnSequence').prop('checked'));
        GM_setValue("g.r:updateURL", $('#updateURL').prop('checked'));
        GM_setValue("g.r:collapseBox", $('#collapseBox').prop('checked'));
        //GM_setValue("g.r:getEmAll", $('#getEmAll').prop('checked'));
        $("#settingbox").hide();
    });

    function GM_onMessage(label, callback) {
        listenerId = GM_addValueChangeListener(label, function() {
            callback.apply(undefined, arguments[2]);
        });
    }

    function GM_sendMessage(label) {
        GM_setValue(label, Array.from(arguments).slice(1));
    }

    function studyFinised() {
        if(lessonsinfo == null) return;
        let resultStr = '所有课程学习完成!'
        let url = '';
        if(isTrain) {
            url = urls['course'];
        } else {
            url = urls['normal'];
        }
        let ul = '';
        if (hasError.length>0) {
            resultStr += '部分课程资源不存在!';
            hasError.forEach(function(id){
                ul += `<li><a href="${url.replace('$id$',id)}" target="_blank">${lessonsinfo[id]}</a></li>`
            })
        }
        if (hasExam.length>0) {
            resultStr += '注意!部分课程包含考试!';
            hasExam.forEach(function(id){
                ul += `<li><a href="${url.replace('$id$',id)}" target="_blank"><span style="color:red;">${lessonsinfo[id]}</span></a></li>`
            })
        }
        $("#autostudy").text('学习完成');
        $("#msg").text(resultStr)
        lessonsinfo = null;
        if(listenerId != null) GM_removeValueChangeListener(listenerId);
        GM_deleteValue('remain.true.to.our.original.aspiration');
        if(resultStr == '所有课程学习完成!') {
            //$("#wsView").remove();
            //$('#progress').remove();
            if(Math.random()>0.9) {
                $('#progress').append(`<ul><li><a href="https://greasyfork.org/zh-CN/scripts/472634-%E7%94%B5%E4%BF%A1%E7%BD%91%E4%B8%8A%E5%A4%A7%E5%AD%A6%E8%B6%85%E7%BA%A7%E5%AD%A6%E4%B9%A0" target="_blank"><span style="color:red;">的确好用,给作者打赏。</span></a></li></ul>`);
            }
        } else {
            $('#progress').append(`<ul>${ul}</ul>`);
            //$("#wsView").show();
            //$('#progress').show();
        }
        $("#settingbox").hide();
        $("#autostudy").show();
        $('#progress').show();
        $("#setting").show();
        sessionStorage.setItem(window.location.href.slice(-36),JSON.stringify($('#autostudydiv').prop("outerHTML")))
        location.reload();
    }

    function return2Contral() {
        window.focus();
        //if($('#progress').is(':hidden')){
        //    $("#wsView").click();
        //}
    }

    function doStudy() {
        if(lessons.length == 0) {
            if(count == 0) studyFinised();
            return2Contral()
            return;
        }
        if(listenerId === null) {
            GM_onMessage('remain.true.to.our.original.aspiration', function(src, message) {
                //console.log((new Date()).toLocaleString() + ' ' + src + ' : ' + message);
                if( !isCourse && (src in studying)){
                    if(message.startsWith('studying')) {
                        //let per = message.replace('studying ','');
                        let per = '还需学 ' + message.split("|")[2] + ' 分钟';
                        if(per) $('#p'+ src).text(per)
                        studying[src] = true;
                        return;
                    }
                    if( message.endsWith("true")) hasExam.push(src)
                    if( message.endsWith("error")) hasError.push(src)
                    count--;
                    $('#' + src).remove();
                    delete studying[src]
                    if(lessons.length == 0) {
			if (count == 0) studyFinised();
		    } else {
			doStudy();
			$("#msg").text('自动学习中,学习中课程:' + count + ',待学习课程:' + lessons.length + '。')
		    }
                }
            })
        }
        //$("#wsView").show();
        if (count < max) {
            let url = ''
            if(isTrain) {
                url = urls['course']
            } else {
                url = urls['normal']
            }
            let id = lessons.shift()
            url = url.replace('$id$',id)
            studying[id] = true;
            $('#progress').append(`<div id="${id}" class="progress_item"><span class="title" title="${lessonsinfo[id]}">${lessonsinfo[id]}</span><span id="p${id}" style="float:right;">待上报</span></div>`)
            count++;
            $("#msg").text(`自动学习中,学习中课程:${count},待学习课程:${lessons.length}。`)
            //console.log(id)
            GM_setValue("g.r:current", id);
            GM_setValue("g.r:task" + id, Date.now());
            GM_openInTab(url,{ active: true, insert: true, setParent :true });
            setTimeout(function(){
               doStudy();
            },9000);
        } else {
            return2Contral()
        }
    }

    function getlessons() {
        if (isTrain) {
            const classId = window.location.href.slice(-36)
            $.ajaxSettings.async = false;
            $.get(`${window.location.origin}/api/v1/training/student/class-info/safe/chapter/paas?classId=${classId}`)
                .done((data)=>{
                $.each(data,(key,value) => {
                    const chapterId = value.id;
                    $.get(`${window.location.origin}/api/v1/training/student/class-info/safe/chapter-activity-list/paas?classId=${classId}&chapterId=${chapterId}&page=1&pageSize=5`)
                        .done((data)=>{
                        $.each(data.items,(key,value)=>{
                            if (value.required && (value.classStudentActivityProgress.finishStatus!=1) && (value.businessType == 8)) {
                                lessonsinfo[value.businessId] = value.businessName;
                            }
                        })
                        $.get(`${window.location.origin}/api/v1/training/student/class-info/assessment?classId=${classId}&chapterId=${chapterId}`)
                            .done((data)=>{
                            const mores = Math.ceil((data.upgradeRequirement[0].mustNum-5)/5);
                            let i=0;
                            while(i<mores) {
                                $.get(`${window.location.origin}/api/v1/training/student/class-info/safe/chapter-activity-list/paas/more?classId=${classId}&chapterId=${chapterId}&page=${i+2}&pageSize=5`)
                                    .done((data)=>{
                                    $.each(data.items,(key,value)=>{
                                        if (value.required && (value.classStudentActivityProgress.finishStatus!=1) && (value.businessType == 8)) {
                                            lessonsinfo[value.businessId] = value.businessName;
                                        }
                                    })
                                })
                                i++;
                            }
                        })
                    })
                })
            })
            $.ajaxSettings.async = true;
        } else {
            const modleId = Object.keys(unsafeWindow.app._modules).find(value => value.startsWith('study/subject/detail--'));
            const courseChapters = unsafeWindow.app._modules[modleId].store.models.subject.data.courseChapters;
            const updateURL = GM_getValue("g.r:updateURL", false);
            $.each(courseChapters,(key,value) => {
				$.each(value.courseChapterSections,(key,value) => {
					if(value.progress.finishStatus!=2) {
						switch(value.sectionType) {
							case 3:
								{
									//lessonsinfo[value.resourceId] = value.name;
                                    if (updateURL) {
                                        const url = `${window.location.origin}/api/v1/course-study/course-front/url-progress`;
                                        const section = `sectionId=${value.id}&beginTime=${Date.now()}&clientType=0&finishStatus=2&completedRate=100`;
                                        $.post(url, section);
                                    }
									break;
								}
							case 10:
								//lessons.push(value.resourceId);
								lessonsinfo[value.resourceId] = value.name;
								break;
							case 9:
								break;
							case 12:
								break;
						}
					}
				});
			});
        }
        //console.log(lessonsinfo);
        lessons = Object.keys(lessonsinfo);
    }

    function heartbeat() {
        if(count == 0) return;
        setTimeout(()=>{
            console.log('检查心跳')
            let needRestart = false;
            for(var key in studying) {
                if(!studying[key]) {
                    taskID++;
                    console.log(key + ' 异常停止,重启')
                    needRestart = true;
                    lessons.unshift(key);
                    delete studying[key];
                     $('#' + key).remove();
                    count--;
                }
                studying[key] = false;
            }
            if(needRestart) doStudy()
            if( taskID > max) {
                //$("#wsView").after('任务页失联次数过多!请参考<a href="https://greasyfork.org/zh-CN/scripts/472634-%E7%94%B5%E4%BF%A1%E7%BD%91%E4%B8%8A%E5%A4%A7%E5%AD%A6%E8%B6%85%E7%BA%A7%E5%AD%A6%E4%B9%A0" target="_blank"><span style="color:red;">浏览器问题</span></a>检测是否关闭浏览器节能功能。');
            } else {
                heartbeat();
            }
        },179000)
    }

    $("#autostudy").click(function () {
        if($(this).text() != "自动学习") return;
        //if (('WebSocket' in unsafeWindow) && (max > 1)) {
        //    $(this).text('❌未检测到多开支持!');
        //    $("#autostudydiv").append('请先安装并启用 <a href="https://greasyfork.org/zh-CN/scripts/472577-%E7%94%B5%E4%BF%A1%E7%BD%91%E4%B8%8A%E5%A4%A7%E5%AD%A6%E8%B6%85%E7%BA%A7%E5%AD%A6%E4%B9%A0%E5%A4%9A%E5%BC%80%E6%94%AF%E6%8C%81" target="_blank"><span style="color:red;">电信网上大学超级学习多开支持</span></a>');
        //    return;
        //}
        //$("#downpdf").attr("disabled", true);
        document.title += '🟥';
        $(this).text('获取课程');
        console.log('开始');
        if( lessons.length > 0) return;
        getlessons();
        doStudy();
        $("#progress").show();
        $(this).text('学习中');
        $(this).attr("disabled", true);
        heartbeat()
    });
})();