Greasy Fork

Greasy Fork is available in English.

云班课高效助手

【高效再度升级啦😃!高效使用云班课,装这一个脚本就够了!😎】 【🧡视频倍速:新增视频倍速控件(支持 倍速递加、递减;倍速重置;一键最佳倍速;视频快进、快退)】、【💛视频连播:新版视频连播功能,支持从当前视频开始连播(配合视频控件,可达到极度自由)】、【💙快捷键:新增快捷键系统,常用功能已都加入,高效更进一步】、【💚资源处理:批量点击、下载、批处理】

当前为 2020-08-24 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         云班课高效助手
// @author       bellamy.n.h
// @namespace    http://tampermonkey.net/
// @version      1.80
// @description  【高效再度升级啦😃!高效使用云班课,装这一个脚本就够了!😎】 【🧡视频倍速:新增视频倍速控件(支持 倍速递加、递减;倍速重置;一键最佳倍速;视频快进、快退)】、【💛视频连播:新版视频连播功能,支持从当前视频开始连播(配合视频控件,可达到极度自由)】、【💙快捷键:新增快捷键系统,常用功能已都加入,高效更进一步】、【💚资源处理:批量点击、下载、批处理】 
// @match        https://www.mosoteach.cn/web/index.php*
// @include      *://www.mosoteach.cn/web/index.php*
// @note         Version 1.80    😁【新增视频倍速控件(支持 倍速递加、递减;倍速重置;一键最佳倍速;视频快进、快退)】、【新版视频连播功能,支持从当前视频开始连播(配合视频控件,可达到极度自由)】、【新增快捷键系统,常用功能已都加入,高效更进一步】、【修复模拟点击/下载失效Bug】、【限制全部连播最大速度为8倍】
// @note         Version 1.70    视频最高16倍速连播;调用系统通知,反馈更佳;
// @note         Version 1.65    偷偷改了些小Bug 🤭,使连播更顺畅。下个版本上16倍速连播喽😊
// @note         Version 1.60    新增测试功能,支持 连续播放所有视频、 立即看完当前视频(测试阶段,还请反馈)
// @note         Version 1.50    加强对输入值约束; 支持多栏处理; chrome浏览器自动打开 设置页面地址更改; 其他Bug修复。
// @note         Version 1.40    优化代码;  新增浏览器类型判断,支持chrome浏览器自动打开 设置页面。
// @note         Version 1.32    优化操作反馈 (可以重置已选择的资源栏数)
// @note         Version 1.31    修复可能存在的Bug (页面无法自动关闭)
// @icon         https://s1.ax1x.com/2020/05/18/Yf6Kcd.png
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/layer/2.3/layer.js
// @grant        GM_openInTab
// @grant        GM_notification
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// ==/UserScript==


