Greasy Fork

Greasy Fork is available in English.

学习通刷课助手||防止鼠标移出暂停|章节结束自动跳转|章节测试[功能测试中,正确率待验证]

学习通课程自动挂机,当前脚本支持课程视频播放完成,自动跳转下一小节,章节测试自动跳过,后台播放防止视频暂停,章节测试刷题。

当前为 2024-12-12 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         学习通刷课助手||防止鼠标移出暂停|章节结束自动跳转|章节测试[功能测试中,正确率待验证]
// @namespace    http://tampermonkey.net/
// @version      2.0.1
// @description  学习通课程自动挂机,当前脚本支持课程视频播放完成,自动跳转下一小节,章节测试自动跳过,后台播放防止视频暂停,章节测试刷题。
// @author       Sweek
// @match        *://*.chaoxing.com/*
// @license      GPLv3
// @icon         https://www.google.com/s2/favicons?sz=64&domain=csdn.net
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @require      https://code.jquery.com/jquery-2.1.4.min.js
// ==/UserScript==



GM_setValue("playbackRate", GM_getValue("playbackRate") || 1);
GM_setValue("volume", GM_getValue("volume") || 0);
GM_setValue("courseImg", GM_getValue("courseImg") || '');
GM_setValue("testType", GM_getValue("testType") || 'save');
GM_setValue("ifTest", GM_getValue("ifTest") || 'skip');
GM_setValue("taskInterval", GM_getValue("taskInterval") || '6000');
GM_setValue("testInterval", GM_getValue("testInterval") || '4000');
GM_setValue("ifReview", GM_getValue("ifReview") || '0');


// 定义全局变量
let currentTime = null // 当前视频当前播放节点
let duration = null // 当前视频总长度
let progress = null // 当前视频播放进度
let playbackRate = GM_getValue("playbackRate") // 当前视频播放倍速
let volume = GM_getValue("volume") // 当前视频音量

// 课程章节相关数据
let courseName = null // 当前课程名称
let courseId = null // 当前课程id
let courseImg = GM_getValue("courseImg") // 当前课程封面
let chapterInfo = [] // 当前课程所有章节数据
let currentChapterId = null // 当前所在章节id
let currentChapterName = null // 当前所在章节名称
let allChapterName = []  // 所有章节名称
let currentChapterTabArr = [] // 当前页面所有的分栏
let currentChapterTabName = '' // 当前页面激活的分栏名称
let videoProgressId = '' // 定时监听页面内容监听事件

// 任务间隔
let taskInterval = GM_getValue("taskInterval")

// 是否复习模式
let ifReview = GM_getValue("ifReview")


// 章节测试数据
let testDom = null
let ifTest = GM_getValue("ifTest")
let testType = GM_getValue("testType")
let testTasks = []
let testBtnDocument = null

let testInterval = GM_getValue("testInterval")

// 获取当前页面的 URL
url = ''
chapterId = ''


let arrayEchelon = []; // 顺序执行梯队,初始化为空数组
const dealEvent = new Event("redeal", { bubbles: false, cancelable: false });
let testArrayEchelon = []; // 顺序执行梯队,初始化为空数组
const testDealEvent = new Event("testRedeal", { bubbles: false, cancelable: false });

// AI请求接口Url
const fetch_url ='https://www.sweek.top/model/sse/data?text='

// 页面模板部分
// 页面模板部分
// 页面模板部分

// 页面样式
var popCSs = `
#my-window {
    position: fixed;
    top: 5px;
    left: 20px;
    width: 320px;
    height: auto;
    background-color: rgba(247, 247, 247, 1);
    border: 1px solid #fff;
    border-radius: 5px;
    z-index: 9999;
    overflow: hidden;
    padding: 0 5px 10px  0;
    font-family: fangsong;
    font-weight: bold;
  }
  
  #my-window .header {
    background-color: #4497fa;
    color: #fff;
    padding: 5px;
    font-size: 16px;
    font-family: 'fangsong';
    font-weight: bold;
    border-radius: 5px;
    cursor: move;
    height: 25px;
    width: 320px;
  }

  #my-window .content {
    width: 320px;
    height: auto;
  }

  #my-window .content .content-title {
    height: 22px;
    width: 300px;
    background-color: #dadada;
    line-height: 22px;
    padding-left: 5px;
    font-size: 12px;
    font-family: 'fangsong';
    font-weight: 600;
    boder-radius: 5px;
    border-left: 4px solid #2196f3;
    border-right: 4px solid #dadada;
    margin-top: 5px;
    border-radius: 3px;
  }

  #my-window .content .content-notice {
    height: 80px;
    width: 300px;
    overflow: auto;
    border: 1px solid gray;
    border-radius: 3px;
    padding: 5px;
    margin-top: 5px;
    font-size: 12px;
    font-weight: bold;
  }

  #my-window .content .content-process {
    height: 60px;
    width: 300px;
    overflow: auto;
    border: 1px solid gray;
    border-radius: 3px;
    padding: 5px;
    margin-top: 5px;
  }

  #my-window .content .content-log {
    height: 120px;
    width: 300px;
    overflow: auto;
    border: 1px solid gray;
    border-radius: 3px;
    padding: 5px;
    margin-top: 5px;
  }
  
  #my-window .content .content-set {
    height: 240px;
    width: 300px;
    overflow-y: auto;
    overflow-x: hidden;
    border: 1px solid gray;
    border-radius: 3px;
    padding: 5px;
    margin-top: 5px;
  }

  #my-window .content .content-set .content-set-content {
    width: 290px;
    display: flex;
    justify-content: space-between;
    border-top: 1px solid gray;
    border-bottom: 1px solid gray;
    padding: 5px 5px;
  }

  #my-window .content .content-set .content-set-content .control {
    width: 190px;
    height: 20px;
    display: flex;
    justify-content: space-between;
  }

  #my-window .content .content-set .content-set-content .control #ifReviewSelect {
    width: 190px;
    height: 20px;
    border-radius: 3px;
    border: 1px solid gray;
    font-size: 13px;
    font-family: fangsong;
    font-weight: bold;
    cursor: pointer;
  }

  #my-window .content .content-set .content-set-content .control #playbackRateSelect {
    width: 190px;
    height: 20px;
    border-radius: 3px;
    border: 1px solid gray;
    font-size: 13px;
    font-family: fangsong;
    font-weight: bold;
    cursor: pointer;
  }

  #my-window .content .content-set .content-set-content .control #volumeSelect {
    width: 190px;
    height: 20px;
    border-radius: 3px;
    border: 1px solid gray;
    font-size: 13px;
    font-family: fangsong;
    font-weight: bold;
    cursor: pointer;
  }

  #my-window .content .content-set .content-set-content .control #ifTestSelect {
    width: 190px;
    height: 20px;
    border-radius: 3px;
    border: 1px solid gray;
    font-size: 13px;
    font-family: fangsong;
    font-weight: bold;
    cursor: pointer;
  }

  #my-window .content .content-set .content-set-content .control #testTypeSelect {
    width: 190px;
    height: 20px;
    border-radius: 3px;
    border: 1px solid gray;
    font-size: 13px;
    font-family: fangsong;
    font-weight: bold;
    cursor: pointer;
  }

  #my-window .content .content-set .content-set-content .control #taskIntervalSelect {
    width: 190px;
    height: 20px;
    border-radius: 3px;
    border: 1px solid gray;
    font-size: 13px;
    font-family: fangsong;
    font-weight: bold;
    cursor: pointer;
  }

  #my-window .content .content-set .content-set-content .control #testIntervalSelect {
    width: 190px;
    height: 20px;
    border-radius: 3px;
    border: 1px solid gray;
    font-size: 13px;
    font-family: fangsong;
    font-weight: bold;
    cursor: pointer;
  }

  #my-window .content .content-set .content-set-content #email-input {
    width: 145px;
    height: 20px;
    border-radius: 3px;
    border: 1px solid gray;
    font-size: 13px;
    font-family: fangsong;
    font-weight: bold;
  }


  #my-window .content .content-set .content-set-content #save-btn {
    color: gray;
    width: 40px;
    height: 22px;
    border-radius: 3px;
    border: 1px solid gray;
    font-size: 13px;
    font-family: fangsong;
    font-weight: bold;
    cursor: pointer;
  }

  #save-btn:hover {
    background-color: #e3e3e3!important;
  }

  #my-window .resizer {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 20px;
    height: 20px;
    background-color: #2196f3;
    cursor: se-resize;
    border-radius: 0px;
    z-index: 1;
  }
  
  #hide-btn {
    height: 25px;
    width: auto;
    float: right;
    margin-right: 10px;
    background-color: #fff;
    border: 1px solid gray;
    border-radius: 5px;
    font-size: 12px;
    padding: 0 5px;
    font-family: 'fangsong';
    cursor: pointer;
  }

  #hide-btn:hover {
    background-color: #4497fa;
    color: #fff;
  }

  #hide-notice-btn {
    height: 20px;
    line-height: 20px;
    width: auto;
    background-color: #fff;
    border: 1px solid gray;
    border-radius: 3px;
    font-size: 12px;
    padding: 0 5px;
    font-family: 'fangsong';
    cursor: pointer;
    float: right;
  }
  #hide-notice-btn:hover {
    background-color: gray;
    color: #fff;
  }

  #hide-process-btn {
    height: 20px;
    line-height: 20px;
    width: auto;
    background-color: #fff;
    border: 1px solid gray;
    border-radius: 3px;
    font-size: 12px;
    padding: 0 5px;
    font-family: 'fangsong';
    cursor: pointer;
    float: right;
  }
  #hide-process-btn:hover {
    background-color: gray;
    color: #fff;
  }

  #hide-log-btn {
    height: 20px;
    line-height: 20px;
    width: auto;
    background-color: #fff;
    border: 1px solid gray;
    border-radius: 3px;
    font-size: 12px;
    padding: 0 5px;
    font-family: 'fangsong';
    cursor: pointer;
    float: right;
  }
  #hide-log-btn:hover {
    background-color: gray;
    color: #fff;
  }

  #hide-set-btn {
    height: 20px;
    line-height: 20px;
    width: auto;
    background-color: #fff;
    border: 1px solid gray;
    border-radius: 3px;
    font-size: 12px;
    padding: 0 5px;
    font-family: 'fangsong';
    cursor: pointer;
    float: right;
  }
  #hide-set-btn:hover {
    background-color: gray;
    color: #fff;
  }
`

