希赛

希赛页面优化

  1. // ==UserScript==
  2. // @name EduCity
  3. // @name:zh-CN 希赛
  4. // @description Optimize the website of educity.cn.
  5. // @description:zh-CN 希赛页面优化
  6. // @namespace https://github.com/HaleShaw
  7. // @version 1.4.8
  8. // @author HaleShaw
  9. // @copyright 2021+, HaleShaw (https://github.com/HaleShaw)
  10. // @license AGPL-3.0-or-later
  11. // @homepage https://github.com/HaleShaw/TM-EduCity
  12. // @supportURL https://github.com/HaleShaw/TM-EduCity/issues
  13. // @contributionURL https://www.jianwudao.com/
  14. // @icon https://wangxiao.xisaiwang.com/favicon.ico
  15. // @match https://www.educity.cn/wangxiao2/*
  16. // @match http://www.educity.cn/wangxiao2/*
  17. // @match https://uc.educity.cn/personalCenter/studyCenter.html
  18. // @match https://uc.educity.cn/tiku/testReport.html*
  19. // @match https://uc.educity.cn/tiku/examinationMode.html*
  20. // @match https://uc.educity.cn/tiku/examinationModeCopy.html*
  21. // @match https://wangxiao.xisaiwang.com/*
  22. // @compatible Chrome
  23. // @grant GM_addStyle
  24. // @grant GM_info
  25. // ==/UserScript==
  26.  
  27. // ==OpenUserJS==
  28. // @author HaleShaw
  29. // @collaborator HaleShaw
  30. // ==/OpenUserJS==
  31.  
  32. (function () {
  33. "use strict";
  34.  
  35. const mainStyle = `
  36. /* 顶部菜单 */
  37. div.ecv2_header_tools > div.fl > ul > li:not(:first-child),
  38. div.ecv2_header_tools > div.fr > :not(:last-child),
  39.  
  40. /* Footer */
  41. .ecv2_footer {
  42. display: none !important;
  43. }
  44. `;
  45.  
  46. const zhiBoStyle = `
  47. /* 左上角标题 */
  48. #mainVid > div.vid_head > div.vid_hleft > a:not(:last-child),
  49.  
  50. /* 右上角“返回旧版、视频课程” */
  51. #mainVid > div.vid_head > div.vid_hright > a,
  52.  
  53. /* 离线观看 */
  54. #mainVid > div.vid_mid > div.vid_midL > div.vid_midL_top,
  55.  
  56. /* 右侧笔记、提问 */
  57. #mainVid > div.vid_mid > div.vid_midR > div.vid_midR_tab > div > a:nth-child(2),
  58. #mainVid > div.vid_mid > div.vid_midR > div.vid_midR_tab > div > a:nth-child(3),
  59.  
  60. /* 鼠标混入视频时的浮标按钮“新建笔记” */
  61. .vid_bj_new,
  62.  
  63. /* 顶部横条 */
  64. .vid_head {
  65. display: none !important;
  66. }
  67.  
  68. .vid_head .vid_hright {
  69. padding-top: 0 !important;
  70. }
  71.  
  72. .vid_mid .vid_midL {
  73. padding: 0 !important;
  74. }
  75.  
  76. .vid_midR_tab,
  77. .vid_midR_tab > a,
  78. .vid_midR_tab > a > i,
  79. .vid_midR_tab > .vid_midR_ul,
  80. .vid_midR_tab > .vid_midR_ul > a,
  81. .vid_midR_tab > .vid_midR_ul > a > i,
  82. .tabhide .vid_midR{
  83. width: 28px !important;
  84. }
  85.  
  86. .tabhide .vid_mid {
  87. padding-right: 28px;
  88. }
  89. .vid_mid {
  90. padding-right: 439px;
  91. }
  92.  
  93. .vid_midR {
  94. width: 439px;
  95. }
  96.  
  97. .vid_tab_content {
  98. padding-bottom: 0;
  99. }
  100.  
  101. /* 进度条上的时间 */
  102. .time-span {
  103. margin: 0 10px;
  104. }
  105.  
  106. /* 视频播放窗口上的遮罩元素 */
  107. #videoCoverTop {
  108. width: 100%;
  109. height: 17.4%;
  110. position: absolute;
  111. left: 0;
  112. top: 0;
  113. text-align: center;
  114. padding-top: 1.5rem;
  115. font-size: 1.75rem;
  116. z-index: 1;
  117. background-color: black;
  118. }
  119.  
  120. #videoCoverBottom {
  121. position: absolute;
  122. width: 100%;
  123. height: 14%;
  124. background-color: black;
  125. z-index: 2;
  126. left: 0;
  127. bottom: 0;
  128. display: flex;
  129. align-items: flex-end;
  130. justify-content: right;
  131. padding-bottom: 63px;
  132. }
  133.  
  134. #videoCoverLeft {
  135. position: absolute;
  136. width: 25.4%;
  137. height: 43%;
  138. background-color: white;
  139. z-index: 0;
  140. left: 0;
  141. bottom: 14%;
  142. }
  143.  
  144. /* 视频播放窗口进度条上的时间 */
  145. .pv-time-remaining-real.time-span,
  146. .pv-time-over.time-span {
  147. color: yellow;
  148. }
  149. `;
  150.  
  151. // 测试报告页面样式
  152. const reportStyle = `
  153. /* 顶部广告 */
  154. #accountSettingsHeader,
  155.  
  156. /* 专家建议 */
  157. div.col-md-7.testportSty > p,
  158. div.col-md-7.testportSty > a,
  159.  
  160. /* 笔记、提问 */
  161. div.doPane.note,
  162. div.doPane.question,
  163.  
  164. /* 参考答案,你的答案 */
  165. .answerEnd,
  166.  
  167. /* 选项前的radio */
  168. .answerContentList.mgt10 > .cbox,
  169.  
  170. /* 查看解析,收藏*/
  171. #dataListWarp > div.dajx > div.pull-right.clearfix {
  172. display: none !important;
  173. }
  174. `;
  175.  
  176. // 考试或练习页面
  177. const examStyle = `
  178. /* 顶部图片Header */
  179. #accountSettingsHeader,
  180.  
  181. /* 右上角二维码 */
  182. div.zt_top_right,
  183.  
  184. /* 正确答案与错误答案选项 */
  185. .analysisAnswer>div.bg-fff.box-shadow.mgt10:first-child,
  186.  
  187. /* 答案解析中的“笔记”和“提问” */
  188. div.tknew.doPane.note,
  189. div.tknew.doPane.question,
  190.  
  191. div.col-md-12>div>div.zt_top_right,
  192. .lh2>span,
  193. #accountSettingsHeader,
  194. .center.answerCard,
  195. .pull-right>span:not(:first-child),
  196. .answerTitle {
  197. display: none !important;
  198. }
  199.  
  200. /* 右侧题目编号列表 */
  201. .dtklist.item {
  202. height: 500px;
  203. }
  204.  
  205. /* 选项前的CheckBox */
  206. div.answerContentList>span.cbox {
  207. display: inline-block !important;
  208. }
  209.  
  210. /* 标题 */
  211. .zt_top_zong {
  212. height: 50px;
  213. background: none !important;
  214. }
  215.  
  216. .right-title {
  217. padding: 0px 20px !important;
  218. margin-bottom: 0px !important;
  219. }
  220.  
  221. /* 进度条 */
  222. .jindu_div {
  223. margin: 5px 0 !important;
  224. }
  225.  
  226. .jindu_div .jindu_line {
  227. width: calc(100% - 200px);
  228. }
  229.  
  230. .bp20 {
  231. padding: 5px !important;
  232. }
  233.  
  234. .ISpan {
  235. margin-bottom: 2px !important;
  236. margin-right: 0 !important;
  237. }
  238.  
  239. div.answerList.mgb20 {
  240. padding: 0 15px 0 25px !important;
  241. margin-bottom: 0px !important;
  242. }
  243.  
  244. /* 题干 */
  245. .subject-content {
  246. padding: 0px 30px !important;
  247. background: none !important;
  248. min-height: 320px !important;
  249. }
  250.  
  251. .single-content {
  252. padding: 5px 10px !important;
  253. }
  254.  
  255. .lh2 {
  256. margin-bottom: 5px !important;
  257. }
  258.  
  259. .examTigan {
  260. max-height: 600px;
  261. overflow-y: auto;
  262. display: block;
  263. }
  264.  
  265. .examTigan,
  266. .examTigan > p,
  267. .answerContentList > label {
  268. font-size: 2.2rem !important;
  269. }
  270.  
  271. /* 选项列表 */
  272. .answerContentList > label {
  273. color: #666;
  274. }
  275.  
  276. /* 参考解析 */
  277. div.analysisAnswer>div {
  278. padding-bottom: 0px !important;
  279. }
  280.  
  281. .answerEnd {
  282. padding: 0 !important;
  283. margin-top: 0 !important;
  284. }
  285.  
  286. .answerList {
  287. padding: 0 15px 5px 25px !important;
  288. }
  289.  
  290. .shitiDesp.pdt15 {
  291. padding-top: 0 !important;
  292. }
  293.  
  294. .jiexinew {
  295. padding: 10px 30px 0 30px !important;
  296. }
  297.  
  298. #jiexispan>p {
  299. margin-bottom: 0 !important;
  300. }
  301.  
  302. .countTime {
  303. padding: 0 !important;
  304. }
  305.  
  306. #ztsetWrap>div.bg-fff.box-shadow {
  307. margin-bottom: 0 !important;
  308. }
  309.  
  310. /* 顶部标题高度 */
  311. .mgt10 {
  312. margin-top: 0px !important;
  313. }
  314.  
  315. .lh2 {
  316. font-weight: bolder !important;
  317. color: #337ab7 !important;
  318. font-size: 1.125em !important;
  319. }
  320.  
  321. .spanExplain {
  322. padding: 5px 20px !important;
  323. }
  324.  
  325. .exBtn {
  326. margin-top: 5px !important;
  327. }
  328.  
  329. .answerWrap {
  330. padding: 0px 30px !important;
  331. }
  332.  
  333. .mgb20 {
  334. margin-bottom: 0px !important;
  335. }
  336.  
  337. .singleR {
  338. font-size: 16px;
  339. }
  340. `;
  341.  
  342. // 个人中心页面
  343. const personalStyle = `
  344. /* 班主任微信 */
  345. #baomingDiv > .kcgw_weixin,
  346.  
  347. /* 用户ID,我的报名 */
  348. div.xpc_top_info > div.xpc_top_V2,
  349.  
  350. /* 左侧边栏菜单栏 */
  351. div.xpc-menu-box > dl:last-child,
  352. div.xpc-menu-box > dl:nth-last-child(2):nth-child(odd),
  353. div.xpc-menu-box > dl:nth-child(2) > dd:last-child,
  354.  
  355. /* 右侧悬浮工具 */
  356. .ecv2_right_tools {
  357. display: none !important;
  358. }
  359.  
  360. #cHeadImg {
  361. height: 48px;
  362. width: 48px;
  363. }
  364.  
  365. .xpc_top,
  366. .xpc_top .xpc_top_con,
  367. .xpc_top .xpc-top-message li a {
  368. height: 60px !important;
  369. }
  370.  
  371. /* 播放列表 */
  372. .ecv2_live_taggleTitles {
  373. padding: 5px 20px 5px 20px !important;
  374. }
  375. .ecv2_live_taggleHide li {
  376. padding: 5px 0 5px 68px !important;
  377. }
  378.  
  379. .xpc_zbmulu {
  380. padding: 0 20px !important;
  381. }
  382.  
  383. /* 主窗口 */
  384. .xpc_main .xpc_main_con {
  385. padding: 0 !important;
  386. }
  387. `;
  388.  
  389. const ANSWER_LIST = ["A", "B", "C", "D"];
  390.  
  391. main();
  392.  
  393. function main() {
  394. logInfo(GM_info.script.name, GM_info.script.version);
  395. GM_addStyle(mainStyle);
  396.  
  397. let url = window.location.href;
  398. // 个人中心页面
  399. if (url.startsWith("https://wangxiao.xisaiwang.com/ucenter2/")) {
  400. GM_addStyle(personalStyle);
  401. setTimeout(() => {
  402. updatePlayButton();
  403. }, 1500);
  404. } else if (
  405. url.startsWith("https://www.educity.cn/wangxiao2") ||
  406. url.startsWith("http://www.educity.cn/wangxiao2") ||
  407. url.startsWith("https://wangxiao.xisaiwang.com/wangxiao2/")
  408. ) {
  409. // 直播回放调节播放速度
  410. setTimeout(() => {
  411. addCover();
  412. updateSpeed();
  413. }, 1500);
  414. } else if (url.startsWith("https://wangxiao.xisaiwang.com/tiku2/ctjx")) {
  415. // 独立的错题解析页面,添加键盘事件
  416. GM_addStyle(examStyle);
  417. addLeftRightKeyListener();
  418. } else if (url.startsWith("https://wangxiao.xisaiwang.com/tiku2/sectionReport")) {
  419. // 测试报告页面
  420. GM_addStyle(reportStyle);
  421. showWrongTopics();
  422. } else if (
  423. url.startsWith("https://uc.educity.cn/tiku/examinationModeCopy.html") ||
  424. url.startsWith("https://uc.educity.cn/tiku/examinationMode.html") ||
  425. url.startsWith("https://wangxiao.xisaiwang.com/tiku2/exam")
  426. ) {
  427. // 添加键盘监听事件,按键答题
  428. GM_addStyle(examStyle);
  429. addKeyListener();
  430. }
  431. }
  432.  
  433. /**
  434. * 更新播放按钮事件
  435. * 将原有事件移除,在新页面打开播放页面。
  436. */
  437. function updatePlayButton() {
  438. let buttons = document.querySelectorAll(
  439. 'div.detail-course-top > a.detail-course-btnC.buyProductDetail[data-type="Video"]'
  440. );
  441. for (let i = 0; i < buttons.length; i++) {
  442. buttons[i].removeAttribute("onclick");
  443. buttons[i].setAttribute("target", "_blank");
  444. const uri =
  445. "/wangxiao2/c" +
  446. $(buttons[i]).attr("data-cid") +
  447. "/sp" +
  448. $(buttons[i]).attr("data-id") +
  449. ".html";
  450. buttons[i].setAttribute("href", uri);
  451. }
  452. }
  453.  
  454. function updateSpeed() {
  455. GM_addStyle(zhiBoStyle);
  456.  
  457. addRemainingTime();
  458. updateVideoTitle();
  459. addRateButton();
  460. addRateListener();
  461. updateSideHeight();
  462. addPersonalCenter();
  463. }
  464.  
  465. /**
  466. * 添加视频播放窗口上的遮罩元素
  467. */
  468. function addCover() {
  469. let parentEle = document.querySelector(".pv-video-wrap");
  470. let videoCoverTop = document.getElementById("videoCoverTop");
  471. let videoCoverBottom = document.getElementById("videoCoverBottom");
  472. let videoCoverLeft = document.getElementById("videoCoverLeft");
  473. let title = document.querySelector("a.log.pointer.video-player.act").textContent.trim();
  474. if (parentEle && !videoCoverTop && !videoCoverBottom && !videoCoverLeft) {
  475. parentEle.appendChild(
  476. $(
  477. `<div id="videoCoverTop">${title} &nbsp;&nbsp;&nbsp;&nbsp;剩余课时:${getRemainingClass()}</div>`
  478. )[0]
  479. );
  480. parentEle.appendChild(
  481. $(`<div id="videoCoverBottom"><span class="videoCoverBottom"></span></div>`)[0]
  482. );
  483. parentEle.appendChild($(`<div id="videoCoverLeft"></div>`)[0]);
  484. }
  485. }
  486.  
  487. /**
  488. * 添加倍速按钮
  489. */
  490. function addRateButton() {
  491. if (!$(".pv-rate-select") || $(".pv-rate-select").length == 0) {
  492. return;
  493. }
  494. $(
  495. '<div data-rate="4">4x</div><div data-rate="3.5">3.5x</div><div data-rate="3">3x</div><div data-rate="2.5">2.5x</div>'
  496. ).insertBefore($(".pv-rate-select").children().eq(0));
  497. }
  498.  
  499. // 获取当前倍速
  500. function getCurrentRate() {
  501. let rate = 1.0;
  502. let rateEle = document.querySelector("button.pv-rate-btn>span");
  503. if (!rateEle) {
  504. return rate;
  505. }
  506. rate = rateEle.textContent.replace("x", "");
  507.  
  508. return rate;
  509. }
  510.  
  511. /**
  512. * 添加控制播放速度的监听事件,仅用于更新显示当前速度,速度控制通过其他通用三方脚本实现。
  513. */
  514. function addRateListener() {
  515. let rateEle = document.querySelector("button.pv-rate-btn>span");
  516.  
  517. document.onkeyup = function (e) {
  518. var theEvent = e || window.event;
  519. var code = theEvent.keyCode || theEvent.which || theEvent.charCode;
  520. if (code == 88) {
  521. // X,减速
  522. let rate = getCurrentRate();
  523. let newRate = (new Number(rate) - new Number(0.1)).toFixed(1);
  524. let rateStr = newRate + "x";
  525. rateEle.textContent = rateStr;
  526. updateRate(rateStr);
  527. }
  528. if (code == 67) {
  529. // C,加速
  530. let rate = getCurrentRate();
  531. let newRate = (new Number(rate) + new Number(0.1)).toFixed(1);
  532. let rateStr = newRate + "x";
  533. rateEle.textContent = rateStr;
  534. updateRate(rateStr);
  535. }
  536. if (code == 90) {
  537. // Z,恢复正常速度
  538. rateEle.textContent = "1x";
  539. updateRate("1x");
  540. }
  541. };
  542. }
  543.  
  544. /**
  545. * 更新右侧边栏上的播放倍率
  546. * @param {String} rate 播放倍率
  547. */
  548. function updateRate(rate) {
  549. let rateEle = document.querySelector("span.rateRight");
  550. if (rateEle) {
  551. rateEle.textContent = rate;
  552. } else {
  553. document
  554. .querySelector(".vid_midR_tab")
  555. .appendChild($(`<span class="rateRight">${rate}</span>`)[0]);
  556. }
  557. }
  558.  
  559. /**
  560. * 更新右侧侧边栏高度
  561. */
  562. function updateSideHeight() {
  563. let sideBar = document.querySelector(".vid_midR_tab");
  564. if (!sideBar) {
  565. return;
  566. }
  567. sideBar.style.height = sideBar.parentElement.previousElementSibling.offsetHeight + "px";
  568. document.querySelector(".vid_midR_wrap").style.height =
  569. sideBar.parentElement.previousElementSibling.offsetHeight + "px";
  570. }
  571.  
  572. /**
  573. * 添加个人中心按钮
  574. */
  575. function addPersonalCenter() {
  576. $('<a href="/ucenter2/personal/index.html" target="_blank">个人中心</a>').insertBefore(
  577. $(".vid_midR_tab").children().eq(0)
  578. );
  579. }
  580.  
  581. // 添加键盘监听事件,按键答题
  582. function addKeyListener() {
  583. document.onkeyup = function (e) {
  584. var theEvent = e || window.event;
  585. var code = theEvent.keyCode || theEvent.which || theEvent.charCode;
  586. // Spacebar. 查看答案解析
  587. if (code == 32 && document.getElementsByClassName("col-md-4 center bottomCenter bp20")[0]) {
  588. if (validatePause()) {
  589. return;
  590. }
  591. document.getElementsByClassName("col-md-4 center bottomCenter bp20")[0].click();
  592. if (document.getElementsByClassName("tknew doPane question")[0]) {
  593. try {
  594. onQestion();
  595. } catch (error) {
  596. console.error(error, "展开提问失败!");
  597. }
  598. }
  599. scrollToBottom();
  600. }
  601. // Q. 查看提问
  602. if (code == 81 && document.getElementsByClassName("tknew doPane question")[0]) {
  603. if (validatePause()) {
  604. return;
  605. }
  606. try {
  607. onQestion();
  608. } catch (error) {
  609. console.error(error, "展开提问失败!");
  610. }
  611. scrollToBottom();
  612. }
  613. // Left Arrow.
  614. if (code == 37 && document.getElementsByClassName("col-md-4 center bp20 bLeftWrap")[0]) {
  615. if (validatePause()) {
  616. return;
  617. }
  618. let btn = document.getElementsByClassName("col-md-4 center bp20 bLeftWrap")[0];
  619. if (btn.classList.contains("notclickn")) {
  620. return;
  621. }
  622. btn.click();
  623. setTimeout(() => {
  624. let tiGan = document.querySelector("div.examTigan");
  625. scrollElementToBottom(tiGan);
  626. }, 200);
  627. scrollToBottom();
  628. }
  629. // Right Arrow.
  630. if (code == 39 && document.getElementsByClassName("col-md-4 center bp20 bRightWrap")[0]) {
  631. if (validatePause()) {
  632. return;
  633. }
  634. let btn = document.getElementsByClassName("col-md-4 center bp20 bRightWrap")[0];
  635. if (btn.classList.contains("notclickn")) {
  636. return;
  637. }
  638. btn.click();
  639. setTimeout(() => {
  640. let tiGan = document.querySelector("div.examTigan");
  641. scrollElementToBottom(tiGan);
  642. }, 200);
  643. scrollToBottom();
  644. }
  645. // A,1.
  646. if (code == 49 || code == 65 || code == 97) {
  647. if (validatePause()) {
  648. return;
  649. }
  650. document.getElementById("slec0A").click();
  651. scrollToBottom();
  652. }
  653. // B,2.
  654. if (code == 50 || code == 66 || code == 98) {
  655. if (validatePause()) {
  656. return;
  657. }
  658. document.getElementById("slec0B").click();
  659. scrollToBottom();
  660. }
  661. // C,3.
  662. if (code == 51 || code == 67 || code == 99) {
  663. if (validatePause()) {
  664. return;
  665. }
  666. document.getElementById("slec0C").click();
  667. scrollToBottom();
  668. }
  669. // D,4.
  670. if (code == 52 || code == 68 || code == 100) {
  671. if (validatePause()) {
  672. return;
  673. }
  674. document.getElementById("slec0D").click();
  675. scrollToBottom();
  676. }
  677. // J. 标记
  678. if (code == 74 && document.getElementsByClassName("bj_icon addBiaoji")[0]) {
  679. if (validatePause()) {
  680. return;
  681. }
  682. document.getElementsByClassName("bj_icon addBiaoji")[0].click();
  683. setTimeout(function () {
  684. if (document.getElementsByClassName("swal-button swal-button--confirm")[0]) {
  685. document.getElementsByClassName("swal-button swal-button--confirm")[0].click();
  686. document.getElementsByClassName("col-md-4 center bp20 bRightWrap")[0].click();
  687. }
  688. }, 300);
  689. }
  690. // J. 取消标记
  691. if (code == 74 && document.getElementsByClassName("bj_icon cancelBiaoji")[0]) {
  692. if (validatePause()) {
  693. return;
  694. }
  695. document.getElementsByClassName("bj_icon cancelBiaoji")[0].click();
  696. setTimeout(function () {
  697. if (document.getElementsByClassName("swal-button swal-button--confirm")[0]) {
  698. document.getElementsByClassName("swal-button swal-button--confirm")[0].click();
  699. }
  700. }, 300);
  701. }
  702. // P.暂停
  703. if (code == 80 && document.getElementsByClassName("inline-block zanTing")[0]) {
  704. document.getElementsByClassName("inline-block zanTing")[0].click();
  705. }
  706. // P.继续做题
  707. if (
  708. code == 80 &&
  709. document.getElementsByClassName("swal-button swal-button--confirm")[0] &&
  710. document.getElementsByClassName("swal-button swal-button--confirm")[0].textContent ==
  711. "继续做题"
  712. ) {
  713. document.getElementsByClassName("swal-button swal-button--confirm")[0].click();
  714. }
  715. };
  716. }
  717.  
  718. // 添加左右方向键监听事件
  719. function addLeftRightKeyListener() {
  720. document.onkeyup = function (e) {
  721. var theEvent = e || window.event;
  722. var code = theEvent.keyCode || theEvent.which || theEvent.charCode;
  723. // Q. 查看提问
  724. if (code == 81 && document.getElementsByClassName("tknew doPane question")[0]) {
  725. if (validatePause()) {
  726. return;
  727. }
  728. try {
  729. onQestion();
  730. } catch (error) {
  731. console.error(error, "展开提问失败!");
  732. }
  733. scrollToBottom();
  734. }
  735. // Left Arrow.
  736. if (code == 37 && document.getElementsByClassName("col-md-4 center bp20 bLeftWrap")[0]) {
  737. if (validatePause()) {
  738. return;
  739. }
  740. let btn = document.getElementsByClassName("col-md-4 center bp20 bLeftWrap")[0];
  741. if (btn.classList.contains("notclickn")) {
  742. return;
  743. }
  744. btn.click();
  745. if (document.getElementsByClassName("tknew doPane question")[0]) {
  746. try {
  747. onQestion();
  748. } catch (error) {
  749. console.error(error, "展开提问失败!");
  750. }
  751. }
  752. scrollToBottom();
  753. }
  754. // Right Arrow.
  755. if (code == 39 && document.getElementsByClassName("col-md-4 center bp20 bRightWrap")[0]) {
  756. if (validatePause()) {
  757. return;
  758. }
  759. let btn = document.getElementsByClassName("col-md-4 center bp20 bRightWrap")[0];
  760. if (btn.classList.contains("notclickn")) {
  761. return;
  762. }
  763. btn.click();
  764. if (document.getElementsByClassName("tknew doPane question")[0]) {
  765. try {
  766. onQestion();
  767. } catch (error) {
  768. console.error(error, "展开提问失败!");
  769. }
  770. }
  771. scrollToBottom();
  772. }
  773. };
  774. }
  775.  
  776. // 只看错题
  777. function showWrongTopics() {
  778. loadErrData();
  779. setTimeout(() => {
  780. let showButtons = document.querySelectorAll("#dataListWarp>ul>li>h4.chak.zhank");
  781. for (let i = 0; i < showButtons.length; i++) {
  782. showButtons[i].click();
  783. }
  784. }, 800);
  785. setTimeout(() => {
  786. let explainButtons = document.querySelectorAll(
  787. "#dataListWarp>div.dajx>div.pull-right.clearfix>a.ckjx"
  788. );
  789. for (let i = 0; i < explainButtons.length; i++) {
  790. explainButtons[i].click();
  791. }
  792. }, 1500);
  793. setTimeout(() => {
  794. autoFillAnswer();
  795. }, 2000);
  796. }
  797.  
  798. // 自动填充答案
  799. function autoFillAnswer() {
  800. const answers = document.getElementsByClassName("answerEnd");
  801. for (let i = 0; i < answers.length; i++) {
  802. let ans = answers[i].children[0].innerText.replace("参考答案:", "").replace(/\s+/g, "");
  803. let your = answers[i].children[1].innerText.replace("你的答案:", "").replace(/\s+/g, "");
  804. if (ans != your) {
  805. let ansId = ANSWER_LIST.indexOf(ans);
  806. let yourId = ANSWER_LIST.indexOf(your);
  807. let ansList = answers[i].parentElement.parentElement.querySelectorAll(
  808. ".answerContentList.mgt10"
  809. );
  810. ansList[ansId].style.fontWeight = "bold";
  811. ansList[ansId].style.color = "#51cb65";
  812. ansList[ansId].children[1].style.fontWeight = "bold";
  813. // ansList[ansId].children[0].children[0].checked = true;
  814. if (yourId != undefined) {
  815. ansList[yourId].style.color = "rgba(128, 128, 145,0.7)";
  816. ansList[yourId].style.textDecoration = "line-through";
  817. }
  818. }
  819. }
  820. }
  821.  
  822. /**
  823. * 验证是否暂停中
  824. * @returns Boolean
  825. */
  826. function validatePause() {
  827. return (
  828. document.getElementsByClassName("swal-button swal-button--confirm")[0] &&
  829. document.getElementsByClassName("swal-button swal-button--confirm")[0].textContent ==
  830. "继续做题"
  831. );
  832. }
  833.  
  834. // ---------------------------------------------------
  835. // 更新播放页面标题
  836. function updateVideoTitle() {
  837. const config = { attributes: true };
  838. const callback = function (mutationsList, observer) {
  839. for (let mutation of mutationsList) {
  840. if (mutation.type === "attributes" && mutation.attributeName === "class") {
  841. let title = "";
  842. let titleEle = document.querySelector("a.log.pointer.video-player.act");
  843. if (titleEle) {
  844. title = titleEle.textContent.trim();
  845. }
  846. let cover = document.getElementById("videoCoverTop");
  847. if (cover) {
  848. cover.innerHTML = `${title} &nbsp;&nbsp;&nbsp;&nbsp;剩余课时:${getRemainingClass()}`;
  849. }
  850. }
  851. }
  852. };
  853. const observer = new MutationObserver(callback);
  854. document.querySelectorAll("a.log.pointer.video-player").forEach(element => {
  855. observer.observe(element, config);
  856. });
  857. }
  858.  
  859. // 获取剩余课时数量
  860. function getRemainingClass() {
  861. let classList = document.querySelectorAll("a.log.pointer.video-player");
  862. let activeClass = document.querySelector("a.log.pointer.video-player.act").textContent.trim();
  863. let index = 0;
  864. let flag = false;
  865. for (let i = 0; i < classList.length; i++) {
  866. const className = classList[i].textContent.trim();
  867. if (flag) {
  868. index++;
  869. }
  870. if (activeClass == className) {
  871. flag = true;
  872. }
  873. }
  874. return index;
  875. }
  876.  
  877. // 添加剩余时间
  878. function addRemainingTime() {
  879. document.querySelector(".pv-time-current").addEventListener(
  880. "DOMSubtreeModified",
  881. function () {
  882. let remainingSeconds = getRemainingSeconds();
  883. let remainingTime = remainingSeconds > 0 ? formatSeconds(remainingSeconds) : "";
  884. let realRemainingSeconds = (
  885. new Number(remainingSeconds) / new Number(getCurrentRate())
  886. ).toFixed(0);
  887. let realRemainingTime = formatSeconds(realRemainingSeconds);
  888. let overTime = getOverTime(realRemainingSeconds);
  889.  
  890. let currentEle = document.querySelector(".pv-time-current");
  891. if (currentEle) {
  892. let parent = currentEle.parentElement;
  893.  
  894. let remainingTimeSpan = document.querySelector(".pv-time-remaining.time-span");
  895. if (!remainingTimeSpan) {
  896. remainingTimeSpan = document.createElement("span");
  897. remainingTimeSpan.setAttribute("class", "pv-time-remaining time-span");
  898. parent.append(remainingTimeSpan);
  899. }
  900. remainingTimeSpan.textContent = "剩余时间:" + remainingTime;
  901.  
  902. let realRemainingTimeSpan = document.querySelector(".pv-time-remaining-real.time-span");
  903. if (!realRemainingTimeSpan) {
  904. realRemainingTimeSpan = document.createElement("span");
  905. realRemainingTimeSpan.setAttribute("class", "pv-time-remaining-real time-span");
  906. parent.append(realRemainingTimeSpan);
  907. }
  908. realRemainingTimeSpan.textContent = "真实剩余时间:" + realRemainingTime;
  909.  
  910. let overTimeSpan = document.querySelector(".pv-time-over.time-span");
  911. if (!overTimeSpan) {
  912. overTimeSpan = document.createElement("span");
  913. overTimeSpan.setAttribute("class", "pv-time-over time-span");
  914. parent.append(overTimeSpan);
  915. }
  916. overTimeSpan.textContent = "结束时间:" + overTime;
  917.  
  918. let nowTimeSpan = document.querySelector(".pv-time-now.time-span");
  919. if (!nowTimeSpan) {
  920. nowTimeSpan = document.createElement("span");
  921. nowTimeSpan.setAttribute("class", "pv-time-now time-span");
  922. document.getElementById("videoCoverBottom").append(nowTimeSpan);
  923. }
  924. nowTimeSpan.textContent = "北京时间:" + dateFormat("HH:MM:SS", new Date());
  925. }
  926. document
  927. .querySelector(".pv-video-wrap")
  928. .nextElementSibling.setAttribute(
  929. "class",
  930. "pv-skin-blue pv-video-bottom pv-subtitle-hide pv-show-fullscreen-page pv-base-control pv-first-h pv-first-hh"
  931. );
  932. },
  933. false
  934. );
  935. }
  936.  
  937. // 获取当前时间
  938. function getNowSeconds() {
  939. let nowSeconds = 0;
  940. let currentEle = document.querySelector(".pv-time-current");
  941. if (!currentEle) {
  942. return nowSeconds;
  943. }
  944.  
  945. let nowTime = currentEle.textContent;
  946. let nowArr = nowTime.split(":");
  947. if (nowArr.length == 2) {
  948. nowSeconds = parseInt(nowArr[0]) * 60 + parseInt(nowArr[1]);
  949. } else if (nowArr.length == 3) {
  950. nowSeconds = parseInt(nowArr[0]) * 60 * 60 + parseInt(nowArr[1]) * 60 + parseInt(nowArr[2]);
  951. }
  952.  
  953. return nowSeconds;
  954. }
  955.  
  956. // 获取总时长
  957. function getAllSeconds() {
  958. let allSeconds = 0;
  959. let durationEle = document.querySelector(".pv-time-duration");
  960. if (!durationEle) {
  961. return allSeconds;
  962. }
  963.  
  964. let allTime = durationEle.textContent;
  965. let allArr = allTime.split(":");
  966. if (allArr.length == 2) {
  967. allSeconds = parseInt(allArr[0]) * 60 + parseInt(allArr[1]);
  968. } else if (allArr.length == 3) {
  969. allSeconds = parseInt(allArr[0] * 60 * 60) + parseInt(allArr[1]) * 60 + parseInt(allArr[2]);
  970. }
  971.  
  972. return allSeconds;
  973. }
  974.  
  975. // 获取剩余时间
  976. function getRemainingSeconds() {
  977. let allSeconds = getAllSeconds();
  978. let nowSeconds = getNowSeconds();
  979. return allSeconds - nowSeconds;
  980. }
  981.  
  982. // 获取结束时间
  983. function getOverTime(seconds) {
  984. let timestamp = new Date().getTime() + seconds * 1000;
  985. return dateFormat("HH:MM:SS", new Date(timestamp));
  986. }
  987.  
  988. // 窗口滚动到底部
  989. function scrollToBottom() {
  990. setTimeout("window.scrollTo(0, document.body.scrollHeight)", 400);
  991. setTimeout("window.scrollTo(0, document.body.scrollHeight)", 800);
  992. }
  993.  
  994. // 元素滚动到底部
  995. function scrollElementToBottom(element) {
  996. element.scrollTop = element.scrollHeight - element.clientHeight;
  997. }
  998.  
  999. // 将秒格式化为时间格式
  1000. function formatSeconds(value) {
  1001. let result = parseInt(value);
  1002. let h =
  1003. Math.floor(result / 3600) < 10 ? "0" + Math.floor(result / 3600) : Math.floor(result / 3600);
  1004. let m =
  1005. Math.floor((result / 60) % 60) < 10
  1006. ? "0" + Math.floor((result / 60) % 60)
  1007. : Math.floor((result / 60) % 60);
  1008. let s = Math.floor(result % 60) < 10 ? "0" + Math.floor(result % 60) : Math.floor(result % 60);
  1009.  
  1010. let res = "";
  1011. if (h !== "00") res += `${h}:`;
  1012. if (m !== "00") res += `${m}:`;
  1013. res += `${s}`;
  1014. return res;
  1015. }
  1016.  
  1017. /**
  1018. * Format date.
  1019. * @param fmt format standard.
  1020. * @param date date.
  1021. * @returns {Time formatted string}
  1022. */
  1023. function dateFormat(fmt, date) {
  1024. let ret;
  1025. let opt = {
  1026. "Y+": date.getFullYear().toString(),
  1027. "m+": (date.getMonth() + 1).toString(),
  1028. "d+": date.getDate().toString(),
  1029. "H+": date.getHours().toString(),
  1030. "M+": date.getMinutes().toString(),
  1031. "S+": date.getSeconds().toString(),
  1032. };
  1033. for (let k in opt) {
  1034. ret = new RegExp("(" + k + ")").exec(fmt);
  1035. if (ret) {
  1036. fmt = fmt.replace(
  1037. ret[1],
  1038. ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0")
  1039. );
  1040. }
  1041. }
  1042. return fmt;
  1043. }
  1044.  
  1045. /**
  1046. * Log the title and version at the front of the console.
  1047. * @param {String} title title.
  1048. * @param {String} version script version.
  1049. */
  1050. function logInfo(title, version) {
  1051. console.clear();
  1052. const titleStyle = "color:white;background-color:#606060";
  1053. const versionStyle = "color:white;background-color:#1475b2";
  1054. const logTitle = " " + title + " ";
  1055. const logVersion = " " + version + " ";
  1056. console.log("%c" + logTitle + "%c" + logVersion, titleStyle, versionStyle);
  1057. }
  1058. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址