// ==UserScript==
// @name tame QTKJ
// @namespace Vionlentmonkey
// @version 2.11.0
// @description at the end of with it.
// @author someone
// @match http://218-94-1-181.sft.ipv6.jiangsu.gov.cn:8087/sfxzwsxy/
// @match http://218-94-1-179.sft.ipv6.jiangsu.gov.cn:8087/sfxzwsxy/
// @match http://218-94-1-175.sft.ipv6.jiangsu.gov.cn:8087/sfxzwsxy/
// @match http://218.94.1.181:8087/sfxzwsxy/*
// @match http://218.94.1.179:8087/sfxzwsxy/*
// @match http://218.94.1.175:8087/sfxzwsxy/*
// @match http://218.94.1.181:5088/unzipapp/project/ware/attach/*
// @require https://openuserjs.org/src/libs/sizzle/GM_config.js
// @require https://cdn.jsdelivr.net/npm/vm.shortcut
// @require https://greasyfork.org/scripts/381401-addstyle/code/addStyle.js
// @require https://greasyfork.org/scripts/381402-clearurls/code/clearURLs.js
// @require https://greasyfork.org/scripts/381403-fakenavigators/code/fakeNavigators.js
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @grant GM_openInTab
// @grant GM_notification
// @grant GM_xmlhttpRequest
// @run-at document-start
// ==/UserScript==
const body = document.body || document.documentElement;
const iframeCSS = `
height: 50% !important;
width: auto !important;
border: 1px solid;
position: fixed;
`;
const windowCSS = `
#tameCfg {background-color: lightblue;}
#tameCfg .reset_holder {float: left; position: relative; bottom: -1em;}
#tameCfg .saveclose_buttons {margin: 1em;}
`;
GM_registerMenuCommand('tame QTKJ Settings', opencfg);
function opencfg() {
// 避免在包含框架的页面打开造成多个设置界面重叠
if (window.top === window.self) {
GM_config.open();
keyCodeCfg.style = iframeCSS;
}
}
GM_config.init({
id: 'tameCfg',
title: 'tame QTJK',
fields: {
open_unclose: {
section: ['keyCode', '批量打开链接的快捷键'],
label: '批量新标签页后台打开',
labelPos: 'right',
type: 'text',
default: 'F8'
},
loginName: {
section: ['网上学院', '完整填写后将尝试自动登录'],
label: '账号',
labelPos: 'right',
type: 'text',
default: ''
},
pwd: {
label: '密码',
labelPos: 'right',
type: 'password',
default: ''
},
batch: {
label: '一次性批量打开',
labelPos: 'right',
type: 'int',
default: 10
},
reload: {
label: '是否无限次尝试登录',
labelPos: 'right',
type: 'checkbox',
default: false
}
},
css: windowCSS,
events: {
save: function() {
GM_config.close();
}
}
});
// 从 QQ 等打开地址后会被加上奇葩后缀 http://218.94.1.179:8087/sfxzwsxy/#?tdsourcetag=s_pctim_aiomsg
clearURLs();
// 统一域名
if (location.host.endsWith('.sft.ipv6.jiangsu.gov.cn:8087')) {
location.host = location.host.replace('.sft.ipv6.jiangsu.gov.cn', '').replace('-', '.');
}
// http://218.94.1.175:8087/sfxzwsxy/
if (
location.href.match(
/^http:\/\/218\.94\.1\.(181|179|175):8087\/sfxzwsxy\/?(serverSelect.jsp)?#?$/i
)
) {
// 自动选择最空闲的服务器
window.onload = () => {
const servers = document.getElementsByClassName('num');
if (servers.length > 0) {
let servers_str = [];
let notificationText = '自动选择最空闲的服务器!';
for (let s of servers) {
servers_str.push(s.textContent);
notificationText += `\n服务器${servers_str.length}使用程度:${s.textContent}%`;
}
// 将字符串元素转为数字
let servers_num = servers_str.map(Number);
// 全满则自动刷新,否则选择最空服务器
let mostFree = Math.min(...servers_num);
if (mostFree === 100) {
if (GM_config.get('reload')) {
// https://developer.mozilla.org/docs/Web/API/Location/reload
location.reload(true);
}
} else {
document.getElementsByClassName('entrybtn')[servers_num.indexOf(mostFree)].click();
GM_notification(notificationText);
}
}
};
}
// 去除登陆验证码校验
// 曾使用 OCR 识别法,参考了 https://www.cnblogs.com/ziyunfei/archive/2012/10/05/2710349.html 但准确度有限。
if (
location.href.startsWith('http://218.94.1.181:8087/sfxzwsxy/index.jsp') ||
location.href.startsWith('http://218.94.1.179:8087/sfxzwsxy/index.jsp') ||
location.href.startsWith('http://218.94.1.175:8087/sfxzwsxy/index.jsp')
) {
window.onload = () => {
// 原函数去掉验证码识别,去除 isBlank 函数依赖
check = () => {
if (document.getElementById('loginName').value === '') {
alert('请输入用户名');
document.getElementById('loginName').focus();
return;
}
if (document.getElementById('pwd').value === '') {
alert('请输入密码');
document.getElementById('pwd').focus();
return;
}
document.getElementById('form1').submit();
};
// 重新绑定点击事件
document.getElementById('Submit').onclick = check;
// 移除验证码并提示
document.getElementById('verifyCode').remove();
document.getElementById('imgCode').value = '已去除验证码可直接登录';
// 以下尝试自动登录
document.getElementById('loginName').value = GM_config.get('loginName'); // 写入预先设置的用户名
document.getElementById('pwd').value = GM_config.get('pwd'); // 写入预先设置的密码
// 自动获取用户名密码输入框焦点
if (document.getElementById('loginName').value === '') {
document.getElementById('loginName').focus();
} else if (document.getElementById('pwd').value === '') {
document.getElementById('pwd').focus();
} else {
// 用户名密码均已填写时才自动登录
document.getElementById('Submit').click();
}
};
}
// 首页
if (
location.href.endsWith('/sfxzwsxy/jypxks/index.html') ||
location.href.endsWith('/sfxzwsxy/jypxks/index.html#')
) {
// 修复页面过长无法完整显示的 Bug; 隐藏 密码修改提示 和 每日一题
const css = `
html[style="overflow: hidden;"] {
overflow: visible !important;
}
#layui-layer1, #layui-layer-shade1, #layui-layer2, #layui-layer-shade2 {
display: none !important;
}
`;
addStyle(css);
// 长时间不动会被弹出;暂时简单粗暴处理,效果未经严谨测试。
setInterval(() => {
location.reload(true);
}, 1200000); // 20分钟刷新一次
// 不保证成功率:自动聚焦
let document_observer = new MutationObserver(() => {
//console.log('changing');
if (document.getElementById('mainFrame')) {
document.getElementById('mainFrame').focus();
console.log('focus');
}
});
document_observer.observe(body, {
childList: true,
subtree: true
});
}
// 目前发现的3学分课程
const credit3 = [
{
apply_pk: '3045823',
fk_course_pk: '1003',
course_name: '引导人民内心拥护和真诚信仰法律——谈“七五”普法工作'
},
{
apply_pk: '3045822',
fk_course_pk: '1023',
course_name: '法律援助工作概述(上)'
},
{
apply_pk: '3028146',
fk_course_pk: '1024',
course_name: '法律援助工作概述(下)'
},
{
apply_pk: '3045825',
fk_course_pk: '1063',
course_name: '公证制度概述(上)'
},
{
apply_pk: '3027963',
fk_course_pk: '1064',
course_name: '公证制度概述(下)'
},
{
apply_pk: '3045826',
fk_course_pk: '1065',
course_name: '司法鉴定工作概论(上)'
},
{
apply_pk: '3045827',
fk_course_pk: '1066',
course_name: '司法鉴定工作概论(下)'
},
{
apply_pk: '3045824',
fk_course_pk: '1067',
course_name: '人民调解制度的发展和完善(上)'
},
{
apply_pk: '3028121',
fk_course_pk: '1068',
course_name: '人民调解制度的发展和完善(下)'
},
{
apply_pk: '3028106',
fk_course_pk: '1069',
course_name: '司法行政戒毒工作概要'
},
{
apply_pk: '3028035',
fk_course_pk: '1083',
course_name: '中国特色社会主义监狱管理制度概论(上)'
},
{
apply_pk: '3028034',
fk_course_pk: '1086',
course_name: '中国特色社会主义监狱管理制度概论(下)'
},
{
apply_pk: '3028032',
fk_course_pk: '1087',
course_name: '司法协助制度概述(上)'
},
{
apply_pk: '3028031',
fk_course_pk: '1088',
course_name: '司法协助制度概述(下)'
},
{
apply_pk: '3028027',
fk_course_pk: '1089',
course_name: '坚持和完善中国特色司法行政制度(上)'
},
{
apply_pk: '3027974',
fk_course_pk: '1090',
course_name: '坚持和完善中国特色司法行政制度(下)'
},
{
apply_pk: '3028020',
fk_course_pk: '1092',
course_name: '刑满释放人员安置帮教制度(上)'
},
{
apply_pk: '3026946',
fk_course_pk: '1093',
course_name: '刑满释放人员安置帮教制度(下)'
},
{
apply_pk: '3026938',
fk_course_pk: '1143',
course_name: '我国社区矫正工作的改革与发展(上)'
},
{
apply_pk: '3027986',
fk_course_pk: '1144',
course_name: '我国社区矫正工作的改革与发展(下)'
}
];
// 首页培训课程 iframe
if (location.href.endsWith('/sfxzwsxy/jypxks/modules/homepage/homepage.jsp')) {
// 彩蛋:自动报名3学分课程,可能需要 iframe 提升才能运行。逻辑有待优化。
if (window.top === window.self) {
// 20分钟刷新一次
setInterval(() => {
location.reload(true);
}, 1200000);
window.onload = () => {
let courses = document.getElementsByClassName('course');
for (let c of courses) {
let cApplyPK = c.getElementsByClassName('applyPk')[0].textContent;
let cID = c.getElementsByClassName('coursePk')[0].textContent;
let cTitle = c.getElementsByClassName('title')[0].getAttribute('title');
let cJdjs = c.getElementsByClassName('jdjs')[0].textContent;
// 远程获取信息
let details = {
method: 'POST',
url: `${location.origin}/sfxzwsxy//jypxks/modules/train/course/course_view.jsp?coursePk=${cID}`,
responseType: 'document',
onload: response => {
//console.log(response.status);
let creditText = response.responseText
.split('课程学分:')[1]
.split('width="35%">')[1]
.trim()
.split(' ')[0];
//console.log(`${cTitle}——学分:` + creditText);
let courseTimeText = response.responseText
.split('可获学时:')[1]
.split('width="35%">')[1]
.trim()
.split(' ')[0];
//console.log(`${cTitle}——学时:` + courseTimeText);
}
};
GM_xmlhttpRequest(details);
if (cJdjs === '未报名') {
let C3_length = 0;
for (let C3 of credit3) {
if (C3_length < credit3.length) {
if (C3.fk_course_pk === cID && C3.course_name === cTitle) {
c.click();
console.log('click:' + cTitle);
const info = document.getElementsByClassName('layui-layer-btn0');
if (info.length === 1) {
document.getElementsByClassName('layui-layer-btn0')[0].click();
console.log('报名:' + cTitle);
return (C3_length += 1);
}
}
} else {
console.log('3学分课程已全部报名');
}
}
} else {
let cURL =
location.origin +
'/sfxzwsxy/jypxks/modules/train/ware/course_ware_view.jsp?applyPk=' +
cApplyPK +
'&courseType=1';
const cOpenClose = GM_openInTab(cURL, true);
// 20 分钟自动关闭
setTimeout(cOpenClose.close, 600000);
c.addEventListener('click', () => {
GM_openInTab(cURL, true);
GM_notification('默认弹出窗口并不影响使用,暂请无视。');
});
}
}
};
}
// F8 批量后台新标签页打开已报名课程播放页面
VM.registerShortcut(GM_config.get('open_unclose'), () => {
let c = 0;
const courses = document.getElementsByClassName('infomai');
for (let course of courses) {
let cID = course.getElementsByClassName('applyPk')[0].textContent;
if (cID !== '') {
let cURL =
location.origin +
'/sfxzwsxy/jypxks/modules/train/ware/course_ware_view.jsp?applyPk=' +
cID +
'&courseType=1';
GM_openInTab(cURL, true);
c++;
// 疑似超过 6 个课程同时学习后台不计数
if (c >= GM_config.get('batch')) {
console.log(`已批量打开${c}个课程`);
return;
}
}
}
});
// 考试提醒 - 课程考试
const openExam1 = () => {
// 第一页 #courseExam1,第二页 #courseExam2
const exams = document.querySelectorAll('div[id^=courseExam] > a[title]');
for (let exam of exams) {
const eURL = location.origin + '/sfxzwsxy/' + exam.getAttribute('onclick').split("'")[1];
//console.log(eURL);
exam.href = eURL;
exam.onclick = '';
exam.target = '_blank';
exam.addEventListener('click', () => {
GM_notification('如需同步打开答案请从顶部在线考试进入。');
});
}
};
// 最新知识库
const openKnowledge = () => {
const knowledges = document.querySelectorAll('#knowledgeType a[title][href="#"]');
for (let knowledge of knowledges) {
const kURL =
location.origin +
'/sfxzwsxy/jypxks/modules/learn/document/learn/document_learn_text.jsp?fkNodePk=' +
knowledge
.getAttribute('onclick')
.split('(')[1]
.split(')')[0];
//console.log(kURL);
knowledge.href = kURL;
knowledge.onclick = '';
knowledge.target = '_blank';
}
};
let document_observer = new MutationObserver(() => {
openExam1();
openKnowledge();
});
document_observer.observe(body, {
childList: true,
subtree: true
});
}
// 培训课程查询 iframe
if (location.href.endsWith('/sfxzwsxy/jypxks/modules/train/query/course_query.jsp')) {
// 清理“参加培训”和“查看”链接
const reallinks = () => {
let courses = document.querySelectorAll('#trainCourseList a[onclick^=bindBeginTrainEvents]');
for (let course of courses) {
let cID = course.getAttribute('onclick').split('"')[1];
//console.log(cID);
let cURL =
location.origin +
'/sfxzwsxy/jypxks/modules/train/ware/course_ware_view.jsp?applyPk=' +
cID +
'&courseType=1';
//console.log(cURL);
course.href = cURL;
course.onclick = '';
course.target = '_blank';
}
let answers = document.querySelectorAll(
'#trainCourseList a[onclick^=bindViewCourseInfoEvents]'
);
for (let answer of answers) {
let aID = answer.getAttribute('onclick').split('"')[1];
//console.log(cID);
let aURL =
location.origin + '/sfxzwsxy/jypxks/modules/train/course/course_view.jsp?coursePk=' + aID;
//console.log(cURL);
answer.href = aURL;
answer.onclick = '';
answer.target = '_blank';
}
};
let document_observer = new MutationObserver(() => {
reallinks();
});
document_observer.observe(body, {
childList: true,
subtree: true
});
// F8 批量后台新标签页打开课程播放页面
VM.registerShortcut(GM_config.get('open_unclose'), () => {
let c = 0;
const courses = document.querySelectorAll('#trainCourseList a[onclick^=bindBeginTrainEvents]');
for (let course of courses) {
let cID = course.getAttribute('onclick').split('"')[1];
let cURL =
location.origin +
'/sfxzwsxy/jypxks/modules/train/ware/course_ware_view.jsp?applyPk=' +
cID +
'&courseType=1';
GM_openInTab(cURL, true);
c++;
// 疑似超过 6 个课程同时学习后台不计数
if (c >= GM_config.get('batch')) {
console.log(`已批量打开${c}个课程`);
return;
}
}
});
}
// 培训课程查询 - 查看 - 题干 iframe
if (
// 点击 “下一页” 或 “上一页” 后 iframe 实际地址会去除 .jsp 之后的尾巴
location.href.startsWith(
'http://218.94.1.181:8087/sfxzwsxy//jypxks/modules/train/course/subject_list.jsp'
) ||
location.href.startsWith(
'http://218.94.1.179:8087/sfxzwsxy//jypxks/modules/train/course/subject_list.jsp'
) ||
location.href.startsWith(
'http://218.94.1.175:8087/sfxzwsxy//jypxks/modules/train/course/subject_list.jsp'
)
) {
// 清理“题干”链接
window.onload = () => {
const views = document.querySelectorAll('a[onclick^=viewSubject]');
for (let view of views) {
const vID = view
.getAttribute('onclick')
.split('(')[1]
.split(')')[0];
//console.log(vID);
const vURL =
location.origin +
'/sfxzwsxy//jypxks/modules/train/course/subject_view.jsp?subjectPk=' +
vID;
//console.log(vURL);
view.href = vURL;
view.onclick = '';
view.target = '_blank';
}
};
}
// 课程视频播放 跨域 iframe
if (location.href.startsWith('http://218.94.1.181:5088/unzipapp/project/ware/attach/')) {
// 旧播放器在 Windows 下要求 Flash,新播放器不兼容苹果系列。老版本播放器能否成功调用 HTML5 似乎是玄学问题。
fakeUA('Linux');
// 检测不到 HTML5 播放器则刷新
const isFlash = () => {
if (document.getElementById('course_player5')) {
//console.log('html5');
clearInterval(noFlash);
} else {
location.reload(true);
}
};
const noFlash = setInterval(isFlash, 1000);
const learn = () => {
// 自动从课程封面进入播放页面
if (document.querySelector('img[src="courseware/iconImg/z3.png"]')) {
document.querySelector('img[src="courseware/iconImg/z3.png"]').click();
}
// 老播放器用的 confirm 对话框手动点击即暂停,新版的 alert 无法模拟点击但手动点击不影响后台播放
if (document.getElementById('cancel')) {
//document.getElementById('confirm').click(); // 继续学习;可能需要多次重复才能完成一课时。
document.getElementById('cancel').click(); // 大侠还请重新来过
}
};
const option = () => {
// 旧播放器自动做题,找不到测试例进一步优化。
if (document.querySelectorAll('div.option>label>input[name="que"]').length > 0) {
document.querySelectorAll('div.option>label>input[name="que"]')[0].click();
document.querySelectorAll('div.option>label>input[name="que"]')[1].click(); // 多选题
document.getElementsByClassName('button')[0].click(); // 提交
if (document.getElementsByClassName('button_xia').length === 1) {
document.getElementsByClassName('button_xia')[0].click(); // 下一题
}
if (document.getElementsByClassName('button_wan').length === 1) {
document.getElementsByClassName('button_wan')[0].click(); // 完成
}
document.getElementById('course_player5').click(); // 播放
}
};
let document_observer = new MutationObserver(() => {
learn();
option();
});
document_observer.observe(body, {
attributes: true,
subtree: true
});
}
// 必修课考试 iframe
if (
location.href.endsWith(
'/sfxzwsxy/jypxks/modules/examination/course_examine/course_exam_list.jsp'
) ||
location.href.endsWith(
'/sfxzwsxy/jypxks/modules/examination/course_examine/course_exam_list.jsp#'
)
) {
// 清理“参加考试”链接,新标签页打开考试及答案。
window.onload = () => {
const exams = document.querySelectorAll('a[href="#"][onclick^=openWindowFullScreen]');
for (let exam of exams) {
const eURL = location.origin + '/sfxzwsxy/' + exam.getAttribute('onclick').split("'")[1];
const course_pk = eURL.split('course_pk=')[1].split('&')[0];
//console.log(eURL);
//console.log(course_pk);
const answerURL =
location.origin +
'/sfxzwsxy//jypxks/modules/train/course/course_view.jsp?coursePk=' +
course_pk;
exam.href = eURL;
exam.onclick = '';
exam.target = '_blank';
exam.addEventListener('click', () => {
GM_openInTab(answerURL, true);
GM_notification('答案已同步打开。\n暂请考试结束后手动关闭。');
});
}
};
}