// 页面Html
var popHtml = `
<div class="header">学习通助手[2.0.1]
  <button id="hide-btn">显示/隐藏</button>
</div>
<div class="content" id="my-window-content">
  <div class="resizer" style="display: none;"></div>
  <div class="row" style="border: 1px solid #ccc; padding: 5px;">
    <div class="content-title">公告
      <button id="hide-notice-btn">显示/隐藏</button>
    </div>
    <div class="content-notice" id="content-notice">
    </div>
    <div class="content-title">任务进度<span id="async-time"></span>
      <button id="hide-process-btn">显示/隐藏</button>
    </div>
    <div class="content-process" id="content-process"></div>
    <div class="content-title">执行日志
      <button id="hide-log-btn">显示/隐藏</button>
    </div>
    <div class="content-log" id="content-log"></div>
    <div class="content-title">配置
      <button id="hide-set-btn">显示/隐藏</button>
    </div>
    <div class="content-set" id="content-set">        
      <div style="margin-bottom: 5px;font-family: 'fangsong';font-weight: bold;">
        <div>
          [该邮箱地址用来登录app网站,该邮箱需要和网站注册邮箱保持一致,不可为空,填写完,点击保存,可在网站查看课程进度。]
        </div>
        <div>[二维码:<a style="color: dodgerblue;cursor: pointer;" id="Qcode">点击查看,微信扫码</a>]</div>
      </div>
      <div class="content-set-content">
        <label for="email-input">[邮箱]</label>
        <div class="control">
          <input type="email" id="email-input" placeholder="请输入邮箱地址">
          <button id="save-btn">保存</button>
        </div>
      </div>
      <div class="content-set-content">
        <label for="email-input">[复习模式]</label>
        <div class="control">
          <select id="ifReviewSelect">
              <option value="1">启用</option>
              <option value="0">关闭</option>
          </select>
        </div>
      </div>
      <div class="content-set-content">
        <label for="email-input">[视频倍速]</label>
        <div class="control">
          <select id="playbackRateSelect">
              <option value="1">1x</option>
              <option value="2">2x[部分场景使用]</option>
              <option value="3">3x[暂时无法使用]</option>
              <option value="4">4x[暂时无法使用]</option>
          </select>
        </div>
      </div>
      <div class="content-set-content">
        <label for="email-input">[视频音量]</label>
        <div class="control">
          <select id="volumeSelect">
            <option value="0">静音</option>
            <option value="0.2">20</option>
            <option value="0.4">40</option>
            <option value="0.6">60</option>
            <option value="0.8">80</option>
            <option value="1">100</option>
          </select>
        </div>
      </div>
      <div class="content-set-content">
        <label for="email-input">[是否章节测试]</label>
        <div class="control">
          <select id="ifTestSelect">
            <option value="take">处理章节测试</option>
            <option value="skip">跳过章节测试</option>
          </select>
        </div>
      </div>
      <div class="content-set-content">
        <label for="email-input">[章节测试完成]</label>
        <div class="control">
          <select id="testTypeSelect">
            <option value="save">完成后暂时保存</option>
            <option value="submit">完成后提交</option>
          </select>
        </div>
      </div>
      <div class="content-set-content">
        <label for="email-input">[章节任务间隔]</label>
        <div class="control">
          <select id="taskIntervalSelect">
            <option value="4000">4s</option>
            <option value="6000">6s</option>
            <option value="8000">8s</option>
            <option value="10000">10s</option>
          </select>
        </div>
      </div>
      <div class="content-set-content">
        <label for="email-input">[章节测试间隔]</label>
        <div class="control">
          <select id="testIntervalSelect">
            <option value="4000">4s</option>
            <option value="6000">6s</option>
            <option value="8000">8s</option>
            <option value="10000">10s</option>
          </select>
        </div>
      </div>
    </div>
  </div>
</div>
`
// 初始化添加页面弹窗以及悬浮球
function initPopup() {
  // 添加CSS样式
  GM_addStyle(popCSs);
  // 创建窗口元素
  const myWindow = document.createElement("div");
  myWindow.id = "my-window";
  myWindow.innerHTML = popHtml;
  // 获取页面body元素
  const body = document.getElementsByTagName("body")[0];
  // 添加窗口和悬浮球到页面
  body.appendChild(myWindow);
  // 绑定隐藏窗口按钮的click事件
  const hideBtn = document.querySelector("#hide-btn");
  hideBtn.addEventListener("click", hideWindow);
    // 绑定隐藏公告按钮的click事件
  const hideNoticeBtn = document.querySelector("#hide-notice-btn");
  hideNoticeBtn.addEventListener("click", hideNotice);
    // 绑定隐藏播放进度按钮的click事件
  const hideProcessBtn = document.querySelector("#hide-process-btn");
  hideProcessBtn.addEventListener("click", hideProcess);
    // 绑定隐藏日志按钮的click事件
  const hideLogBtn = document.querySelector("#hide-log-btn");
  hideLogBtn.addEventListener("click", hideLog);
    // 绑定隐藏设置按钮的click事件
  const hideSetBtn = document.querySelector("#hide-set-btn");
  hideSetBtn.addEventListener("click", hideSet);
  // 获取弹窗内容
  var htmlContent = '<div style="width: 800px; height: 400px;display: flex;justify-content: center;align-items: center;"><img style="width: 400px; height: 400px;" src="https://www.sweek.top/api/preview/Qcode.png" alt="微信扫码"><img style="height: 400px;" src="https://www.sweek.top/api/preview/Qrcode.jpg" alt="微信扫码"></div>';

  // 点击链接显示弹窗
  document.getElementById('Qcode').addEventListener('click', function() {
    showCustomPopup(htmlContent, 800, 400);
  });
  

  // 获取头部元素
  const header = myWindow.querySelector('.header');

  // 处理移动事件
  let isDragging = false;
  let mouseX = 0;
  let mouseY = 0;

  header.addEventListener('mousedown', function (e) {
    e.preventDefault();
    isDragging = true;
    mouseX = e.clientX;
    mouseY = e.clientY;
  });

  document.addEventListener('mousemove', function (e) {
    if (isDragging) {
      const deltaX = e.clientX - mouseX;
      const deltaY = e.clientY - mouseY;
      const newLeft = Math.min(
        Math.max(0, myWindow.offsetLeft + deltaX),
        window.innerWidth - myWindow.offsetWidth
      );
      const newTop = Math.min(
        Math.max(0, myWindow.offsetTop + deltaY),
        window.innerHeight - myWindow.offsetHeight
      );
      myWindow.style.left = newLeft + 'px';
      myWindow.style.top = newTop + 'px';
      mouseX = e.clientX;
      mouseY = e.clientY;
    }
  });

  document.addEventListener('mouseup', function () {
    isDragging = false;
  });

  document.getElementById('ifReviewSelect').addEventListener('change', function() {
      updateIfReview(this.value);
  });
  document.getElementById("ifReviewSelect").value = ifReview;
  document.getElementById('playbackRateSelect').addEventListener('change', function() {
      updatePlaybackRate(this.value);
  });
  document.getElementById("playbackRateSelect").value = playbackRate;
  document.getElementById('volumeSelect').addEventListener('change', function() {
      updateVolume(this.value);
  });
  document.getElementById("volumeSelect").value = volume;
  document.getElementById('testTypeSelect').addEventListener('change', function() {
      updateTestType(this.value);
  });
  document.getElementById("testTypeSelect").value = testType;
  document.getElementById('ifTestSelect').addEventListener('change', function() {
      updateIfTest(this.value);
  });
  document.getElementById("ifTestSelect").value = ifTest;
  document.getElementById('taskIntervalSelect').addEventListener('change', function() {
      updateTaskInterval(this.value);
  });
  document.getElementById("taskIntervalSelect").value = taskInterval;
  document.getElementById('testIntervalSelect').addEventListener('change', function() {
      updateTestInterval(this.value);
  });
  document.getElementById("testIntervalSelect").value = testInterval;
}

