Greasy Fork

Greasy Fork is available in English.

云课堂|职教云|Icve --网课兼考试助手 (绿版v3)

职教云学习效率提升助手小脚本,中文化高度可定制参数,自动课件,课件一目十行,保险模式,解除Ctrl+C限制,下载课件,自动四项评论,课堂智能跟帖讨论,支持自动答题(作业,测验,考试),搜题填题,软件定制

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         云课堂|职教云|Icve --网课兼考试助手 (绿版v3)
// @version      3.6.5
// @description  职教云学习效率提升助手小脚本,中文化高度可定制参数,自动课件,课件一目十行,保险模式,解除Ctrl+C限制,下载课件,自动四项评论,课堂智能跟帖讨论,支持自动答题(作业,测验,考试),搜题填题,软件定制
// @author        tuChanged
// @run-       document-start
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @match       *://*.zjy2.icve.com.cn/*
// @match       *://*zjy2.icve.com.cn/*
// @exclude     *://*zjy2.icve.com.cn/study/homework/docHomeworkPreview.html*
// @license      MIT
// @namespace http://greasyfork.icu/users/449085
// @connect 可添加 API 地址
// @supportURL https://github.com/tuchg
// @require http://greasyfork.icu/scripts/404781.js
// @contributionURL http://greasyfork.icu/users/449085
// ==/UserScript==
/*jshint esversion:6 */
'use strict'
const setting = {
    /**
     * 根据下方根据提示修改脚本配置
     */
    /*
      * 📣如果您有软件定制(APP,小程序,管理系统等),毕设困扰,又或者课程设计困扰等欢迎联系,
      *    价格从优,源码调试成功,线上稳定运行再付款💰,
   *    支持第三方托管交易
      * 实力保证,包远程,包讲解 QQ:2622321887
    */

    // true 为打开,false 为关闭
    //正确率不高,题目格式不适配,不推荐打开
    自动答题: false,
    //针对某些 nt 老师的点点点,快速完成课堂讨论
    智能跟帖讨论: true,
    激活仅评论: false,//与学神模式冲突,需二选一
    // 随机评论,自行扩充格式如     "你好",     (英文符号)
    随机评论词库: ["..", "🇨🇳", "💬"],
    //开启所有选项卡的评论,最高优先等级,打开该项会覆盖下面的细分设置,
    激活所有选项卡的评论: false,
    激活评论选项卡: false,
    激活问答选项卡: false,
    激活笔记选项卡: false,
    激活报错选项卡: false,
    显示评论数: 1000,
    // 刺激!风险未知,暂知时长不良 打开需关闭仅评论
    学神模式: false,
    保险模式: false,//如果课件始终不跳下一个,请勿打开该项
    //解除课件下载
    打开课件下载: true,
    // 部分课件存在无检测机制问题,会尝试自动关闭保险模式
    自动关闭保险模式: true,
    /*影响速度关键选项,延时非最优解,过慢请自行谨慎调整*/
    最高延迟响应时间: 4000,//毫秒
    最低延迟响应时间: 3000,//毫秒
    组件等待时间: 1500,//毫秒 组件包括视频播放器,JQuery,答题等,视网络,设备性能而定,启动失败则调整
    考试填题时间: 30000,//30秒 1 秒=1000 毫秒
    //0-高清 1-清晰 2-流畅 3-原画
    //感谢tonylu00提供最新实测参数 --0-原画 1-高清 2-清晰 3-流畅
    视频清晰度: 3,
    //2倍速,允许开倍速则有效,请放心使用,失败是正常现象
    视频播放倍速: 2,
    //是否保持静音
    是否保持静音: true,
    //当前版本可不用理会该项, 题库 IP地址 ,可在553行查看对接接口要求
    自定义题库服务器: "🔐"// 协议://IP
}, top = unsafeWindow,
    url = location.pathname
//产生区间随机数
const rnd = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
const classId = getQueryValue("openClassId")
const cellID = getQueryValue("cellId")
const stuId = localStorage.getItem("userId")
// 课件完成相关判定数据
let pageCount, mediaLong, cellType, startTime, lastArchiveCount
//课件是否已完成
let isFinshed = false;
// 评论标志位
const isUnFinishedTabs = [setting.激活所有选项卡的评论 || setting.激活评论选项卡, setting.激活所有选项卡的评论 || setting.激活笔记选项卡, setting.激活所有选项卡的评论 || setting.激活问答选项卡, setting.激活所有选项卡的评论 || setting.激活报错选项卡]

//定时任务栈
let taskStack = 0
/**
 * 使用异步包装
 *  随机延迟执行方法
 * @param {需委托执行的函数} func
 */
