网络学堂2018助手

微调排版,提醒更醒目; 支持导出日历,课程一目了然;课件批量下载,公告一键标记,拯救强迫症。

  1. // ==UserScript==
  2. // @icon http://www.tsinghua.edu.cn/publish/newthu/images/favicon.ico
  3. // @name 网络学堂2018助手
  4. // @namespace exhen32@live.com
  5. // @version 2019年03月23日01版
  6. // @description 微调排版,提醒更醒目; 支持导出日历,课程一目了然;课件批量下载,公告一键标记,拯救强迫症。
  7. // @require http://cdn.bootcss.com/jquery/3.2.1/jquery.min.js
  8. // @require https://cdn.bootcss.com/jqueryui/1.12.1/jquery-ui.min.js
  9. // @author Exhen
  10. // @match http*://learn.tsinghua.edu.cn/f/wlxt/index/course/student/
  11. // @grant GM_xmlhttpRequest
  12. // @grant GM_setClipboard
  13. // @grant GM_addStyle
  14. // @grant GM_setValue
  15. // @grant GM_getValue
  16. // @connect learn.tsinghua.edu.cn
  17. // @run-at document-idle
  18. // ==/UserScript==
  19.  
  20. var blocker = $('<div class="blocker" id="manualAlert" style="position: fixed;width: 100%;height: 100%;background: #4646466b;z-index: 999;"></div>')
  21.  
  22. $('head').append('<style type="text/css">.fixedCenter {left: 50%;position: absolute;right: 50%;top: 50%;bottom: 50%;}')
  23.  
  24. $('head').append('<style type="text/css">.myToobar {margin: 5px;display: inline-block;background: white;border: 1px solid gray;padding: 5px;border-radius: 5px; color:black} .myToobar a {color: black}')
  25.  
  26. var saveAs = saveAs || (function (view) {
  27. "use strict";
  28. // IE <10 is explicitly unsupported
  29. if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
  30. return;
  31. }
  32. var
  33. doc = view.document
  34. // only get URL when necessary in case Blob.js hasn't overridden it yet
  35. ,
  36. get_URL = function () {
  37. return view.URL || view.webkitURL || view;
  38. },
  39. save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a"),
  40. can_use_save_link = "download" in save_link,
  41. click = function (node) {
  42. var event = new MouseEvent("click");
  43. node.dispatchEvent(event);
  44. },
  45. is_safari = /constructor/i.test(view.HTMLElement) || view.safari,
  46. is_chrome_ios = /CriOS\/[\d]+/.test(navigator.userAgent),
  47. throw_outside = function (ex) {
  48. (view.setImmediate || view.setTimeout)(function () {
  49. throw ex;
  50. }, 0);
  51. },
  52. force_saveable_type = "application/octet-stream"
  53. // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
  54. ,
  55. arbitrary_revoke_timeout = 1000 * 40 // in ms
  56. ,
  57. revoke = function (file) {
  58. var revoker = function () {
  59. if (typeof file === "string") { // file is an object URL
  60. get_URL().revokeObjectURL(file);
  61. } else { // file is a File
  62. file.remove();
  63. }
  64. };
  65. setTimeout(revoker, arbitrary_revoke_timeout);
  66. },
  67. dispatch = function (filesaver, event_types, event) {
  68. event_types = [].concat(event_types);
  69. var i = event_types.length;
  70. while (i--) {
  71. var listener = filesaver["on" + event_types[i]];
  72. if (typeof listener === "function") {
  73. try {
  74. listener.call(filesaver, event || filesaver);
  75. } catch (ex) {
  76. throw_outside(ex);
  77. }
  78. }
  79. }
  80. },
  81. auto_bom = function (blob) {
  82. // prepend BOM for UTF-8 XML and text/* types (including HTML)
  83. // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
  84. if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
  85. return new Blob([String.fromCharCode(0xFEFF), blob], {
  86. type: blob.type
  87. });
  88. }
  89. return blob;
  90. },
  91. FileSaver = function (blob, name, no_auto_bom) {
  92. if (!no_auto_bom) {
  93. blob = auto_bom(blob);
  94. }
  95. // First try a.download, then web filesystem, then object URLs
  96. var
  97. filesaver = this,
  98. type = blob.type,
  99. force = type === force_saveable_type,
  100. object_url, dispatch_all = function () {
  101. dispatch(filesaver, "writestart progress write writeend".split(" "));
  102. }
  103. // on any filesys errors revert to saving with object URLs
  104. ,
  105. fs_error = function () {
  106. if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
  107. // Safari doesn't allow downloading of blob urls
  108. var reader = new FileReader();
  109. reader.onloadend = function () {
  110. var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
  111. var popup = view.open(url, '_blank');
  112. if (!popup) view.location.href = url;
  113. url = undefined; // release reference before dispatching
  114. filesaver.readyState = filesaver.DONE;
  115. dispatch_all();
  116. };
  117. reader.readAsDataURL(blob);
  118. filesaver.readyState = filesaver.INIT;
  119. return;
  120. }
  121. // don't create more object URLs than needed
  122. if (!object_url) {
  123. object_url = get_URL().createObjectURL(blob);
  124. }
  125. if (force) {
  126. view.location.href = object_url;
  127. } else {
  128. var opened = view.open(object_url, "_blank");
  129. if (!opened) {
  130. // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
  131. view.location.href = object_url;
  132. }
  133. }
  134. filesaver.readyState = filesaver.DONE;
  135. dispatch_all();
  136. revoke(object_url);
  137. };
  138. filesaver.readyState = filesaver.INIT;
  139.  
  140. if (can_use_save_link) {
  141. object_url = get_URL().createObjectURL(blob);
  142. setTimeout(function () {
  143. save_link.href = object_url;
  144. save_link.download = name;
  145. click(save_link);
  146. dispatch_all();
  147. revoke(object_url);
  148. filesaver.readyState = filesaver.DONE;
  149. });
  150. return;
  151. }
  152.  
  153. fs_error();
  154. },
  155. FS_proto = FileSaver.prototype,
  156. saveAs = function (blob, name, no_auto_bom) {
  157. return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
  158. };
  159. // IE 10+ (native saveAs)
  160. if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
  161. return function (blob, name, no_auto_bom) {
  162. name = name || blob.name || "download";
  163.  
  164. if (!no_auto_bom) {
  165. blob = auto_bom(blob);
  166. }
  167. return navigator.msSaveOrOpenBlob(blob, name);
  168. };
  169. }
  170.  
  171. FS_proto.abort = function () {};
  172. FS_proto.readyState = FS_proto.INIT = 0;
  173. FS_proto.WRITING = 1;
  174. FS_proto.DONE = 2;
  175.  
  176. FS_proto.error =
  177. FS_proto.onwritestart =
  178. FS_proto.onprogress =
  179. FS_proto.onwrite =
  180. FS_proto.onabort =
  181. FS_proto.onerror =
  182. FS_proto.onwriteend =
  183. null;
  184.  
  185. return saveAs;
  186. }(
  187. typeof self !== "undefined" && self ||
  188. typeof window !== "undefined" && window ||
  189. this.content
  190. ));
  191. // `self` is undefined in Firefox for Android content script context
  192. // while `this` is nsIContentFrameMessageManager
  193. // with an attribute `content` that corresponds to the window
  194.  
  195. if (typeof module !== "undefined" && module.exports) {
  196. module.exports.saveAs = saveAs;
  197. } else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
  198. define("FileSaver.js", function () {
  199. return saveAs;
  200. });
  201. }
  202.  
  203. var getJSON = function (url, meta, callback) {
  204. GM_xmlhttpRequest({
  205. method: 'GET',
  206. url: url,
  207. headers: {
  208. 'Accept': 'application/json'
  209. },
  210. onload: function (response) {
  211. if (response.status >= 200 && response.status < 400) {
  212. callback(JSON.parse(response.responseText), meta, url);
  213. } else {
  214. callback(false, meta, url);
  215. }
  216. }
  217. });
  218. };
  219.  
  220.  
  221. function waitForKeyElements(
  222. selectorTxt,
  223. /* Required: The jQuery selector string that
  224. specifies the desired element(s).
  225. */
  226. actionFunction,
  227. /* Required: The code to run when elements are
  228. found. It is passed a jNode to the matched
  229. element.
  230. */
  231. bWaitOnce,
  232. /* Optional: If false, will continue to scan for
  233. new elements even after the first match is
  234. found.
  235. */
  236. iframeSelector
  237. /* Optional: If set, identifies the iframe to
  238. search.
  239. */
  240. ) {
  241. var targetNodes, btargetsFound;
  242.  
  243. if (typeof iframeSelector == "undefined")
  244. targetNodes = jQuery(selectorTxt);
  245. else
  246. targetNodes = jQuery(iframeSelector).contents()
  247. .find(selectorTxt);
  248.  
  249. if (targetNodes && targetNodes.length > 0) {
  250. btargetsFound = true;
  251. /*--- Found target node(s). Go through each and act if they
  252. are new.
  253. */
  254. targetNodes.each(function () {
  255. var jThis = jQuery(this);
  256. var alreadyFound = jThis.data('alreadyFound') || false;
  257.  
  258. if (!alreadyFound) {
  259. //--- Call the payload function.
  260. var cancelFound = actionFunction(jThis);
  261. if (cancelFound)
  262. btargetsFound = false;
  263. else
  264. jThis.data('alreadyFound', true);
  265. }
  266. });
  267. } else {
  268. btargetsFound = false;
  269. }
  270.  
  271. //--- Get the timer-control variable for this selector.
  272. var controlObj = waitForKeyElements.controlObj || {};
  273. var controlKey = selectorTxt.replace(/[^\w]/g, "_");
  274. var timeControl = controlObj[controlKey];
  275.  
  276. //--- Now set or clear the timer as appropriate.
  277. if (btargetsFound && bWaitOnce && timeControl) {
  278. //--- The only condition where we need to clear the timer.
  279. clearInterval(timeControl);
  280. delete controlObj[controlKey]
  281. } else {
  282. //--- Set a timer, if needed.
  283. if (!timeControl) {
  284. timeControl = setInterval(function () {
  285. waitForKeyElements(selectorTxt,
  286. actionFunction,
  287. bWaitOnce,
  288. iframeSelector
  289. );
  290. },
  291. 300
  292. );
  293. controlObj[controlKey] = timeControl;
  294. }
  295. }
  296. waitForKeyElements.controlObj = controlObj;
  297. }
  298.  
  299. function PrefixInteger(num, length) {
  300. return (Array(length).join(0) + num).slice(-length);
  301. }
  302.  
  303. function init() {
  304. if (!document.getElementById("dUietC") && $('ul.stu').length) {
  305. var dUietC = document.createElement("a");
  306. dUietC.id = "dUietC";
  307. document.getElementsByTagName("html")[0].appendChild(dUietC);
  308.  
  309. console.log('网络学堂2018助手 is running!')
  310. // 新通知数量重新排版
  311. $('.unsee').remove();
  312. $('li.clearfix').each(function () {
  313. $(this).css('height', '90px')
  314. $(this).css('padding', '8px 8px')
  315. if (parseInt($(this).find('span.stud').text()) > 0) {
  316. $(this).find('span.stud').css('font-size', '50px');
  317. $(this).find('span.stud').css('display', 'block');
  318. $(this).find('span.stud').css('padding-left', 'none');
  319. $(this).find('span.stud').css('text-align', 'center');
  320. //$(this).find('span.name').text($(this).find('span.liulan').text());
  321. $(this).find('span.liulan').remove();
  322. } else {
  323. $(this).find('span.stud').remove();
  324. }
  325.  
  326. })
  327. $('ul.stu').each(function () {
  328. $(this).find('li').first().css('padding', '0px');
  329. })
  330.  
  331.  
  332.  
  333. $('dd.stu').each(function () {
  334. // 图片提醒
  335. //var wlkcid = $(this).find('.hdtitle a').attr('href').match(/(?<=wlkcid=).*/);
  336. var wlkcid = $(this).find('.hdtitle a').attr('href').slice(43);
  337. $(this).attr('id', wlkcid)
  338. if (parseInt($(this).find('span.green').text()) > 0) {
  339. getJSON(`http://learn.tsinghua.edu.cn/b/wlxt/kczy/zy/student/index/zyListWj?wlkcid=${wlkcid}&size=999`, null, function (doc, meta, url) {
  340. if (doc) {
  341. var ddl = 0;
  342. var now = new Date();
  343. for (var i = 0; i < doc.object.iTotalRecords; i++) {
  344. if (ddl <= 0 || (ddl > doc.object.aaData[i].jzsj && doc.object.aaData[i].jzsj > now.getTime())) {
  345. ddl = doc.object.aaData[i].jzsj
  346. }
  347. }
  348. console.log(ddl)
  349. var now = new Date();
  350. var time = ddl - now.getTime();
  351. console.log(time)
  352. var days = Math.ceil(time / 86400000);
  353. if (time <= 0) {
  354. $(`#${wlkcid}`).find('li.clearfix').first().css('background', 'url(https://raw.githubusercontent.com/Exhen/learn2018helper/master/liangle.jpg)');
  355. $(`#${wlkcid}`).find('li.clearfix').first().append(`<span style="color: red;font-size: 16px;padding: 10px 18px;line-height: 18px;width: 18px;text-align: center;display: block;float: right;">已经截止</span>`)
  356. } else if (time <= 86400000) { //多于7天
  357. $(`#${wlkcid}`).find('li.clearfix').first().css('background', 'url(https://raw.githubusercontent.com/Exhen/learn2018helper/master/ddl.jpg)');
  358. $(`#${wlkcid}`).find('li.clearfix').first().append(`<span style="color: red;font-size: 16px;padding: 10px 18px;line-height: 18px;width: 18px;text-align: center;display: block;float: right;">最后一天</span>`)
  359. } else {
  360. $(`#${wlkcid}`).find('li.clearfix').first().css('background', 'url(https://raw.githubusercontent.com/Exhen/learn2018helper/master/ddl.jpg)');
  361. $(`#${wlkcid}`).find('li.clearfix').first().append(`<span style="color: red;font-size: 16px;padding: 10px 18px;line-height: 18px;width: 18px;text-align: center;display: block;float: right;">还剩<span style="text-align: center;">${days}</span>天</span>`)
  362. }
  363. $(`#${wlkcid}`).find('p.p_img').remove();
  364. }
  365. })
  366. } else {
  367. $(this).find('li.clearfix').first().css('background', 'url(https://raw.githubusercontent.com/Exhen/learn2018helper/master/good.jpg)');
  368. $(this).find('li.clearfix').first().append(`<span style="color: black;font-size: 16px;padding: 10px 18px;line-height: 18px;width: 18px;text-align: center;display: block;float: right;">没有作业</span>`)
  369. $(this).find('p.p_img').remove();
  370. }
  371.  
  372. // 导出日历
  373. var calendarBtn = $('<p class="calendar_btn myToobar"><a href="javascript:void(0)">导出上课时间到日历文件</a></p>');
  374. calendarBtn.click(function () {
  375. console.log($(this).attr('class'))
  376. var classTitle = $(this).parent().parent().find('a.stu').text().replace(/\(.*-.*\)/, '').trim();
  377. var classDesc = $(this).parent().parent().find('.stu_btn_pai span').last().attr('title');
  378. var classTeacher = $(this).parent().parent().find('.stu_btn span').text();
  379. var thisSems = $('#profile0-tab').attr('onClick').match(/\(.*-.*\)/)
  380. var classUntil = thisSems[0].split('-')[1] + (thisSems[0].split('-')[2] > 1 ? '0101' : '0701') + 'T000000Z';
  381. console.log(classDesc);
  382. if (classDesc && !classDesc.match('星期第0节')) {
  383. for (var i = 0; i < classDesc.split(',').length; i++) {
  384. var eachClass = classDesc.split(',')[i];
  385. console.log(eachClass)
  386. var classLocation = eachClass.split(',')[1];
  387. var classTimeBegin = '',
  388. classTimeEnd = $(),
  389. classWeek = '';
  390. switch (eachClass.match(/星期(.)/)[1]) {
  391. case '日':
  392. classWeek = 'SU';
  393. break;
  394. case '一':
  395. classWeek = 'MO';
  396. break;
  397. case '二':
  398. classWeek = 'TU';
  399. break;
  400. case '三':
  401. classWeek = 'WE';
  402. break;
  403. case '四':
  404. classWeek = 'TH';
  405. break;
  406. case '五':
  407. classWeek = 'FR';
  408. break;
  409. case '六':
  410. classWeek = 'SA';
  411. break;
  412. }
  413. var now = new Date();
  414. var today = PrefixInteger(now.getUTCFullYear(), 4) + PrefixInteger(now.getUTCMonth() + 1, 2) + PrefixInteger(now.getUTCDate(), 2);
  415. switch (eachClass.match(/第(.)节/)[1]) {
  416. case '1':
  417. var classTimeBegin = today + 'T000000Z';
  418. var classTimeEnd = today + 'T013500Z';
  419. break;
  420. case '2':
  421. var classTimeBegin = today + 'T015000Z';
  422. var classTimeEnd = today + 'T041500Z';
  423. break;
  424. case '3':
  425. var classTimeBegin = today + 'T053000Z';
  426. var classTimeEnd = today + 'T070500Z';
  427. break;
  428. case '4':
  429. var classTimeBegin = today + 'T072000Z';
  430. var classTimeEnd = today + 'T085500Z';
  431. break;
  432. case '5':
  433. var classTimeBegin = today + 'T090500Z';
  434. var classTimeEnd = today + 'T104000Z';
  435. break;
  436. case '6':
  437. var classTimeBegin = today + 'T112000Z';
  438. var classTimeEnd = today + 'T134500Z';
  439. break;
  440. }
  441. var calendarData = `BEGIN:VCALENDAR\nVERSION:2.0\nMETHOD:PUBLISH\nBEGIN:VEVENT\nORGANIZER:${classTeacher}\nDTSTART;TZID=Asia/Shanghai:${classTimeBegin}\nDTEND;TZID=Asia/Shanghai:${classTimeEnd}\nRRULE:FREQ=WEEKLY;BYDAY=${classWeek};UNTIL=${classUntil};WKST=MO\nLOCATION:${classLocation}\nSUMMARY:${classTitle}(${classTeacher})\nDESCRIPTION:${classDesc}\nPRIORITY:5\nCLASS:PUBLIC\nBEGIN:VALARM\nTRIGGER:-PT15M\nACTION:DISPLAY\nDESCRIPTION:Reminder\nEND:VALARM\nEND:VEVENT\nEND:VCALENDAR`;
  442. var file = new File([calendarData], (classTitle + '-' + i + '.ics'), {
  443. type: "text/plain;charset=utf-8"
  444. });
  445. saveAs(file)
  446. }
  447. alert('日历文件下载成功,使用Outlook等邮件客户端打开即可将日历同步至邮件账户。')
  448.  
  449. } else {
  450. alert('课程时间错误,无法导出。')
  451. }
  452.  
  453. })
  454. $(this).find('div.state.stu').append(calendarBtn);
  455.  
  456.  
  457.  
  458. // 作业日历
  459. var ddlBtn = $('<p class="calendar_btn myToobar"><a href="javascript:void(0)">导出作业DDL到日历文件</a></p>');
  460. ddlBtn.click(function () {
  461. blockerTemp = blocker;
  462. blockerTemp.addClass('ddlBtn')
  463. $('body').prepend(blockerTemp);
  464. $('.blocker.ddlBtn').empty();
  465. $('.blocker.ddlBtn').append('<span class="fixedCenter" style="font-size:30px;color:white">Loading...</span>')
  466. if (parseInt($(this).parent().parent().parent().find('span.green').text()) > 0) {
  467. var classTitle = $(this).parent().parent().find('a.stu').text().replace(/\(.*-.*\)/, '').trim();
  468. var classTeacher = $(this).parent().parent().find('.stu_btn span').text();
  469. getJSON(`http://learn.tsinghua.edu.cn/b/wlxt/kczy/zy/student/index/zyListWj?wlkcid=${wlkcid}&size=999`, null, function (doc, meta, url) {
  470. $('.blocker.ddlBtn').remove();
  471. if (doc) {
  472. var ddl = 0;
  473. for (var i = 0; i < doc.object.iTotalRecords; i++) {
  474. var current = doc.object.aaData[i];
  475. var tempDate = new Date();
  476. tempDate.setTime(current.jzsj - 3600000);
  477. console.log(current.jzsj)
  478. var tempDateBefore = new Date();
  479. tempDateBefore.setTime(current.jzsj - 86400000 - 3600000);
  480. var currDDL = PrefixInteger(tempDate.getUTCFullYear(), 4) + PrefixInteger(tempDate.getUTCMonth() + 1, 2) + PrefixInteger(tempDate.getUTCDate(), 2) + 'T' + PrefixInteger(tempDate.getUTCHours(), 2) + PrefixInteger(tempDate.getUTCMinutes(), 2) + PrefixInteger(tempDate.getUTCSeconds(), 2) + 'Z';
  481. var currDDLBefore = PrefixInteger(tempDateBefore.getUTCFullYear(), 4) + PrefixInteger(tempDateBefore.getUTCMonth() + 1, 2) + PrefixInteger(tempDateBefore.getUTCDate(), 2) + 'T' + PrefixInteger(tempDateBefore.getUTCHours(), 2) + PrefixInteger(tempDateBefore.getUTCMinutes(), 2) + PrefixInteger(tempDateBefore.getUTCSeconds(), 2) + 'Z';
  482. var currTitle = current.bt;
  483.  
  484. var calendarData = `BEGIN:VCALENDAR\nVERSION:2.0\nMETHOD:PUBLISH\nBEGIN:VEVENT\nORGANIZER:${classTeacher}\nDTSTART;TZID=Asia/Shanghai:${currDDLBefore}\nDTEND;TZID=Asia/Shanghai:${currDDL}\nSUMMARY:${currTitle}(${classTitle})\nDESCRIPTION:${classTitle}(${classTeacher}),截止时间:${current.jzsjStr}\nPRIORITY:5\nCLASS:PUBLIC\nBEGIN:VALARM\nTRIGGER:-PT1440M\nACTION:DISPLAY\nDESCRIPTION:Reminder\nEND:VALARM\nEND:VEVENT\nEND:VCALENDAR`;
  485. var file = new File([calendarData], (classTitle + '-' + PrefixInteger(i, 2) + '-' + currTitle + '.ics'), {
  486. type: "text/plain;charset=utf-8"
  487. });
  488. saveAs(file)
  489.  
  490. }
  491. alert('日历文件下载成功,使用Outlook等邮件客户端打开即可将日历同步至邮件账户。')
  492. } else {
  493. alert('获取列表失败!请检查网络。')
  494. }
  495. })
  496.  
  497. } else {
  498. $('.blocker.ddlBtn').remove();
  499. alert('暂时没有可以导出的DDL')
  500. }
  501.  
  502. delete blockerTemp;
  503. })
  504. $(this).find('div.state.stu').append(ddlBtn);
  505.  
  506. // 一键已读
  507. var notificationBtn = $('<p class="calendar_btn myToobar"><a href="javascript:void(0)">新公告一键标记已读</a></p>');
  508. notificationBtn.click(function () {
  509. blockerTemp = blocker;
  510. blockerTemp.addClass('notificationBtn');
  511. $('body').prepend(blockerTemp);
  512. $('.blocker.ddlBtn').empty();
  513. $('.notificationBtn.blocker').append('<span style="background: #fffffff5;border-radius: 3px;left: 30%;right: 30%;position: fixed;text-align: center;padding: 3%;line-height: 40px;font-size: 30px;">一键已读功能不稳定,容易引起BUG,调试ing!</br>扫码催更<img style="width:400px" src="https://exhen.github.io//assets/img/qrcode.png"></span>').click(function () {
  514. $(this).fadeOut().remove()
  515. })
  516. $('#manualAlert').fadeIn();
  517. setTimeout(function () {
  518. $('.blocker.notificationBtn').fadeOut();
  519. $('.blocker.notificationBtn').remove();
  520. }, 10000)
  521. delete blockerTemp;
  522. })
  523.  
  524. // notificationBtn.click(function () {
  525. // var unreadNum = parseInt($(this).parent().parent().parent().find('span.orange.stud').text());
  526. // if (unreadNum > 0) {
  527. // blockerTemp = blocker;
  528. // blockerTemp.attr('class', 'blocker notificationBtn')
  529. // $('body').prepend(blockerTemp);$('.blocker.ddlBtn').empty();
  530. // $('.blocker.notificationBtn').append('<span class="fixedCenter" style="font-size:30px;color:white">Loading...</span>')
  531. // getJSON(`http://learn.tsinghua.edu.cn/b/wlxt/kcgg/wlkc_ggb/student/kcggListXs?size=999&wlkcid=${wlkcid}`, null, function (doc, meta, url) {
  532. // $('.blocker.notificationBtn').remove();
  533. // if (doc) {
  534. // console.log(doc)
  535. // var sucessNum = 0,
  536. // arrivedNum = 0,
  537. // sentNum = 0;
  538. // for (var i = 0; i < doc.object.iTotalRecords; i++) {
  539. // if (doc.object.aaData[i].sfyd == '否') {
  540. // sentNum++;
  541. // GM_xmlhttpRequest({
  542. // method: 'GET',
  543. // url: `http://learn.tsinghua.edu.cn/f/wlxt/kcgg/wlkc_ggb/student/beforeViewXs?wlkcid=${wlkcid}&id=${doc.object.aaData[i].ggid}`,
  544. // headers: {
  545. // 'Accept': 'application/json'
  546. // },
  547. // onload: function (response) {
  548. // arrivedNum++;
  549. // if (response.status >= 200 && response.status < 400) {
  550. // console.log('gg has been read');
  551. // sucessNum++
  552. // } else {
  553. // console.log(doc.object.aaData[i].ggid + ' error!')
  554. // }
  555. // if (arrivedNum == unreadNum) {
  556. // if (sucessNum == unreadNum) {
  557. // alert('一键已读成功!');
  558. // location.reload();
  559. // } else {
  560. // alert(`${unreadNum-sucessNum}/${unreadNum}条公告标记已读失败!`);
  561. // location.reload();
  562. // }
  563. // }
  564. // }
  565. // });
  566. // }
  567. // }
  568. // if (sentNum !== unreadNum) {
  569. // alert('学堂系统BUG,未读数量显示不对,建议反馈给ITS!')
  570. // }
  571. // } else {
  572. // alert('获取列表失败!请检查网络。')
  573. // }
  574.  
  575. // })
  576. // } else {
  577. // alert('没有未读公告。')
  578. // }
  579. // })
  580.  
  581. $(this).find('div.state.stu').append(notificationBtn);
  582.  
  583. // 批量下载
  584.  
  585. function downloadFromJson(doc, flagForOld, downloadList) {
  586. var totalSize = 0;
  587. for (var i = 0; i < doc.object.length; i++) {
  588. if (!flagForOld && !doc.object[i].isNew) {
  589. continue;
  590. }
  591. totalSize = totalSize + doc.object[i].wjdx;
  592. downloadList.push(doc.object[i].wjid);
  593. }
  594. return totalSize
  595. }
  596.  
  597. function getFileSize(fileByte) {
  598. var fileSizeByte = fileByte;
  599. var fileSizeMsg = "";
  600. if (fileSizeByte < 1048576) fileSizeMsg = (fileSizeByte / 1024).toFixed(2) + "KB";
  601. else if (fileSizeByte == 1048576) fileSizeMsg = "1MB";
  602. else if (fileSizeByte > 1048576 && fileSizeByte < 1073741824) fileSizeMsg = (fileSizeByte / (1024 * 1024)).toFixed(2) + "MB";
  603. else if (fileSizeByte > 1048576 && fileSizeByte == 1073741824) fileSizeMsg = "1GB";
  604. else if (fileSizeByte > 1073741824 && fileSizeByte < 1099511627776) fileSizeMsg = (fileSizeByte / (1024 * 1024 * 1024)).toFixed(2) + "GB";
  605. else fileSizeMsg = "超过1TB";
  606. return fileSizeMsg;
  607. }
  608.  
  609. var attachmentAllBtn = $('<p class="calendar_btn myToobar"><a href="javascript:void(0)">全部课件批量下载</a></p>');
  610. attachmentAllBtn.click(function () {
  611. blockerTemp = blocker;
  612. blockerTemp.attr('class', 'blocker attachmentAllBtn')
  613. $('body').prepend(blockerTemp);
  614. $('.blocker.ddlBtn').empty();
  615. $('.blocker.attachmentAllBtn').append('<span class="fixedCenter" style="font-size:30px;color:white">Loading...</span>')
  616. getJSON(`http://learn.tsinghua.edu.cn/b/wlxt/kj/wlkc_kjxxb/student/kjxxbByWlkcidAndSizeForStudent?size=999&wlkcid=${wlkcid}`, null, function (doc, meta, url) {
  617. $('.blocker.attachmentAllBtn').remove();
  618. if (doc) {
  619. // console.log(doc)
  620. var downloadList = new Array();
  621. var totalSize = downloadFromJson(doc, true, downloadList);
  622. // console.log(downloadList, totalSize)
  623. if (downloadList.length) {
  624. if (confirm(`按确认键开始下载全部${downloadList.length}个文件(共计${getFileSize(totalSize)})。\n如果下载未开始,请检查浏览器是否拦截了本网页的弹出窗口(例如Chrome地址栏最右侧出现带小红叉的图标)`)) {
  625. for (var i = 0; i < downloadList.length; i++) {
  626. window.open('http://learn.tsinghua.edu.cn/b/wlxt/kj/wlkc_kjxxb/student/downloadFile?sfgk=0&wjid=' + downloadList[i])
  627. }
  628. }
  629. } else {
  630. alert('暂时无文件供下载。')
  631. }
  632.  
  633. } else {
  634. alert('获取列表失败!请检查网络。')
  635. }
  636. })
  637. delete blockerTemp;
  638. })
  639. $(this).find('div.state.stu').append(attachmentAllBtn);
  640. var attachmentNewBtn = $('<p class="calendar_btn myToobar"><a href="javascript:void(0)">新课件批量下载</a></p>');
  641. attachmentNewBtn.click(function () {
  642. blockerTemp = blocker;
  643. blockerTemp.attr('class', 'blocker attachmentNewBtn')
  644. $('body').prepend(blockerTemp);
  645. $('.blocker.ddlBtn').empty();
  646. $('.blocker.attachmentNewBtn').append('<span class="fixedCenter" style="font-size:30px;color:white">Loading...</span>')
  647. getJSON(`http://learn.tsinghua.edu.cn/b/wlxt/kj/wlkc_kjxxb/student/kjxxbByWlkcidAndSizeForStudent?size=999&wlkcid=${wlkcid}`, null, function (doc, meta, url) {
  648. $('.blocker.attachmentNewBtn').remove();
  649. if (doc) {
  650. console.log(doc)
  651. var downloadList = new Array();
  652. var totalSize = downloadFromJson(doc, false, downloadList);
  653. console.log(downloadList, totalSize)
  654. if (downloadList.length) {
  655. if (confirm(`按确认键开始下载全部${downloadList.length}个文件(共计${getFileSize(totalSize)})。\n如果下载未开始,请检查浏览器是否拦截了本网页的弹出窗口(例如Chrome地址栏最右侧出现带小红叉的图标)`)) {
  656. for (var i = 0; i < downloadList.length; i++) {
  657. window.open('http://learn.tsinghua.edu.cn/b/wlxt/kj/wlkc_kjxxb/student/downloadFile?sfgk=0&wjid=' + downloadList[i])
  658. }
  659. }
  660. } else {
  661. alert('暂时无文件供下载。')
  662. }
  663.  
  664. } else {
  665. alert('获取列表失败!请检查网络。')
  666. }
  667. })
  668. delete blockerTemp;
  669. })
  670. $(this).find('div.state.stu').append(attachmentNewBtn);
  671.  
  672. })
  673.  
  674. return true
  675. } else {
  676. console.log('nothing happened!')
  677. return false
  678. }
  679. }
  680.  
  681. window.addEventListener('load', function () {
  682. var icon = $('<div id="manualScript"><a ref="javascript:void(0);"><i class="webicon-recycle"></i>手动加载</a></div>');
  683. icon.find('a').click(function () {
  684. init();
  685. });
  686. $('div.header div.w div.right').append(icon)
  687.  
  688. waitForKeyElements('dd.stu', init, true)
  689.  
  690. // if (!init()) {
  691. // console.log('appending suggestion')
  692. // $('body').prepend('<div onClick="$(this).hide()" id="manualAlert" style="display: none;position: absolute;width: 100%;height: 100%;background: #4646466b;z-index: 999;"><span style="background: #fffffff5;border-radius: 3px;left: 30%;right: 30%;top: 25%;bottom: 25%;position: fixed;text-align: center;padding: 3%;line-height: 40px;font-size: 30px;">当前网速缓慢,脚本可能加载不畅,可以尝试右上角“手动加载”。</span></div>')
  693. // $('#manualAlert').fadeIn();
  694. // setTimeout(function () {
  695. // $('#manualAlert').fadeOut();
  696. // }, 2000)
  697. // };
  698.  
  699.  
  700. // $('body').prepend('<div onClick="$(this).hide()" id="manualAlert" style="display: none;position: absolute;width: 100%;height: 100%;background: #4646466b;z-index: 999;"><span style="background: #fffffff5;border-radius: 3px;left: 30%;right: 30%;position: fixed;text-align: center;padding: 3%;line-height: 40px;font-size: 30px;">作者偷懒,代码还没敲完!扫码催活<img src="https://exhen.github.io//assets/img/qrcode.png"></span></div>')
  701. // $('#manualAlert').fadeIn();
  702. // setTimeout(function () {
  703. // $('#manualAlert').fadeOut();
  704. // }, 10000)
  705.  
  706. })

QingJ © 2025

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