// 当下拉框的值改变时,更新变量值
function updateIfReview(val) {
  ifReview = val
  GM_setValue("ifReview", ifReview);
}
function updatePlaybackRate(val) {
  playbackRate = val
  GM_setValue("playbackRate", playbackRate);
  // 刷新页面
  location.reload();
}
function updateVolume(val) {
  volume = val
  GM_setValue("volume", volume);
  // 刷新页面
  location.reload();
}
function updateIfTest(val) {
  if(val == "take") {
    // const email = GM_getValue("savedEmail")
    // if(email) {
    //   ifTest = val
    //   GM_setValue("ifTest", ifTest);
    //   notify(`已将章节测试设为${ifTest == 'take' ? '处理' : '跳过'}状态`, 2500)
    // } else {
    //   notify('请先填写邮箱(邮箱账号需要扫码登录网站注册)', 5000)
    // }
    // 获取输入的邮箱地址
    const email = GM_getValue("savedEmail");
    // 检查邮箱地址是否有效
    if (!isValidEmail(email)) {
      notify('请输入有效的邮箱地址!', 5000)
      document.getElementById('ifTestSelect').value = 'skip';
      return;
    }
    
    const url = 'https://www.sweek.top/api/checkEmailExists';
    const data = { 
      email
    };
    if(email) {
      fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json', // 声明请求主体的内容类型为 JSON
        },
        body: JSON.stringify(data), // 将数据对象转换为 JSON 字符串并作为请求主体
      }).then(response => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json(); // 解析 JSON 响应数据
      }).then(data => {
        if(data.exists){
          ifTest = val
          GM_setValue("ifTest", ifTest);
          notify(`已将章节测试设为${ifTest == 'take' ? '处理' : '跳过'}状态`, 2500)
        } else {
          document.getElementById('ifTestSelect').value = 'skip';
          notify('邮箱账号尚未注册,请扫码前往留言信箱网站注册,然后再尝试启用章节测试刷题功能', 5000)
        }
      }).catch(error => {
        // console.error('Error:', error);
      });
    }
  } else if (val == "skip"){
    ifTest = val
    GM_setValue("ifTest", ifTest);
    notify(`已将章节测试设为${ifTest == 'take' ? '处理' : '跳过'}状态`, 2500)
  }
}
function updateTestType(val) {
  testType = val
  GM_setValue("testType", testType);
  notify(`章节测试完成后将会${testType == 'save' ? '暂时保存' : '提交'}`, 2500)
  // 刷新页面
  // location.reload();
}
function updateTaskInterval(val) {
  taskInterval = val
  GM_setValue("taskInterval", taskInterval);
  notify(`章节任务执行间隔已设置为${taskInterval/1000}秒`, 2500)
  // 刷新页面
  // location.reload();
}
function updateTestInterval(val) {
  testInterval = val
  GM_setValue("testInterval", testInterval);
  notify(`章节测试执行间隔已设置为${testInterval/1000}秒`, 2500)
  // 刷新页面
  // location.reload();
}


// 隐藏窗口函数
function hideWindow() {
  var myWindowContent = document.getElementById("my-window-content");
  var showPop = myWindowContent.style.display
  if (showPop == '' || showPop == 'block') {
    myWindowContent.style.display = "none";
  } else {
    myWindowContent.style.display = "block";
  }
}

// 隐藏公告函数
function hideNotice() {
  var contentNotice = document.getElementById("content-notice");
  var showPop = contentNotice.style.display
  if (showPop == '' || showPop == 'block') {
    contentNotice.style.display = "none";
  } else {
    contentNotice.style.display = "block";
  }
}

// 隐藏播放进度函数
function hideProcess() {
  var contentProcess = document.getElementById("content-process");
  var showPop = contentProcess.style.display
  if (showPop == '' || showPop == 'block') {
    contentProcess.style.display = "none";
  } else {
    contentProcess.style.display = "block";
  }
}

// 隐藏日志函数
function hideLog() {
  var contentLog = document.getElementById("content-log");
  var showPop = contentLog.style.display
  if (showPop == '' || showPop == 'block') {
    contentLog.style.display = "none";
  } else {
    contentLog.style.display = "block";
  }
}

// 隐藏设置函数
function hideSet() {
  var contentSet = document.getElementById("content-set");
  var showPop = contentSet.style.display
  if (showPop == '' || showPop == 'block') {
    contentSet.style.display = "none";
  } else {
    contentSet.style.display = "block";
  }
}

