Better NXU
// ==UserScript==
// @name Better NXU
// @namespace https://thisish.com/
// @version 0.3.0.2
// @description 这是一个增强 NXU 网站使用体验的JavaScript脚本.
// @author H
// @run-at document-idle
// @storageName h.nxu
// @match *://webvpn.nxu.edu.cn*
// @match *://jsfzyjxzlxt.nxu.edu.cn*
// @match *://jwgl.nxu.edu.cn*
// @match *://202.201.128.234*
// @match *://tuanwei.nxu.edu.cn*
// @match *://ids.nxu.edu.cn*
// @grant unsafewindow
// @grant GM_info
// @grant GM_log
// @grant CAT_userConfig
// @grant GM_addStyle
// @grant GM_getResourceText
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addElement
// @grant GM_setClipboard
// @grant GM_xmlhttpRequest
// @grant window.close
// @require https://scriptcat.org/lib/1405/^1.0.6/h.notification.js
// @require https://unpkg.com/tesseract.js@5.1.1/dist/tesseract.min.js
// @require https://unpkg.com/vue@3/dist/vue.global.js
// @resource svg-logo https://cdn.bootcdn.net/ajax/libs/font-awesome/6.2.1/css/all.min.css
// @resource tesseract https://unpkg.com/tesseract.js@5.1.1/dist/tesseract.min.js
// @resource vant-css https://unpkg.com/vant@4/lib/index.css
// @resource vue-js https://unpkg.com/vue@3/dist/vue.global.js
// @resource vant-js https://unpkg.com/vant@4.8.0/lib/vant.min.js
// @connect webvpn.nxu.edu.cn
// ==/UserScript==
// // @require https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js
/* ==UserConfig==
WebVPN:
username:
title: 账号
description: 连接校园网的账号
type: text
default: null
password:
title: 密码
description: 连接校园网的密码
type: text
default: null
password: true
autoLogin:
title: 自动登录WebVPN
description: 是否自动登录WebVPN
type: checkbox
default: false
autoClose:
title: 自动关闭错误网站
description: 是否自动关闭显示错误的网站
type: checkbox
default: false
courseGrab:
title: 抢课备用列表
description: 是否添加抢课备用列表
type: checkbox
default: false
customCard:
title: 在主页需要添加的卡片
description: 这里可以选择在主页增加的自定义卡片
type: mult-select
default: ['教务管理','学工系统','信息门户','中国知网', '万方数据', 'H小工具','大先生']
values: ['教务管理','学工系统','信息门户','中国知网', '万方数据', 'H小工具','大先生']
qualityJson:
title: 评教系统自定义配置(未实现)
description: 这里可以配置评教系统的自定义设置,请严格按照以下要求:1. 每一条规则均用[]表示,每条规则之间用英文逗号,隔开。2. 内有三个参数,每个参数之间用英文逗号,隔开。3. 参数1为一个数字,表示第几列;参数2为一个字符串,需用英文单引号'引用,表示这一列匹配的内容是什么;参数3为一个数字,1表示完全同意,2表示同意,以此类推。 示例:[[0, '“四史”教育', 2], [1, 'XX学院', 1]]
type: textarea
default: []
Jwgl:
username:
title: 账号
description: 登录教务系统的账号(一般同校园网)
type: text
default: null
password:
title: 密码
description: 登录教务系统的密码(一般同校园网)
type: text
default: null
password: true
autoLogin:
title: 自动登录教务系统
description: 是否自动登录教务系统(抢课时可解放双手)
type: checkbox
default: false
courseBeautify:
title: 课表美化
description: 是否自动美化课表
type: checkbox
default: false
customMenu:
title: 在菜单需要添加的条目
description: 这里可以选择在主页左侧边栏增加的自定义条目
type: mult-select
default: ['全部学期成绩']
values: ['全部学期成绩']
TuanWei:
autoDownload:
title: 自动下载附件(未实现)
description: 是否自动填写二维码并下载附件
type: checkbox
default: false
autoDownloadClose:
title: 自动关闭下载页面(未实现)
description: 是否自动下载后自动关闭页面
type: checkbox
default: false
==/UserConfig== */
(async function() {
'use strict';
// ==Basic==
function Basic() {
// 添加Notification组件
// 添加组件
addToast();
//createToast("success", "测试消息", 0);
// 添加css样式
GM_addStyle(ToastCss);
GM_addStyle(GM_getResourceText("svg-logo").replace(/\.\.\/webfonts/g, "https://cdn.bootcdn.net/ajax/libs/font-awesome/6.2.1/webfonts"));
//绑定Toast事件
unsafeWindow.createToast = createToast;
unsafeWindow.removeToast = removeToast;
// 绑定事件
unsafeWindow.CAT_userConfig = CAT_userConfig;
// UI
unsafeWindow.Vue = Vue;
GM_addStyle(GM_getResourceText("vant-css"));
unsafeWindow.eval(GM_getResourceText("vant-js"));
unsafeWindow.vant = vant;
}
// /==Basic==
// ==Constant==
const Info = GM_info;
const Url = window.location.href;
const Host = window.location.host;
const Origin = window.location.origin;
const Path = window.location.pathname;
const LoadMessage = { "loading tesseract core": "核心加载", "initializing tesseract": "初始化", "loading language traineddata": "加载语言训练数据", "initializing api": "初始化接口", "recognizing text": "识别验证码" };
const ConfigVersion = 3;
// /==Constant==
// ==Function==
function GetVerificationCode(web) {
var url = "";
switch (web) {
case "WebVPN":
url = "https://webvpn.nxu.edu.cn/https/77726476706e69737468656265737421f9f352d229287d1e7b0c9ce29b5b/authserver/captcha.html?vpn-1&ts=225";
break;
case "Jwgl":
url = "captcha/image.action";
break;
case "TuanWei":
url = "https://tuanwei.nxu.edu.cn/system/resource/js/filedownload/createimage.jsp";
break;
default:
return;
}
MyConsole(url)
return new Promise(async function (resolve, reject) {
Tesseract.recognize(
url,
'eng',
{ logger: m => LoadMessage[m.status] ? (MyConsole(LoadMessage[m.status])) : (null) }
).then(({ data: { text } }) => {
MyConsole(text.replace(/\s+/g, ''));
resolve(text.replace(/\s+/g, ''));
})
});
}
function MyConsole(msg) {
if (typeof (msg) != 'object') {
// if (/\n/.test(msg)) {
// console.log("======== Better NXU ========\n" + msg + "\n======== Better NXU ========");
// } else {
console.log(
'%c %s %c %s',
'border-radius: 5px;padding: 3px 4px;color: white;background-color: #3a8bff;margin-bottom: 0.5em',
'Better NXU',
'margin-left: 0.6em;font-size:1.2em',
'\n' + msg,
);
// }
} else {
console.log(
'%c %s %c %s',
'border-radius: 5px;padding: 3px 4px;color: white;background-color: #3a8bff;margin-bottom: 0.5em',
'Better NXU',
'margin-left: 0.6em;',
'\n下面是一个object对象',
);
console.log(msg);
}
}
function CheckUsernameAndSecret(web) {
const username = GM_getValue(web + ".username", false);
const password = GM_getValue(web + ".password", false);
if (!username || !password) {
MyConsole("账号密码未配置\n请前往配置相关信息");
createToast("error", `
<p style="margin-bottom:0.5em;margin-top: 0">账号密码未配置<br>请前往配置相关信息</p>
<a href="javascript:void(0)" onclick="CAT_userConfig()" style="font-weight:bold;font-size:small">> 前往配置 <</a>
`);
return false;
}
return true;
}
function GetQuery(msg) {
// 获取当前页面的 URL
let urlString = window.location.href;
// 创建 URL 对象
let url = new URL(urlString);
// 获取查询参数
let searchParams = new URLSearchParams(url.search);
let result = searchParams.get(msg);
return result;
}
// 随机数
function Random(min, max) {
return parseInt(Math.random() * (max - min + 1) + min, 10);
}
// 等待执行
function WaitTime(min, max = 0, log = true, msg = "无") {
var waitmsg, waittime, line;
if (max == 0) {
waittime = min;
waitmsg = `====================\n等待了:${(waittime / 1000)} 秒\n备注:${msg}\n====================`;
} else {
waittime = Random(min, max);
waitmsg = `====================\n随机等待了:${(waittime / 1000)} 秒\n备注:${msg}\n====================`;
}
return new Promise(function (resolve, reject) {
setTimeout(function () {
if (log) {
MyConsole(waitmsg.replace(/ /g, ""));
}
resolve();
}, waittime);
});
}
function CloseWin() {
try {
window.opener = window;
var win = window.open("", "_self");
win.close();
top.close();
} catch (e) {
}
}
// /==Function==
MyConsole(`开始运行`);
MyConsole(Info);
// MyConsole(`预判断...`);
// switch (Host) {
// default:
// break;
// }
// MyConsole(`预判断未匹配`);
MyConsole(`等待页面加载完毕...`);
while (document.readyState != "complete") {
await WaitTime(500);
}
MyConsole(`判断页面...`);
switch (Host) {
case 'webvpn.nxu.edu.cn':
MyConsole("欢迎使用 webvpn");
if (Url.indexOf("service=https%3A%2F%2Fwebvpn.nxu.edu.cn%2Flogin%3Fcas_login%3Dtrue") != -1) {
MyConsole("这里是 - 登录页");
unsafeWindow.eval(GM_getResourceText("tesseract"));
Tesseract = unsafeWindow.Tesseract;
Basic();
webvpnLogin();
} else if (Url == 'https://webvpn.nxu.edu.cn/' || Path == "/") {
MyConsole("这里是 - 主页");
Basic();
webvpnMain();
} else if (Url.indexOf('xsfw/sys/xggzptapp/*default/index.do') != -1) {
MyConsole("这里是 - 学工系统");
} else if (Url.indexOf('/77726476706e69737468656265737421fae04690693e7045300d8db9d6562d/') != -1 || Url.indexOf('/77726476706e69737468656265737421a2a713d27560391e2f5ad1e2ca0677/') != -1) {
MyConsole("这里是 - 教务系统");
if (Url.indexOf('index.action') != -1 || Url.indexOf('login.action') != -1) {
MyConsole("正在 - 登录页");
unsafeWindow.eval(GM_getResourceText("tesseract"));
Tesseract = unsafeWindow.Tesseract;
Basic();
jwglLogin();
} else if (Url.indexOf('cas.action') != -1 || Url.indexOf('home.action') != -1) {
MyConsole("正在 - 主页");
Basic();
jwglMain();
} else if (Url.indexOf('courseTableForStd.action') != -1 && GetQuery('method') == 'stdHome') {
MyConsole("正在 - 课表");
jwglCourseIframe();
} else if (Url.indexOf('courseTableForStd.action') != -1 && GetQuery('method') == 'courseTable') {
MyConsole("正在 - 课表");
jwglCourseBeautify();
}
} else if (Url.indexOf('/77726476706e69737468656265737421fbf952d2243e635930068cb8') != -1 || Url.indexOf('/77726476706e69737468656265737421e7e056d2243e635930068cb8') != -1) {
MyConsole("这里是 - 中国知网");
if (Url.indexOf('/xmlRead/trialRead') != -1) {
MyConsole("正在 - html阅读");
webvpnCnkiHtml();
}
} else if (Path == '/wengine-vpn/failed') {
if (document.body.innerHTML.indexOf('地址:/h/tools') != -1) {
MyConsole("正在 - 小工具");
Basic();
webvpnHTools();
} else {
MyConsole("正在 - 错误页");
errorHtml();
}
} else {
if (document.querySelector("h1").innerHTML == `404页面不存在`) {
errorHtml();
}
}
break;
case 'jsfzyjxzlxt.nxu.edu.cn':
MyConsole("欢迎使用评教系统");
if (Path == "/quality/student/evaluate/item_tasks") {
MyConsole("这里是 - 选择页");
qualityChoose();
} else if (Path == "/quality/student/evaluate/item_tasks_text") {
MyConsole("这里是 - 填写页");
qualityText();
}
break;
case 'jwgl.nxu.edu.cn':
MyConsole("欢迎使用教务系统");
if (Path == '/index.action' || Path == '/login.action') {
MyConsole("这里是 - 登录页");
Basic();
jwglLogin();
} else if (Url.indexOf('cas.action') != -1 || Url.indexOf('home.action') != -1) {
MyConsole("正在 - 主页");
Basic();
jwglMain();
} else if (Url.indexOf('courseTableForStd.action') != -1 && GetQuery('method') == 'stdHome') {
MyConsole("正在 - 课表");
jwglCourseIframe();
} else if (Url.indexOf('courseTableForStd.action') != -1 && GetQuery('method') == 'courseTable') {
MyConsole("正在 - 课表");
jwglCourseBeautify();
}
break;
case 'ids.nxu.edu.cn':
MyConsole("欢迎使用统一身份认证系统");
if (Path == "/authserver/login") {
MyConsole("这里是 - 登录页");
unsafeWindow.eval(GM_getResourceText("tesseract"));
Tesseract = unsafeWindow.Tesseract;
Basic();
webvpnLogin();
}
// case 'tuanwei.nxu.edu.cn':
// MyConsole("欢迎使用团委");
// if (Path == '/system/_content/download.jsp') {
// MyConsole("这里是 - 附件下载页");
// unsafeWindow.eval(GM_getResourceText("tesseract-webvpn").replace(/^vpn_eval\(\(function\(\)\{/, '').replace(/\}[\n\r]*\)\.toString\(\)\.slice\(12\,[\s]*\-2\)\,\"\"\)\;$/, ''));
// Tesseract = unsafeWindow.Tesseract;
// Basic();
// tuanweiDownload();
// // } else if (Path == '/info/1003/1022.htm') {
// } else {
// tuanweiDownloadBridge();
// }
// break;
default:
if (Host.indexOf('202.201.128.234') != -1) {
MyConsole("欢迎使用教务系统");
if (Path == '/index.action' || Path == '/login.action') {
MyConsole("这里是 - 登录页");
Basic();
jwglLogin();
} else if (Url.indexOf('cas.action') != -1 || Url.indexOf('home.action') != -1) {
MyConsole("正在 - 主页");
Basic();
jwglMain();
} else if (Url.indexOf('courseTableForStd.action') != -1 && GetQuery('method') == 'stdHome') {
MyConsole("正在 - 课表");
jwglCourseIframe();
} else if (Url.indexOf('courseTableForStd.action') != -1 && GetQuery('method') == 'courseTable') {
MyConsole("正在 - 课表");
jwglCourseBeautify();
}
}
return;
}
return;
async function errorHtml() {
if (GM_getValue("WebVPN.autoClose", false)) {
try {
window.opener = window;
var win = window.open("","_self");
win.close();
top.close();
} catch (e) {
}
}
}
async function webvpnLogin() {
if (!GM_getValue("WebVPN.autoLogin", false)) {
return;
}
createToast("info", `自动登录...`);
if (!CheckUsernameAndSecret("WebVPN")) {
return;
}
if (document.querySelector('span#msg.auth_error') && document.querySelector('span#msg.auth_error').innerHTML && document.querySelector('span#msg.auth_error').innerHTML == '您提供的用户名或者密码有误') {
createToast("error", `
<p style="margin-bottom:0.5em;margin-top: 0">账号密码配置错误<br>请前往配置相关信息</p>
<a href="javascript:void(0)" onclick="CAT_userConfig()" style="font-weight:bold;font-size:small">> 前往配置 <</a>
`);
return;
}
if (document.querySelector("p#cpatchaDiv") && document.querySelector("p#cpatchaDiv").innerHTML && document.querySelector("p#cpatchaDiv").innerHTML.replace(/\s/g, '') != '') {
// var verification = await GetVerificationCode("WebVPN");
// document.querySelector('input#captchaResponse').value = verification;
createToast("warning", `请手动输入验证码登录`, 0);
return;
}
const username = GM_getValue("WebVPN.username");
const password = GM_getValue("WebVPN.password");
while (document.querySelector('input#username').value != username || document.querySelector('input#password').value != password) {
document.querySelector('input#username').value = GM_getValue("WebVPN.username");
document.querySelector('input#password').value = GM_getValue("WebVPN.password");
await WaitTime(100);
}
document.querySelector('button[type=submit]').click();
}
async function jwglLogin() {
if (!GM_getValue("Jwgl.autoLogin", false)) {
return;
}
createToast("info", `自动登录...`);
if (!CheckUsernameAndSecret("Jwgl")) {
return;
}
if (document.querySelector('div#errors.message') && document.querySelector('div#errors.message').innerHTML) {
const errorText = document.querySelector('div#errors.message').innerHTML;
if (errorText == "密码错误" || errorText == "账户不存在") {
createToast("error", `
<p style="margin-bottom:0.5em;margin-top: 0">账号密码配置错误<br>请前往配置相关信息</p>
<a href="javascript:void(0)" onclick="CAT_userConfig()" style="font-weight:bold;font-size:small">> 前往配置 <</a>
`);
return;
}
}
var verification = await GetVerificationCode("Jwgl");
document.getElementsByName("loginForm.name")[0].value = GM_getValue("Jwgl.username");
document.getElementsByName("loginForm.password")[0].value = GM_getValue("Jwgl.password");
document.getElementsByName("loginForm.captcha")[0].value = verification;
document.querySelector("input#loginSubmit").click();
}
async function webvpnMain() {
while (!document.querySelector("div[title=教务管理平台]")) {
await WaitTime(500);
}
while (!document.querySelector("div[title=教务管理平台]").innerHTML) {
await WaitTime(500);
}
const firstSet = GM_getValue('firstSet', 0);
const configVersion = GM_getValue('configVersion', -1);
// createToast("success", "测试消息", 0);
//更新配置弹窗
MyConsole("检查配置信息");
GM_addElement(document.querySelector('body'), 'div', { id: 'update' });
var update_template = '', update_show = false;
if (!firstSet) {
update_template = `
<van-dialog v-model:show="show" title="Better NXU 首次配置" show-cancel-button confirmButtonText="前往配置" @confirm="goConfig" @close="closeFunc" style="--van-dialog-font-size:1.5em;--van-dialog-header-padding-top:18px">
<div style="font-size: 16px;color:var(--van-dialog-has-title-message-text-color);margin: 0.5em 0;padding: 0 2em;display: flex;flex-direction: column;justify-content: center;">
<p style="text-align: center;line-height: 22px;">
<span style="font-weight:bold;">这好像是你<span style="color:#0283EF">第一次</span>使用本插件</span><br>
我们需要一些配置信息<br>
你可以选择<span style="color:#32AE57">前往配置</span><br>
或点击<span style="color:#FF7B35">取消</span>不进行配置<br>
后续自行前往设置页面进行配置
</p>
</div>
</van-dialog>
`;
update_show = true;
} else if (configVersion != ConfigVersion) {
update_template = `
<van-dialog v-model:show="show" title="Better NXU 版本更新" show-cancel-button confirmButtonText='前往配置' @confirm="goConfig" @close="closeFunc" style="--van-dialog-font-size:1.5em;--van-dialog-header-padding-top:18px">
<div style="font-size: 16px;color:var(--van-dialog-has-title-message-text-color);margin: 0.5em 0;padding: 0 2em;display: flex;flex-direction: column;justify-content: center;">
<p style="text-align: center;line-height: 23px;">
<span style="font-weight:bold;">V ${Info.script.version}</span><br>
我们更新了一些配置信息<br>
你可以选择<span style="color:#32AE57">前往配置</span><br>
或点击<span style="color:#FF7B35">取消</span>不进行配置<br>
后续自行前往设置页面进行配置
</p>
</div>
</van-dialog>
`;
update_show = true;
}
const update = Vue.createApp({
template: update_template,
setup() {
const show = Vue.ref(false);
show.value = update_show;
const goConfig = () => {
CAT_userConfig();
}
const closeFunc = () => {
GM_setValue('firstSet', true);
GM_setValue('configVersion', ConfigVersion);
}
return { show, goConfig, closeFunc };
}
});
update.use(vant);
update.mount("#update");
// //更新弹窗
// const version = GM_getValue('version');
// if (version != Version) {
// GM_setValue('version', Version);
// createToast("info", `
// <p style="font-weight:bold;margin-bottom:0.5em">Better NXU V ${Version} 已更新!</p>
// <p style="margin-bottom:0.5em">${VersionContent}</p>
// <a href="javascript:void(0)" onclick="CAT_userConfig()" style="font-weight:bold;font-size:small">> 前往配置 <</a>
// `);
// }
// 抢课备用列表
function divCard(href, icon, title, content) {
var div = document.createElement('div');
div.innerHTML = `
<a target="_blank" href="${href}" class="block-group__item is-active">
<div class="block-group__item__logo__wrap">
${icon}
</div>
<div class="block-group__item__content">
<h2 title="${title}" class="block-group__item__name">${title}</h2>
<div title="${content}" class="block-group__item__desc">${content}</div>
</div>
</a>
`;
div.className = 'block-group__item__wrap';
return div;
}
function titleCard(title, id) {
var div = document.createElement('div');
div.innerHTML = `
<h1 class="block-group__title">${title}</h1>
<div class="block-group__content"></div>
`;
div.className = 'block-group';
div.dataset.id = id;
return div;
}
const mainDiv = document.querySelector('.portal-content__block .el-scrollbar__view');
if (GM_getValue('WebVPN.courseGrab', false)) {
mainDiv.prepend(titleCard("抢课备用网址", "classes"));
const classesDiv = document.querySelector('div[data-id=classes] div.block-group__content');
for (let i = 0; i <= 3; i++) {
classesDiv.appendChild(divCard(
`http://202.201.128.234:${8080 + i}`,
'<div class="block-group__item__logo" style="background-color: rgb(80, 135, 229);">抢</div>',
`备用${i + 1}`, "仅校园网可用")
);
}
for (let i = 0; i <= 3; i++) {
classesDiv.appendChild(divCard(
`https://webvpn.nxu.edu.cn/http-${8080 + i}/77726476706e69737468656265737421a2a713d27560391e2f5ad1e2ca0677/index.action`,
'<div class="block-group__item__logo" style="background-color: rgb(80, 135, 229);">抢</div>',
`备用${i + 5}`, "校外可用")
);
}
}
//自定义卡片
const webVPNCustomCard = GM_getValue("WebVPN.customCard", ['教务管理','学工系统','信息门户','中国知网', '万方数据', 'H小工具','大先生']);
if (webVPNCustomCard.length != 0) {
mainDiv.prepend(titleCard("自定义", "custom"));
const customDiv = document.querySelector('div[data-id=custom] div.block-group__content');
if (webVPNCustomCard.indexOf('教务管理') != -1) {
customDiv.appendChild(divCard(
`https://webvpn.nxu.edu.cn/https-443/77726476706e69737468656265737421fae04690693e7045300d8db9d6562d/cas.action`,
'<div class="block-group__item__logo" style="background-color: rgb(235, 94, 94);">教</div>',
`教务平台`, "教务管理平台")
);
}
if (webVPNCustomCard.indexOf('学工系统') != -1) {
customDiv.appendChild(divCard(
`https://webvpn.nxu.edu.cn/https/77726476706e69737468656265737421e8e4478b693e7045300d8db9d6562d/`,
'<div class="block-group__item__logo" style="background-color: #95c2fb;">学</div>',
`学工系统`, "学工平台")
);
}
if (webVPNCustomCard.indexOf('信息门户') != -1) {
customDiv.appendChild(divCard(
`https://webvpn.nxu.edu.cn/https/77726476706e69737468656265737421f5fe51d229287d1e7b0c9ce29b5b/`,
'<div class="block-group__item__logo" style="background-color: #0966b5;">信</div>',
`信息门户`, "综合信息服务门户")
);
}
if (webVPNCustomCard.indexOf('中国知网') != -1) {
customDiv.appendChild(divCard(
`https://webvpn.nxu.edu.cn/https/77726476706e69737468656265737421e7e056d2243e635930068cb8/`,
'<div class="block-group__item__logo" style="background-color: #1b66e6;">知</div>',
`中国知网`, "中国期刊全文数据库")
);
}
if (webVPNCustomCard.indexOf('万方数据') != -1) {
customDiv.appendChild(divCard(
`https://webvpn.nxu.edu.cn/https/77726476706e69737468656265737421e7e056d2303166567f068ea89941227bfcd3ca21bd0c/`,
'<div class="block-group__item__logo" style="background-color: #00417e;">万</div>',
`万方数据`, "万方数据知识服务平台")
);
}
if (webVPNCustomCard.indexOf('H小工具') != -1) {
customDiv.appendChild(divCard(
`https://webvpn.nxu.edu.cn/h/tools`,
'<div class="block-group__item__logo" style="background-color: #4472c4;">工</div>',
`小工具 - H`, "一些方便的小工具 - H")
);
}
if (webVPNCustomCard.indexOf('大先生') != -1) {
customDiv.appendChild(divCard(
`https://chat.zju.edu.cn`,
'<div class="block-group__item__logo" style="background-color: #4472c4;">工</div>',
`大先生`, "浙江大学深度融合智能体")
);
}
}
}
async function jwglMain() {
const jwglCustomMenu = GM_getValue("Jwgl.customMenu", []);
MyConsole(jwglCustomMenu)
if (jwglCustomMenu.length != 0) {
function addMenu(menu, menu_dd, href, content) {
const menuCourseManage = document.querySelectorAll('div.layui-side.layui-bg-black.layuimini-menu-left li.layui-nav-item.menu-li');
MyConsole(menuCourseManage)
const menuDdMyGrade = menuCourseManage[menu].querySelectorAll('dd.menu-dd')[menu_dd];
var menu_dd_all_grade = document.createElement('dd');
menu_dd_all_grade.class = 'menu-dd';
menu_dd_all_grade.innerHTML = `
<a href="javascript:this.top.vpn_inject_script(this);vpn_eval((function () { ; }).toString().slice(14, -2))" layuimini-href="${href}" target="_self">
<i class="fa fa-file-text-o"></i>
<span class="layui-left-nav">${content}</span>
</a>
`;
menuCourseManage[menu].querySelector("dl").insertBefore(menu_dd_all_grade, menuDdMyGrade);
}
while (!document.querySelector('div.layui-side.layui-bg-black.layuimini-menu-left li.layui-nav-item.menu-li')) {
await WaitTime(500);
}
while (!document.querySelector('div.layui-side.layui-bg-black.layuimini-menu-left li.layui-nav-item.menu-li').innerHTML) {
await WaitTime(500);
}
if (jwglCustomMenu.indexOf('全部学期成绩') != -1) {
addMenu(1,4,'personGrade.action?method=historyCourseGrade','全部学期成绩');
}
}
// MyConsole("开启页面监听器");
// const tabNode = document.querySelector("div.layui-tab-content");
// var config = { attributes: true, childList: true };
// var callback = async function(mutationsList) {
// MyConsole("监听到变化");
// if (document.querySelector("div.layui-tab-item.layui-show").querySelector("iframe").src.indexOf('personGrade.action') != -1 && !document.querySelector("div.layui-tab-item.layui-show").querySelector("iframe").contentDocument.querySelector("table")) {
// await WaitTime(1000);
// var div = document.createElement('div');
// document.querySelector("div.layui-tab-item.layui-show").appendChild(div);
// var div_button = document.createElement('div');
// div_button.style = "width:100%;display: flex;justify-content: center;align-items: center;padding-top:3em";
// div_button.id = "h-grade-button";
// div.appendChild(div_button);
// const course_beautify = Vue.createApp({
// template: `
// <van-button type="primary" @click="onClick">主要按钮</van-button>
// `,
// setup() {
// const onClick = () => {
// jwglShowGrade();
// }
// return { onClick };
// }
// });
// course_beautify.use(vant);
// course_beautify.mount("#h-grade-button");
// document.querySelector("div.layui-tab-item.layui-show").querySelector("iframe").style.display = "none";
// }
// };
// var observer = new MutationObserver(callback);
// observer.observe(tabNode, config);
}
// async function jwglShowGrade() {
// var iframe = document.createElement('iframe');
// iframe.id = "h-grade"
// iframe.src = "personGrade.action?method=historyCourseGrade";
// iframe.height = 0;
// iframe.width = 0;
// iframe.style = "display:none";
// document.body.appendChild(iframe);
// iframe = document.querySelector("iframe#h-grade");
// while (!iframe.contentDocument.querySelector("table")) {
// await WaitTime(100);
// }
// const personInfo = iframe.contentDocument.querySelector("div[align=center]").innerHTML;
// MyConsole(personInfo);
// }
async function jwglCourseIframe() {
// 获取 iframe 元素
var iframe = document.querySelector("#contentListFrame");
while (iframe.contentDocument && iframe.contentDocument.readyState != "complete") {
await WaitTime(500);
}
// 等待 iframe 中的内容加载完成后获取内容高度并设置 iframe 高度
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
var height = iframeDocument.body.scrollHeight + 'px';
iframe.style.height = height;
}
async function jwglCourseBeautify() {
if (!GM_getValue('Jwgl.courseBeautify')) {
return;
}
function jwglClass(content_array, mode = -1) {
if (mode == 0) {
return `
<div class="class_main" style="display:flex;flex-direction:column;align-items:center;margin-bottom:1em">
<div class="classroom">${content_array[3]}</div>
<div class="subject">${content_array[1]}</div>
<div class="teacher">${content_array[2]}<br>${content_array[0]}</div>
</div>
`;
} else if (mode > 0) {
return `
<div class="class_main" style="display:flex;flex-direction:column;align-items:center;margin-bottom:1em">
<div class="teacher">${content_array[2]}<br>${content_array[0]}</div>
</div>
`;
} else {
return `
<div class="class_main" style="display:flex;flex-direction:column;align-items:center;margin-bottom:1em">
<div class="classroom">${content_array[3]}</div>
<div class="subject">${content_array[0]}</div>
<div class="teacher">${content_array[2]}<br>${content_array[1]}</div>
</div>
`;
}
};
GM_addStyle(`
<style>
.class_main > div {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
margin: 0;
}
.teacher {
font-size: 12px;
padding: 0;
}
.classroom {
font-size: 12px;
padding: 0;
/* color: #eff0ea; */
}
.subject {
font-size: 16px;
font-weight: bold;
padding: 0.5em 0;
}
</style>
`);
$("body").html($("body").html().replace(".noneprint{\n\tdisplay:none\n} \n\n", ""))
$("table.listTable#contentListFrame").addClass('optimized');
$("table").css("margin-bottom", "3em");
$("tr").attr("height", "auto");
$("tr").css("min-height", "45px");
$("td").css("padding-left", "0");
$("td > div").each(function () {
var div = $(this);
//div.html(div.attr('title'));
div.css("height", "auto");
div.css("padding", "1em 0.5em 0");
var content = div.attr('title');
// content = content.split(/(?<!\d|\[|\]|\(|\))[ \n\r]+(?<!\d|\[|\]|\(|\))/);
content = content.split(/\n|(?<!\n)\s{2,}?(?!\n)/);
console.log(content);
(content.length == 3) ? (content.splice(1, 0, "未定")) : (null);
var content_array = [];
for (let i = 0; i < content.length; i += 4) {
content_array.push(content.slice(i, i + 4));
}
console.log(content_array);
div.css("display", "flex");
div.css("flex-direction", "column");
div.css("justify-content", "center");
div.css("align-items", "center");
div.css("text-align", "center");
div.html("");
for (let i = 0; i < content_array.length; i++) {
if (content_array.length > 1) {
if (i == 0 || content_array[i][1] == content_array[i - 1][1]) {
div.append(jwglClass(content_array[i], i));
} else {
div.append('<div style="height:1px; background-color: grey;width: 90%;margin-bottom:1em"></div>')
div.append(jwglClass(content_array[i], 0));
}
} else {
div.append(jwglClass(content_array[i]));
}
}
});
}
async function webvpnCnkiHtml() {
function getCurrentSelect() {
let selectionObj = null, rangeObj = null;
let selectedText = "", selectedHtml = "";
// 处理兼容性
if (window.getSelection) {
// 现代浏览器
// 获取text
selectionObj = window.getSelection();
selectedText = selectionObj.toString();
// 获取html
rangeObj = selectionObj.getRangeAt(0);
var docFragment = rangeObj.cloneContents();
var tempDiv = document.createElement("div");
tempDiv.appendChild(docFragment);
selectedHtml = tempDiv.innerHTML;
} else if (document.selection) {
// 非主流浏览器IE
selectionObj = document.selection;
rangeObj = selectionObj.createRange();
selectedText = rangeObj.text;
selectedHtml = rangeObj.htmlText;
}
return {
text: selectedText,
html: selectedHtml
}
};
// 使标题可选中
document.querySelector('h1.Chapter').setAttribute('style', 'user-select:auto;');
MyConsole("复制已开启");
// 监听内容区域鼠标抬起事件
document.addEventListener('mouseup', function () {
var copy = getCurrentSelect();
if (copy.text == "") {
return;
}
// myConsole('onmouseup');
MyConsole(getCurrentSelect());
GM_setClipboard(getCurrentSelect().text);
});
}
async function webvpnHTools() {
function searchTeachers(name, page = 1) {
function xmlToJson(xml) {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xml, 'application/xml');
const json = parseElement(xmlDoc.documentElement);
// return JSON.stringify(json, null, 2);
return json;
}
function parseElement(element) {
let obj = {};
if (element.nodeType === 1) { // Element
if (element.attributes.length > 0) {
obj['@attributes'] = {};
for (let j = 0; j < element.attributes.length; j++) {
let attribute = element.attributes.item(j);
obj['@attributes'][attribute.nodeName] = attribute.nodeValue;
}
}
} else if (element.nodeType === 3) { // Text
obj = element.nodeValue;
}
if (element.hasChildNodes()) {
for (let i = 0; i < element.childNodes.length; i++) {
let item = element.childNodes.item(i);
let nodeName = item.nodeName;
if (typeof(obj[nodeName]) === 'undefined') {
obj[nodeName] = parseElement(item);
} else {
if (typeof(obj[nodeName].push) === 'undefined') {
let old = obj[nodeName];
obj[nodeName] = [];
obj[nodeName].push(old);
}
obj[nodeName].push(parseElement(item));
}
}
}
return obj;
}
return new Promise((resolve, reject) => {
const data = `word=${name}&index=0¤tPage=${page}&sql=teacher&tea=1&srtp_teacher_project_num=4&planyear=null&planid=undefined&university_en_name=undefined`;
const xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === this.DONE) {
if (this.status >= 200 && this.status < 300) {
const data = this.responseText;
const raw_result = xmlToJson(data);
// console.log(raw_result);
if (raw_result.page == undefined) {
resolve({success: false,msg: "webvpn登录已过期"});
}
// 构建最终的JSON对象
console.log(raw_result);
const pages = raw_result.page["#text"].match(/第(\d+)\/(\d+)页/);
const now_page = parseInt(pages[1]);
const all_page = parseInt(pages[2]);
if (all_page == 0) {
resolve({success: false,msg: "查询不到该教师"});
}
var result = {success: true, page: [now_page, all_page], data: []};
if (raw_result.val.length == undefined) {
var name = raw_result.word["#text"].replace(raw_result.val["#text"] + "-", "");
result.data.push({name: name, number: raw_result.val["#text"], unit: raw_result.remind["#text"]});
} else {
for (let i = 0; i < raw_result.val.length; i++) {
// if (result.data[i%3] == undefined) {
// result.data[i%3] = [];
// }
var name = raw_result.word[i]["#text"].replace(raw_result.val[i]["#text"] + "-", "");
result.data.push({name: name, number: raw_result.val[i]["#text"], unit: raw_result.remind[i]["#text"]});
}
}
resolve(result);
} else {
reject(new Error(`Request failed with status ${this.status}`));
}
}
});
xhr.addEventListener("error", function () {
reject(new Error("Network error"));
});
xhr.open("POST", "https://webvpn.nxu.edu.cn/http/77726476706e69737468656265737421a2a713d27560391e2f5ad1e2c90171/StuExpbook/AutoCompleteServletSrtp?vpn-12-o1-202.201.128.142=");
xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
try {
xhr.send(data);
} catch (err) {
reject(err);
}
});
}
createToast("info", `请等待工具部署`, 0);
document.querySelector("body").innerHTML = ``;
document.title = `小工具 - H`;
// 添加Notification组件
// 添加组件
addToast();
//createToast("success", "测试消息", 0);
// 添加css样式
GM_addStyle(ToastCss);
GM_addStyle(GM_getResourceText("svg-logo").replace(/\.\.\/webfonts/g, "https://cdn.bootcdn.net/ajax/libs/font-awesome/6.2.1/webfonts"));
//绑定Toast事件
unsafeWindow.createToast = createToast;
unsafeWindow.removeToast = removeToast;
unsafeWindow.searchTeachers = searchTeachers;
GM_addElement(document.querySelector('body'), 'div', { id: 'tools' });
GM_addStyle(`
#main, #main * {
box-sizing: border-box;
}
#main {
width: calc(100% - 80px);
height: calc(100% - 46px);
position: absolute;
top: 46px;
left: 80px;
padding-right: 20px;
overflow: hidden;
}
#main > div {
display: none;
width: 100%;
height: 100%;
box-sizing: border-box;
}
#main > div.show {
display: block;
}
#searchTeacher > .credits-bar {
margin: 0;
box-sizing: border-box;
position: fixed;
bottom: 20px;
left: calc(50% + 64px);
transform: translateX(-50%);
background: rgba(255, 255, 255, 0.1);
color: black;
padding: 10px 25px;
border-radius: 30px;
font-size: 0.9rem;
backdrop-filter: blur(8px);
z-index: 1000;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
animation: slideUp 0.6s ease-out;
display: flex;
align-items: center;
white-space: nowrap;
gap: 8px;
}
`);
const tools_template = `
<van-nav-bar
title="小工具 - H"
left-text="返回"
left-arrow
@click-left="onClickLeft"
/>
<van-sidebar v-model="active" @change="onChange" style="z-index:9999">
<van-sidebar-item title="在职教师工号查询" />
<van-sidebar-item title="Campus Charge" />
<van-sidebar-item title="🚧等待⚠️施工" />
</van-sidebar>
<div id="main">
<div id="searchTeacher" class="show" style="width: 100%;height:100%;padding:10px">
<form action="/">
<van-search
v-model="searchValue"
show-action
placeholder="请输入需要查询的教师姓名"
@search="onSearch"
>
<template #action>
<div @click="onSearchClick">搜索</div>
</template>
</van-search>
</form>
<div style="width:100%;display:flex;align-content: center;justify-content: center;">
<div v-for="teachers in teacherList" style="width:33%;margin-bottom:60px">
<van-cell-group inset>
<van-cell v-for="teacher in teachers" :number="teacher.number" :title="teacher.name" :value="teacher.number" :label="teacher.unit" clickable @click="teacherClick"/>
</van-cell-group>
</div>
</div>
<div class="credits-bar">
<span>数据来源:</span>
<a href="https://cxcy.nxu.edu.cn/" target="_blank" rel="noopener">
宁夏大学创新创业学院
</a>
<span>宁夏大学创新创业服务平台</span>
</div>
</div>
<div>
<iframe src="//campus-charge.thisish.cn" style="width: 100%;height: 100%;border: none;"></iframe>
</div>
<div>
<h3 style="width:100%;text-align:center">静候佳音...</h3>
</div>
</div>
`
const tools = Vue.createApp({
template: tools_template,
setup() {
const active = Vue.ref(0);
const onClickLeft = () => { CloseWin(); };
const onChange = (index) => {
document.querySelector("div#main > div.show").classList.remove("show");
const tools_tab = document.querySelectorAll("div#main > div");
tools_tab[index].classList.add("show");
};
const searchValue = Vue.ref('');
const teacherList = Vue.ref([]);
const onSearch = (val) => {
searchTeacher(val);
};
const onSearchClick = () => {
searchTeacher(searchValue.value);
};
const searchTeacher = async(val) => {
// console.log(val)
// vant.showToast(val)
if (val.toLowerCase() == "moss") {
createToast("info", decodeURI("%E6%81%AD%E5%96%9C%E4%BD%A0%E5%8F%91%E7%8E%B0%E4%BA%86%E8%BF%99%E4%B8%AA%E5%B0%8F%E5%BD%A9%E8%9B%8B~"), 0);
open("//moss.thisish.cn");
return;
}
teacherList.value = [];
var now_page = 1;
var all_page = 1;
var now_row = 0;
var row = 0;
while (now_page <= all_page) {
var list = await searchTeachers(val, now_page);
console.log(list)
if (!list.success) {
createToast("error", list.msg);
break;
}
for (let i = 0; i < list.data.length; i++) {
row = i%3 + now_row;
if (row >= 3) {
row -= 3;
}
if (teacherList.value[row] == undefined) {
teacherList.value[row] = [];
}
teacherList.value[row].push(list.data[i]);
}
now_row = row + 1;
if (now_row >= 3) {
now_row -= 3;
}
// if (list.data.length == 1) {
// teacherList.value = [list.data];
// } else if (list.data.length == 2) {
// teacherList.value = [[list.data[0]],[list.data[1]]];
// } else {
// if (teacherList.value.length == 0) {
// teacherList.value = [[],[],[]];
// }
// teacherList.value[0].push(...list.data[0]);
// teacherList.value[1].push(...list.data[1]);
// teacherList.value[2].push(...list.data[2]);
// }
console.log(JSON.stringify(teacherList.value))
now_page = list.page[0] + 1;
all_page = list.page[1];
}
};
const teacherClick = (event) => {
// console.log(event)
// console.log(event.target.closest('.van-cell'))
GM_setClipboard(event.target.closest('.van-cell').getAttribute("number"));
vant.showToast("工号已复制");
}
return { active, onClickLeft, onChange, searchValue, onSearch, onSearchClick, teacherList, teacherClick };
}
});
tools.use(vant);
tools.mount("#tools");
}
async function errorHtml() {
if (!GM_getValue('WebVPN.autoClose')) {
return;
}
CloseWin();
}
// if (!GM_getValue("TuanWei.autoDownload", false)) {
// return;
// }
// createToast("info", `自动下载...`);
// var verification = await GetVerificationCode("TuanWei");
// MyConsole(verification);
// // document.querySelector("input#codeValue").value = 1234;
// // document.querySelector("input[type=button]").click();
// // if (!GM_getValue("TuanWei.autoDownloadClose", false)) {
// // return;
// // }
// // createToast("info", `5秒后自动关闭...`);
// // await WaitTime(5000);
// // CloseWin();
async function jwglGrade() {
MyConsole(window.parent.document.readyState);
}
})();