您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
南信大课表导出为 iCal 格式
- // ==UserScript==
- // @name NUIST TimeTable Export
- // @version 0.1
- // @description 南信大课表导出为 iCal 格式
- // @author 凌莞
- // @license MIT
- // @match http://bkxk.nuist.edu.cn/*/student/mykebiaoall1.aspx
- // @icon https://www.google.com/s2/favicons?sz=64&domain=nuist.edu.cn
- // @grant none
- // @namespace https://gf.qytechs.cn/users/874549
- // ==/UserScript==
- ((neko, nya) => {
- const INFO_SEPERATOR = '◇';
- const TIMETABLE = [
- // [begin, end]
- ['080000', '094000'],
- ['101000', '115000'],
- ['134500', '152500'],
- ['155500', '173500'],
- ['184500', '202500'],
- ];
- const WEEKS = 16;
- // 开学第一周的星期一
- const FIRST_SCHOOL_DAY = new Date('2022-02-21');
- const ONE_DAY = 24 * 60 * 60 * 1000;
- const lastOf = (iterable) => iterable[iterable.length - 1];
- const $ = neko
- Date.prototype.format = function (fmt) {
- var o = {
- 'M+': this.getMonth() + 1, //月份
- 'd+': this.getDate(), //日
- 'h+': this.getHours(), //小时
- 'm+': this.getMinutes(), //分
- 's+': this.getSeconds(), //秒
- 'q+': Math.floor((this.getMonth() + 3) / 3), //季度
- S: this.getMilliseconds(), //毫秒
- }
- if (/(y+)/.test(fmt)) {
- fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length))
- }
- for (var k in o) {
- if (new RegExp('(' + k + ')').test(fmt)) {
- fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length))
- }
- }
- return fmt
- }
- /**
- * 将课表上的课程文本转换成对象
- * @param {string} lesson 课表上的课程文本,暂不支持黑色菱形(多节课)
- *
- * 如:数字图像处理◇范春年(1-16)(软工合作20(2)班;软工合作20(1)班;)◇(西苑)揽江楼N505◇多媒体教室◇{12节}
- */
- function parselesson(lesson) {
- if (!lesson.trim()) return null
- const info = lesson.split(INFO_SEPERATOR);
- const name = info[0].trim();
- let place = info.length === 5 ? info[2].trim() : '';
- if (['(西苑)', '(东苑)', '(中苑)'].some(it => place.startsWith(it))) {
- place = place.substring(4);
- }
- const infoPart2 = parseInfo(info[1].trim());
- const teacher = infoPart2[0];
- // 一起上课的班级
- const coClass = infoPart2.length === 3 ? infoPart2[2] : '';
- let weeks = [1, WEEKS];
- if (infoPart2.length === 3) {
- const weeksExec = /(\d+)-(\d+)/.exec(infoPart2[1]);
- weeks = [parseInt(weeksExec[1]), parseInt(weeksExec[2])];
- }
- let weekSpec = 'all';
- lastOf(info).startsWith('单周') && (weekSpec = 'odd');
- lastOf(info).startsWith('双周') && (weekSpec = 'even');
- return { name, place, teacher, coClass, weeks, weekSpec, raw: lesson };
- }
- /**
- * 解析课程文本的第二串
- * @param {string} info 第二串信息,大概包含老师姓名,周次,班级
- *
- * 如:范春年(1-16)(软工合作20(2)班;软工合作20(1)班;)
- */
- function parseInfo(info) {
- let lastBrackletLength = 0;
- const infoArray = [''];
- for (const i of info) {
- if (i === '(') {
- lastBrackletLength++;
- if (lastBrackletLength === 1)
- lastOf(infoArray) !== '' && infoArray.push('');
- else
- infoArray[infoArray.length - 1] += i;
- }
- else if (i === ')') {
- lastBrackletLength--;
- if (lastBrackletLength === 0)
- lastOf(infoArray) !== '' && infoArray.push('');
- else
- infoArray[infoArray.length - 1] += i;
- }
- else {
- infoArray[infoArray.length - 1] += i;
- }
- }
- if (lastOf(infoArray) === '')
- infoArray.pop();
- return infoArray;
- }
- function getSessionTimeWrapper(beginEnd) {
- return (weekday, session, isEvenWeek, firstWeek) => {
- weekday += firstWeek - 1;
- isEvenWeek && (weekday += 7);
- const firstLessonDay = new Date(FIRST_SCHOOL_DAY.getTime() + weekday * ONE_DAY);
- return `${firstLessonDay.format('yyyyMMdd')}T${TIMETABLE[session][beginEnd]}`;
- }
- }
- const getSessionBeginTime = getSessionTimeWrapper(0);
- const getSessionEndTime = getSessionTimeWrapper(1);
- /**
- * 根据网页获取课程列表
- */
- function getLessonInfos() {
- const $rows = $('table#TABLE1>tbody>tr');
- const weekdays = [];
- for (let i = 1; i < 7; i++) {
- const $row = $rows.eq(i);
- const sessions = [];
- for (let j = 1; j < 6; j++) {
- sessions.push(parselesson($row.children().eq(j).text()));
- }
- weekdays.push(sessions);
- }
- return weekdays;
- }
- function getPageInfo() {
- const schoolYear = $('select#DropDownList1').val()
- const semester = $('select#DropDownList2').val()
- return `${schoolYear} 学年第 ${semester} 学期课表`
- }
- function convertTimeTableToRfc5545(title, timeTable) {
- let icsData = `BEGIN:VCALENDAR
- PRODID:-//Clansty//NUIST TimeTable Export 1.0//EN
- VERSION:2.0
- CALSCALE:GREGORIAN
- X-WR-CALNAME:${title}
- X-WR-TIMEZONE:Asia/Shanghai
- BEGIN:VTIMEZONE
- TZID:Asia/Shanghai
- X-LIC-LOCATION:Asia/Shanghai
- BEGIN:STANDARD
- TZOFFSETFROM:+0800
- TZOFFSETTO:+0800
- TZNAME:CST
- DTSTART:19700101T000000
- END:STANDARD
- END:VTIMEZONE`
- let idCount = 1000;
- const id = () => ++idCount;
- /**
- * 把课程写入 ics
- * @param {object} lesson 课程对象
- * @param {number} weekday 星期几
- * @param {number} session 第几节
- */
- const writeLesson = (lesson, weekday, session) => {
- if (!lesson) return;
- const WEEKDAYS = ['MO', 'TU', 'WE', 'TH', 'FR'];
- const { name, place, teacher, coClass, weeks, weekSpec, raw } = lesson;
- const lessonWeeks = weeks[1] - weeks[0] + 1;
- icsData += `
- BEGIN:VEVENT
- DTSTART;TZID=Asia/Shanghai:${getSessionBeginTime(weekday, session, weekSpec === 'even', weeks[0])}
- DTEND;TZID=Asia/Shanghai:${getSessionEndTime(weekday, session, weekSpec === 'even', weeks[0])}
- DTSTAMP:${new Date().format('yyyyMMddThhmmss')}
- UID:${id()}@clansty.com
- SUMMARY:${name}
- DESCRIPTION:${teacher}\\n${coClass}\\n\\n${raw}
- LOCATION:${place}
- RRULE:FREQ=WEEKLY;WKST=MO;INTERVAL=${weekSpec === 'all' ? 1 : 2};BYDAY=${WEEKDAYS[weekday]
- };COUNT=${Math.floor(weekSpec === 'all' ? lessonWeeks : lessonWeeks / 2)}
- END:VEVENT`
- }
- // 节次
- for (let session = 0; session < timeTable.length; session++) {
- // 星期几
- for (let weekday = 0; weekday < timeTable[session].length; weekday++) {
- const lesson = timeTable[session][weekday];
- lesson && writeLesson(lesson, weekday, session);
- }
- }
- icsData += '\nEND:VCALENDAR';
- return icsData;
- }
- function downloadText(content, filename) {
- const blob = new Blob([content]);
- const url = URL.createObjectURL(blob);
- const link = document.createElement('a');
- link.href = url;
- link.download = filename;
- link.click();
- }
- function doGetIcs() {
- const title = getPageInfo();
- const icsContent = convertTimeTableToRfc5545(title, getLessonInfos());
- downloadText(icsContent, `${title}-${new Date().format('yyyyMMddhhmmss')}.ics`);
- }
- function injectExportButton() {
- const btn = document.createElement('input');
- btn.type = 'button';
- btn.value = '导出为 iCal';
- btn.onclick = doGetIcs;
- $('input#Button1').after(btn);
- }
- injectExportButton();
- })(jQuery)
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址