function takeEmail() {
  // 获取邮箱输入框和保存按钮
  const emailInput = document.getElementById("email-input");
  const saveBtn = document.getElementById("save-btn");

  // 当保存按钮被点击时
  saveBtn.addEventListener("click", function() {
    // 获取输入的邮箱地址
    const email = emailInput.value.trim();
    // 检查邮箱地址是否有效
    if (!isValidEmail(email)) {
      notify('请输入有效的邮箱地址!', 2500)
      return;
    }
    
    const url = 'https://www.sweek.top/api/checkEmailExists';
    const data = { 
      email
    };
    if(email) {
      fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json', // 声明请求主体的内容类型为 JSON
        },
        body: JSON.stringify(data), // 将数据对象转换为 JSON 字符串并作为请求主体
      }).then(response => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json(); // 解析 JSON 响应数据
      }).then(data => {
        if(data.exists){
          // 将邮箱地址存储到本地存储中
          GM_setValue("savedEmail", email);
          // 提示保存成功
          notify('保存成功!课程信息将同步到该邮箱账号', 2500)
        } else {
          notify('邮箱账号尚未注册,请扫码前往留言信箱网站注册', 2500)
        }
      }).catch(error => {
        // console.error('Error:', error);
      });
    }

  });

  // 页面加载时,尝试从本地存储中获取已保存的邮箱地址并反显到输入框中
  const savedEmail = GM_getValue("savedEmail");
  if (savedEmail) {
    emailInput.value = savedEmail;
  }
}

// 邮箱地址验证函数
function isValidEmail(email) {
  // 此处可以使用正则表达式等方式进行邮箱地址的验证
  // 这里简单地判断邮箱是否包含 '@' 符号
  return email.includes("@");
}




// 封装页面组件方法
// 封装页面组件方法
// 封装页面组件方法

// 页面弹窗显示
function showCustomPopup(htmlContent, width, height) {
    // 创建弹窗容器
    var popupContainer = document.createElement('div');
    popupContainer.className = 'custom-popup-container';
    // 设置弹窗容器样式
    popupContainer.style.position = 'fixed';
    popupContainer.style.top = '50px';
    popupContainer.style.left = '0';
    popupContainer.style.width = '100%';
    popupContainer.style.height = 'calc(100% - 50px)';
    popupContainer.style.display = 'flex';
    popupContainer.style.justifyContent = 'center';
    popupContainer.style.alignItems = 'center';
    popupContainer.style.zIndex = '999999';

    // 创建弹窗内容容器
    var popupContent = document.createElement('div');
    popupContent.className = 'custom-popup-content';
    // 设置弹窗内容样式
    popupContent.style.width = width + 'px';
    popupContent.style.height = height + 'px';
    popupContent.style.backgroundColor = '#fff';
    popupContent.style.border = '1px solid #ccc';
    popupContent.style.borderRadius = '5px';
    popupContent.style.position = 'relative';

    // 创建关闭按钮
    var closeButton = document.createElement('button');
    closeButton.innerHTML = '关闭';
    closeButton.style.position = 'absolute';
    closeButton.style.top = '5px';
    closeButton.style.right = '5px';
    closeButton.style.padding = '5px 10px';
    closeButton.style.backgroundColor = '#ccc';
    closeButton.style.border = 'none';
    closeButton.style.borderRadius = '3px';
    closeButton.style.cursor = 'pointer';
    closeButton.style.fontFamily = 'Arial, sans-serif';

    // 关闭按钮点击事件处理
    closeButton.addEventListener('click', function() {
        popupContainer.remove();
    });

    // 设置弹窗内容
    popupContent.innerHTML = htmlContent;

    // 将关闭按钮和弹窗内容添加到弹窗容器中
    popupContent.appendChild(closeButton);
    popupContainer.appendChild(popupContent);

    // 将弹窗容器添加到页面中
    document.body.appendChild(popupContainer);
}

// 页面通知提示
function notify(text, time) {
  // 创建通知元素
  var notification = document.createElement('div');
  notification.className = 'notification';
  // 设置通知内容
  notification.innerHTML = '<div class="notification-content"><h2 style="font-size: 16px;font-weight: bold;color: #307dff;font-family: fangsong;">' + '学习通助手提示' + '</h2><p style="font-family: fangsong;font-size: 13px;font-weight: bold;">' + text + '</p></div>';
  // 将通知添加到页面中
  document.body.appendChild(notification);
  // 设置通知样式
  notification.style.position = 'fixed';
  notification.style.top = '50px';
  notification.style.left = '-400px'; // 从左边弹出
  notification.style.transform = 'translateY(-50%)'; // 垂直居中
  notification.style.transition = 'left 0.5s ease-in-out'; // 添加过渡效果
  notification.style.zIndex = '999999';
  notification.style.backgroundColor = '#fff';
  notification.style.border = '1px solid #ccc';
  notification.style.padding = '10px';
  notification.style.borderRadius = '5px';
  notification.style.lineHeight = '25px';

  // 等待一小段时间后,移动通知到左边
  setTimeout(function() {
      notification.style.left = '20px'; // 移动到左边
  }, 100);

  // 设置定时器,在指定时间后移除通知
  setTimeout(function() {
      // 移动通知到左边以外
      notification.style.left = '-400px';
      // 等待过渡效果完成后,移除通知元素
      setTimeout(function() {
      notification.remove();
      }, 500);
  }, time);
}

// 添加执行日志
function addlog(str, color) {
  var _time = new Date().toLocaleTimeString()
  var contentLog = window.top.document.querySelector('.content-log');
  var newContent = '<p style="color: ' + color + ';">[' + _time + ']' + str + '</p><hr>';
  contentLog.innerHTML += newContent;
  // 将滚动条滚动到底部
  contentLog.scrollTop = contentLog.scrollHeight;
}


/** 监听事件-章节任务 */
document.addEventListener("redeal", () => {
    dealAnsEchelon(arrayEchelon);
});

/** 监听事件-章节测试 */
document.addEventListener("testRedeal", () => {
    dealTestEchelon(testArrayEchelon);
});


// 章节任务处理
// 章节任务处理
// 章节任务处理

/** 初始化 */
function initAll() {
  // console.log('location.pathname:::+ ', location.pathname)
  if(location.pathname == '/mooc-ans/knowledge/cards') {  
    addlog('正在获取当前页面的任务...');
    const arrayAnsAll = document.querySelectorAll(".ans-attach-ct");
    addlog(`当前页面任务数量为: ${Array.from(arrayAnsAll).length}`);
    // 获取当前页面将会处理的任务-[根据是否启用复习模式]
    const arrayAns = ifReview == 1 ? document.querySelectorAll(".ans-attach-ct") : document.querySelectorAll('.ans-attach-ct:not(.ans-job-finished)')
    const taskCount = arrayAns.length;
    // addlog(`找到的任务数量: ${taskCount}`);
    if (arrayAns && taskCount > 0) {
      try {
        const arrayType = getIframesType(arrayAns);
        console.log('任务类型数组:', arrayType);

        const arrayDocument = getAllIframesDocument(arrayAns);
        console.log('任务文档数组:', arrayDocument);

        arrayEchelon = distributeAns(arrayType, arrayDocument);
        // addlog(`当前页面待办任务数量为: ${arrayEchelon.length}`);

        // 触发处理任务的事件
        document.dispatchEvent(dealEvent);
      } catch (error) {
        // console.error("初始化过程中发生错误:", error);
      }
    } else {
      addlog("当前页面没有可处理的任务,直接跳过章节");
      // 如果没有任务,
      skipChapter();
    }
  }

}