async function delayExec(func, fixedDelay = null) {
    // taskStack.push(func)
    taskStack++
    const newTask = new Promise((resolve, reject) => {
        const newTime = rnd(fixedDelay || (setting.最低延迟响应时间) * (taskStack / 3), fixedDelay || (setting.最高延迟响应时间) * (taskStack / 2.5));
        setTimeout(() => {
            resolve(func())
            taskStack--
            console.log(`完成延时${newTime}ms的任务,待执行任务总计:${taskStack}`);
        }, newTime);
        console.log(`新增任务,等待时间${newTime}ms,待执行任务总计:${taskStack}`);
    });
    return newTask
}
function autoCloseDialog() {
    const $dialog = $(".ui-dialog");
    //关闭限制弹窗
    if ($dialog.length > 0)
        $dialog.find("#studyNow").click()

    if ($(".xcConfirm")) {
        $(".xcConfirm").css({ "display": "none" })
    }
}

GM_registerMenuCommand("🔄重新获取未完成小节", function () {
    sessionStorage.clear()
    goPage("p")
});

GM_registerMenuCommand("问题反馈", function () {
    top.open("https://github.com/W-ChihC/SimpleIcveMoocHelper/issues")
});
GM_registerMenuCommand("🌹为脚本维护工作助力", function () {
    top.open("http://greasyfork.icu/zh-CN/users/449085")
});
GM_registerMenuCommand("📝检查脚本配置", function () {
    alert(`
    当前版本:绿版 v3.6.4✅
    题库:${setting.自定义题库服务器 ? setting.自定义题库服务器 : "❌无"}
    学神模式: ${setting.学神模式 ? "✅打开" : "❌关闭"}
    保险模式: ${setting.保险模式 ? "✅打开" : "❌关闭"}
    仅评论模式: ${setting.激活仅评论 ? "✅打开" : "❌关闭"}
    自动填题:${setting.自动答题 ? "✅打开" : "❌关闭"}
    智能跟帖讨论:${setting.智能跟帖讨论 ? "✅打开" : "❌关闭"}
    当前组件响应时间:${setting.组件等待时间 % (1000 * 60) / 1000} 秒
    考试填题时间:${setting.考试填题时间 % (1000 * 60) / 1000} 秒
    当前评论库: [ ${setting.随机评论词库} ]
    已激活的评论选项卡:${((setting.激活所有选项卡的评论 || setting.激活评论选项卡) ? "评论;" : "") + ((setting.激活所有选项卡的评论 || setting.激活问答选项卡) ? "问答;" : "") + ((setting.激活所有选项卡的评论 || setting.激活笔记选项卡) ? "笔记;" : "") + ((setting.激活所有选项卡的评论 || setting.激活报错选项卡) ? "报错" : "")}\n

    📝修改配置请找到油猴插件的管理面板

    插件仅供提升学习效率减少,繁杂工作,解放双手之用,未利用任何漏洞达成目的,均为网页自动化测试技术,切勿滥用

    脚本完全免费开源,遵循 MIT 协议,严禁倒卖,如果您是购买使用请举报售卖者
    `)
});



// 一页页面加载后的工作
delayExec(() => {
    autoCloseDialog()
    //匹配不需要监听网络的URL
    switch (url) {
        //作业区
        case "/study/homework/preview.html":
        case "/study/homework/do.html":
        case "/study/faceTeachInfo/testPreview.html":
            homeworkHandler()
            break;
        //考试
        case "/study/onlineExam/preview.html":
        case "/study/onlineExam/do.html":
            alert("请勿过快提交,同时也尽量调整脚本考试填题时间设置\n答题过快会被检测然后翻车哦")
            setting.组件等待时间 = setting.考试填题时间
            homeworkHandler()
            break
        //课堂
        case "/study/faceTeachInfo/faceTeachActivityListInfo.html":
            $(".np-hw-li.progressing .np-hw-score:contains('未参加')").each((i, e) => {
                const x = $(e).parent().parent().find(".am-inline-block:not(.zuoda)")
                if (x.length > 0) {
                    x.attr("target", "_blank")
                    x[0].click()
                }
            })
            break
        case "/study/faceTeachInfo/newDiscussStuInfo.html":
            if (!setting.智能跟帖讨论 || $(`.commentli[data-stuid='${localStorage.getItem("userId")}']`).length > 0) {
                return
            }
            const t = findOneVaildDiscuss()
            if (t) {
                delayExec(() => {
                    $(".faceContent").val(t)
                    $(".replyOk")[0].click()
                })
            } else {
                alert("暂无人参与")
            }
            break
    }

    $(document).ajaxSend((e, xhr, options) => {
        if (!$.parseParams)
            $.extend({
                parseParams: function (e) {
                    for (var o, i = /([^&=]+)=?([^&]*)/g, n = /\+/g, c = function (e) {
                        return decodeURIComponent(e.replace(n, " "))
                    }, r = {}; o = i.exec(e);) {
                        var f = c(o[1])
                            , a = c(o[2]);
                        "[]" === f.substring(f.length - 2) ? (f = f.substring(0, f.length - 2),
                            (r[f] || (r[f] = [])).push(a)) : r[f] = a
                    }
                    return r
                },
                htmlencode: function (o) {
                    return e("<div />").text(o).html()
                },
                htmldecode: function (o) {
                    return e("<div />").html(o).text()
                }
            })
        if (setting.学神模式 && !setting.激活仅评论)
            if (options.url.indexOf("stuProcessCellLog") > -1) {

                const params = $.parseParams && $.parseParams(options.data);
                if (params)
                    options.data = $.param({
                        ...params,
                        studyNewlyTime: mediaLong,
                        picNum: pageCount,
                        studyNewlyPicNum: pageCount
                    })
            }
        // 修改评论页数
        if (options.url.indexOf("getCellCommentData") > -1) {
            const params = $.parseParams && $.parseParams(options.data);
            if (params)
                options.data = $.param({
                    ...params,
                    pageSize: setting.显示评论数
                })
        }
    });

}, setting.组件等待时间);
function findOneVaildDiscuss() {
    let str = null
    $(".np-question-detail").each((i, e) => {
        if (i > 0) {
            const text = e.innerText
            if (text.trim().length > 10) {
                str = text
                return false
            }
        }
    })
    return str
}


