// ==UserScript==
// @name 课表助手 - xtu
// @namespace http://tampermonkey.net/
// @version 1.0.0
// @description 一键导出课表/考试安排为ics文件
// @author DeltaX
// @match http://jwxt.xtu.edu.cn/jsxsd/*
// @require https://cdn.bootcdn.net/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js
// @grant none
// @License MIT
// ==/UserScript==
(function () {
'use strict';
let SEPARATOR = navigator.appVersion.indexOf('Win') !== -1 ? '\r\n' : '\n';
// 设置上课时间及下课时间
let class_start_summer = [
[],
[8, 0],
[9, 40],
[10, 10],
[11, 50],
[14, 30],
[16, 10],
[16, 40],
[18, 10],
[19, 30],
[22, 5]
];
let class_start_winter = [
[],
[8, 0],
[9, 40],
[10, 10],
[11, 50],
[14, 0],
[15, 40],
[16, 10],
[17, 40],
[19, 0],
[21, 35]
];
let class_time = 100;
// let weekToNum = { "日": 0, "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6 }
let weekRegexp = /(?:(\d{1,2}-\d{1,2})|(\d{1,2})\(单周\))/g;
let nowDate = new Date();
let weekStartQuery = term =>
new Promise(function (resolve, reject) {
let xhr = new XMLHttpRequest();
xhr.open('POST', 'http://jwxt.xtu.edu.cn/jsxsd/jxzl/jxzl_query');
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.withCredentials = true;
xhr.onload = function () {
if (xhr.status === 200) {
resolve(
/<tr height='28'><td>1<\/td><td title='(.*?)'>/.exec(
this.response
)[1]
);
} else {
reject(this);
}
};
xhr.send('xnxq01id=' + term);
});
let toString = function (date) {
return (
date
.toISOString()
.split(/-|:|[.]/)
.slice(0, 4)
.join('') + '00Z'
);
};
// 生成课表日历文件
function GenerateCourseICS() {
// 获取main-->page_iframe-->iframe0
let iframe0 = document.getElementById('Form1');
// 添加按钮"保存ics"
let buttonPre = iframe0.querySelector('select#xnxq01id');
buttonPre.outerHTML +=
' <input type="button" onclick="saveics()" class="button" name="submit" value="保存ics">';
if (typeof iframe0.saveics === 'function') return;
// 添加按钮操作,核心部分
iframe0.saveics = function () {
window.console.log('开始生成...');
if (
navigator.userAgent.indexOf('MISE') > -1 &&
navigator.userAgent.indexOf('MSIE 10') == -1
) {
alert('浏览器不支持哦~😆');
return;
}
let calendar_start = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:Curriculum-to-iCalendar'
].join(SEPARATOR);
let calendar_end = SEPARATOR + 'END:VCALENDAR' + SEPARATOR;
let calendarEvents = [];
// 获取到课程表
try {
let table = document.querySelector('#kbtable').tBodies[0];
let yearTerm = document
.querySelector('#xnxq01id')
[document.querySelector('#xnxq01id').selectedIndex].innerText.split(
'-'
);
let year = yearTerm[0] + '-' + yearTerm[1];
let term = yearTerm[2];
// 请求起始周
console.log('查询学期起始日期中,学期: ' + yearTerm.join('-'));
weekStartQuery(yearTerm.join('-')).then(weekStart => {
let termStartDate = new Date(weekStart.split(/年|月|日/));
// let currentWeek = parseInt((nowDate - startDate) / 1000 * 60 * 60 * 24 * 7);
// 逐行添加event至cal
console.log('添加课程事件中...');
for (let i = 1; i < table.rows.length - 1; i++) {
// 逐课程添加
// let timeAddress = table.rows[i].cells[0].innerText;
// let events = timeAddress.split(' ').filter(n => n != " ");
for (let j = 1; j < table.rows[i].cells.length; j++) {
// 同一时间不同课程
let lessonInfos = table.rows[i].cells[j].innerText.split(
'---------------------\n'
);
for (let lesInfStr of lessonInfos) {
let lessonInfo = lesInfStr.split('\n').filter(n => n !== '');
// console.log(lessonInfo);
// 空课程跳过
if (lessonInfo.length < 4) {
continue;
}
let courseName = lessonInfo[0];
let teacherName = lessonInfo[1];
// TODO 多周问题
let weeksStr = lessonInfo[2];
let location = lessonInfo[3];
let description = courseName + ' ' + teacherName + ' ';
let weekDay = j;
// let interval = parseInt(informations[2]);
// TODO 判断夏令时冬令时
let isSummerTime = false;
let startTime = [];
let endTime = [];
if (isSummerTime) {
startTime = class_start_summer[2 * i - 1];
endTime = class_start_summer[2 * i];
} else {
startTime = class_start_winter[2 * i - 1];
endTime = class_start_winter[2 * i];
}
// 按照多周划分后迭代
for (
let weekStrs = weekRegexp.exec(weeksStr);
weekStrs !== null;
weekStrs = weekRegexp.exec(weeksStr)
) {
// 分多周 "2-5,7-9,11-13(周)" 和单周 "13(单周)"
let week = [];
if (weekStrs[1] !== undefined) {
week = weekStrs[1].split('-'); // 多周
} else {
week = [weekStrs[2], weekStrs[2]]; // 单周
}
let startWeek = parseInt(week[0]);
let endWeek = parseInt(week[1]);
let startDate = new Date(termStartDate),
endDate = new Date(termStartDate),
untilDate = new Date(termStartDate);
// 课程时间
startDate.setDate(
startDate.getDate() + (startWeek - 1) * 7 + weekDay - 1
);
startDate.setHours(startTime[0], startTime[1], 0, 0);
endDate.setDate(
endDate.getDate() + (startWeek - 1) * 7 + weekDay - 1
);
endDate.setHours(endTime[0], endTime[1]);
// 课程截止
untilDate.setDate(
untilDate.getDate() + (endWeek - 1) * 7 + weekDay
);
untilDate.setHours(endTime[0], endTime[1]);
calendarEvents.push(
[
'BEGIN:VEVENT',
'DTSTAMP:' + toString(nowDate),
'UID:' +
'COURSE' +
calendarEvents.length +
'@' +
'https://1000delta.top',
'SUMMARY:' + courseName,
'DTSTART:' + toString(startDate),
'DTEND:' + toString(endDate),
'RRULE:FREQ=WEEKLY;UNTIL=' + toString(untilDate),
'LOCATION:' + location,
'DESCRIPTION:' + description,
'END:VEVENT'
].join(SEPARATOR)
);
}
}
}
}
// console.log(calendarEvents);
if (calendarEvents.length < 1) {
alert('课表里没课哦~😆');
return;
}
let filename = year + '学年第' + term + '学期课表.ics';
let calendar =
calendar_start +
SEPARATOR +
calendarEvents.join(SEPARATOR) +
calendar_end;
//保存到文件
let blob;
if (navigator.userAgent.indexOf('MSIE 10') === -1) {
// chrome or firefox
blob = new Blob([calendar]);
} else {
// ie
let bb = new BlobBuilder();
bb.append(calendar);
blob = bb.getBlob(
'text/x-vCalendar;charset=' + document.characterSet
);
}
saveAs(blob, filename);
});
} catch (error) {
console.log(error);
alert('出错啦!/(ㄒoㄒ)/~~');
}
};
}
const EXAM_ORDER = 0,
EXAM_SESSION = 1,
EXAM_NAME = 2,
EXAM_TYPE = 3,
EXAM_MODE = 4,
EXAM_TIME = 5,
EXAM_LOCATION = 6,
EXAM_SEAT = 7,
EXAM_ID = 8,
EXAM_NOTE = 9;
function GenerateExamICS() {
window.console.log('考试安排生成准备');
let buttonPre = document.querySelector('input#btn_back');
buttonPre.outerHTML +=
' <input type="button" onclick="saveics()" class="button" name="saveics" value="保存ics">';
let term = /学年学期【(.*)】/.exec(buttonPre.parentElement ? buttonPre.parentElement.innerText : "");
window.saveics = function () {
// 格式编辑
let calendar_start = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:Exam-to-iCalendar'
].join(SEPARATOR);
let calendar_end = SEPARATOR + 'END:VCALENDAR' + SEPARATOR;
let calendarEvents = [];
// 数据获取
let examTitle;
let rows = document.querySelector('table#dataList').rows;
for (let exam of rows) {
if (exam.cells[EXAM_ORDER].innerText === '序号') {
examTitle = exam;
continue;
}
let timeTexts = exam.cells[EXAM_TIME].innerText.split(/ |~/);
let startDate = new Date(timeTexts[0] + ' ' + timeTexts[1]);
let endDate = new Date(timeTexts[0] + ' ' + timeTexts[2]);
let description = `
${examTitle.cells[EXAM_SESSION].innerText}: ${exam.cells[EXAM_SESSION].innerText}
${examTitle.cells[EXAM_NAME].innerText}: ${exam.cells[EXAM_NAME].innerText}
${examTitle.cells[EXAM_TYPE].innerText}: ${exam.cells[EXAM_TYPE].innerText}
${examTitle.cells[EXAM_MODE].innerText}: ${exam.cells[EXAM_MODE].innerText}
${examTitle.cells[EXAM_SEAT].innerText}: ${exam.cells[EXAM_SEAT].innerText}
${examTitle.cells[EXAM_ID].innerText}: ${exam.cells[EXAM_ID].innerText}
${examTitle.cells[EXAM_NOTE].innerText}: ${exam.cells[EXAM_NOTE].innerText}
`;
calendarEvents.push(
[
'BEGIN:VEVENT',
'DTSTAMP:' + toString(nowDate),
'UID:' +
'EXAM-' +
term +
calendarEvents.length +
'@' +
'https://1000delta.top',
'SUMMARY:' + exam.cells[EXAM_NAME].innerText,
'DTSTART:' + toString(startDate),
'DTEND:' + toString(endDate),
'LOCATION:' + exam.cells[EXAM_LOCATION].innerText,
'DESCRIPTION:' + description,
'END:VEVENT'
].join(SEPARATOR)
);
}
if (calendarEvents.length < 1) {
window.alert('当前没有考试安排哦~');
return;
}
let filename = term ? term[1] : "" + '考试安排.ics';
let calendar =
calendar_start +
SEPARATOR +
calendarEvents.join(SEPARATOR) +
calendar_end;
//保存到文件
let blob;
if (navigator.userAgent.indexOf('MSIE 10') === -1) {
// chrome or firefox
blob = new Blob([calendar]);
} else {
// ie
let bb = new BlobBuilder();
bb.append(calendar);
blob = bb.getBlob('text/x-vCalendar;charset=' + document.characterSet);
}
saveAs(blob, filename);
}
}
let queryTable = {
'http://jwxt.xtu.edu.cn/jsxsd/xskb/xskb_list.do': GenerateCourseICS,
'http://jwxt.xtu.edu.cn/jsxsd/xsks/xsksap_list': GenerateExamICS
};
// 窗口加载完成调用函数
window.onload = function () {
try {
queryTable[document.URL]();
} catch (error) {
if (error.message !== window.error_message) {
window.error_message = error.message;
console.log(error);
}
return;
}
};
// 保证页面刷新后重新执行
// setInterval(onload, 50);
})();