Greasy Fork镜像 支持简体中文。

Rain Classroom Helper

优化雨课堂使用体验

目前為 2020-02-14 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Rain Classroom Helper
  3. // @namespace https://raineggplant.com/
  4. // @version 0.1.1
  5. // @description 优化雨课堂使用体验
  6. // @author RainEggplant
  7. // @match *://www.yuketang.cn/web*
  8. // @grant GM_addStyle
  9. // @require https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js
  10. // @homepageURL https://github.com/RainEggplant/rain-classroom-helper
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15. const DEBUG = false;
  16.  
  17. // 调整左边栏样式
  18. GM_addStyle(`
  19. .panel {
  20. padding-top: 34px !important;
  21. }
  22. .nav-list {
  23. font-size: 18px !important;
  24. }
  25. .nav-item {
  26. height: 50px !important;
  27. line-height: 32px !important;
  28. }
  29. .kecheng, .kejian, .shiti, .geren, .addlink {
  30. width: 32px !important;
  31. }
  32. .left .contact-us {
  33. display: block !important;
  34. }
  35. `);
  36.  
  37. // 调整右边栏样式
  38. GM_addStyle(`
  39. .right {
  40. width: 300px !important;
  41. }
  42. .control-panel {
  43. padding-top: 32px !important;
  44. }
  45. .title {
  46. font-size: 22px !important;
  47. }
  48. .page-nav-control {
  49. width: 300px !important;
  50. }
  51. .page-control {
  52. padding-top: 15px !important;
  53. }
  54. .page-nav {
  55. padding: unset !important;
  56. font-size: 18px !important;
  57. }
  58. .kecheng, .kejian, .shiti, .geren, .addlink {
  59. width: 32px !important;
  60. }
  61. .print-preview-box {
  62. margin: 0px 0 0 !important;
  63. }
  64. .contact-us {
  65. display: none;
  66. }
  67. `);
  68.  
  69. // 调整中间 iframe 为自适应宽度
  70. GM_addStyle(`
  71. .wrapper-inner {
  72. width: 92% !important;
  73. }
  74. .center {
  75. width: auto !important;
  76. margin-left: 180px !important;
  77. margin-right: 300px !important;
  78. float: none !important;
  79. }
  80. .rain-iframe {
  81. width: calc(100% - 50px) !important;
  82. }
  83. `);
  84.  
  85. // 调整布局
  86. waitForKeyElements('div.index-view.none.J_index', function () {
  87. // note: you must move div.right instead of div.center, or the sidebar
  88. // will lost its funtion
  89. $('div.right.fr').insertBefore($('div.center.fl'));
  90. });
  91.  
  92. // 缩小 “体验新版” 尺寸
  93. waitForKeyElements('a.newWebEntry', function () {
  94. $('a.newWebEntry')
  95. .find('img')
  96. .attr('style', 'width: 150px; margin-top: 20px;');
  97. });
  98.  
  99. // 添加右边栏视频框
  100. GM_addStyle(`
  101. #video-iframe {
  102. width: 300px;
  103. height: 285px;
  104. margin-top: 20px;
  105. }
  106. `);
  107.  
  108. waitForKeyElements('div.control-panel.Absolute-Center', function () {
  109. const videoIFrame = '<iframe id="video-iframe" src="about:blank" style="display: none;"/>';
  110. $('div.page-control.J_pageNo').after(videoIFrame);
  111. });
  112.  
  113. // 添加 GitHub 项目图标
  114. waitForKeyElements('ul.nav-list', function () {
  115. const liAbout = `
  116. <li class="nav-item clearfix J_nav">
  117. <a href="https://github.com/RainEggplant/rain-classroom-helper" target="_blank">
  118. <img alt="GitHub stars" style="width:auto;" src="https://img.shields.io/github/stars/RainEggplant/rain-classroom-helper?style=social">
  119. </a>
  120. </li>
  121. `;
  122. $('ul.nav-list').append(liAbout);
  123. });
  124.  
  125. // 中间 iframe 加载出内容后绑定处理视频的函数
  126. waitForKeyElements('body', addVideoHandler, true, '#rainiframe');
  127.  
  128. // 启动将课件中视频提到右边栏播放的处理函数
  129. function startObservation() {
  130. const rainIFrame = document.getElementById('rainiframe').contentWindow
  131. .document.body;
  132. let iframeUrl = '';
  133. let isVideoLoaded = false;
  134.  
  135. const observer = new MutationObserver(function () {
  136. // change in #rainiframe detected
  137. DEBUG && console.log('change in #rainiframe detected');
  138. const newIFrameUrl = $('#rainiframe').contents().get(0).location.href;
  139. const videoSection = rainIFrame.querySelector('section.live__wrap');
  140. if (videoSection) {
  141. // 存在视频
  142. // 去除中央 rainIFrame 中的视频
  143. $(videoSection).attr('style', 'display: none;');
  144. $(videoSection).contents().find('video').removeAttr('src');
  145. $(videoSection).empty();
  146.  
  147. if (newIFrameUrl === iframeUrl) return;
  148.  
  149. // 在右边栏显示视频
  150. // note: 不要使用 $("#video-iframe").attr("src", iframeUrl);
  151. // 因为这样会留下访问记录,从而使后退、前进功能异常
  152. iframeUrl = newIFrameUrl;
  153. const videoIFrame = $('#video-iframe')[0];
  154. videoIFrame.contentWindow.location.replace(iframeUrl);
  155. $('#video-iframe').css({ display: 'block' });
  156.  
  157. isVideoLoaded = true;
  158. } else {
  159. if (isVideoLoaded) {
  160. // 退出视频课程时停止播放并隐藏右边栏视频
  161. $('#video-iframe').css({ display: 'none' });
  162. iframeUrl = newIFrameUrl;
  163. // DO NOT USE: $("#video-iframe").attr("src", "/v/index");
  164. const videoIFrame = $('#video-iframe')[0];
  165. videoIFrame.contentWindow.location.replace('about:blank');
  166.  
  167. isVideoLoaded = false;
  168. }
  169. }
  170. });
  171.  
  172. const config = { childList: true, subtree: true };
  173. observer.observe(rainIFrame, config);
  174. DEBUG && console.log('Observation started');
  175. }
  176.  
  177. function addVideoHandler() {
  178. // 首次进入或通过左边栏改变页面时
  179. $('#rainiframe').on('load', function () {
  180. DEBUG && console.log('#rainiframe has (re)loaded');
  181. // 右边栏视频停止播放并隐藏
  182. $('#video-iframe').css({ display: 'none' });
  183. const videoIFrame = $('#video-iframe')[0];
  184. videoIFrame.contentWindow.location.replace('about:blank');
  185.  
  186. startObservation();
  187. });
  188. }
  189.  
  190.  
  191. // ==== DO NOT MODIFY
  192. // ==== third-party utility functions:
  193. /*--- waitForKeyElements(): A utility function, for Greasemonkey scripts,
  194. that detects and handles AJAXed content.
  195. IMPORTANT: This function requires your script to have loaded jQuery.
  196. */
  197. function waitForKeyElements(
  198. selectorTxt, /* Required: The jQuery selector string that
  199. specifies the desired element(s).
  200. */
  201. actionFunction, /* Required: The code to run when elements are
  202. found. It is passed a jNode to the matched
  203. element.
  204. */
  205. bWaitOnce, /* Optional: If false, will continue to scan for
  206. new elements even after the first match is
  207. found.
  208. */
  209. iframeSelector /* Optional: If set, identifies the iframe to
  210. search.
  211. */
  212. ) {
  213. var targetNodes, btargetsFound;
  214.  
  215. if (typeof iframeSelector == 'undefined')
  216. targetNodes = $(selectorTxt);
  217. else
  218. targetNodes = $(iframeSelector).contents()
  219. .find(selectorTxt);
  220.  
  221. if (targetNodes && targetNodes.length > 0) {
  222. btargetsFound = true;
  223. /*--- Found target node(s). Go through each and act if they
  224. are new.
  225. */
  226. targetNodes.each(function () {
  227. var jThis = $(this);
  228. var alreadyFound = jThis.data('alreadyFound') || false;
  229.  
  230. if (!alreadyFound) {
  231. //--- Call the payload function.
  232. var cancelFound = actionFunction(jThis);
  233. if (cancelFound)
  234. btargetsFound = false;
  235. else
  236. jThis.data('alreadyFound', true);
  237. }
  238. });
  239. }
  240. else {
  241. btargetsFound = false;
  242. }
  243.  
  244. //--- Get the timer-control variable for this selector.
  245. var controlObj = waitForKeyElements.controlObj || {};
  246. var controlKey = selectorTxt.replace(/[^\w]/g, '_');
  247. var timeControl = controlObj[controlKey];
  248.  
  249. //--- Now set or clear the timer as appropriate.
  250. if (btargetsFound && bWaitOnce && timeControl) {
  251. //--- The only condition where we need to clear the timer.
  252. clearInterval(timeControl);
  253. delete controlObj[controlKey];
  254. }
  255. else {
  256. //--- Set a timer, if needed.
  257. if (!timeControl) {
  258. timeControl = setInterval(function () {
  259. waitForKeyElements(
  260. selectorTxt, actionFunction, bWaitOnce, iframeSelector
  261. );
  262. }, 300);
  263. controlObj[controlKey] = timeControl;
  264. }
  265. }
  266. waitForKeyElements.controlObj = controlObj;
  267. }
  268.  
  269. })();

QingJ © 2025

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