let lastNum = 10;
let currentCellData = {};
let isPassMonit = false;



// 全局请求拦截器
(function (open, send) {

    // 拦截发出的请求
    XMLHttpRequest.prototype.send = function (data) {

        // 学生课件状态检查
        if (data && data.indexOf("studyNewlyTime") >= 0) {
            // 关闭错误弹窗
            $(".sgBtn.ok").click();
            autoCloseDialog()

            try {
                isPassMonit = true
                autoCloseDialog()
                if (!setting.激活仅评论) {
                    let readedNum = parseInt(getQueryValue("studyNewlyPicNum", "?" + data));
                    // 四舍五入留两位与服务器计时同步
                    const readedTime = Math.round(parseFloat(getQueryValue("studyNewlyTime", "?" + data)) * 100) / 100;
                    const picNum = parseInt(getQueryValue("picNum", "?" + data))
                    // 非媒体课件下启动
                    if ((!readedTime || setting.学神模式) && !startTime)
                        startTime = $.now()
                    // 纠正空课件监控问题
                    if (pageCount === 1)
                        readedNum = 1
                    // 损坏课件的问题
                    if (picNum === 1 && readedNum === 0)
                        pageCount = 1
                    console.log(`文档同步进度:${readedNum}/${pageCount}`, `视频同步进度:${readedTime}/${mediaLong}`);
                    // 某些课件未被检测
                    lastNum = readedNum && readedNum
                    if (lastNum === 0 && setting.保险模式) {
                        console.log("保险模式启动失败,已尝试关闭");
                        if (setting.自动关闭保险模式) {
                            setting.保险模式 = false
                            requestMatcher("viewDirectory", currentCellData)
                        }
                        return
                    }

                    // 判断当前课件是否已结束
                    if ((readedNum && pageCount && (readedNum >= pageCount)) || setting.学神模式) {
                        isFinshed = true
                        const endTime = $.now()
                        // 应对检测需停留 10 秒
                        if (startTime && (endTime - startTime >= 10000)) {
                            // 评论任务均已完成则跳转
                            if (isUnFinishedTabs.indexOf(true) === -1) {
                                nextCell()
                                return
                            }
                        }
                        console.log(`未满足职教云课件完成检测 10 秒要求,继续等待中,已等待:${endTime - startTime}ms`);
                    } else {
                        if (setting.保险模式)
                            pageCount && console.log(`文档类🔐模式:${readedNum}/${pageCount}`);
                        nextDOCPPT()
                    }
                } else {
                    // 评论任务均已完成则跳转
                    if (isUnFinishedTabs.indexOf(true) === -1) {
                        nextCell()
                        return
                    }
                }
            } catch (error) {
                console.log(error);
            }
        }
        send.apply(this, arguments);
    };

    // 拦截数据响应
    XMLHttpRequest.prototype.open = function () {
        this.addEventListener("readystatechange", () => {
            if (this.readyState >= 4)
                requestMatcher(this.responseURL, JSON.parse(this.responseText), this)
        }, false);
        open.apply(this, arguments);
    };
})(XMLHttpRequest.prototype.open, XMLHttpRequest.prototype.send);

/**
 * 请求匹配器,任务调度中心
 */
