// ==UserScript==
// @name 北交大iCalender课表生成
// @namespace https://github.com/ZiuChen/BJTU-Schedule-ics-csvGenerator
// @version 1.3
// @description 📁📄导出ics/csv格式的日程文件! 💻📱支持多端同步! 📝支持Excel编辑! 📆📅支持导入各系统原生日历!
// @author Ziu
// @icon https://gitee.com/ziuc/utool-filebed/raw/master/20210514-231824-0795.png
// @match https://aa.bjtu.edu.cn/course_selection/courseselect/stuschedule/*
// @match https://aa.bjtu.edu.cn/course_selection/courseselecttask/schedule/
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require https://cdn.jsdelivr.net/gh/nwcell/ics.js@dfec67f37a3c267b3f97dd229c9b6a3521222794/demo/ics.deps.min.js
// @grant none
// @license MIT
// ==/UserScript==
"use strict";
const defaultStartMonday = "2022-02-28"; // 第一个教学周的第一个周一
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="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);
});
$("#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);
}
buttonGenerate();