// ==UserScript==
// @name 东南大学课表样式调整
// @namespace http://tampermonkey.net/
// @version 1.0.4
// @description 修改了原东南大学办事大厅“我的课表”的一些愚蠢设计。注意,目前本脚本仅在tampermonkey上测试过,且仅针对本人课表做了优化,保证打印时不会超出页面。如果在您的浏览器中出现周课表横屏打印页面超出,请适当调整代码第383行function printTitle()内的margin-top属性(该属性控制标题离页面顶部的距离),或者,选择竖屏格式打印。如果你有更好的解决方法,欢迎联系我!(联系方式见文档底部)
// @author wqy
// @license MIT
// @match https://ehall.seu.edu.cn/jwapp/sys/wdkb/*default/index.do?*
// @run-at document-loaded
// ==/UserScript==
// 通过立即执行函数在页面载入时运行
(() => {
'use strict';
// 状态变量,当状态变量为true时说明页面需要被改写
let counter = false;
// 定义一个数组,用来存放每节课的上课时间
const courseTime = ["08:00-08:45","08:50-09:35","09:50-10:35","10:40-11:25","11:30-12:15","14:00-14:45","14:50-15:35","15:50-16:35","16:40-17:25","17:30-18:15","19:00-19:45","19:50-20:35","20:40-21:25"]
// 定义一个数组,用于存储星期几
const days = [];
// 初始化星期数组
for (let i = 1; i <= 7; i++) {
days.push("星期" + i);
}
// 从页面获取原课表信息,当该部分长度不为0时说明页面已加载完毕,是"启动监听器"的一部分,在主函数中被用于获取页面课表
const tables = document.getElementsByClassName("wut_table");
// "启动监听器",递归调用,检测页面启动后,调用"变化监听器",并运行主脚本
function loadObserver() {
// 从页面获取所有课程的信息
if(tables.length===0){
console.log("课表正在加载...")
setTimeout(loadObserver, 500);
}
else{
// 是表格的容器,当页面切换时,容器内表格被删除,并添加新的表格
const activator = document.getElementById("kcb_container")
// 监测表格内容变化
observer.observe(activator, { childList: true, subtree: true });
// 开始脚本运行
mainScript();
}
}
// "变化监听器",当表格容器内容变化时运行主脚本
const observer = new MutationObserver(() => {
console.log("表格内容已发生变化");
// 因为脚本本身也会导致表格容器内容变化,所以模拟了D触发器,使脚本间隔启动
console.log("当前状态为",counter);
// 当状态为true时启动脚本,并切换状态
if(counter===true){
mainScript();
counter = false;
}
// 状态为false时不启动脚本,切换状态
else{
counter = true;
}
});
// 主脚本
function mainScript() {
console.log("脚本已启动!")
// 转换页面样式,使打印时表格居中
printCenter()
// 检查当前页面是否为学期课表
const check = checkWeekOrSemester();
// 从页面获取所有课程的信息
const courses = getCourses()
// 将获取的课程信息存入课程表
const courseTable = setCourseTable(courses)
// 打印课表详情(用于调试)
console.log(courseTable);
// 获取页面上原来的表格(本来这样写可能会出问题,但因为主函数的每次被调用都在页面表格生成后,且速度快于脚本执行速度,所以并没有什么事)
const table = tables[0];
table.style.width = "90%";
// 清空页面原来的课表信息,并修改表头,添加上课时间
let rows = tableInitialization(table,check)
// 重新插入课表信息
tableInsert(rows,courseTable)
// 修改打印周课表标题
if(check === false) {
printTitle();
}
if(check === true) {
// 删除原来插入的课表标题
deleteTitle();
}
}
// 从页面获取所有课程的信息
function getCourses() {
const courses = document.getElementsByClassName("mtt_item_kcmc");
console.log("已获取",courses.length,"节课")
return courses;
}
// 将获取的课程信息存入课程表
function setCourseTable(courses) {
// 创建一个空的json对象,用于存储课程表信息
let courseTable = {};
// 初始化课程表信息
for (let i = 0; i < days.length; i++) {
courseTable[days[i]] = [];
}
for (let i = 0; i < courses.length; i++) {
console.log("正在读取第",i+1,"节课的详细信息")
const course = courses[i].childNodes;
// 临时存储课程详细信息
let detail = {}
// 获取课程背景颜色
const color = courses[i].parentNode.style.backgroundColor
detail.color = color;
// 获取课程名称
detail.name = course[0].data;
console.log("课程名称:",detail.name)
// 获取授课老师(可能为空)
if(course[1].innerText !== ""){
detail.teacher = course[1].innerText;
console.log("授课老师:",detail.teacher);
}
if(course[1].innerText === ""){
console.log("未检测到授课老师信息");
}
const others = course[3].innerText.split(",");
const weeks = others[0].split("-");
// 判断是单一周还是多周
if (weeks.length === 1) {
// 单一周
detail.startWeek = parseInt(weeks[0]);
detail.endWeek = detail.startWeek;
} else {
// 多周
detail.startWeek = parseInt(weeks[0]);
detail.endWeek = parseInt(weeks[1].replace(/\(单\)|\(双\)/g, '')); // 去除(单)或(双)
// 检查是否存在单双周上课情况
let check = weeks[1].endsWith("(双)") || weeks[1].endsWith("(单)");
if (check === true) {
if (weeks[1].endsWith("(双)") === true) {
detail.oddOrEven = "even";
} else {
detail.oddOrEven = "odd";
}
}
}
// 获取上课节次信息
const time = others[2].split("-");
detail.startTime = parseInt(time[0]);
detail.endTime = parseInt(time[1]);
console.log("上课时间:第",detail.startTime,"节 - 第",detail.endTime,"节")
// 获取上课地点信息(可能为空)
if(others[3] !== undefined){
detail.location = others[3];
console.log("上课地点:",detail.location)
}
if(others[3] === undefined){
console.log("未检测到上课地点信息")
}
// 获取课程群信息(可能为undefined)
if(others[4] !== undefined){
// 鉴于偶尔群号会有额外备注,不一定是数字,改用split方法
// const num = others[4].match(/\d+$/);
// detail.group = num[0];
// 把获取的字符串按冒号分开,冒号后的部分就是群号,备注等信息,但考虑到备注中仍有可能含冒号
const arr = others[4].split(":");
// 所以先分隔,删除数组第一个元素“教学班群号:”
arr.shift();
// 再重新组合数组,换用中文分号,节省空间
detail.group = arr.join(":");
console.log("教学班群号:",detail.group)
}
if(others[4] === undefined){
console.log("未检测到课程群信息")
}
// 获取上课在星期几,并存入课程表
const day = others[1];
courseTable[day].push(detail)
}
return courseTable;
}
// 清空页面原来的课表信息,如果是周课表,修改表头,添加上课时间
function tableInitialization(table,check) {
let rows = table.rows;
// 清空页面原来的课表信息
for (let i = 1; i < rows.length; i++) {
let cells = rows[i].cells;
if (i % 5 === 1) {
for (let j = 2; j < cells.length; j++) {
rows[i].deleteCell(j);
j--;
}
}
else {
for (let j = 1; j < cells.length; j++) {
rows[i].deleteCell(j);
j--;
}
}
}
console.log("已清空原课表信息")
if(check === false) addTime(rows);
return rows;
}
// 修改表头,添加上课时间
function addTime(rows) {
// 修改表头,添加条目,显示上课的具体时间
rows[0].cells[0].colSpan = 3;
// 修改表头,改变间距
for (let i = 1; i < rows[0].cells.length; i++) {
let cell = rows[0].cells[i];
cell.style.width = "12%";
}
// 逐行添加上课的具体时间,并调整样式,使其符合整体风格
for (let i = 1; i < rows.length; i++) {
let cell = document.createElement("td");
cell.innerHTML = courseTime[i-1];
cell.classList.add("mtt_bgcolor_grey");
// 规定宽度,防止打印变形
cell.style.width = "10%";
rows[i].appendChild(cell);
}
console.log("已修改表头,添加条目,显示上课的具体时间")
}
// 逐行插入表格
function tableInsert(rows,courseTable) {
for (let k = 1; k <= 13; k++){
let rowIndex = k;
let row = rows[rowIndex];
coursesInsert(courseTable,rowIndex,row)
}
}
// 插入每一行的课程
function coursesInsert(courseTable,rowIndex,row) {
// 挨个处理这一行每一天的课程
for (let i = 0; i < days.length; i++) {
const day = days[i];
const cell = cellInsert(courseTable,rowIndex,day)
if(cell !== null) row.appendChild(cell);
}
}
// 插入每一节课程
function cellInsert(courseTable,rowIndex,day) {
// 获取课程表该日所有课程
let courses = courseTable[day];
// 遍历该日每节课
for (let j = 0; j < courses.length; j++) {
let course = courses[j];
// 若课程在该节开始,插入课程,返回插入单元格
if (course.startTime == rowIndex) {
// 新建一个单元格并初始化单元格
let cell = document.createElement("td");
cell.rowSpan = course.endTime - course.startTime + 1;
cell.classList.add("mtt_arrange_item")
// 插入一个莫名其妙的空div(为了与原页面结构保持一致)
let div = document.createElement("div");
div.classList.add("mtt_item_tzkcicon")
cell.appendChild(div);
// 处理cell单元格,添加课程信息
cell = courseInsert(cell,course);
return cell;
}
// 若课程覆盖该节,返回null
if (course.startTime < rowIndex && course.endTime >= rowIndex) {
return null;
}
}
// 若遍历所有课程后仍未返回,说明该节
if (!status) {
const cell = document.createElement("td");
cell.classList.add("mtt_arrange_item")
return cell;
}
}
// 插入指定课程
function courseInsert(cell,course) {
// 新建一个空div并初始化(为原页面格式),用于存储课程名称(老师)和其他信息(包括周数,地点,课程群)
let basic = document.createElement("div");
basic.classList.add("mtt_item_kcmc");
// 设置背景颜色
basic.style.backgroundColor = course.color;
// 设置格内间距
basic.style.padding = "4px";
// 设置边框弧度
basic.style.borderRadius = "8px";
// 设置高度为父元素的100%
basic.style.height = "100%";
// 设置为flex布局(因为只会flex布局)
basic.style.display = "flex";
// 设置为flex布局下的按列排列
basic.style.flexDirection = "column";
// 设置垂直居中
basic.style.justifyContent = "center"
// 新建准备添加的字符串,默认添加课程名称
let str = course.name
// 添加老师信息(如果有的话)
if(course.teacher !== undefined){
let teacher = '<br/>' + course.teacher;
str += teacher;
}
basic.innerHTML = str;
// 处理basic块,添加其他信息
basic = othersInsert(basic,course)
// 将处理后的basic块插入表格单元格
cell.appendChild(basic);
return cell;
}
// 插入指定课程的其他信息
function othersInsert(basic,course) {
// 新建一个空div并初始化(为原页面格式),用于存储其他信息(包括周数,地点,课程群)
let others = document.createElement("div");
others.classList.add("mtt_item_room");
// 新建准备添加的字符串,默认为空
let str = [];
// 检查当前状态(学期课表/周课表)
const check = checkWeekOrSemester()
// 添加周数信息(如果当前展示的是学期课表)
if(course.startWeek !== false && course.endWeek !== false && check === true){
if(course.oddOrEven !== undefined) {
if(course.oddOrEven ==="odd") str.push(course.startWeek + "-" + course.endWeek + "周(单)");
if(course.oddOrEven ==="even") str.push(course.startWeek + "-" + course.endWeek + "周(双)");
}
else str.push(course.startWeek + "-" + course.endWeek + "周");
}
// 添加地点信息(如果有的话)
if(course.location !== undefined){
str.push(course.location);
}
// 添加课程群信息(如果有的话)
if(course.group !== undefined && check === true){
str.push("教学班班号:" + course.group)
}
others.innerText = str.join(",");
basic.appendChild(others);
return basic;
}
// 检查当前展示的是否为学期课表
function checkWeekOrSemester() {
const check = document.getElementsByClassName("bh-color-primary");
// check[2]为显示学期课表按钮,若显示学期课表按钮不显示,则返回true
if(check[2].style.display === "none") return true;
// check[3]为显示周课表按钮,若显示周课表按钮不展示,则返回false
if(check[3].style.display === "none") return false;
// 否则说明出问题了
else console.log(check);
}
// 转换页面样式,使打印时表格居中
function printCenter() {
const containers = document.getElementsByClassName("wut_container");
const container = containers[0];
container.style.display = "flex";
container.style.alignItems = "center";
container.style.justifyContent = "center";
container.style.width = "100%";
const printArea = document.getElementById("print_area");
printArea.style.display = "flex";
printArea.style.alignItems = "center";
printArea.style.justifyContent = "center";
printArea.style.flexDirection = "column";
}
// 修改打印周课表标题
function printTitle() {
// 隐藏原打印标题
const kb_title = document.getElementById("kb_title");
kb_title.style.display = "none";
// 获取title
const title = document.getElementById("dqxnxq2");
// 获取周数
const week = document.getElementById("zkbzc");
// 编辑新的课表标题
const newTitle = '<div id="new-title" style="text-align: center;font-size: 20px; margin-top: 15px; margin-bottom: 4px" >' + title.innerText + " 第" + week.innerText + "周</div>";
// 删除原来插入的课表标题
deleteTitle();
// 把新课表标题插入到原标题前
kb_title.insertAdjacentHTML('beforebegin', newTitle)
console.log("已添加标题")
}
// 删除原来插入的课表标题
function deleteTitle() {
const title = document.getElementById("new-title");
if(title !== null) title.parentNode.removeChild(title);
}
// 启动监听器
loadObserver();
})()