async function requestMatcher(url, data, that) {
    autoCloseDialog()
    // debugger
    switch (url) {
        // 评论
        case String(url.match(/.*getCellCommentData$/)):
            if (isUnFinishedTabs[0] || isUnFinishedTabs[1] || isUnFinishedTabs[2] || isUnFinishedTabs[3] || setting.激活所有选项卡的评论) {
                const userId = localStorage.getItem("userId");
                const item = data.list && data.list.find(item => item.userId === userId);
                // 评论已完成
                console.log("我的评论: ", item);
                switch (data.type) {
                    case 1: {
                        if (setting.激活评论选项卡 || setting.激活所有选项卡的评论) {
                            if (!item) {
                                await submitComment()
                                console.log("已完成评论提交");
                            }
                            isUnFinishedTabs[data.type - 1] = false
                        }
                    }
                        break;
                    case 2:
                        {
                            if (setting.激活笔记选项卡 || setting.激活所有选项卡的评论) {
                                if (!item) {
                                    await submitNote()
                                    console.log("已完成笔记提交");
                                }
                                isUnFinishedTabs[data.type - 1] = false
                            }

                        }
                        break;
                    case 3:
                        {
                            if (setting.激活问答选项卡 || setting.激活所有选项卡的评论) {
                                if (!item) {
                                    await submitQuestion()
                                    console.log("已完成问答提交");
                                }
                                isUnFinishedTabs[data.type - 1] = false
                            }
                        }
                        break;
                    case 4:
                        {
                            if (setting.激活报错选项卡 || setting.激活所有选项卡的评论) {
                                if (!item) {
                                    await submitReport()
                                    console.log("已完成报错提交");
                                }
                                isUnFinishedTabs[data.type - 1] = false
                            }
                        }
                        break;
                }
                if (isUnFinishedTabs.indexOf(true) != -1) {
                    if (data.type == 4)
                        data.type = 2
                    await delayExec(() => {
                        $($(".am-tabs-nav>li a")[data.type]).click()
                    })
                    console.log("完成");
                }

                // let tab = isUnFinishedTabs.indexOf(true);
                // if (!setting.激活笔记选项卡 && data.type !== 1)
                //     tab -= 1
                // if (tab > -1 && tab + 2 !== data.type) {
                //     await delayExec(() => {
                //         $($(".am-tabs-nav>li a")[tab]).click()
                //     })
                // }



                //解决不同机制判断问题
                if ((setting.激活仅评论 || isFinshed) && isUnFinishedTabs.indexOf(true) === -1 && taskStack === 0) {
                    nextCell()
                }
            }
            break;
        // 载入课件
        case String(url.match(/.*viewDirectory|loadCellResource$/)):
            {
                if (data.code === -33) {
                    nextCell()
                    return
                }

                autoCloseDialog()
                if (setting.激活仅评论) {
                    console.log("仅开启评论已打开");
                    // commentHandler()
                    return
                }

                if (currentCellData && setting.打开课件下载) {
                    // 课件下载 todo
                    data.isAllowDownLoad = true
                    data.isDownLoad = true
                    console.log("当前课件下载地址:", data.downLoadUrl);
                    // 修改服务器返回数据
                    if (!that._responseText) {
                        Object.defineProperty(that, 'responseText', {
                            get: () => that['_responseText'] === undefined ? that.responseText : that['_responseText'],
                            set: (val) => {
                                that['_responseText'] = val
                            },
                            enumerable: true
                        });
                    }
                    //修改响应数据
                    that._responseText = JSON.stringify(data)
                }
                // 课件页数
                pageCount = data.pageCount
                // // 课件当前已阅览时间
                // readTime = data.stuStudyNewlyTime
                // 媒体时间长度
                mediaLong = data.audioVideoLong;
                // 课件进度
                const cellPercent = data.cellPercent
                // 课件类型
                cellType = data.categoryName
                // 如果当前课件为遗漏课件则进入下一个课件
                if (cellPercent === 100 && isUnFinishedTabs.indexOf(true) === -1) {
                    nextCell()
                    return
                }
                currentCellData = data
                console.log("当前课件: ", data);
                cellHandlerMatcher()
            }
            break;

        case String(url.match(/.*faceTeachActivityInfo$/)):
            {


            }
            break
        // 课程章节目录
        case String(url.match(/.*getProcessList$/)):
            {
                const localS = sessionStorage.getItem(classId);
                //未在本地找到遗留数据则重新获取
                if (!localS || localS === "[]" || localS === "null") {

                    if (!confirm("正在获取未完成小节数据,为避免检测,请耐心等待🖥\n✅确定以继续,确认后勿关闭本页\n直到再次弹窗,否则脚本将结束工作\n  ‼️插件仅供提升学习效率减少,繁杂工作,解放双手之用,未利用任何漏洞达成目的,均为网页自动化技术,请健康使用勿要滥用\n"))
                        return
                    const parentNode = data && data.progress;
                    //过滤已经学习完的课件
                    let dirs = parentNode && parentNode.moduleList.filter(item => item.percent !== 100)
                    if (setting.激活仅评论)
                        dirs = parentNode.moduleList
                    //请求课程所有数据
                    const orginalData = (await sendIcveRequest(urls2.courseView_getCourseDetailList)).courseProcessInfo
                    //过滤掉已完成的章节
                    const list = orginalData && orginalData.filter(item => dirs.find(i => i.id === item.id))
                    const cid = getQueryValue("courseOpenId")
                    const oid = getQueryValue("openClassId")
                    //最终处理数据
                    const finalData = []
                    //提取未完成的课件
                    for (const item of list) {
                        for (const i of item.topics) {
                            // 最终需要处理的数据
                            const cellList = (await sendIcveRequest(urls.process_getCellByTopicId, { courseOpenId: cid, openClassId: oid, topicId: i.id })).cellList
                            cellList && cellList.forEach(item => {
                                const childList = item.childNodeList;
                                if (childList && childList.length !== 0) {
                                    const childVaildList = childList.filter(i => {
                                        // if (i.cellType !== 4 && i.fromType !== 4) {
                                        if (i.cellType !== 4) {
                                            if (setting.激活仅评论)
                                                return true
                                            if (i.stuCellFourPercent !== 100)
                                                return true
                                        }
                                        return false
                                    });
                                    console.log(childVaildList);
                                    finalData.push(...childVaildList)
                                    // } else if (item.cellType !== 4 && item.fromType !== 4) {
                                } else if (item.cellType !== 4) {
                                    if (setting.激活仅评论)
                                        finalData.push(item)
                                    else if (item.stuCellPercent !== 100)
                                        finalData.push(item)
                                    console.log(item);
                                }
                            })
                        }
                    }
                    console.log(`已成功缓存${finalData.length}条未完成小节信息`);
                    sessionStorage.setItem(classId, JSON.stringify(finalData.reverse()))
                }
                const data_ = JSON.parse(sessionStorage.getItem(classId))
                console.log(data_);
                if (confirm(`✅已初始化完成,发现${data_.length}个课件未完成,是否立即启动不知疲倦学习🙇🏼‍♂️📚模式`))
                    goPage(null, data_[data_.length - 1])
            }
            break;
        default:
            if (data && data.msg && data.msg.indexOf("操作成功") < 0) {
                console.log("无任务可分配", data);
            }
            break;
    }
}
/**
 * 查找下一个课件,并在本地缓存更新相应信息
 */
