Greasy Fork is available in English.
导出ics/csv/json格式的日程文件! 💻支持多端同步! 📝支持Excel编辑! 📆支持导入各系统原生日历!
// ==UserScript== // @name 北交大iCalender课表生成 // @namespace https://github.com/ZiuChen/userscript // @version 1.5.1 // @description 导出ics/csv/json格式的日程文件! 💻支持多端同步! 📝支持Excel编辑! 📆支持导入各系统原生日历! // @author ZiuChen // @match https://aa.bjtu.edu.cn/course_selection/courseselect/stuschedule/* // @match https://aa.bjtu.edu.cn/course_selection/courseselecttask/schedule/ // @require https://fastly.jsdelivr.net/npm/[email protected]/dist/jquery.min.js // @require https://fastly.jsdelivr.net/gh/nwcell/ics.js@dfec67f37a3c267b3f97dd229c9b6a3521222794/demo/ics.deps.min.js // @icon https://fastly.jsdelivr.net/gh/ZiuChen/ZiuChen@main/avatar.jpg // @grant none // @license MIT // ==/UserScript== "use strict" const defaultStartMonday = "2022-08-29" // 第一个教学周的第一个周一 if (localStorage.getItem("defaultStartMonday") === null) { localStorage.setItem("defaultStartMonday", defaultStartMonday) } function buttonGenerate() { $(".widget-title").append(/* html */ ` <button id="scheduleIcsGenerate" class="btn btn-white btn-xs">导出为ics文件</button> <button id="csvGenerate" class="btn btn-white btn-xs">导出为csv文件</button> <button id="jsonGenerate" class="btn btn-white btn-xs">导出为json文件</button> <button id="scheduleRedirect" title="点击跳转校历" class="btn btn-white btn-xs">校历</button> <input id="startMonday" class="form-control" type="text" placeholder="第一个教学周的第一个周一" title="第一个教学周的第一个周一" style="height: 26px;width: 200px;display: inline;vertical-align: middle;"></input> <button id="restoreSetting" title="点击重置设置" class="btn btn-white btn-xs">重置</button> `) bindEvents() } function bindEvents() { $("#scheduleRedirect").click(() => { window.open("https://bksy.bjtu.edu.cn/Semester.html") }) let pageFlag = 0 if (window.location.href.search("/courseselect/stuschedule/") != -1) { // 本学期课表 } else if (window.location.href.search("/courseselecttask/schedule/") != -1) { // 选课课表 pageFlag = 1 } $("#scheduleIcsGenerate").click(() => { icsmain(pageFlag) }) $("#csvGenerate").click(() => { csvmain(pageFlag) }) $("#jsonGenerate").click(() => { jsonmain(pageFlag) }) $("#restoreSetting").click(() => { localStorage.clear() location.reload() }) $("#startMonday") .change((e) => { localStorage.setItem("defaultStartMonday", e.target.value) }) .val(localStorage.getItem("defaultStartMonday")) } // generateWeekTable() @github ygowill function generateWeekTable() { let startMondayString = $("#startMonday").val() if (validCheck(startMondayString) === false) return const startMonday = new Date(startMondayString) let weekDateTable = [] for (let i = 0; i < 30; i++) { // 生成到30周 let weekArr = [] for (let j = 0; j < 7; j++) { let tmpDate = new Date(startMonday) tmpDate.setDate(tmpDate.getDate() + 7 * i + j) weekArr.push(tmpDate) } weekDateTable.push(weekArr) } return weekDateTable } function validCheck(startMondayString) { const re = new RegExp(/^\d{4}-\d{1,2}-\d{1,2}/) if (re.test(startMondayString) === false) { alert("输入日期值非法,请重新输入。") throw Error("输入日期值非法") } else { return true } } function tableTransfer(rowTable, isOrigin) { // 7*7行转列 let tmpTable = [] let columnTable = [] for (let i = 0; i < 7; i++) { if (isOrigin) { for (let j = 0; j < 7; j++) { tmpTable.push(rowTable[j]) } } else { for (let j = i; j < 49; j += 7) { tmpTable.push(rowTable[j]) } } columnTable[i] = tmpTable tmpTable = [] } return columnTable } function removeZero(iArr) { for (let i = 0; i < iArr.length; i++) { iArr[i] = parseInt(iArr[i], 10) } return iArr } function dateStr2Arr(dateStr) { let dateArr = [] if (dateStr) { if (dateStr.indexOf("-") != -1) { // 第X-Y周 let indexArr = dateStr.split("-") removeZero(indexArr) for (let i = indexArr[0]; i < indexArr[1] + 1; i++) { dateArr.push(i) } } else if (dateStr.indexOf(",") != -1) { // 单双周 dateArr = dateStr.split(", ") removeZero(dateArr) } else dateArr.push(parseInt(dateStr, 10)) // 第X周 } return dateArr } // courseList[x]示例: // allInfo: "国际贸易实务模拟 第03-06周思源东楼 SD401卜伟" // courseNum: 1 // date: (4) [3, 4, 5, 6] // initInfo: "第03-06周" // location: "思源东楼 SD401" // name: "国际贸易实务模拟 " // teacher: "卜伟" // weekNum: 6 function stuScheduleGetTable(isOrigin) { let courseListTmp = tableTransfer($("tr>td[style!='height:80px;']"), isOrigin) let courseList = [] let courseTmp = {} for (let i = 0; i < 7; i++) { for (let j = 0; j < 7; j++) { for ( let k = 0; k < courseListTmp[i][j].querySelectorAll('span[style="color:#000"]').length; k++ ) { courseTmp.weekNum = i + 1 courseTmp.courseNum = j + 1 if (courseListTmp[i][j].querySelectorAll('span[style="color:#000"]')[k]) { courseTmp.name = courseListTmp[i][j] .querySelectorAll('span[style="color:#000"]') [k].innerText.split("[本")[0] courseTmp.location = courseListTmp[i][j].querySelectorAll('span[class="text-muted"]')[ k ].innerText let dateStr = courseListTmp[i][j].querySelectorAll('div[style="max-width:120px;"]')[k] .innerText dateStr = dateStr.substring(dateStr.indexOf("第") + 1, dateStr.indexOf("周")) // 预处理 courseTmp.initInfo = "第" + dateStr + "周" courseTmp.date = dateStr2Arr(dateStr) courseTmp.teacher = courseListTmp[i][j].querySelectorAll("i")[k].innerText courseTmp.allInfo = courseTmp.name + " " + courseTmp.initInfo + " " + courseTmp.location + " " + courseTmp.teacher courseList.push(courseTmp) courseTmp = {} } } } } return courseList } function scheduleGetTable(isOrigin) { let courseListTmp = tableTransfer($("tr>td[style!='height:80px;']"), isOrigin) let courseList = [] let courseTmp = {} for (let i = 0; i < 7; i++) { for (let j = 0; j < 7; j++) { for ( let k = 0; k < courseListTmp[i][j].querySelectorAll('div[style="max-width:120px;"]').length; k++ ) { courseTmp.weekNum = i + 1 courseTmp.courseNum = j + 1 if (courseListTmp[i][j].querySelectorAll("span")[k]) { courseTmp.name = courseListTmp[i][j] .getElementsByTagName("span") [k * 3].innerText.split("\n")[1] courseTmp.location = courseListTmp[i][j].querySelectorAll('span[class="text-muted"]')[ k ].innerText let dateStr = courseListTmp[i][j].querySelectorAll('div[style="max-width:120px;"]')[k] .innerText dateStr = dateStr.substring(dateStr.indexOf("第") + 1, dateStr.indexOf("周")) // 预处理 courseTmp.initInfo = "第" + dateStr + "周" courseTmp.date = dateStr2Arr(dateStr) courseTmp.teacher = courseListTmp[i][j].querySelectorAll("i")[k].innerText courseTmp.allInfo = courseTmp.name + " " + courseTmp.initInfo + " " + courseTmp.location + " " + courseTmp.teacher courseList.push(courseTmp) courseTmp = {} } } } } return courseList } function timeConstructor(weekTh, weekNum, courseNum, isStamp, isDelay) { let standardTimeTable = [ ["08:00", "09:50"], ["10:10", "12:00"], ["12:10", "14:00"], ["14:10", "16:00"], ["16:20", "18:10"], ["19:00", "20:50"], ["21:00", "21:50"] ] let delayTimeTable = [ ["08:00", "09:50"], ["10:30", "12:20"], ["12:10", "14:00"], ["14:10", "16:00"], ["16:20", "18:10"], ["19:00", "20:50"], ["21:00", "21:50"] ] let WeekTable = generateWeekTable() let DayTime = new Date(WeekTable[weekTh - 1][weekNum - 1]) let rtnTime = [] let startTimeStamp, endTimeStamp let delayClassroom = ["思源西楼", "逸夫"] for (let item of delayClassroom) { if (isDelay.search(item) != -1) { startTimeStamp = DayTime.setHours( delayTimeTable[courseNum - 1][0].split(":")[0], delayTimeTable[courseNum - 1][0].split(":")[1] ) endTimeStamp = DayTime.setHours( delayTimeTable[courseNum - 1][1].split(":")[0], delayTimeTable[courseNum - 1][1].split(":")[1] ) } else { startTimeStamp = DayTime.setHours( standardTimeTable[courseNum - 1][0].split(":")[0], standardTimeTable[courseNum - 1][0].split(":")[1] ) endTimeStamp = DayTime.setHours( standardTimeTable[courseNum - 1][1].split(":")[0], standardTimeTable[courseNum - 1][1].split(":")[1] ) } } if (isStamp === 1) { rtnTime.push(startTimeStamp) rtnTime.push(endTimeStamp) return rtnTime } let startTime = new Date(startTimeStamp) let endTime = new Date(endTimeStamp) startTime = startTime.toString() endTime = endTime.toString() rtnTime.push(startTime) rtnTime.push(endTime) return rtnTime } function icsConstructor(icsEventList) { let cal = ics() let today = new Date() today = today.toLocaleDateString() for (let i = 0; i < icsEventList.length; i++) { cal.addEvent( icsEventList[i].name, icsEventList[i].description, icsEventList[i].location, icsEventList[i].startTime, icsEventList[i].endTime ) } cal.download("iCalender - 课表 - " + today) } function eventConstructor(courseList) { let icsEvent = {} let icsEventList = [] for (let i = 0; i < courseList.length; i++) { for (let j = 0; j < courseList[i].date.length; j++) { let timeRst = timeConstructor( courseList[i].date[j], courseList[i].weekNum, courseList[i].courseNum, 0, courseList[i].location ) let timeRstStamp = timeConstructor( courseList[i].date[j], courseList[i].weekNum, courseList[i].courseNum, 1, courseList[i].location ) icsEvent.name = courseList[i].name icsEvent.description = courseList[i].location + " " + courseList[i].initInfo + " 任课教师:" + courseList[i].teacher icsEvent.location = courseList[i].location icsEvent.startTime = timeRst[0] icsEvent.endTime = timeRst[1] icsEvent.startTimeStamp = timeRstStamp[0] icsEvent.endTimeStamp = timeRstStamp[1] icsEventList.push(icsEvent) icsEvent = {} } } return icsEventList } function toExcelFormatter(courseList) { let standardTimeTable = [ ["08:00", "09:50"], ["10:10", "12:00"], ["12:10", "14:00"], ["14:10", "16:00"], ["16:20", "18:10"], ["19:00", "20:50"], ["21:00", "21:50"] ] let jsonData = [ { column1: "", column2: "", column3: "", column4: "", column5: "", column6: "", column7: "" }, { column1: "", column2: "", column3: "", column4: "", column5: "", column6: "", column7: "" }, { column1: "", column2: "", column3: "", column4: "", column5: "", column6: "", column7: "" }, { column1: "", column2: "", column3: "", column4: "", column5: "", column6: "", column7: "" }, { column1: "", column2: "", column3: "", column4: "", column5: "", column6: "", column7: "" }, { column1: "", column2: "", column3: "", column4: "", column5: "", column6: "", column7: "" }, { column1: "", column2: "", column3: "", column4: "", column5: "", column6: "", column7: "" } ] let charArr = ["第一节", "第二节", "第三节", "第四节", "第五节", "第六节", "第七节"] let objKeys = Object.keys(jsonData[0]) for (let i = 0; i < 7; i++) { for (let j = 0; j < 7; j++) { let tmpKey = objKeys[j + 1] jsonData[i].column1 = charArr[i] + " [" + standardTimeTable[i][0] + " - " + standardTimeTable[i][0] + "]" for (let k = 0; k < courseList.length; k++) { if (courseList[k].courseNum == i + 1 && courseList[k].weekNum == j + 1) { jsonData[i][tmpKey] = jsonData[i][tmpKey] + " " + courseList[k].allInfo } } } } return jsonData } // tableToExcel() @csdn hhzzcc_ function tableToExcel(jsonData) { let str = `课程|星期,星期一,星期二,星期三,星期四,星期五,星期六,星期日\n` for (let i = 0; i < jsonData.length; i++) { for (let key in jsonData[i]) { str += `${jsonData[i][key] + "\t"},` } str += "\n" } const uri = "data:text/csv;charset=utf-8,\ufeff" + encodeURIComponent(str) const link = document.createElement("a") link.href = uri link.download = "课程表.csv" link.click() } function icsmain(icase) { let icsEventList if (icase === 0) { icsEventList = eventConstructor(stuScheduleGetTable()) } else if (icase === 1) { icsEventList = eventConstructor(scheduleGetTable()) } icsConstructor(icsEventList) } function csvmain(icase) { let jsonData if (icase === 0) { jsonData = toExcelFormatter(stuScheduleGetTable()) } else if (icase === 1) { jsonData = toExcelFormatter(scheduleGetTable()) } tableToExcel(jsonData) } function jsonmain(icase) { let jsonData if (icase === 0) { jsonData = stuScheduleGetTable() } else if (icase === 1) { jsonData = scheduleGetTable() } const uri = "data:text/json;charset=utf-8,\ufeff" + encodeURIComponent(JSON.stringify(jsonData)) const link = document.createElement("a") link.href = uri link.download = "课程表.json" link.click() } buttonGenerate()