// 获取任务属性
function getIframesType(arrayAns) {
  return Array.from(arrayAns)
      .map(ans => {
          const jsonStr = ans.querySelector("iframe")?.getAttribute("data");
          if (!jsonStr) {
              // console.warn("未找到 data 属性或 data 属性为空。");
              return null;
          }
          try {
              const json = JSON.parse(jsonStr);
              // 根据需求获取任务类型
              // 课程类型
              if(json && json.type) {
                return json.type;
              }
              // 测试类型
              if(json && json.worktype) {
                return json.worktype;
              }
          } catch (error) {
              // console.warn("解析 JSON 失败:", error);
              return null;
          }
      })
      .filter(type => type !== null);
}

// 获取任务 document
function getAllIframesDocument(arrayAns) {
return Array.from(arrayAns)
  .map(ans => ans.querySelector("iframe")?.contentWindow?.document)
  .filter(doc => doc !== undefined && doc !== null);
}

// 按任务属性映射处理函数
const handlerMap = {
  ".mp4": videoHandler,
  ".wmv": videoHandler,
  ".avi": videoHandler,
  ".mkv": videoHandler,
  ".flv": videoHandler,
  ".doc": pptxHandler,
  ".docx": pptxHandler,
  ".pptx": pptxHandler,
  ".pdf": pptxHandler,
  ".ppt": pptxHandler,
  ".mp3": audioHandler,
  ".wav": audioHandler,
  "workA": testHandler
};

// 按任务属性分配执行函数
function distributeAns(arrayType, arrayDocument) {
  return arrayType.map((type, index) => {
      const handler = handlerMap[type] || ignoreAns;
      return { document: arrayDocument[index], handler };
  });
}

// 处理单个任务
function dealSingleAns(singleAns) {
  if (singleAns && singleAns.handler && typeof singleAns.handler === 'function') {
      singleAns.handler(singleAns.document);
  } else {
      // console.warn("无效的任务或处理函数。");
      document.dispatchEvent(dealEvent);
  }
}


// 章节测试处理
// 章节测试处理
// 章节测试处理


// 按章节测试题目属性映射处理函数
const handlerTestMap = {
  "单选题": choiceHandler,
  "多选题": choiceHandler,
  "判断题": choiceHandler
};

// 按任务属性分配执行函数
function distributeTest(arrayType) {
  return arrayType.map((item, index) => {
      const handler = handlerTestMap[item.type] || ignoreTest;
      return { document: item.document, handler, testObj: item };
  });
}

// 处理单个章节测试任务
function dealSingleTest(singleAns) {
  if (singleAns && singleAns.handler && typeof singleAns.handler === 'function') {
      singleAns.handler(singleAns.document, singleAns.testObj);
  } else {
      // console.warn("无效的任务或处理函数。");
      document.dispatchEvent(testDealEvent);
  }
}


/** 工具函数 */
/** 工具函数 */
/** 工具函数 */

// 章节测试执行方法
// 章节测试执行方法
// 章节测试执行方法


// 忽略章节测试题
function ignoreTest() {
	addlog("无法处理, 忽略该章节测试题");
	setTimeout(() => {
			document.dispatchEvent(testDealEvent);
	}, testInterval);
}

// 处理单选题/多选题/判断题
function choiceHandler(idocument, testObj) {
  // console.log('idocument:::+ ', idocument)
  // console.log('testObj:::+ ', testObj)
  addlog(`处理${testObj.type}任务中...`);
  fetch(fetch_url + testObj.question + '请你提供给我正确答案,不需要解析,答案需要精简,返回选项即可')
  .then(response => {
    let model_text = ''
    const reader = response.body.getReader()
    const decoder = new TextDecoder('utf-8')

    const readChunk = () => {
      reader.read().then(({ done, value }) => {
        if (done) {
          console.log('model_text:::+ ', model_text)
          const answer_index = findABCDEFPositions(model_text)
          console.log('answer_index:::+ ', answer_index)
          const trueOptions = Array.from(idocument.querySelectorAll('li'));
          trueOptions.forEach((option, index) => {
            if (answer_index.includes(index)) {
              option.click();
            }
          });
          const answer_text = mapIndexToOption(answer_index)
          console.log('answer_text:::+ ', answer_text)
          const currentTestTask = testTasks.find((task) => task.data == testObj.data)
          if (currentTestTask && answer_index.length > 0) {
            currentTestTask.model_text = model_text
            currentTestTask.answer_text = answer_text
            currentTestTask.status = true
          } else {
            currentTestTask.status = false
          }
          // 使用 findIndex 获取当前任务的索引
          const taskIndex = testTasks.findIndex((task) => task.data === testObj.data);
          const process = (((taskIndex + 1)/testTasks.length)*100).toFixed(2) + '%'
          setVideoProcess(process, taskIndex + 1, testTasks.length, 'test')
          // 同步章节测试题目-AI模型生成
          const email = GM_getValue("savedEmail")
          const course_name = GM_getValue("courseName") || '未获取课程名称';
          const testList = [{ question: testObj.question, answer: answer_text, type: testObj.type, email: email, course_name: course_name }]
          insertTestModel(testList)
          setTimeout(() => {
            document.dispatchEvent(testDealEvent);
          }, testInterval);
          return
        }
        const decodedValue = decoder.decode(value, { stream: true })
        model_text += decodedValue
        readChunk()
      })
    }
    readChunk()
  })
  .catch(error => {
    // 在这里处理错误
    // console.error('Error:', error);
  });
}

/** 章节测试任务梯队顺序处理 */
function dealTestEchelon(testArrayEchelon) {
  const remainingTasks = testArrayEchelon.length;
  addlog(`待处理章节测试任务数量为: ${remainingTasks}`);
  if (remainingTasks === 0) {
    // 获取章节测试题完成度
    const test_process = getTrueStatusPercentage(testTasks)
    const save_btn = testBtnDocument.querySelector('.btnSave')
    const submit_btn = testBtnDocument.querySelector('.btnSubmit')
    const mask_div = window.top.document.querySelector('.maskDiv')
    const pop_ok = mask_div.querySelector('#popok')
    if(test_process == 100) {
      addlog('章节测试任务已全部完成');
      switch (testType) {
        case 'save':
          save_btn.click()
          addlog('章节测试答案已暂时保存');
          break;
        case 'submit':
          submit_btn.click()
          setTimeout(() => {
            pop_ok.click()
            setTimeout(() => {
              const TiMus = getTestTopics()
              // console.log('TiMus:::+ ', TiMus)
              // 计算正确率
              const rightRate = (TiMus.length/testTasks.length*100).toFixed(2) + '%';
              notify(`章节测试答题情况[${TiMus.lengt}/${testTasks.length}],章节测试正确率[${rightRate}]`, 5000)
              insertTest(TiMus)
            }, 3000);     
          }, 1000);
          addlog('章节测试答案已提交');
          break;
        default:
          break;
      }
    } else {
      addlog(`章节测试任务完成度为${test_process}%,不足100%,章节测试答案将暂时保存`);
      save_btn.click()
    }
    setTimeout(() => {
      if(testType == 'submit') {
        
      }
      addlog("章节测试任务已完成");
      dealAnsEchelon(arrayEchelon);
    }, 10000);
    return;
  }

  const nextTask = testArrayEchelon.shift();
  try {
    dealSingleTest(nextTask);
  } catch (error) {
      // console.error("处理任务时发生错误:", error);
      // 继续处理下一个任务
      dealTestEchelon(testArrayEchelon);
  }
}