function nextCell() {
    // debugger
    const data = JSON.parse(sessionStorage.getItem(classId));
    if (!data) {
        if (confirm("🆇未从缓存中检测到课程数据,是否进入正常运行流程\n如果您是购买使用,请举报售卖方,本脚本完全免费开源使用")) {
            goPage("p")
            return
        }
    }
    const surplusData = data && data.filter(item => item.Id !== cellID);
    sessionStorage.setItem(classId, JSON.stringify(surplusData))

    if (surplusData && surplusData.length === 0) {
        alert("课程已完成\n脚本完全免费开源,遵循 MIT 协议,严禁倒卖,如果您是购买使用请举报售卖者")
        return
    }

    console.log("当前课件任务已完成----");

    delayExec(() => {
        goPage(null, surplusData.pop())
    })
}


/**
 * 跳转到某页面
 */
function goPage(url, data = undefined) {
    let newPage;
    if (!url) {
        newPage = `${location.origin}/common/directory/directory.html?courseOpenId=${data.courseOpenId}&openClassId=${classId}&cellId=${data.Id}&flag=${data.flag || "s"}&moduleId=${data.parentId}`;
        console.log("下一个课件: ", newPage);
    } else {
        newPage = `${location.origin}/study/process/process.html?courseOpenId=${getQueryValue("courseOpenId")}&openClassId=${getQueryValue("openClassId")}`
    }
    top.location.href = newPage
}

/**
 * 对网站发送请求集中处理,解析结果,处理成功与否逻辑
 */
function sendIcveRequest(url, data = {}) {
    return new Promise((resolve, reject) => {
        delayExec(() => {
            _.ajax(url, data, (r) => {
                if (r.code == 1) {
                    resolve(r)
                } else {
                    console.log("请求出问题了🔐", r)
                    reject(r)
                }
            })
        })
    })
}


/**
 * 课件匹配处理调度
 */
function cellHandlerMatcher() {

    if (!setting.激活仅评论)
        switch (cellType) {
            case "图片":
            case "文档":
            case "excel文档":
            case "office文档":
            case "pdf文档":
            case "其它":
            case "ppt文档":
                if (!setting.保险模式)
                    delayExec(() => {
                        docHandler()
                    })
                break;
            case "ppt":
                if (!setting.保险模式)
                    delayExec(async () => {
                        await pptHandler()
                    })
                break;
            case "swf":
                swfHandler()
                break;
            case "视频":
            case "音频":
                delayExec(() => {
                    mediaHandler()
                }, setting.组件等待时间)
                break;
            case "图文":
            case "压缩包":
                emptyHandler()
                break;
            default:
                console.log(`课件 : ${cellType} 未提供兼容, ${setting.未做兼容课件打开评论 ? '已开启兼容评论,仅运行评论' : '已跳过处理'},请在github issue  (https://github.com/W-ChihC/SimpleIcveMoocHelper)  反馈该日志,与作者取得联系`);
                break
        }
}





/**
 * 获取url查询字段
 * @param {查询字段} query
 * @param 默认为地址栏
 */
function getQueryValue(query, url = window.location.search) {
    let theRequest = new Object();
    if (url.indexOf("?") != -1) {
        let str = url.substr(1);
        let strs = str.split("&");
        for (let i = 0; i < strs.length; i++)
            theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]);
    }
    return theRequest[query];
}