$(function() {
    'use strict';

    var config = {
        isCRX: false,
        notificationTitle: "云班课高效助手",
        icon128: "https://s1.ax1x.com/2020/05/18/Yf6pp4.png",
        icon48: "https://s1.ax1x.com/2020/05/18/Yf6Kcd.png",
        icon32: "https://s1.ax1x.com/2020/05/18/Yf6BBq.png",
        icon16: 'https://s1.ax1x.com/2020/05/18/Yfg71e.png',
        layer_css: "https://cdn.jsdelivr.net/npm/[email protected]/layer.min.css",
        layer_js: "https://cdnjs.cloudflare.com/ajax/libs/layer/2.3/layer.js",
        jquery_js: "https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js",
        layui_js: "https://cdn.jsdelivr.net/npm/[email protected]/dist/layui.min.js",
        fontawesome_css: "https://cdn.jsdelivr.net/npm/@fortawesome/[email protected]/css/all.min.css"
    };

    var openInTab;
    var setVal;
    var getVal;
    var notification;
    var delVal;
    var listVals;

    if (config.isCRX) {
        console.log("in CRX");
        /**
         * ***********
         * For CRX  Begin
         * ***********
         *
         * ***********
         * Override the following apis provided by TamperMonkey
         * these apis can only work well in TamperMonkey Script
         * but they can not work in CRX
         */

        /**
         * [GM_openInTab : send message to bg.js to create New Tab according to these following parameters]
         * @param {[String]} forWhat [onDownload  or  offDownload]
         * @param {[String]} _url    [new tab]
         * @param {[Boolean]} _option [is or not active]
         */
        function GM_openInTab(_url, _option, forWhat) {

            chrome.runtime.sendMessage({
                createTab: forWhat,
                url: _url,
                option: "active" === _option,
            });

        }

        function GM_setValue(name, value) {

        }

        function GM_getValue(name, defaultValue) {

        }
        /**
         * send message to chrome API
         *      chrome.notifications.create(string notificationId, NotificationOptions options, function callback)
         * @param {[type]}   notificationDetails [description]
         * @param {Function} callback            [description]
         */
        function GM_notification(notificationDetails, callback) {
            chrome.runtime.sendMessage({
                notifDetails: {
                    details: notificationDetails,
                    callbackFunc: callback
                }
            });
        }

        function GM_deleteValue(name) {

        }

        function GM_listValues() {

        }

        /**
         * ***********
         * For CRX End
         * ***********
         */
        openInTab = GM_openInTab; //GM_openInTab(url, option);
        setVal = GM_setValue; // GM_setValue(name, value)
        getVal = GM_getValue; // GM_getValue(name, defaultValue)
        notification = GM_notification; // GM_notification(text, title, image, onclick)
        delVal = GM_deleteValue; // GM_deleteValue(name)
        listVals = GM_listValues; // GM_listValues()


        // inject layer.css
        $("<link>")
            .attr({
                rel: "stylesheet",
                type: "text/css",
                href: config.fontawesome_css
            })
            .appendTo("head");


    } else {
        console.log("in Script ");

        openInTab = GM_openInTab; //GM_openInTab(url, option);
        setVal = GM_setValue; // GM_setValue(name, value)
        getVal = GM_getValue; // GM_getValue(name, defaultValue)
        notification = GM_notification; // GM_notification(text, title, image, onclick)
        delVal = GM_deleteValue; // GM_deleteValue(name)
        listVals = GM_listValues; // GM_listValues()



        // inject layer.css
        $("<link>")
            .attr({
                rel: "stylesheet",
                type: "text/css",
                href: config.layer_css
            })
            .appendTo("head");


        // inject layer.css
        $("<link>")
            .attr({
                rel: "stylesheet",
                type: "text/css",
                href: config.fontawesome_css
            })
            .appendTo("head");

    }

    /**
     * For  notification  function
     * 
     * text - the text of the notification (required unless highlight is set)
     * title - the notificaton title
     * image - the image
     * highlight - a boolean flag whether to highlight the tab that sends the notfication (required unless text is set)
     * silent - a boolean flag whether to not play a sound
     * timeout - the time after that the notification will be hidden (0 = disabled)
     * ondone - called when the notification is closed (no matter if this was triggered by a timeout or a click) or the tab was highlighted
     * onclick - called in case the user clicks the notification
     */
    function getNotificationDetails(_text, _timeout, _title, _image, _highlight, _silent, _ondone, _onclick) {

        let details = {
            text: _text === undefined ? '' : _text,
            title: _title === undefined || _title === null ? config.notificationTitle : _title,
            image: _image === undefined || _image === null ? config.icon48 : _image,
            highlight: _highlight === undefined || _highlight === null ? true : _highlight,
            silent: _silent === undefined || _silent === null ? false : _silent,
            timeout: _timeout === undefined || _timeout === null ? 6000 : _timeout,
            ondone: _ondone === undefined || _ondone === null ? null : _ondone,
            onclick: _onclick === undefined || _onclick === null ? null : _onclick
        };
        return details;

    };


    /**
     *  Determine the browser type
     */
    function browserType() {
        var userAgent = navigator.userAgent; //get browser userAgent string
        var isOpera = userAgent.indexOf("Opera") > -1;
        if (isOpera) {
            return "Opera"
        }; //is Opera or not
        if (userAgent.indexOf("Firefox") > -1) {
            return "FF";
        } //is Firefox or not
        if (userAgent.indexOf("Chrome") > -1) {
            return "Chrome";
        }
        if (userAgent.indexOf("Safari") > -1) {
            return "Safari";
        } //is Safari or not
        if (userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1 && !isOpera) {
            return "IE";
        }; //is IE or not
    }
    /**
     *  sleep function
     *  @param numberMillis -- 要睡眠的毫秒数
     */
    function sleep(numberMillis) {
        var now = new Date();
        var exitTime = now.getTime() + numberMillis;
        while (true) {
            now = new Date();
            if (now.getTime() > exitTime)
                return;
        }
    }

    /**
     * Remove duplicate value in array
     */
    function removeDuplicate(arr) {
        let x = new Set(arr);
        return [...x];
    }
    /**
     *   download resources function
     */
    function download(name, href) {
        var a = document.createElement("a"), //创建a标签
            e = document.createEvent("MouseEvents"); //创建鼠标事件对象
        e.initEvent("click", false, false); //初始化事件对象
        a.href = href; //设置下载地址
        a.download = name; //设置下载文件名
        a.dispatchEvent(e); //给指定的元素,执行事件click事件
    }

    /**
     * Refresh page tips
     */
    function refreshPage() {
        alert("操作完成,请小可爱刷新页面查看结果!!!");
    }

    /**
     * cancel action
     */
    function cancel() {
        alert("已取消操作!");
    }
    /**
     *  点击和下载前以弹窗二次确认
     *  modeName
     *  return boolean
     **/
    function popupComfirm(modeName) {
        let conf_str = false;
        conf_str = confirm("小可爱,你即将执行“" + modeName + "”操作!!!" + "\n\n" +
            "根据选择资源数量的不同,会打开相应数量的页面,如果数量较多,请不要惊慌,因为这些页面会自动关闭的哦!!!" + "\n\n" +
            "你是否按照上一个提示,进行了相应的操作?" + "\n\n" + "如果是,你是否要开始执行本次操作?");
        return conf_str;
    }

    /**
     * cleaning data  数据清洗 inputString -> idsArr
     */
    function cleanData(inputString) {
        //去除字符串中的所有空格
        inputString.replace(/\s*/g, "");
        //去掉首尾的 - 字符
        if (inputString.charAt(0) == "-") {
            if (inputString.charAt(inputString.length - 1) == "-")
                inputString = inputString.substring(1, inputString.length - 1);
            else
                inputString = inputString.substring(1, inputString.length);
        } else if (inputString.charAt(inputString.length - 1) == "-") {
            inputString = inputString.substring(0, inputString.length - 1);
        }
        //console.log(inputString + "/" + inputString.charAt(0) +"/" + inputString.charAt(inputString.length-1));
        //分割出要点击的栏号,存入数组,用于映射出对应的资源栏id
        let idsArr = inputString.split("-");
        //去重并排序
        idsArr = removeDuplicate(idsArr).sort();
        //去除超出资源栏总数的无效值
        let temp = [];
        for (let i = 0; i < idsArr.length; i++) {
            // console.log("srcBarSum is" + srcBarSum);
            if (idsArr[i] <= srcBarSum && idsArr[i] > 0) {
                temp.push(idsArr[i]);
                // console.log("temp is" + temp);
            }
        }
        // console.log("idsArr is" + idsArr);
        return idsArr = temp;

    }

    /**
     *  根据模式名执行对应的批量处理操作
     *
     *  点击确认按钮弹出确认弹窗,
     *  如果确认执行,则执行点击操作,
     *  否则执行 取消操作
     */
    function batchForMoreSrcBars(modeName, ids) {
        if (ids.length == 0)
            ids.push(".res-row-box");

        let isDownloadMode = modeName == "模拟点击" ? "false" : (modeName == "批量下载" ? "true" : "其他");

        if (popupComfirm(modeName)) {
            try {
                // console.log(chosenIDs);
                let startIndex = $("#head").val();
                let endIndex = $("#tail").val();
                for (let id of ids) {
                    // console.log(thisID);
                    try {
                        batch(isDownloadMode, id, startIndex, endIndex);
                    } catch (e) {
                        console.log(id + "该栏执行异常,跳过执行");
                        continue;
                    }
                }
            } finally {
                //点击完成,提示刷新页面
                setTimeout(refreshPage, 0);
                //置空栏号输入框
                $(".indexNum").val("");

            }
        } else {
            cancel();
        }
    }

    /**
     *  Click or download in bulk according to
     *  isDownload : true -> Download Mode  ;  false -> Click Mode
     *  thisBarID  : 此次要执行的资源栏 id
     *  startIndex : 此次资源栏中执行的开始资源编号
     *  endIndex   : 此次资源栏中执行的结束资源编号
     *
     */
    function batch(isDownload, thisBarID, startIndex, endIndex) {
        //let isDownloadMesg = isDownload == "false" ? "模拟点击" : "批量下载";

        //  以下五个等价,实现相同功能,但写法是逐步优化
        //  var list = document.getElementsByClassName("res-row-open-enable");
        //  var list = $(".res-row-open-enable");
        //  var list = $(".hide-div").children();
        //  var list = $(".res-row-box").children(".hide-div").children();
        let list = $(thisBarID).children(".hide-div").children();
        let succNum = 0;
        let failNum = 0;
        let tempUrl;
        let win;

        let actualStartIndex = startIndex <= list.length && startIndex > 0 ? startIndex : (startIndex <= 0 ? 1 : list.length); //小于0则为 1 ; 大于 最大值 则为 最大值
        let actualEndIndex = endIndex <= list.length && endIndex > 0 ? endIndex : (endIndex <= 0 ? 1 : list.length); //输入值超出资源总数的值,则将输入值置为总数的值
        if (actualStartIndex > actualEndIndex) {
            //console.log("here");
            alert("小可爱😀,你的起始结束值写反了哟!");
            return;
        }
        // console.log("actualStartIndex: " + actualStartIndex);
        // console.log("actualEndIndex: " + actualEndIndex);
        // list 存在并不为空
        if (null == list || list.length == 0) {
            console.log(thisBarID + "对应的资源栏为空");
        } else {

            for (let i = actualStartIndex - 1; i < actualEndIndex; i++) {
                // console.log(i);
                // console.log(list);
                // console.log(list[i]);
                try {

                    tempUrl = list[i].getAttribute("data-href");
                    if (null == tempUrl || tempUrl == "") {

                        console.log("资源栏:" + thisBarId + "的第 " + (i + 1) + " 条资源未获取到URL");

                    } else {

                        win = window.open(tempUrl);
                        if (isDownload == 'false') {
                            sleep(100); //睡眠,是为了确保每个资源都被正常获取
                            win.close();
                        }
                        succNum++;
                        // console.log(tempUrl);

                    }
                } catch (e) {
                    console.log(e.name + ": " + e.message);
                    console.log("资源栏:" + thisBarId + "的第 " + (i + 1) + " 条未成功执行 ;URL :   " + list[i].getAttribute("data-href"));
                    failNum++;
                    continue;
                }
            }

        }
        console.log("共检索到 " + list.length + "条; 成功执行 " + succNum + " 次! 失败 " + failNum + " 次! 操作范围:从第 " + actualStartIndex + " 条 至 第 " + actualEndIndex + " 条。");
    }

    /**
     *  click all resources in two ways according to 'isPositive'
     */
    function clickAll(isPositive) {

        let isPositiveMesg = isPositive == "true" ? "正序点击" : "倒序点击";

        let conf_str = false;
        conf_str = confirm("小可爱,你即将执行“" + isPositiveMesg +
            "全部资源”操作,如果资源量较大(> 1000),耗时就会较久,打开的页面也会较多哦!不过都会自动关闭的哦!!!" + "\n\n" +
            "小可爱,资源较多时,还请三思啊!!!" + "\n\n" + "你是否要执行?");
        if (conf_str) {
            let list = document.getElementsByClassName("res-row-open-enable");
            let succNum = 0;
            let failNum = 0;
            let tempUrl;
            let win;
            if (isPositive == "true") {
                for (let i = 0; i < list.length; i++) {
                    try {
                        tempUrl = list[i].getAttribute("data-href");
                        win = window.open(tempUrl);
                        sleep(100); //睡眠,是为了确保每个资源都被正常获取
                        win.close();
                        succNum++;
                        // console.log(tempUrl);
                    } catch (e) {
                        console.log(e.name + ": " + e.message);
                        console.log("该条未成功执行 ;URL :   " + list[i].getAttribute("data-href"));
                        failNum++;
                        continue;
                    }
                }
            } else {
                for (let i = list.length - 1; i >= 0; i--) {
                    try {
                        tempUrl = list[i].getAttribute("data-href");
                        win = window.open(tempUrl);
                        sleep(100); //睡眠,是为了确保每个资源都被正常获取
                        win.close();
                        succNum++;
                        // console.log(tempUrl);
                    } catch (e) {
                        console.log(e.name + ": " + e.message);
                        console.log("该条未成功执行 ;URL :   " + list[i].getAttribute("data-href"));
                        failNum++;
                        continue;
                    }
                }
            }
            console.log(isPositiveMesg + ":  共检索到 " + list.length + "条; 成功执行 " + succNum + " 次! 失败 " + failNum + " 次!");
            setTimeout(refreshPage, 0);
        } else {
            alert("已取消操作!");
        }
    }

    /**
     *  open a new tab according the url and execute callback function
     */
    function newTabAlert(forWhat, url, option, callback) {
        if (config.isCRX)
            openInTab(url, option, forWhat);
        else
            openInTab(url, option);
        if (typeof callback === "function") {
            callback();
        }
    }



    /******************************************
     * play videos
     * 
     */

    let playVideoConfig = {
        isContinuous: false,
        isPlayAll: false,
        isPlayPart: false,
        videoDuration: '',
    };



    /**********************************************
     *  Play  all videos continuously
     */

    let arr = [];
    let count = 0;
    let interval;
    let timeout;
    let intervalTime = 4000; //millisecond
    let isContinuousPaly = false;
    let rate = 1; // <=10
    let weight = 1000 / rate;
    let currentVideoIndex = 0;
    let nextVideoIndex = 0; //当前第几个视频
    let bufferTime = 10000; // millisecond
    let maxRate = 8;
    let log = '';

    //将所有视频资源存入数组,以作点击使用
    let a = $("div[data-mime='video']");
    Object.keys(a).forEach((key) => {
        //console.log(key, a[key]);
        arr.push(a[key]);
    });
    //console.log(arr.length);
    // for (let a in arr) {
    //  console.log(a);
    // }
    // 
    playVideoConfig.videoDuration = $('.video-duration');


    function onContinuousPlayFunc() {

        if (playVideoConfig.isContinuous) {
            layer.msg('【无效操作】 : 连播功能已开启');
            return;
        }

        playVideoConfig.isContinuous = true;

        if (typeof($("#continuousPlay").attr("class")) != "undefined") {
            let text = "连续播放已开启,无需重复开启";
            //alert(text);
            notification(getNotificationDetails(text), null);
            //layer.msg("test");
            return;
        }

        alert("请先关闭 【 Win10 专注助手 】 再使用,否则无法正常提示信息 \n\n 提示:在通知托盘中关闭");


        //      $('<div id = "continuousPlay" class="mejs__button">\
        // <button type="button" aria-controls="mep_0" title="开始连续播放" aria-label="Play" tabindex="0"></button>\
        // </div>\
        // <div id = "stopContinuousPlay" class="mejs__button mejs__playpause-button mejs__pause">\
        // <button type="button" aria-controls="mep_0" title="暂停连续播放" aria-label="Pause" tabindex="0"></button>\
        // </div>\
        // <div id = "continuousPlayN" class="mejs__button">\
        // <button type="button" aria-controls="mep_0" title="开始连续播放(n)" aria-label="Play" tabindex="0"></button>\
        // </div>\
        // <div id = "stopContinuousPlayN" class="mejs__button mejs__playpause-button mejs__pause">\
        // <button type="button" aria-controls="mep_0" title="暂停连续播放(n)" aria-label="Pause" tabindex="0"></button>\
        // </div>\
        // ').insertAfter(".mejs__fullscreen-button");

        $('<div id="helper-btn" class="content-center" style="background-color:rgba(255, 255, 255, 0.5);">\
            <span id="continuousPlayAll" class="video-btn content-center"><i class="fa fa-play-circle" aria-hidden="true" style="cursor:pointer"></i></span>\
            <span id="stopContinuousPlayAll" class="video-btn content-center"><i class="fa fa-stop-circle" aria-hidden="true" style="cursor:pointer"></i></span>\
            <span id="continuousPlayPart"  class="video-btn content-center"><i class="fa fa-play" aria-hidden="true" style="cursor:pointer"></i></span>\
            <span id="stopContinuousPlayPart" class="video-btn content-center"><i class="fa fa-stop" aria-hidden="true" style="cursor:pointer"></i></span>\
            </div>').insertBefore("#preview-video");

        //For all
        $("#continuousPlayAll").click(() => {
            startContinuousPlayAll();
        });

        $("#stopContinuousPlayAll").click(() => {
            stopContinuousPlayAll();

        });


        //For part
        $("#continuousPlayPart").click(() => {
            startContinuousPlayForPart();
        });

        $("#stopContinuousPlayPart").click(() => {
            stopContinuousPlayForPart();
        });



        let txt = "连续播放已开启,请到视频播放页面使用";
        notification(getNotificationDetails(txt), null);
    }


    /**
     * close continuous play
     * 
     */
    function offContinuousPlayFunc() {

        if (!playVideoConfig.isContinuous) {
            layer.msg('【无效操作】 : 连播功能已关闭');
            return;
        }

        $('#continuousplayAll, #stopContinusPlayAll, #continuousPlayPart, #stopContinusPlayPart').unbind();
        $('#helper-btn').remove();
        if (playVideoConfig.isPlayAll) {
            stopContinuousPlayAll();
        }
        if (playVideoConfig.isPlayPart) {
            stopContinuousPlayForPart();
        }
        playVideoConfig.isContinuous = false;
        layer.msg('连播功能已关闭!');
    }



    /**
     * For all
     * steps that must be taken when start  playing continuously
     */
    function startContinuousPlayAll() {

        if (playVideoConfig.isPlayPart) {
            layer.msg('【无效操作】 : 正常连播进行中...');
            return;
        }
        if (isContinuousPaly) {
            layer.msg("【无效操作】:全部连播进行中...");
            return;
        }


        if (playBySpecifiedRateAndNotify()) {
            $('.video-duration').remove();
            playVideoConfig.isPlayAll = true;
            isContinuousPaly = true;
            layer.msg('禁用进度条', function() {
                clickDiv();
            });

        } else {
            notification(getNotificationDetails("已取消本次操作!"), null);
        }
    }

    /**
     * For all
     * steps that must be taken when stopping  playing continuously
     */
    function stopContinuousPlayAll() {


        if (playVideoConfig.isPlayPart) {
            stopContinuousPlayForPart();
            playVideoConfig.isPlayPart = false;
        }

        if (!isContinuousPaly) {
            layer.msg("【无效操作】:全部连播未执行");
            return;
        }
        // console.log("llllllllll:"+$('.video-duration') );
        if ($('.video-duration').length == 0) {
            $(playVideoConfig.videoDuration).insertAfter('#mep_0');
        }
        isContinuousPaly = false;
        playVideoConfig.isPlayAll = false;
        //停掉当前还未执行完的 interval timeout
        clearInterval(interval);
        clearTimeout(timeout);
        layer.msg('全部连播已关闭');
        let stopContinusPlayText = "已退出连续播放模式,但保留了关闭视频即可看完功能;\n下一次连续播放从第 " + (nextVideoIndex + 1) + " 个视频开始。";
        //alert(stopContinusPlayText);
        notification(getNotificationDetails(stopContinusPlayText), null);
    }

    /**
     * For part
     * start playing continuously part of all the specified videos 
     */
    function startContinuousPlayForPart() {

        if (playVideoConfig.isPlayAll) {
            layer.msg('【无效操作】 : 全部连播进行中...');
            return;
        }
        if (videoConfig.isContinuousPaly) {
            layer.msg("【无效操作】:正常连播进行中...");
            return;
        }


        playVideoConfig.isPlayPart = true;
        videoConfig.isContinuousPaly = true;
        play(videoConfig.currentVideoDivs);
    }

    //部分连播
    //开始时不需要设置播放速度,
    //结束时不需要提示下次播放位置
    //只需开始/结束提即可
    /**
     * For part
     * stop playing continuously part of all the specified videos 
     */
    function stopContinuousPlayForPart() {


        if (playVideoConfig.isPlayAll) {
            stopContinuousPlayAll();
            playVideoConfig.isPlayAll = false;
        }


        if (!videoConfig.isContinuousPaly) {
            layer.msg("【无效操作】:正常连播未执行");
            return;
        }

        videoConfig.isContinuousPaly = false;
        playVideoConfig.isPlayPart = false;

        let video = document.querySelector('video');
        let isPaused = video.paused;
        if (isPaused) {
            video.play();
            video.pause();
        } else {
            video.pause();
            video.play();
        }
    }

    /**
     * unlock progress bar and click this div
     * return the index of layer
     */
    function unlockBarAndClickDiv(div, func) {
        layer.msg('解锁进度条中...', {
                time: 1500,
            },
            function() {
                let info = '未上锁';
                if ($(div).attr('data-drag') == 'N') {

                    $(div).attr('data-drag', 'Y');
                    info = '已解锁!';

                }
                layer.msg(info, {
                        time: 1500
                    },
                    function() {
                        $(div).trigger('click');
                        if (typeof func === "function") {
                            func();
                        }
                    });

            });
    }

    /**
     * is or not a number
     * @param  {[type]}  val [description]
     * @return {Boolean}     [description]
     */
    function isNumber(val) {

        var regPos = /^\d+(\.\d+)?$/; //非负浮点数
        var regNeg = /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/; //负浮点数
        if (regPos.test(val) || regNeg.test(val)) {
            return true;
        } else {
            return false;
        }

    }

    /**
     * play next videos according to the specified  rate inputed by user  and  notify user
     */
    function playBySpecifiedRateAndNotify() {

        let inputRate = prompt("以几倍速度进行连续播放呀🧐(最高" + maxRate + "倍哦!)建议1.8倍最佳🤭");
        //console.log(inputRate);
        if (inputRate == null) {
            return false;
        }
        if (!isNumber(inputRate)) {
            let text = "啥❓ 你输入了啥,那是数字吗?\n 再输一次吧,别输出咯!😀";
            notification(getNotificationDetails(text), null);
            return false;
        }
        rate = inputRate <= 0 ? 1 : (inputRate > maxRate ? maxRate : inputRate);
        weight = 1000 / rate;
        let text = "连续播放已开始!\n将以 " + rate + " 倍速 播放 " + (arr.length - nextVideoIndex) + " 个视频。";
        //console.log(text);
        notification(getNotificationDetails(text), null);
        return true;

    }
    /**
     * edit those value about duration before sending ajax
     */
    function send() {
        $.ajaxSetup({
            beforeSend: function() {
                let argsData = arguments[1].data
                let falseArgsData = "";
                let falseVal;
                for (let k in argsData) {

                    if (k.includes("watch_to")) {
                        //console.log("before: " + k + " : " + argsData[k]);
                        falseVal = argsData.duration;
                        //console.log("after: " + k + " : " + falseVal);
                    } else {
                        falseVal = argsData[k];
                    }
                    falseArgsData = falseArgsData + "&" + k + "=" + falseVal;
                }
                arguments[1].data = falseArgsData.substring(1, falseArgsData.length);
            },
            processData: false,
            complete: function() {
                console.log("send completed");
            }
        });
    }
    /**
     * trigger the click action of the current DIV
     *
     */
    function clickDiv() {
        currentVideoIndex = count++;
        nextVideoIndex = currentVideoIndex + 1;

        if (isContinuousPaly == false) {
            //console.log("在播放第 " + (nextVideoIndex) + " 个视频时退出了连续播放");
            return;
        }

        //第一次使用连续播放开启 关闭即可看完
        if (currentVideoIndex == 0) {
            send();
        }

        if (currentVideoIndex < arr.length) {
            // $(arr[currentVideoIndex]).trigger("click");
            unlockBarAndClickDiv(arr[currentVideoIndex], playThisVideo);
            // setTimeout(function() {layer.close(index)}, videoConfig.loadingTime);
            //console.log(currentVideoIndex + " :  " + arr[currentVideoIndex]);
            // playThisVideo();
        } else {
            setTimeout(() => {
                clearInterval(interval);
            }, 0);
            //console.log("currentVideoIndex: " + currentVideoIndex);
            //关掉最后一个视频
            $(".close-window").trigger("click");
            alert("连续播放结束, 共连续播放了 " + arr.length + " 个视频,即将刷新页面");
            location.reload();
        }
    }

    /**
     * play the current video until it is over and play the next video
     */
    function playThisVideo() {
        if (currentVideoIndex >= arr.length) {
            return;
        }
        let duration;
        let currentTime;
        setTimeout(() => {
            let video = document.querySelector('video');
            let duration = video.duration;
            let currentTime = video.currentTime;
            let isPaused = video.paused;
            //console.log(isPaused ? "暂停" : "未停");
            // if video has paused then play the video
            if (isPaused) {
                $(".mejs__replay").trigger("click");
                //console.log("开始播放");
            }
            setTimeout(() => {
                //console.log(document.querySelector('video').paused ? "暂停" : "未停");
            }, 500);
            video.playbackRate = rate;

            //second --> millisecond
            let remain = (duration - currentTime) * weight;

            console.log("该视频剩余播放时长 :" + remain + " 毫秒");

            //停掉上一个interval timeout
            clearInterval(interval);
            clearTimeout(timeout);

            //is NaN
            if (duration != duration || currentTime != currentTime || remain != remain) {

                stopContinuousPlayAll();
                notification(getNotificationDetails("执行异常,已停止本次连播。" +
                    "\n下一次连续播放从第 " + (nextVideoIndex + 1) + " 个视频开始。", 10000, ), null);
                return;

            }


            interval = setInterval(clickDiv, remain + intervalTime);

            timeout = setTimeout(() => {
                //console.log("当前视频播放到:" + document.querySelector('video').currentTime);
                $(".close-window").trigger("click");
                //console.log("关闭第" + nextVideoIndex + "个视频");
                //console.log(intervalTime + " 毫秒后播放下一个视频");
            }, remain);

        }, bufferTime);
    }


    /**
     * play all the videos since this video
     */
    let videoConfig = {
        videoSum: 0,
        currentVideoId: '',
        currentVideoDivs: arr,
        isContinuousPaly: false,
        loadingTime: 6000,
        rate: 1,

    }

    $("div[data-mime='video']").each(function(i, e) {
        let ts = $(this);
        ts.attr('id', 'vdoId_' + i);
        ts.bind('click', function(event) {
            /* Act on the event */
            let id = videoConfig.currentVideoId = ts.attr('id');
            let split = id.split('_');
            let newFirstIndex = Number(split[1]);
            videoConfig.currentVideoDivs = arr.slice(newFirstIndex);
            //console.log(videoConfig.currentVideoDivs);

        });
        videoConfig.videoSum = ++i;
    });


    //每拿一个阻塞一次,
    function play(videosArr) {

        layer.msg(
            "连播开始!(共" + videosArr.length + "个)", {
                time: 3000
            },

            async function() {
                let isOver = true;
                for (let i = 0; isOver && i < videosArr.length; i++) {
                    //console.log("time:" + i);
                    isOver = await playOne(videosArr[i]);

                }
                //console.log("Done all");
                videoConfig.isContinuousPaly = false;
                layer.msg("连播结束!");
            }
        );
    }



    function playOne(div) {
        unlockBarAndClickDiv(div);
        let index = layer.load();

        // if(document.querySelector('video').readyState == 4){
        //  layer.msg("OK");
        // };
        return new Promise(resolve => {
            setTimeout(() => {

                //close load
                layer.close(index);

                let video = document.querySelector('video');

                let onPause = function() {
                    let a = video.currentTime == 0 || video.currentTime == video.duration;
                    let b = videoConfig.isContinuousPaly;
                    if (b && a) {
                        //视频播完会回到开头如果没有回到开头应该在结尾
                        resolve(true);
                        //console.log("连播&结束");
                    } else if (!b && !a) {
                        //如果按下暂停前关闭了连续播放 == 结束本次列表循环
                        resolve(false);
                        //console.log("play stopped");
                    } else if (b && !a) {
                        //如果还在连续播放但是按下暂停 == 暂停 ,什么也不做
                        //console.log("play blocked");
                    } else if (!b && a) {
                        //不再连播但播放结束
                        resolve(false);
                        //console.log("不连播&结束");
                    }
                }

                video.removeEventListener('pause', onPause, false);
                // let duration = video.duration;
                // let currentTime = video.currentTime;
                let isPaused = video.paused;
                //console.log(isPaused ? "本是暂停" : "本是播放");
                // if video has paused then play the video
                if (isPaused) {
                    $(".mejs__replay").trigger("click");
                    //console.log("暂停-》开始播放");
                }
                setTimeout(() => {
                    //console.log(document.querySelector('video').paused ? "依旧暂停" : "已打开播放");
                }, 500);
                video.playbackRate = keyboardEvent.currentSpeed;
                // video.addEventListener("ended", function() {
                //  resolve(true);
                //  console.log("this over");
                // });
                video.addEventListener('pause', onPause);

            }, videoConfig.loadingTime);

        });

    }


    /**********************************************
     * keyMap module
     */
    let keyboardEvent = {
        keyBindings: [],
        speedStep: 0,
        rewindTime: 0,
        advanceTime: 0,
        fastSpeed: 0,
        slowerKeyCode: 0,
        fasterKeyCode: 0,
        rewindKeyCode: 0,
        advanceKeyCode: 0,
        resetKeyCode: 0,
        fasterKeyCode: 0,
        currentSpeed: 1.0,
        functionKey: {
            keyMap: 0,
            playAll: 0,
            stopPlayAll: 0,
            playPart: 0,
            stopPlayPart: 0,
            onContinuousPlayFunc: 0,
            offContinuousPlayFunc: 0,
            showTips: 0

        },
        keyMapInfo: ``

    };


    // for video
    keyboardEvent.keyBindings.push({
        action: "slower",
        key: Number(keyboardEvent.slowerKeyCode) || 83,
        value: Number(keyboardEvent.speedStep) || 0.1,
        force: false,
        predefined: true
    }); // default S
    keyboardEvent.keyBindings.push({
        action: "faster",
        key: Number(keyboardEvent.fasterKeyCode) || 87,
        value: Number(keyboardEvent.speedStep) || 0.1,
        force: false,
        predefined: true
    }); // default: W
    keyboardEvent.keyBindings.push({
        action: "rewind",
        key: Number(keyboardEvent.rewindKeyCode) || 65,
        value: Number(keyboardEvent.rewindTime) || 10,
        force: false,
        predefined: true
    }); // default: A
    keyboardEvent.keyBindings.push({
        action: "advance",
        key: Number(keyboardEvent.advanceKeyCode) || 68,
        value: Number(keyboardEvent.advanceTime) || 10,
        force: false,
        predefined: true
    }); // default: D
    keyboardEvent.keyBindings.push({
        action: "reset",
        key: Number(keyboardEvent.resetKeyCode) || 82,
        value: 1.0,
        force: false,
        predefined: true
    }); // default: R
    keyboardEvent.keyBindings.push({
        action: "fast",
        key: Number(keyboardEvent.fastKeyCode) || 71,
        value: Number(keyboardEvent.fastSpeed) || 1.8,
        force: false,
        predefined: true
    }); // default: G


    // for functions
    keyboardEvent.keyBindings.push({
        action: 'keyMap',
        key: Number(keyboardEvent.functionKey.keyMap) || 77
    }); // M
    keyboardEvent.keyBindings.push({
        action: 'playAll',
        key: Number(keyboardEvent.functionKey.playAll) || 90
    }); // Z
    keyboardEvent.keyBindings.push({
        action: 'stopPlayAll',
        key: Number(keyboardEvent.functionKey.stopPlayAll) || 88
    }); // X
    keyboardEvent.keyBindings.push({
        action: 'playPart',
        key: Number(keyboardEvent.functionKey.playPart) || 67
    }); // C
    keyboardEvent.keyBindings.push({
        action: 'stopPlayPart',
        key: Number(keyboardEvent.functionKey.stopPlayPart) || 86
    }); // V
    keyboardEvent.keyBindings.push({
        action: 'onContinuousPlayFunc',
        key: Number(keyboardEvent.functionKey.onContinuousPlayFunc) || 66
    }); // B
    keyboardEvent.keyBindings.push({
        action: 'offContinuousPlayFunc',
        key: Number(keyboardEvent.functionKey.offContinuousPlayFunc) || 78
    }); // N


    keyboardEvent.keyBindings.push({
        action: 'showTips',
        key: Number(keyboardEvent.functionKey.showTips) || 84
    }); // T


    /**
     * get the content of the action specified 
     * the action bound to some event
     * @return  json
     */
    function getKeyBindingsByAction(action) {

        let item = keyboardEvent.keyBindings.find(item => item.action === action);
        return item;

    }

    /**
     * get the value  by specified action and keyname
     * @param  {string} action  [the action bound to some event]
     * @param  {string} keyname 
     * @return {[type]}         
     */
    function getValueByActionAndKeyname(action, keyname) {
        return getKeyBindingsByAction(action)[keyname];
    }

    /**
     * [get  all  values by specified keyname  ]
     * @return {[array]} [all values]
     */
    function getAllValuesByKeyname(keyname) {
        let all = [];
        let arr = keyboardEvent.keyBindings;
        for (let i in arr) {
            let x = arr[i];
            /**
             * access value by variable key
             * x.keyname  ==> x[keyname]
             */
            all.push(x[keyname]);
        }
        // console.log('all:'+ all);
        return all;
    }

    function changeKeycode(keycodeArr, toLowercase) {

        let arr = [];
        for (let i in keycodeArr) {
            // if (toLowercase) {
            //  arr.push(keycodeArr[i] + 32);
            // }else{
            //  arr.push(keycodeArr[i] - 32);
            // }
            toLowercase == true ? arr.push(keycodeArr[i] + 32) : arr.push(keycodeArr[i] - 32);

        }
        //console.log(keycodeArr + '****' + arr);

        return arr;

    }

    /**
     * initialize  keyboardEvent.keyMapInfo
     * @type {[type]}
     */
    keyboardEvent.keyMapInfo = `
        <div id="keyMapInfo">
        <p class="content-center keyMap-head"><span class="keyMap-name">功能</span><span class="keyMap-value">快捷键</span></p>
        <hr>
        <p class="content-center"><span class="keyMap-name">强制关闭Chrome</span><span class="keyMap-value">Alt + F4</span></p>
        <p class="content-center"><span class="keyMap-name">查看快捷键</span><span class="keyMap-value">shift + m</span></p>
        <p class="content-center"><span class="keyMap-name">显示提示</span><span class="keyMap-value">shift + t</span></p>
        <p class="content-center"><span class="keyMap-name">视频加速 (+${getKeyBindingsByAction('faster').value})</span><span class="keyMap-value">W</span></p>
        <p class="content-center"><span class="keyMap-name">视频减速 (-${getKeyBindingsByAction('slower').value})</span><span class="keyMap-value">S</span></p>
        <p class="content-center"><span class="keyMap-name">视频快退 ${getKeyBindingsByAction('rewind').value}s</span><span class="keyMap-value">A</span></p>
        <p class="content-center"><span class="keyMap-name">视频前进 ${getKeyBindingsByAction('advance').value}s</span><span class="keyMap-value">D</span></p>
        <p class="content-center"><span class="keyMap-name">最佳倍速 (${getKeyBindingsByAction('fast').value})</span><span class="keyMap-value">G</span></p>
        <p class="content-center"><span class="keyMap-name">重置倍速 (${getKeyBindingsByAction('reset').value})</span><span class="keyMap-value">R</span></p>
        <p class="content-center"><span class="keyMap-name">开启连播</span><span class="keyMap-value">shift + b</span></p>
        <p class="content-center"><span class="keyMap-name">关闭连播</span><span class="keyMap-value">shift + n</span></p>
        <p class="content-center"><span class="keyMap-name">开始正常连播</span><span class="keyMap-value">shift + c</span></p>
        <p class="content-center"><span class="keyMap-name">结束正常连播</span><span class="keyMap-value">shift + v</span></p>
        <p class="content-center"><span class="keyMap-name">开始全部连播</span><span class="keyMap-value">shift + z</span></p>
        <p class="content-center"><span class="keyMap-name">结束全部连播</span><span class="keyMap-value">shift + x</span></p>
        </div>
        `;

    /**
     * bind keyboard eventListener to document
     */
    $(document).bind('keypress', function(event) {
        /* Act on the event */
        let keyCode = event.keyCode;
        let altKey = event.altKey;
        let ctrlKey = event.ctrlKey;
        let shiftKey = event.shiftKey;
        //console.log("keyCode:" + keyCode);

        let lowercase = changeKeycode(getAllValuesByKeyname('key').slice(0, 6), true);
        // console.log('[119, 115, 97, 100, 114, 103]:' + lowercase);
        let funcKeyLowercase = changeKeycode(getAllValuesByKeyname('key').slice(6), true);
        // console.log("[109, 122, 120, 99, 118, 98, 110]:" + funcKeyLowercase);
        let funcKeyUppercase = getAllValuesByKeyname('key').slice(6);
        // console.log("[77, 90, 88, 67, 86, 66, 78]:" + funcKeyUppercase);
        let playVdoFuncKeyLowercase = getAllValuesByKeyname('key').slice(7, 11);

        // shift + lowercase => uppercase    小写键盘
        let shiftAndLowercase = shiftKey && ((funcKeyUppercase.find(item => item === keyCode) === undefined ? false : true));
        // shift + uppercase => lowercase    大写键盘
        let shiftAndUppercase = shiftKey && ((funcKeyLowercase.find(item => item === keyCode) === undefined ? false : true));

        let shiftAndPlayVdoLowercase = shiftKey && ((playVdoFuncKeyLowercase.find(item => item === keyCode) === undefined ? false : true));

        if (!document.querySelector('video').paused) {

            if (lowercase.find(item => item === keyCode)) {
                //console.log("is pause:"+ document.querySelector('video').paused);
                layer.msg('请打开大写键盘 以使用 【视频控件】');
                return;
            }
            if (shiftAndUppercase) {
                layer.msg('请关闭大写键盘 以使用完整的快捷键功能');
                return;
            }
            if (shiftAndPlayVdoLowercase) {
                if (!playVideoConfig.isContinuous) {
                    layer.msg('请先开启连播功能');
                    return;
                }

            }

        } else if (document.querySelector('video').paused) {

            if (shiftAndUppercase) {
                layer.msg('请关闭大写键盘!以使用完整的快捷键功能');
                return;
            }
            //四个连播功能(ZXCV)在没有开启连播时,提醒开启连播功能
            if (shiftAndPlayVdoLowercase) {
                if (!playVideoConfig.isContinuous) {
                    layer.msg('请先开启连播功能');
                    return;
                }
            }
            if (!shiftAndLowercase) {
                return;
            }

        }


        let item = keyboardEvent.keyBindings.find(item => item.key === keyCode);
        if (item) {

            let video = document.querySelector('video');
            doAction(item, video);

        }

    });



    /**
     * [doAction description]
     * @param  {[type]} item  [that event triggered]
     * @param  {[type]} video [description]
     */
    function doAction(item, video) {

        let action = item.action;
        let value = item.value;
        let num = (video.playbackRate).toFixed(1);


        if (action == 'slower') {

            video.playbackRate -= value;
            num = (video.playbackRate).toFixed(1);
            keyboardEvent.currentSpeed = num;
            layer.msg(num + " 倍");

        } else if (action == 'faster') {

            video.playbackRate += value;
            num = (video.playbackRate).toFixed(1);
            keyboardEvent.currentSpeed = num;
            layer.msg(num + " 倍");

        } else if (action == 'rewind') {

            video.currentTime -= value;
            layer.msg("- " + value + 's');
            return;

        } else if (action == 'advance') {

            video.currentTime += value;
            layer.msg("+ " + value + 's');
            return;

        } else if (action == 'reset') {

            video.playbackRate = value;
            num = (video.playbackRate).toFixed(1);
            keyboardEvent.currentSpeed = num;
            layer.msg(num + " 倍");

        } else if (action == 'fast') {

            video.playbackRate = value;
            num = (video.playbackRate).toFixed(1);
            keyboardEvent.currentSpeed = num;
            layer.msg(num + " 倍");

        } else if (action == 'keyMap') {

            let i = layer.alert(
                keyboardEvent.keyMapInfo, {
                    //icon: 1
                    anim: 2
                },
                function(index) {
                    //layer.msg('操作成功!');
                    layer.close(index);
                });

            layer.title('Key Map', i);
            return;


        } else if (action == 'playAll') {
            $("#continuousPlayAll").trigger('click');
            return;

        } else if (action == 'stopPlayAll') {
            $("#stopContinuousPlayAll").trigger('click');
            return;

        } else if (action == 'playPart') {
            $("#continuousPlayPart").trigger('click');
            return;

        } else if (action == 'stopPlayPart') {
            $("#stopContinuousPlayPart").trigger('click');
            return;
        } else if (action == 'onContinuousPlayFunc') {
            onContinuousPlayFunc();
            return;

        } else if (action == 'offContinuousPlayFunc') {
            offContinuousPlayFunc();
            return;
        } else if (action == 'showTips') {
            showTips();
            return;
        }



    }


    /************************************
     * tips module
     */

    let tipsConfig = {
        params: {
            tipsMore: true,
            tips: 1,
            time: 6000
        },
    };

    function showTips() {
        layer.tips('全部连播', '#continuousPlayAll', tipsConfig.params);
        layer.tips('终止全部连播', '#stopContinuousPlayAll', tipsConfig.params);
        layer.tips('正常连播', '#continuousPlayPart', tipsConfig.params);
        layer.tips('终止正常连播', '#stopContinuousPlayPart', tipsConfig.params);
    }



    /**
     * MosoteachHelper CSS
     */
    const styleTag = `
<style>
.helper-btn{
border:1px solid #aaa;
border-radius:25px;
width:10%;
color:#fff;
font-weight:1000;
box-shadow:darkgrey 3px 3px 7px 2px;
cursor:pointer;
transition: .2s;
}
.helper-btn-a:hover{
//     opacity: 0.6;    //透明度
//     background-color: #4d79ff !important;
background-color: rgba(0, 151, 179,1) !important;
box-shadow: darkgrey 2px 2px 5px 1px !important;
}
.helper-btn-b:hover{
background-color:rgba(204, 0, 0,1) !important;
}
.helper-btn:active{
background-color:#002b80 !important;
border:3px solid #eee !important;
box-shadow: darkgrey 1px 1px 2px 1px !important;
}
#refresh{
float:right;
background-color:rgba(204, 0, 0,0.6);
}
#reset{
float:right;
background-color:rgba(204, 0, 0,0.6);
}
#mode-click{ background:rgba(0, 151, 179,0.7);}
#mode-download{ background:rgba(0, 151, 179,0.7);}
#confirm{ background:rgba(0, 151, 179,0.7);}
#downloadSrc{ background:rgba(0, 151, 179,0.7);}
#choose{ background:rgba(204, 0, 0,0.6);}
//#refresh{ background:rgba(0, 151, 179,0.7);}

.content-center{
    display: -webkit-box;
    display: -moz-box;
    display: -ms-flexbox;
    display: -webkit-flex;
    display: flex;
    /*垂直居中*/
    -webkit-box-align: center;
    -moz-box-align: center;
    -ms-flex-align: center;
    -webkit-align-items: center;
    justify-content: center;
    align-items: center;
    justify-content: center;
}

.video-btn{
    color:white; 
    font-size:20px; 
    width:20%;
    height: 34px;
}

#keyMapInfo{
    width:300px;
}

.keyMap-head{
    font-size: 16px;
    font-weight: 700;
}

.keyMap-name{
    width: 50%;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
}

.keyMap-value{
    width: 50%;
    font-size: 16px;
    font-weight: 700;
    color: orange;
}

</style>`;
    $(styleTag).appendTo('head');

    //为每个资源添加下载按钮
    $(".res-row-open-enable").each(function() {
        if ($(this).find(".download-res-button").length > 0) return; //如果已经存在下载按钮(例如mp3),则不再添加
        $(this).find("ul").html('<li class="download-ress download-res-button">下载</li>' + $(this).find("ul").html());
        // $(this).find("ul").html('<li class="forward">正序点击</li>' + $(this).find("ul").html());
        // $(this).find("ul").html('<li class="reverse">倒序点击</li>' + $(this).find("ul").html());
    });
    //单个资源下载
    $(document).on('click', '.download-ress', function() {
        var resHref = $(this).parents(".res-row-open-enable").attr('data-href');
        window.open(resHref);
    });

    $('<div id="functionAreaTitle" style="padding:0 20px">\
<div class="clear20"></div>\
<HR style="FILTER: alpha(opacity=100,finishopacity=0,style=3)" width="100%" color=#0BD SIZE=4>\
<div class="clear10"></div>\
<div class="res-row-title">\
<span style="color: #0BD;font-weight:600; font-size:16px"> 功能区 </span>\
<span > Powered by </span>\
<span ><a href="http://greasyfork.icu/zh-CN/scripts/390978-%E4%BA%91%E7%8F%AD%E8%AF%BE%E9%AB%98%E6%95%88%E5%8A%A9%E6%89%8B">云班课高效助手  </a></span>\
<span style="color: orange;font-weight:500; font-size:14px">  查看快捷键 :shift + m </span>\
<i class="slidedown-button manual-order-hide-part icon-angle-down" data-sort="1001"></i>\
</div>\
</div>\
<div class="clear20"></div>\
<!-- helper area Start -->\
<div id="functionAreaContent" class="hide-div" data-status="N" data-sort="1001" style="display: none;">\
<div id="helper" style="padding:0 40px;">\
<div class="res-row-title" >\
<span class="res-group-name">当前模式: </span>\
<span id="modeName" style="color: #0BD;font-weight:600">未选择 </span>\
<span class="span-display" style="color: red"> | ( 选择模式后,请按照提示操作,否则会出错;“模拟点击/下载”执行完毕后需刷新页面,数据才会更新。)</span>\
<i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="997"></i>\
</div>\
<div class="hide-div" data-status="N" data-sort="997" style="display: none;">\
<form class="appendTxt res-row" style="padding:20px 20px 0px 20px ; !important">\
<input id="mode-click" class="helper-btn helper-btn-a"  type="button" value="模拟点击">\
<input id="mode-download" class="helper-btn helper-btn-a" type="button" value="批量下载">\
<input id="reset" class="helper-btn helper-btn-a helper-btn-b" type="button" value="重置">\
<input id="refresh" class="helper-btn helper-btn-a helper-btn-b" type="button" value="刷新页面">\
</form>\
</div>\
<div id="module-3">\
<div class="clear30"></div>\
<div class="res-row-title" >\
<span class="res-group-name" >已选栏号:</span>\
<span id="barID" style="color: #0BD;font-weight:600"> 全选 </span>\
<span class="span-display" style="color: #0BD" > | (范围: 最大值为资源栏总数 / 不填写 则视为全选)</span>\
<span class="span-display" style="color: red">(注意:资源栏号是从资源区里第一栏开始)</span>\
<i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="1000"></i>\
</div>\
<div class="hide-div" data-status="N" data-sort="1000" style="display: none;">\
<form class="appendTxt res-row" style="padding:20px 20px 0px 20px ; !important">\
<input id="bar_index" placeholder="选择栏号   [  if (value < 1) --> 1 ; if (value > max) --> max  ]      选择多栏语法: 3-2-4-6  "  \
onkeyup="this.value=this.value.replace(/[^\\d][-]/g,\'\')" onafterpaste="this.value=this.value.replace(/[^\\d][-]/g,\'\')" style="border:1px solid #0BD; border-radius:8px;width:86%">&nbsp\
<input id="choose" class="helper-btn helper-btn-a helper-btn-b"  type="button" value="重置">\
</form>\
</div>\
</div>\
<div id="module-1">\
<div class="clear30"></div>\
<div class="res-row-title" >\
<span class="res-group-name" >模拟批量点击/下载</span>\
<span class="span-display" style="color: #0BD" >(范围:以资源总数值作为范围最大值)</span>\
<span class="span-display" style="color: red">( 点击对应按钮,将打开较多页面,请耐心等待其自动关闭。可在“控制台”里查看运行日志)</span>\
<i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="998"></i>\
</div>\
<div class="hide-div" data-status="N" data-sort="998" style="display: none;">\
<form class="appendTxt res-row" style="padding:20px 20px 0px 20px ; !important">\
<input id="head" class="indexNum" placeholder="起始位置    [  if (value < 1) --> 1 ; if (value > max) --> max  ]"  style="border:1px solid #0BD; border-radius:8px;width:42%" >&nbsp\
<input id="tail" class="indexNum" placeholder="结束位置    [  if (value < 1) --> 1 ; if (value > max) --> max  ]" style="border:1px solid #0BD; border-radius:8px;width:42%">&nbsp\
<input id="confirm" class="helper-btn helper-btn-a"  type="button" value="模拟点击">\
<input id="downloadSrc" class="helper-btn helper-btn-a"  type="button" value="批量下载">\
</form>\
</div>\
</div>\
<div id="module-2">\
<div class="clear30"></div>\
<div class="res-row-title" >\
<span class="res-group-name" >模拟全部点击(耗时较长)</span>\
<span class="span-display" style="color: #0BD" >(范围:所有资源)</span>\
<span class="span-display" style="color: red">( 点击后,将会自动打开较多页面,请耐心等待其自动关闭。可在“控制台(F12 -> console)”里查看运行日志)</span>\
<i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="999"></i>\
</div>\
<div class="hide-div" data-status="N" data-sort="999" style="display: none;">\
<div class="res-row drag-res-row" style="height:37px !important">\
<div class="operation manual-order-hide-part" style="float:left;!important">\
<ul style="margin-top:0px;"><li class="reverse">倒序点击</li><li class="forward">正序点击</li>\
<div class="clear"></div>\
</ul>\
</div>\
</div>\
</div>\
</div>\
<div id="module-4">\
<div class="clear30"></div>\
<div class="res-row-title" >\
<span class="res-group-name" >功能测试模块  </span>\
<span style="color: red"><a href = "http://greasyfork.icu/en/scripts/390978-%E4%BA%91%E7%8F%AD%E8%AF%BE%E9%AB%98%E6%95%88%E5%8A%A9%E6%89%8B/feedback">  点此反馈 (维护不易,还请好评 🙇 ‍)</a></span>\
<i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="1002"></i>\
</div>\
<div class="hide-div" data-status="N" data-sort="1002" style="display: none;">\
<div class="res-row drag-res-row" style="height:37px !important">\
<div class="operation manual-order-hide-part" style="float:left;!important">\
<ul style="margin-top:0px;">\
<li id ="continuousPlayMode">视频连续播放控件(按钮在视频界面)</li>\
<li > 快捷键系统(  shift + m  )</li>\
<div class="clear"></div>\
</ul>\
</div>\
</div>\
</div>\
</div>\
</div>\
</div>\
<!-- helper area End -->\
<div id="sourceTitle" style="padding:0 20px">\
<div class="clear10"></div>\
<HR style="FILTER: alpha(opacity=100,finishopacity=0,style=3)" width="100%" color=#0BD SIZE=4>\
<div class="clear10"></div>\
<div class="res-row-title">\
<span style="color: #0BD;font-weight:600; font-size:16px"> 资源区 </span>\
</div>\
</div>\
').insertAfter("#res-view-way");
    // 初始化
    $("#module-1,#module-2").css("display", "none");
    $("#confirm, #downloadSrc, #mode-click, #mode-download").css("display", "inline");
    // change mode
    $(document).on('click', '#mode-click', function() {
        $("#module-1, #module-2").css("display", "block");
        //         等价于
        //         document.getElementById("module-1").style.display="block";
        //         document.getElementById("module-2").style.display="block";
        //         document.getElementById('confirm').style.display = document.getElementById('confirm').style.display=="inline"?"inline":"none";
        $("#downloadSrc, #mode-download").css("display", "none");
        //         $("#mode-click").css({"background-color":"#0BD","color":"#fff"});
        $("#modeName").text("模拟点击");
        if (browserType() == "Chrome") {
            newTabAlert("onDownload", "chrome://settings/downloads", 'active', function() {
                alert("操作提醒:\n" + "务必操作,否则请不要向下执行任何操作!!!\n" + "\n" +
                    "(识别到您使用的是Chrome浏览器)" + "\n\n" +
                    "   已自动为你打开浏览器【设置】页面" + "\n" +
                    "   【提醒】:如果没有结果可在搜索框中搜索【保存位置】" + "\n" +
                    " 【 打开 】 “下载前询问每个文件的保存位置” 右侧按钮");
            });
        } else {
            alert("操作提醒:\n" + "务必操作,否则请不要向下执行任何操作!!!\n" + "\n" +
                "(以下只是 chrome 浏览器操作步骤)" + "\n" +
                "  1. 新建 Tab 页\n" + "   -->\n" +
                "  2. 地址栏输入: chrome://settings/?search=downloads\n" + "   -->\n" +
                "  3. 打开 “下载前询问每个文件的保存位置” 右侧按钮");
        }
    });
    $(document).on('click', '#mode-download', function() {
        document.getElementById("module-1").style.display = "block";
        $("#module-2, #confirm, #mode-click").css("display", "none");
        //         $("#mode-download").css({"background-color":"#0BD","color":"#fff"});
        $("#modeName").text("批量下载");
        if (browserType() == "Chrome") {
            newTabAlert("offDownload", "chrome://settings/downloads", 'active', function() {
                alert("操作提醒:\n" + "务必操作,否则请不要向下执行任何操作!!!\n" + "\n" +
                    "(识别到您使用的是Chrome浏览器)" + "\n\n" +
                    "   已自动为你打开浏览器【设置】页面" + "\n" +
                    "   【提醒】:如果没有结果可在搜索框中搜索【保存位置】" + "\n" +
                    " 【 关闭 】 “下载前询问每个文件的保存位置” 右侧按钮")
            });
        } else {
            alert("操作提醒:\n" + "务必操作,否则请不要向下执行任何操作!!!\n" + "\n" +
                "(以下只是 chrome 浏览器操作步骤)" + "\n" +
                "  1. 新建 Tab 页\n" + "   -->\n" +
                "  2. 地址栏输入:chrome://settings/?search=downloads\n" + "   -->\n" +
                "  3. 关闭 “下载前询问每个文件的保存位置” 右侧按钮");
        }
    });
    $(document).on('click', '#reset', function() {
        $("#module-1,#module-2").css("display", "none");
        $("#confirm, #downloadSrc, #mode-click, #mode-download").css("display", "inline");
        //         $("#mode-download, #mode-click").css({"background-color":"#fff","color":"#000"});
        $("#modeName").text("未选择");

    });
    // 刷新
    $(document).on('click', '#refresh', function() {
        location.reload()
    })
    //资源栏总数
    var srcBarSum = 0;
    //   给分栏添加 id 易于按栏操作
    $(".res-row-box").each(function(i, e) {
        $(this).attr('id', 'id_' + i);
        srcBarSum = i + 1;
    });
    //存储所有被选择的资源栏 id
    var chosenIDs = [];
    $(document).on('click', '#choose', function() {
        //获取点击时按钮值
        var val = $("#choose").val();
        //接受用户输入的id 字符串
        let inputString = $("#bar_index").val().trim();
        //inputString经过清洗后得到的有效资源栏编号
        let idsArr = cleanData(inputString);

        if (val == "确认选择") {
            /**
             * 用户修改要选择的资源栏点击确认后
             * 根据有效资源栏编号生成对应资源栏id存入数组备用
             * 并显示被选择的所有有效资源栏
             */

            //无输入内容,选择全部栏
            if (idsArr.length == 0) {
                chosenIDs.push(".res-row-box");
            } else {
                //有输入内容,转化成对应的id,放入数组备用
                for (let id of idsArr) {
                    chosenIDs.push("#id_" + (id - 1));
                }
            }
            //test
            // console.log(idsArr);
            //var barID = $("#bar_index").val();
            let barID_str = idsArr.length == 0 ? "全选" : idsArr;
            //var barID_str =  (barID > 0 && barID < 21) ? barID : "全选";
            alert("小可爱,你已将要操作的资源栏修改为: " + barID_str);
            $("#barID").text(barID_str);
            $("#choose").val("重置");
            $("#choose").css('background-color', 'rgba(204, 0, 0,0.6)');

        } else {
            /**
             * 用户重置资源栏输入框
             * 置空输入框 和 存储的所有资源栏id
             * 被选择的资源栏设为全选
             */
            $("#bar_index").val("");
            chosenIDs = [];
            $("#choose").val("确认选择");
            $("#choose").click();

        }

    });

    // reset  bar_index
    $('#bar_index').bind("input propertychange", function(event) {
        $("#choose").val("确认选择");
        $("#choose").css('background-color', 'rgba(0, 151, 179,0.7)');
    });


    /**
     * Main body
     *
     */

    /**
     * 根据指定的所有资源栏id,进行模拟点击
     */
    $(document).on('click', '#confirm', function() {
        batchForMoreSrcBars("模拟点击", chosenIDs)
    });

    /**
     * 根据指定的所有资源栏id,进行批量下载
     *
     */
    $(document).on('click', '#downloadSrc', function() {
        batchForMoreSrcBars("批量下载", chosenIDs)
    });

    /**
     * 模拟正序点击全部资源
     *
     */
    $(document).on('click', '.forward', function() {
        clickAll("true")
    });

    /**
     * 模拟倒序点击全部资源
     *
     */
    $(document).on('click', '.reverse', function() {
        clickAll("false")
    });

    /**
     * Play videos continuously
     */
    $(document).on('click', '#continuousPlayMode', () => {
        continuousPlay()
    })


});