// 课程任务执行方法
// 课程任务执行方法
// 课程任务执行方法

// 直接跳过
function skipChapter() {
	addlog("跳过章节");
	const chapterNext = window.top.document.querySelector("#prevNextFocusNext");
	if (chapterNext) {
			chapterNext.click();
	} else {
			// console.warn("未找到 #prevNextFocusNext 元素。");
	}

	setTimeout(() => {
			const tip = document.querySelector(".maskDiv.jobFinishTip.maskFadeOut");
			if (tip) {
					const tipNextChapter = document.querySelector(".jb_btn.jb_btn_92.fr.fs14.nextChapter");
					tipNextChapter?.click();
			} else {
					// console.warn("未找到完成提示或下一章节按钮。");
			}
      // 页面加载完成之后触发这个方法
      window.onload = function() {
        // 5秒后执行initAll
        setTimeout(() => {
            initAll();
        }, 5000);
      };
	}, taskInterval);
}

// 忽略任务
function ignoreAns() {
	addlog("无法处理, 忽略任务");
	setTimeout(() => {
			document.dispatchEvent(dealEvent);
	}, taskInterval);
}

// 处理章节测试
function testHandler(idocument) {
  testDom = idocument
	addlog("处理章节测试任务中...");
  if (ifTest == 'take') {
    const iframe = idocument.querySelector("iframe");
    // console.log('iframe:::+ ', iframe)
    if (iframe) {
      const sDocument = iframe.contentWindow?.document;
      // console.log('sDocument:::+ ', sDocument)
      if(sDocument) {
        const form = sDocument.querySelector("form");
        // 存储章节测试按钮DOM
        testBtnDocument = sDocument.querySelector(".ZY_sub");
        // console.log('form:::+ ', form)
        if (form) {
          const testTemps = Array.from(form.querySelectorAll('.singleQuesId, .newTestType'));
          testTasks = testTemps.map((temp) => {
            // console.log('temp:::+ ', temp)
            return {
              document: temp,
              data: temp.getAttribute('data'),
              class: temp.getAttribute('class'),
              question: removeNewlines(temp.innerText),
              type: extractText(temp.innerText)
            }
          }).filter(item => item.class == "singleQuesId")
          console.log('testTasks:::+ ', testTasks)
          testArrayEchelon = distributeTest(testTasks);
          // 触发处理章节测试任务的事件
          document.dispatchEvent(testDealEvent);
        }
      }
    }
  } else if (ifTest == 'skip') {
    setTimeout(() => {
      addlog("根据配置选项,跳过章节测试");
      document.dispatchEvent(dealEvent);
    }, taskInterval);
  }
}

// 处理视频
function videoHandler(idocument) {
  addlog("处理视频任务中...");
  console.log('idocument:::+ ', idocument)
  const video = idocument.querySelector("video");
  const videoPlayButton = idocument.querySelector(".vjs-big-play-button");
  const modalDialog = idocument.querySelector(".vjs-modal-dialog-content");
  const closeButton = idocument.querySelector(".vjs-done-button");

  // console.log('video:::+ ', video);
  // console.log('videoPlayButton:::+ ', videoPlayButton);

  // 如果视频或播放按钮不存在,直接返回
  if (!video || !videoPlayButton) {
    return;
  }

  // 播放视频
  const playVideo = () => videoPlayButton.click();

  // 弹窗关闭函数
  const closeModalDialog = () => {
    if (modalDialog && closeButton && modalDialog.getAttribute("aria-hidden") !== "true") {
      setTimeout(() => {
        closeButton.click();
        // addlog('模块弹窗已关闭...');
      }, 2000);
    }
  };

  // 设置视频播放的倍速和音量
  const onLoadedData = () => {
    // 设置音量
    video.volume = volume;
    addlog(`已将视频音量调节为${volume * 100}%`);

    // 设置倍速
    video.playbackRate = playbackRate;
    addlog(`已将视频倍速调节为${playbackRate}X`);
  };

  // 处理暂停事件
  const handlePause = () => {
    playVideo();  // 自动继续播放
    closeModalDialog();  // 如果有弹窗,尝试关闭
  };

  // 同步显示视频进度
  const updateProgress = () => {
    const currentTime = video.currentTime;
    const duration = video.duration;
    const progress = (currentTime / duration) * 100;
    setVideoProcess(progress.toFixed(2) + '%', currentTime.toFixed(0), duration.toFixed(0), 'video');
  };

  // 绑定事件
  video.addEventListener("loadeddata", onLoadedData);
  video.addEventListener("pause", handlePause);
  video.addEventListener("timeupdate", updateProgress);

  // 监听视频播放结束事件
  video.addEventListener("ended", () => {
    addlog("视频任务已完成");
    document.dispatchEvent(dealEvent);
    // 清理事件监听
    video.removeEventListener("pause", handlePause);
    video.removeEventListener("timeupdate", updateProgress);
  }, { once: true });

  // 如果视频播放被暂停,尝试在10秒后重新点击播放按钮/刷新页面
  setTimeout(() => {
    if (video.paused && !video.ended) {
      playVideo();
      addlog('由于程序出错未自动播放,现重新模拟点击播放按钮...');
      // addlog('由于程序出错未自动播放,现重新加载页面解决视频卡顿问题...');
      // location.reload(true);
    }
  }, 10000);

  // 开始播放视频
  playVideo();
}

// 处理音频
function audioHandler(idocument) {
	  addlog("处理音频任务中...");
    const audio = idocument.querySelector("audio");
    const audioPlayButton = idocument.querySelector(".vjs-play-control.vjs-control.vjs-button");

    if (!audio) {
        // console.warn("未找到音频元素。");
        document.dispatchEvent(dealEvent);
        return;
    }

    // 播放音频
    audioPlayButton?.click();

    // 监听音频播放结束事件
    audio.addEventListener("ended", () => {
        addlog("音频任务已完成");
        document.dispatchEvent(dealEvent);
    }, { once: true });

    // 监听音频暂停事件并在暂停时继续播放
    audio.addEventListener("pause", () => {
        // console.log("音频暂停,自动恢复播放...");
        audioPlayButton?.click();
    });
    // 监听音频进度变化
    audio.addEventListener("timeupdate", () => {
      const currentTime = audio.currentTime;
      const duration = audio.duration;
      const progress = (currentTime / duration) * 100;
      setVideoProcess(progress.toFixed(2) + '%', currentTime.toFixed(0), duration.toFixed(0), 'audio');
    });
}

// 处理 PPT & PDF
function pptxHandler(idocument) {
  // 添加日志提示
  addlog("处理 PPT/PDF 任务中...");

  // 获取嵌入的 iframe 元素
  const iframe = idocument.querySelector("iframe");
  if (!iframe) {
    console.warn("未找到嵌入的 iframe 元素。");
    document.dispatchEvent(dealEvent);
    return;
  }

  const sDocument = iframe.contentWindow?.document;
  if (!sDocument) {
    console.warn("无法获取 PPT/PDF 的文档内容。");
    document.dispatchEvent(dealEvent);
    return;
  }

  let finalHeight = sDocument.documentElement.scrollHeight;
  let currentHeight = 0;

  const scrollStep = 5;  // 滚动的步长,较小的步长会让滚动更加平滑
  const maxHeight = finalHeight;  // 目标滚动的最大高度

  // 平滑滚动函数
  function smoothScroll() {
    finalHeight = sDocument.documentElement.scrollHeight; // 动态更新最终高度
    if (currentHeight >= maxHeight) {      
      addlog("PPT/PDF任务已完成");
      document.dispatchEvent(dealEvent); // 完成滚动
      return;
    }

    currentHeight += scrollStep; // 每次滚动小步长
    sDocument.defaultView.scrollTo(0, currentHeight); // 执行滚动

    // 计算滚动进度并更新
    const progress = (currentHeight / maxHeight) * 100;
    setVideoProcess(
      progress.toFixed(2) + '%', 
      currentHeight.toFixed(0), 
      maxHeight.toFixed(0), 
      'pdf'
    );

    // 请求下一帧
    requestAnimationFrame(smoothScroll);
  }

  // 开始滚动动画
  smoothScroll();
}