/**
 * 仅仅评论的处理器
 */
async function emptyHandler() {
    console.log("啥也没干,请联系作者", cellType);
}

async function swfHandler() {
    //当不支持flash时执行
    if ($('.popBox').length !== 0) {
        $($('.popBox a')[1]).click()
    }
}

/**
 * 视频/音频类处理
 */
function mediaHandler() {
    try {
        let player = top.jwplayer($(".jwplayer").attr("id"));
        let state = null;
        state = player.getState();
        //视频暂停状态
        if (state == "paused" || state === 'idle') {
            console.log("媒体已暂停,恢复播放");
            player.play()
        }
        if (player.getDuration() === 0) {
            if (state === "buffering") {
                const timer = setInterval(() => {
                    if (player.getPosition() === 0)
                        delayExec(() => {
                            if (player.getPosition() === 0)
                                player.play();
                        }, setting.组件等待时间);
                    else
                        clearInterval(timer)
                }, setting.组件等待时间);

            } else {
                if (setting.学神模式 || isUnFinishedTabs.indexOf(true) === -1) {
                    nextCell()
                    return
                }
                isFinshed = true
            }
        }
        //播放原已完成
        if (player.getState() == "complete") {
            console.log("媒体播放已完成");
            // 评论任务均已完成则跳转
            if (isUnFinishedTabs.indexOf(true) === -1) {
                nextCell()
                return
            }
            isFinshed = true
            return
        }
        //播放回调
        player.on("playlistComplete", () => {
            console.log("媒体播放完成");
            // 评论任务均已完成则跳转
            if (isUnFinishedTabs.indexOf(true) === -1) {
                nextCell()
                return
            }
            isFinshed = true
        })
        //配置
        player.setMute(setting.是否保持静音)//静音
        player.setCurrentQuality(setting.视频清晰度)
        try {
            player.setPlaybackRate(setting.视频播放倍速)
        } catch (error) {
            console.log('倍速开启失败...正常现象.');
        }
    } catch (error) {
        console.log("课件为空或无法解析", error);
        // 评论任务均已完成则跳转
        if (isUnFinishedTabs.indexOf(true) === -1) {
            nextCell()
            return
        }
        isFinshed = true
    }

}
/**
 * 文档处理
 */
async function docHandler() {

    if ($(".MPreview-pageNext").length !== 0) {
        //根据按钮状态判断是否还有下一页
        while ($(".MPreview-pageNext").hasClass('current')) {
            console.log(`文档翻页,总页数:${pageCount}`);
            //ppt翻页 异步方式
            await delayExec(() => {
                $(".MPreview-pageNext").click()
            })
        }
    } else {
        await pptHandler()
    }
}


/**
 * PPT类别处理
 */
async function pptHandler() {
    // 异步处理
    return new Promise(async (resolve, reject) => {
        for (let i = 1; i <= pageCount * 2; i++) {
            //点击下一页
            await delayExec(() => {
                nextDOCPPT()
                // console.log(`ppt第${i}页,总页数:${pageCount}`);
                //达到次数解除阻塞
                if (isFinshed || i === pageCount && mediaLong === 0)
                    resolve()
            })
        }
        // if (pageCount === 1) {
        //     for (let i = 0; i < 5; i++)
        //         nextDOCPPT()
        //     delayExec(() => {
        //         nextCell()
        //         resolve()
        //     }, 15000)
        // }
    })
}
/**
 * 下一页PPT 或文档
 */
function nextDOCPPT() {
    const pptNext = $(".stage-next"), docNext = $(".MPreview-pageNext"), sNext = $(".stage-next-btn");
    pptNext && pptNext.click()
    docNext && docNext.click()
    sNext && sNext.click()
}
/**
* 对XHR的二次全局封装,方便后期扩展
* @param {*} method
* @param {*} url
* @param {*} headers
* @param {*} data
* @param {*} onSuccess
*/
function requestAPI(method, url, { headers = {}, data, onSuccess } = {}) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method: method,
            url: url,
            headers: headers,
            data: data,
            //关闭 cookie
            anonymous: true,
            timeout: 2000,
            onload: function (xhr) {
                switch (xhr.status) {
                    case 200:
                    case 404:
                        // let obj = $.parseJSON(xhr.responseText) || {};
                        if (onSuccess)
                            onSuccess(xhr)
                        else
                            resolve(xhr)
                        break;
                    default:
                        resolve(xhr)
                        break;
                }
            },
            onabort: function (params) {
                reject(params)

            },
            onerror: function (params) {
                // debugger
                reject(params)
            },
            ontimeout: function (params) {
                reject(params)
            }
        });
    })

}


/**
 * 评论
 */
async function submitComment() {
    // debugger
    return new Promise(async (resolve, reject) => {

        //评5星
        $("#star #starImg4").click();
        //随机从词库填写评论
        $(".commentContent").text(setting.随机评论词库[rnd(0, setting.随机评论词库.length - 1)])
        //提交
        await delayExec(async () => {
            $("#btnComment").click();
            resolve()
        });
    })
}

