ncut_01.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. (function() {
  2. 'use strict';
  3. function timeToMinutes(t) { const [h,m]=t.split(':').map(Number); return h*60+m; }
  4. function minutesToTime(m) { const hh=String(Math.floor(m/60)).padStart(2,'0'); const mm=String(m%60).padStart(2,'0'); return hh+':'+mm; }
  5. function parseWeeks(weekStr) {
  6. const cleaned = weekStr.replace(/[\[\]]/g,'').trim();
  7. let parity=null;
  8. if(cleaned.includes('(单周)')||cleaned.includes('单周')) parity=1;
  9. else if(cleaned.includes('(双周)')||cleaned.includes('双周')) parity=2;
  10. const m=cleaned.match(/(\d+)\s*[-~—]\s*(\d+)/);
  11. if(!m) return [];
  12. let s=parseInt(m[1]), e=parseInt(m[2]), weeks=[];
  13. for(let i=s;i<=e;i++) {
  14. if(parity===1 && i%2===0) continue;
  15. if(parity===2 && i%2!==0) continue;
  16. weeks.push(i);
  17. }
  18. return weeks;
  19. }
  20. function getScheduleDocument() {
  21. let dv = document.getElementById('dv1');
  22. if (dv && dv.querySelector('table tbody tr')) return document;
  23. const iframes = document.querySelectorAll('iframe');
  24. for (let f of iframes) {
  25. try {
  26. const doc = f.contentDocument || f.contentWindow.document;
  27. dv = doc.getElementById('dv1');
  28. if (dv && dv.querySelector('table tbody tr')) return doc;
  29. } catch(e) {}
  30. }
  31. return null;
  32. }
  33. function parseConfig(doc) {
  34. const ws = doc.getElementById('week');
  35. if(!ws) return { semesterStartDate:null, semesterTotalWeeks:20 };
  36. let sDate=null, total=0;
  37. ws.querySelectorAll('option').forEach(o=>{
  38. if(o.value && o.value!=='all'){
  39. if(!sDate) sDate=o.value;
  40. total++;
  41. }
  42. });
  43. return { semesterStartDate:sDate, semesterTotalWeeks:total||20 };
  44. }
  45. function parseTimeSlots(doc) {
  46. const slots=[];
  47. doc.querySelectorAll('#dv1 tbody tr').forEach(row=>{
  48. const first=row.querySelector('td:first-child');
  49. if(!first) return;
  50. const ps=first.querySelectorAll('p');
  51. if(ps.length<2) return;
  52. const secMatch=ps[0].textContent.trim().match(/\((\d+),\s*(\d+)小节\)/);
  53. if(!secMatch) return;
  54. const start=parseInt(secMatch[1]), end=parseInt(secMatch[2]);
  55. const timeMatch=ps[1].textContent.trim().match(/(\d{2}:\d{2})\s*[-~—]\s*(\d{2}:\d{2})/);
  56. if(!timeMatch) return;
  57. const bs=timeToMinutes(timeMatch[1]), be=timeToMinutes(timeMatch[2]);
  58. const dur=45;
  59. slots.push({number:start,startTime:timeMatch[1],endTime:minutesToTime(bs+dur)});
  60. slots.push({number:end,startTime:minutesToTime(be-dur),endTime:timeMatch[2]});
  61. });
  62. return slots.sort((a,b)=>a.number-b.number);
  63. }
  64. function parseCourses(doc) {
  65. const courses=[];
  66. doc.querySelectorAll('#dv1 tbody tr').forEach(row=>{
  67. const first=row.querySelector('td:first-child');
  68. if(!first) return;
  69. const ps=first.querySelectorAll('p');
  70. if(ps.length<2) return;
  71. const secMatch=ps[0].textContent.trim().match(/\((\d+),\s*(\d+)小节\)/);
  72. if(!secMatch) return;
  73. const startSec=parseInt(secMatch[1]), endSec=parseInt(secMatch[2]);
  74. row.querySelectorAll('td.kb-cell').forEach((cell,idx)=>{
  75. const day=idx+1;
  76. const classDiv=cell.querySelector('.person-class');
  77. if(!classDiv) return;
  78. const nameEl=classDiv.querySelector('h3 a');
  79. if(!nameEl) return;
  80. const lis=classDiv.querySelectorAll('ul>li');
  81. if(lis.length<4) return;
  82. const weeks=parseWeeks(lis[1].textContent.trim());
  83. if(!weeks.length) return;
  84. courses.push({
  85. name:nameEl.textContent.trim(),
  86. teacher:lis[2].textContent.trim(),
  87. position:lis[3].textContent.trim(),
  88. day, startSection:startSec, endSection:endSec,
  89. weeks, isCustomTime:false
  90. });
  91. });
  92. });
  93. return courses;
  94. }
  95. async function saveCourses(c){ try{await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(c));}catch(e){} }
  96. async function saveTimeSlots(s){ try{await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(s));}catch(e){} }
  97. async function saveConfig(c){ try{await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify(c));}catch(e){} }
  98. function waitForScheduleDoc(maxWait=15000) {
  99. const start=Date.now();
  100. return new Promise((resolve,reject)=>{
  101. function check(){
  102. const doc = getScheduleDocument();
  103. if(doc) return resolve(doc);
  104. if(Date.now()-start > maxWait) return reject(new Error('课表加载超时'));
  105. setTimeout(check,300);
  106. }
  107. check();
  108. });
  109. }
  110. async function main() {
  111. try {
  112. const doc = await waitForScheduleDoc();
  113. const config = parseConfig(doc);
  114. const timeSlots = parseTimeSlots(doc);
  115. const courses = parseCourses(doc);
  116. await saveConfig(config);
  117. await saveTimeSlots(timeSlots);
  118. await saveCourses(courses);
  119. AndroidBridge.notifyTaskCompletion();
  120. } catch(e) {}
  121. }
  122. setTimeout(main, 500);
  123. })();