/** 任务梯队顺序处理 */
function dealAnsEchelon(arrayEchelon) {
    const remainingTasks = arrayEchelon.length;
		addlog(`待处理任务数量为: ${remainingTasks}`);
    if (remainingTasks === 0) {
      addlog('该章节任务已处理完成,即将跳转下一章节')
      setTimeout(() => {
        skipChapter();
      }, taskInterval);
      return;
    }
    const nextTask = arrayEchelon.shift();
    try {
        dealSingleAns(nextTask);
    } catch (error) {
        // console.error("处理任务时发生错误:", error);
        // 继续处理下一个任务
        dealAnsEchelon(arrayEchelon);
    }
}


// 其他执行方法
// 其他执行方法
// 其他执行方法

// 获取章节测试题目-正确的
function getTestTopics() {

  // 获取第一个符合条件的 iframe 并获取其中的子 iframe
  const testIframe = testDom.querySelector("iframe")?.contentWindow?.document.querySelector('.CeYan');
  console.log('testIframe:::+ ', testIframe);

  // 如果没有找到目标元素,返回空数组
  if (!testIframe) {
    console.error('没有找到目标的测试 iframe');
    return [];
  }

  // 获取题目元素
  const TiMusDom = testIframe.querySelectorAll('.TiMu');
  console.log('TiMusDom:::+ ', TiMusDom);

  // 筛选出正确答案的题目
  const rightTiMusDom = Array.from(TiMusDom).filter(TiMu => {
    return TiMu.querySelector('.marking_dui') !== null;
  });
  console.log('rightTiMusDom:::+ ', rightTiMusDom);

  // 提取题目和答案
  const TiMus = Array.from(rightTiMusDom).map(TiMu => {
    const filteredText = Array.from(TiMu.children)
      .filter(child => !child.classList.contains('newAnswerBx') || !child.classList.contains('fl') ) // 过滤掉 class 为 newAnswerBx 的子元素
      .map(child => child.innerText) // 获取剩余子元素的 innerText
      .join(''); // 拼接所有文本内容

    const question = removeNewlines(filteredText);
    const answer = TiMu.querySelector('.answerCon')?.innerText || ''; // 使用 ? 确保 answerCon 存在
    const type = extractText(question)
    const email  = GM_getValue("savedEmail")
    const course_name = GM_getValue("courseName") || '未获取课程名称';
    return { question: question, answer: answer, type: type, email: email, course_name: course_name };
  });

  // console.log('TiMus:::+ ', TiMus);
  return TiMus;
}



// 获取任务成功占比
function getTrueStatusPercentage(arr) {
  // 过滤出status为true的元素
  const trueCount = arr.filter(item => item.status === true).length;

  // 计算占比
  const percentage = (trueCount / arr.length) * 100;

  return percentage;
}


// 索引转换选项
function mapIndexToOption(arr) {
  arr = arr.map(item => {
    switch (item) {
      case 0:
        return 'A';
      case 1:
        return 'B';
      case 2:
        return 'C';
      case 3:
        return 'D';
      case 4:
        return 'E';
      case 5:
        return 'F';
      case 6:
        return 'G';
      default:
        break
    }
  })
  return arr.join('');
}

// 根据选项匹配索引
function findABCDEFPositions(str) {
  const result = [];
  const targetChars = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];  // 目标字符集合
  targetChars.forEach((text, index) => {
    if(str.indexOf(text) !== -1) {
      result.push(index);  // 记录该字符的索引
    }
  })
  return result;
}

function removeNewlines(str) {
  // 使用正则表达式去除所有的换行符(\n)
  return str.replace(/\n/g, '');
}

function extractText(str) {
  // 使用正则表达式提取“【多选题】”中的“多选题”部分
  const match = str.match(/【(.*?)】/);
  return match ? match[1] : null;
}

// 获取当前时间,年月日时分秒
function getCurrentDateTime() {
	var now = new Date();
	var year = now.getFullYear();
	var month = (now.getMonth() + 1).toString().padStart(2, '0'); // 月份从0开始,需要加1,并确保两位数格式
	var day = now.getDate().toString().padStart(2, '0'); // 确保两位数格式
	var hours = now.getHours().toString().padStart(2, '0'); // 确保两位数格式
	var minutes = now.getMinutes().toString().padStart(2, '0'); // 确保两位数格式
	var seconds = now.getSeconds().toString().padStart(2, '0'); // 确保两位数格式

	return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}

// 设置播放进度
function setVideoProcess(val1, val2, val3, type) {
  if(type == 'video') {
    var _process = window.top.document.querySelector('#content-process');
    var _time = getCurrentDateTime()
    var newContent = '<p style="color: #000;">[' + _time + ']' + '</p><hr><p>' + '播放进度:' + val1 + '</p><hr><p>' + '视频长度:' + val2 + 's' + '/' +val3 + 's' + '</p>';
    _process.innerHTML = newContent;
  } else if(type == 'audio') {
    var _process = window.top.document.querySelector('#content-process');
    var _time = getCurrentDateTime()
    var newContent = '<p style="color: #000;">[' + _time + ']' + '</p><hr><p>' + '播放进度:' + val1 + '</p><hr><p>' + '音频长度:' + val2 + 's' + '/' +val3 + 's' + '</p>';
    _process.innerHTML = newContent;
  } else if(type == 'pdf') {
    var _process = window.top.document.querySelector('#content-process');
    var _time = getCurrentDateTime()
    var newContent = '<p style="color: #000;">[' + _time + ']' + '</p><hr><p>' + '浏览进度:' + val1 + '</p><hr><p>' + 'pdf高度:' + val2 + 'px' + '/' +val3 + 'px' + '</p>';
    _process.innerHTML = newContent;
  } else if(type == 'test') {
    var _process = window.top.document.querySelector('#content-process');
    var _time = getCurrentDateTime()
    var newContent = '<p style="color: #000;">[' + _time + ']' + '[' + '答题进度:' + val1 + ']'  + '[' + val2 + '/' + val3 + ']' + '</p>';
    testTasks.forEach((item,index) => {
      const testItem = item.status ? `<p><span style="color: green">[${index + 1}]</span><span>${item.answer_text}</span></p>` : `<p><span style="color: red">[${index + 1}]</span><span>${item.answer_text || '暂无答案'}</span></p>`
      newContent += testItem
    })
    _process.innerHTML = newContent;
  }
}

// 获取视频播放进度-定时监听页面内容进行下一步处理
function getVideoProgress() {
  // 同步课程进度
  if(location.pathname == '/mycourse/studentstudy') {
    const emailInput = document.getElementById("email-input");
    const email = emailInput.value.trim();
    const url = 'https://www.sweek.top/api/checkEmailExists';
    const data = { 
      email
    };
    if(email) {
      fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json', // 声明请求主体的内容类型为 JSON
        },
        body: JSON.stringify(data), // 将数据对象转换为 JSON 字符串并作为请求主体
      }).then(response => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json(); // 解析 JSON 响应数据
      }).then(data => {
        if(data.exists){  
          syncCourseData()
        } else {
          var _async_time = window.top.document.querySelector('#async-time');
          var newContent = `<span style="color: #000;">[同步课程信息失败,邮箱未注册]</span>`;
          _async_time.innerHTML = newContent;
        }
      }).catch(error => {
        // console.error('Error:', error);
      });
    }

    // 脚本运行过程中如果弹出弹窗,发现后关闭-10s执行一次
		const jobFinishTip = document.querySelector(".jobFinishTip");
		const nextChapter = document.querySelector(".nextChapter");
    if (jobFinishTip) {
      const computedStyle = getComputedStyle(jobFinishTip);
      if (computedStyle.display !== 'none') {
        nextChapter.click()
      }
    }
  }
}