/**
 * 问答
 */
async function submitQuestion() {
    // debugger
    return new Promise(async (resolve, reject) => {
        //随机从词库填写评论
        $(".questionContent").text(setting.随机评论词库[rnd(0, setting.随机评论词库.length - 1)])
        //提交
        await delayExec(async () => {
            $("#btnQuestion").click();
            resolve()
        }, 60000);
    })
}
const list = []
/**
 * 笔记
 */
async function submitNote() {
    // debugger
    return new Promise(async (resolve, reject) => {
        //随机从词库填写评论
        $(".noteContent").text(setting.随机评论词库[rnd(0, setting.随机评论词库.length - 1)])
        //提交
        await delayExec(async () => {
            $("#btnNote").click();
            resolve()
        }, 60000);
    })
}
/**
 * 报错
 */
async function submitReport() {
    return new Promise(async (resolve, reject) => {
        //随机从词库填写评论
        $(".cellErrorContent").text(setting.随机评论词库[rnd(0, setting.随机评论词库.length - 1)])
        //提交
        await delayExec(async () => {
            $("#btnCellError").click();
            resolve()
        }, 60000);
    })
}


/*
*  解除文本限制
*/
function uncageCopyLimit() {
    let arr = ["oncontextmenu", "ondragstart", "onselectstart", "onselect", "oncopy", "onbeforecopy"]
    for (let i of arr)
        $(".hasNoLeft").attr(i, "return true")
    console.log("已成功解除复制限制,📣如果您有软件定制(管理系统,APP,小程序等任何形式私活)等欢迎联系\n价格从优,源码调试成功再付款💰\n实力保证,包远程,包讲解 QQ:2622321887")
}


/**
* 作业处理
*/
async function homeworkHandler() {

    uncageCopyLimit()
    if (!setting.自定义题库服务器) {
        alert("未填写题库📝,无法正常使用答题,仅提供解除网站限制")
    }
    bindBtnToQuestion()
    if (setting.自动答题)
        autoFill()
}

let isAutoFilling = false
/**
 * 单选 多选 判断 填空 问答
 */
async function autoFill() {
    const q = $(".qBtn");
    for (let i = 0; i < q.length; i++) {
        const e = q[i];
        await delayExec(() => {
            isAutoFilling = true
            e.click()
        }, setting.组件等待时间)
    }
    delayExec(() => {
        if (setting.组件等待时间 === setting.考试填题时间) {
            alert("如果你不想被老师打零分,就别智障的过快提交")
        }
        $("#submitHomeWork").click()
        isAutoFilling = false
    }, setting.组件等待时间)
}


// 重新渲染答题区的标志位
let reRender = false

/**
 * 将查询按钮按ID调用插入到题目区未位
*/
function bindBtnToQuestion() {
    // $(`<button class="qBtn" type="button">🔍</button>`).appendTo(".e-q-quest")
    // $($(".e-a-g")[2]).prev(".e-q-q")
    $(".e-q-quest").each(async (i, e) => {
        $(`<button class="qBtn" x="${i}" type="button">🔍</button>`).appendTo($(e))
    })
    //去除填空按钮,提高答案匹配
    $('.fillbox').detach()

    //绕过网站全局事件注册
    $(".qBtn").on("click", (event) => {
        reRender = true
        searchAnswer(event.srcElement.attributes["x"].value)
    })
}

const server = setting.自定义题库服务器 || "http://127.0.0.1:5000"

/**
 * //接口对接规范(JSON) 快速通道(/q?q=问题) 更多信息(/q2?q=问题)
 *  [
 *   {
 *    'question': '问题,可留空',
 *    'answer': '答案', //判断题 1 为正确,其余为错误
 *    'options':'题目选项,可留空',
 *    'msg': '消息,可留空'
 * },{
 *
 *    }
 * ]
 *
 */


/**
 * 填题
 * @param {*} id  答案 ID
 */
function fillAnswer(aID, qId) {
    // 多选 及自动答题模块
    //todo 后端: 1,2,3
    let answer = $(`#${aID}`).text();
    const qBody = $($(".qBtn")[qId]).parents(".e-q-body");
    const questionType = qBody.data("questiontype");
    let inputBlock;
    switch (questionType) {
        // <!-- 1:单选 2:多选 -->
        case 1:
        case 2:
            answer.split(",").forEach(e => {
                inputBlock = $(qBody.find(`.e-a-g li:contains("${e}")`));
                inputBlock.click()
                inputBlock.focus()
            })
            break;
        // < !--3:判断题-- >
        case 3:
            answer = answer.trim()
            inputBlock = $(qBody.find(".e-a-g li")[(answer == "1" || answer == "正确" || answer == "对" || answer == "√") ? 0 : 1]);
            //默认第一项为正确
            inputBlock.click()
            inputBlock.focus()
            break;
        // <!-- 4:填空题(主观) 5:填空题(客观) 6 问答-->
        case 4:
        case 5:
            answer.split(",").forEach((e, i) => {
                inputBlock = $(qBody.find(".e-a-g input")[i])
                inputBlock.val(e)
                inputBlock.blur()
            })
            break;
        case 6:
            inputBlock = $(qBody.find("textarea")[0])
            inputBlock.val(answer)
            inputBlock.blur()
            break;
        default:
            break;
    }
}
// 查看更多答案的锁
let nextLock = false
/**
 * 显示搜索框
 * @param {*} params
 */
async function showAnswerListDiv(questionTitle, data, id) {
    const title = setting.组件等待时间 === setting.考试填题时间 ? `脚本提倡诚信考试,真材实料应考,<b>答案仅供参考</b>,不可全信<br>为保证考试公平,将会在一定范围内返回随机<em>错误答案</em><br>针对考试特殊处理,请耐心等待,出现提示前勿要乱动,否则<em>按舞弊处理</em>其后果自负<br> ${setting.自动答题 ? `下一道题将在<b>${setting.考试填题时间 % (1000 * 60) / 1000}</b>秒后继续` : ""}` : questionTitle.substr(0, 30)
    if ($("#answerBlock").length == 0) {
        const baseDiv = ` <div id="answerBlock"   style="background: #cccccc8c;max-width:50%; float: right; margin-right: 230px;overflow:auto; position: fixed; top: 0; right: 0; z-index: 9999;">
                                    <table border="1" cellspacing="0" align="center" style="font-size: 14px;">
                                        <caption style="min-width:200px;">${title}</caption>
                                        <thead>
                                            <tr>
                                                <th>题目</th>
                                                <th>📝</th>
                                                <th>消息</th>
                                            </tr>
                                            <tr>
                                                <th colspan="2">选项</th>
                                            </tr>
                                            <tr>
                                                <th colspan="2">结果</th>
                                            </tr>
                                        </thead>
                                        <tbody align="left">
                                        </tbody>
                                </table>
                                <center><a type="button" id="nextBtn" >查找更多 (慢)</a></center>

                            </div>`
        $(baseDiv).appendTo("body")
        // 初次初始化后关闭
        reRender = false
        //允许查看更多
        nextLock = false
    } else {
        if (reRender) {
            //更新对应数据
            $("#answerBlock caption").html(title)
            //删除原有的数据
            $('#answerBlock tbody tr').detach()
            // 换题后立即关闭
            reRender = false
            //允许查看更多
            nextLock = false
        }
    }
    let tbody = "";
    data && data.forEach((item, i) => {
        if (item != null) {
            let { question, answer, options, msg } = item
            const x = rnd(10, 1000000) + i
            tbody += `
                    <tr>
                        <td>${question || ""}</td>
                        <td><a class="aBtn" aId="${x}" qId=${id} type="button" style="margin:2px">填入</a></td>
                        <td>
                            <p>${msg || ""}</p>
                        </td>
                    </tr>
                    <tr style="height:50px">
                        <td colspan="3">${options || ""}</td>
                    </tr>
                    <tr style="height:50px">
                        <td colspan="3"><b id=${x} ><a class="aBtn" aId="${x}" qId=${id}> ${answer || ""}</a></b></td>
                    </tr>
                    `
        }
    });
    /**
      * 查看更多
      */
    if (!nextLock) {
        $("#nextBtn").off("click")
        $("#nextBtn").on("click", async () => {
            if (!nextLock) {
                /**
                 * 慢速接口
                 * @param questionTitle 问题 
                 * @param id 对应 div id (用于定位答题)
                 */
                slowSearch(questionTitle, id)
                //不再允许重复访问
                nextLock = true
            }
        })
    }
    /**
     * tbody区
     */
    $(tbody).appendTo("#answerBlock table tbody")
    $('#answerBlock p').css({ margin: '0', wordwrap: 'break-word', maxwidth: '50px' });
    $('#answerBlock em').css({ color: 'red' })
    //绕过网站全局事件注册
    $(".aBtn").on("click", (event) => {
        fillAnswer(event.srcElement.attributes["aId"].value, event.srcElement.attributes["qId"].value)
    })
    // if (setting.自动答题)
    /**填写第一项到答案 */
    try {

        $(".aBtn")[0].click()

    } catch (e) {

    }
}
/**
 * 搜索答案
 * @param {*} i
 */
async function searchAnswer(i) {
    // 往前查找同辈元素
    const question = $($(".qBtn")[i]).prevAll(".e-q-q").text().trim();

    showAnswerListDiv("搜索中...", [], i)
    /**
      * 快速接口 
      * @param questionTitle 问题 
      * @param id 对应 div id(用于定位答题)
      */
    try {
        await quickSearch(question, i)
    } catch (e) {
        reRender = true
        showAnswerListDiv("搜索失败...", [{ options: "<center><b>作者删库跑路了</b></center>" }], i)
    }
}