// 获取页面url
function getURLInfo() {
  if(location.pathname == '/mycourse/studentstudy') {
    url = location.href
    // 获取问号后面的部分
    var queryString = url.split('?')[1];
    // 将查询字符串拆分为参数对
    var queryParams = queryString.split('&');
    // 创建一个对象来存储参数
    var params = {};
    // 遍历参数对,将它们存储在对象中
    queryParams.forEach(function(queryParam) {
        var parts = queryParam.split('=');
        var key = decodeURIComponent(parts[0]);
        var value = decodeURIComponent(parts[1]);
        params[key] = value;
    });
    chapterId = params['chapterId']
    courseId = params['courseId']
    GM_setValue("courseId", courseId);
  }
}

// 获取当前页面章节小节的所有分栏
function getSubChapter() {
  try {
    if(location.pathname == '/mycourse/studentstudy') {
      var subChapter = window.top.document.querySelector('#prev_tab');
      if(subChapter) {
        currentChapterTabArr = subChapter.querySelectorAll('li');
        currentChapterTabArr.forEach(function(item) {
          if(item.className === 'active') {
            currentChapterTabName = item.innerText
          }
        })
      }
      
    }
  } catch (error) {
    // console.error('An error occurred:', error);
    // location.reload(); // 刷新页面
  }
}

// 获取课程所有章节节点数据
function getChapterCodeInfo() {
  if(location.pathname === '/mooc-ans/knowledge/cards') {
    var chapter = window.top.document.querySelectorAll('.posCatalog_select')
    chapterInfo = chapter
    allChapterName=[]

    chapterInfo.forEach(function(item) {
      allChapterName.push({
        id: item.id,
        title: item.innerText,
        active: item.classList.contains('posCatalog_active') ? 1 : 0,
        ifTitle: item.classList.contains('firstLayer') ? 1 : 0,
        status: item.childNodes[3]?.className == 'icon_Completed prevTips' ? 1 : 0
      })
      if (item.classList.contains('posCatalog_active')) {
        currentChapterId = item.id
        GM_setValue("currentChapterId", currentChapterId);
        currentChapterName = item.innerText
        GM_setValue("currentChapterName", currentChapterName);
      }
    });
    GM_setValue("chapterInfo", JSON.parse(JSON.stringify(allChapterName)));
    addlog('当前章节为' + currentChapterName, 'green')
  }
}

// 获取公告数据
function getBoard() {
  fetch('https://www.sweek.top/api/board')
  .then(response => response.json())
  .then(data => {
    // 在这里处理接收到的数据
    var notice = document.querySelector('#content-notice');
    notice.innerHTML = data.text
  })
  .catch(error => {
    // 在这里处理错误
    // console.error('Error:', error);
  });
}

// 同步课程数据至数据库
async function syncCourseData() {
  const url = 'https://www.sweek.top/api/insertOrUpdateCourse';
  
  const email = GM_getValue("savedEmail");
  const course_name = GM_getValue("courseName") || '测试';
  const course_id = GM_getValue("courseId") || '';
  const course_img = GM_getValue("courseImg") || '';
  const process = document.querySelector('#content-process').innerHTML;  
  const chapter_info = GM_getValue("chapterInfo");
  
  const chapters = [...window.top.document.querySelectorAll('.posCatalog_select')];
  const allChapterName = chapters.map(item => {
    const isActive = item.classList.contains('posCatalog_active');
    const isFirstLayer = item.classList.contains('firstLayer');
    const isCompleted = item.childNodes[3]?.className === 'icon_Completed prevTips';

    if (isActive) {
      GM_setValue("currentChapterId", item.id);
      GM_setValue("currentChapterName", item.innerText);
    }

    return {
      id: item.id,
      title: item.innerText,
      active: isActive ? 1 : 0,
      ifTitle: isFirstLayer ? 1 : 0,
      status: isCompleted ? 1 : 0,
    };
  });

  const data = { 
    email,
    course_id,
    course_name,
    course_img,
    chapter: JSON.stringify(allChapterName),
    current_chapter: `${GM_getValue("currentChapterId") || ''},${GM_getValue("currentChapterName") || ''}`,
    process,
  };

  // 播放异常刷新页面
  // if(process.includes("NaN")) {
  //   location.reload(); // 刷新页面
  //   return;
  // }

  if (email && process) {
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(data),
      });

      if (!response.ok) {
        throw new Error('Network response was not ok');
      }

      const responseData = await response.json();
      const _async_time = window.top.document.querySelector('#async-time');
      const _time = getCurrentDateTime();
      _async_time.innerHTML = `<span style="color: #000;">[同步时间:${_time}]</span>`;
    } catch (error) {
      console.error('Error:', error.message);
    }
  }
}

// 存储章节测试题目至数据库
async function insertTest(arr) {
  const url = 'https://www.sweek.top/api/insertTest';
  const data = {
    testList: arr
  }
  if (arr.length > 0) {
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(data),
      });

      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      console.log('题目同步成功!:::+ ', arr)
    } catch (error) {
      console.error('Error:', error.message);
    }
  }
}
// 存储章节测试题目至数据库
async function insertTestModel(arr) {
  const url = 'https://www.sweek.top/api/insertTestModel';
  const data = {
    testList: arr
  }
  if (arr.length > 0) {
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(data),
      });

      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      console.log('题目同步成功!:::+ ', arr)
    } catch (error) {
      console.error('Error:', error.message);
    }
  }
}


// 方法执行入口
(function () {
	// 进入学习通弹出提示
	if(location.pathname == '/base') {
		notify('已进入学习通首页,请进入课程,选择需要学习的课程', 5000)
	}
  // 进入课程弹出提示
  if (location.pathname == '/mooc2-ans/mycourse/stu') {
    let courseName = window.top.document.querySelector('.classDl .colorDeep')?.getAttribute('title');
    let courseImg = window.top.document.querySelector('.classDl').getElementsByTagName("img")[0]?.getAttribute('src');

    if (courseName && courseImg) {
        GM_setValue("courseName", courseName);
        GM_setValue("courseImg", courseImg);
        notify('已进入课程:' + courseName + ',请选择需要学习的章节', 5000);
    } else {
        console.error('课程信息未能获取');
    }
  }

	// 初始化显示页面弹窗
	if(location.pathname == '/mycourse/studentstudy') {
		initPopup()  
		// 获取公告数据
		getBoard()  
		// 邮箱操作
		takeEmail() 
	}
	// 获取页面章节节点数据
	getChapterCodeInfo()
	// 获取当前页面章节小节的所有分栏
	getSubChapter()
	// 获取页面url信息
	getURLInfo()

	// 定时打印视频播放进度-定时监听页面内容进行下一步处理
	videoProgressId = setInterval(() => {
		getVideoProgress()
	}, 10000);

  // 页面加载完成之后触发这个方法
  window.onload = function() {
    // 5秒后执行initAll
    setTimeout(() => {
        initAll();
    }, 5000);
  };
})();