const css = '* { -webkit-tap-highlight-color: transparent;}:root { --themeColor: #fa3333; --scale: 1; font-size: calc(10px * var(--scale));}@media (min-height: 678px) and (max-height: 768px) { :root { --scale: 0.8; }}@media (max-height: 667px) { :root { --scale: 0.75; }}@keyframes fade { from { opacity: 0.8; } to { opacity: 0.4; background: #ccc; }}.egg_icon { width: 1em; height: 1em; fill: currentColor;}.egg_hr_wrap { position: relative; display: flex; justify-content: center; color: #ccc;}.egg_hr_wrap .egg_hr { position: absolute; top: 50%; transform: translateY(-50%); background: currentColor; height: 0.1rem; width: 30%;}.egg_hr_wrap .egg_hr:nth-of-type(1) { left: 0;}.egg_hr_wrap .egg_hr:nth-last-of-type(1) { right: 0;}.egg_hr_title { font-size: 1.2rem;}.egg_exam_btn { transition: background-color 80ms; outline: none; border: none; padding: 1.2rem 2rem; border-radius: 1.2rem; cursor: pointer; font-size: 1.8rem; font-weight: bold; text-align: center; color: #ffffff; background: #666777;}.egg_exam_btn.manual { background: #e3484b;}.egg_panel_wrap * { padding: 0; margin: 0; box-sizing: border-box; outline: none; border: none;}.egg_panel_wrap { position: fixed; left: 0; top: 0; z-index: 99999; width: 100%; height: 100%; color: #333; font-size: 1.6rem; pointer-events: none;}.egg_panel { position: absolute; top: 5rem; left: 1rem; padding: 1.2rem 2rem; border-radius: 1rem; background: #ffffffe6; backdrop-filter: blur(1rem); box-shadow: 0 0 0.1rem 0.1rem #f1f1f1; transition: 80ms ease-out; pointer-events: all;}.egg_panel.hide { left: 0; transform: translateX(-100%);}.egg_panel_wrap.mobile .egg_panel { top: 1rem;}@media (min-height: 678px) and (max-height: 768px) { .egg_panel { top: 2rem; }}@media (max-height: 667px) { .egg_panel { top: 1rem; }}.egg_panel button { outline: none; border: none; padding: 0; cursor: pointer; background: none;}.egg_panel .egg_btns_wrap { position: absolute; left: 100%; top: 50%; transform: translate(-50%, -50%); transition: 80ms ease; z-index: 9;}.egg_panel.hide .egg_btns_wrap { left: 100%; transform: translate(0, -50%);}.egg_panel .egg_btns_wrap button { border-radius: 50%; width: 3rem; height: 3rem; padding: 0; overflow: hidden; border: 0.2rem solid currentColor; color: white; display: grid; place-items: center; font-size: 1.8rem;}.egg_panel.hide .egg_panel_show_btn { background: var(--themeColor);}.egg_panel .egg_panel_show_btn { background: #ccc;}.egg_panel .egg_frame_show_btn { background: var(--themeColor); margin-bottom: 1rem;}.egg_panel .egg_frame_show_btn.hide { display: none;}.egg_panel .egg_settings_show_btn { background: #ccc; margin-top: 1rem;}.egg_panel .egg_settings_show_btn.active { background: var(--themeColor);}.egg_panel .egg_settings_reset_btn { background: #ccc; margin-top: 1rem;}.egg_panel .egg_settings_reset_btn:active { background: var(--themeColor);}.egg_login_item { display: flex; justify-content: center; align-items: center; flex-direction: column; padding: 0.5rem 0;}.egg_login_item .egg_login_btn { font-size: 1.4rem; border-radius: 1rem; transition: 80ms ease; color: white; background: var(--themeColor); padding: 0.8rem 2.4rem;}.egg_login_item .egg_login_btn:active { opacity: 0.8;}.egg_login_item .egg_login_img_wrap { height: 0; border-radius: 1rem; transition: height 80ms ease; overflow: hidden;}.egg_login_item .egg_login_img_wrap.active { padding: 0.8rem; margin-top: 0.8rem; height: auto; background: white;}.egg_login_img_wrap .egg_login_img { width: 15rem; height: 15rem;}.egg_info_item .egg_login_btn { font-size: 1.4rem; border-radius: 1rem; transition: 80ms ease; color: white;}.egg_info_item .egg_login_btn:active { opacity: 0.8;}.egg_info_item .egg_login_btn { background: #ccc; padding: 0.4rem 0.8rem;}.egg_info_item { display: flex; justify-content: space-between; align-items: center;}.egg_info_item .egg_userinfo { display: flex; justify-content: center; align-items: center; padding: 0.5rem 0;}.egg_userinfo .egg_avatar .egg_avatar_nick,.egg_userinfo .egg_avatar .egg_avatar_img { height: 5rem; width: 5rem; border-radius: 50%; background: var(--themeColor); display: flex; justify-content: center; align-items: center; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; font-size: 2rem; color: white;}.egg_userinfo .egg_nick { padding-left: 0.5rem; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; max-width: 10rem;}.egg_score_item .egg_scoreinfo { display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0;}.egg_scoreinfo .egg_totalscore,.egg_scoreinfo .egg_todayscore { font-size: 1.2rem; user-select: none;}.egg_scoreinfo .egg_totalscore span,.egg_scoreinfo .egg_todayscore .egg_todayscore_btn span { padding-left: 0.2rem;}.egg_scoreinfo .egg_totalscore span,.egg_todayscore .egg_todayscore_btn span,.egg_todayscore .egg_score_details span { color: var(--themeColor); font-weight: bold;}.egg_scoreinfo .egg_todayscore { position: relative;}.egg_todayscore .egg_todayscore_btn { display: flex; align-items: center;}.egg_todayscore_btn .egg_icon { opacity: 0.3;}.egg_todayscore .egg_score_details { position: absolute; left: calc(100% + 1rem); top: 0; background: #fffffff2; border-radius: 0.5rem; opacity: 1; width: 10rem; box-shadow: 0 0 0.1rem 0.1rem #f1f1f1; transition: 80ms ease; z-index: 9;}.egg_todayscore .egg_score_details.hide { visibility: hidden; opacity: 0; left: 100%;}.egg_score_details .egg_score_title { border-bottom: 0.1rem solid #eee; padding: 0.5rem 0.8rem; display: flex; align-items: center;}.egg_score_details .egg_score_title .egg_icon { font-size: 1.4rem;}.egg_score_details .egg_score_title .egg_score_title_text { font-weight: bold; padding-left: 0.2rem;}.egg_score_details .egg_score_item { display: flex; align-items: center; justify-content: space-between; padding: 0.5rem 0.8rem;}.egg_task_list { position: relative;}.egg_task_item { user-select: none; min-height: 3rem; min-width: 18rem; display: flex; align-items: center; justify-content: space-between; padding: 0.5rem 0;}.egg_task_item .egg_label_wrap { flex-grow: 1; padding-right: 0.5rem;}.egg_label_wrap .egg_task_title_wrap { display: flex; justify-content: space-between; align-items: center;}.egg_task_title_wrap .egg_task_progress_wrap { display: flex; align-items: center; font-size: 1.4rem; width: 3.5rem;}.egg_task_progress_wrap .egg_task_current { color: var(--themeColor);}.egg_task_progress_wrap .egg_task_max { color: #999; font-size: 1.2rem;}.egg_label_wrap .egg_progress { display: flex; justify-content: space-between; align-items: center; padding-top: 0.8rem;}.egg_progress .egg_track { background: #ccc; height: 0.5rem; border-radius: 1rem; flex: 1 1 auto; overflow: hidden;}.egg_progress .egg_track .egg_bar { height: 0.5rem; background: var(--themeColor); border-radius: 1rem; width: 0; transition: width 0.5s;}.egg_setting_item { min-height: 3rem; min-width: 18rem; display: flex; align-items: center; justify-content: space-between; box-sizing: border-box;}.egg_setting_item .egg_label_wrap { flex-grow: 1;}.egg_detail { background: #ccc; color: white; border-radius: 10rem; font-size: 1.2rem; width: 1.6rem; height: 1.6rem; margin-left: 0.4rem; display: inline-block; text-align: center; line-height: 1.6rem; cursor: pointer;}.egg_switch { cursor: pointer; margin: 0; outline: 0; appearance: none; -webkit-appearance: none; -moz-appearance: none; position: relative; width: 4.2rem; height: 2.2rem; background: #ccc; border-radius: 5rem; transition: background 0.3s; --border-padding: 0.5rem; box-shadow: -0.1rem 0 0.1rem -0.1rem #999 inset, 0.1rem 0 0.1rem -0.1rem #999 inset;}.egg_switch::after { content: \'\'; display: inline-block; width: 1.4rem; height: 1.4rem; border-radius: 50%; background: #fff; box-shadow: 0 0 0.2rem #999; transition: left 0.4s; position: absolute; top: calc(50% - (1.4rem / 2)); position: absolute; left: var(--border-padding);}.egg_switch:checked { background: var(--themeColor);}.egg_switch:disabled { opacity: 0.5; background: #ccc;}.egg_switch:checked::after { left: calc(100% - var(--border-padding) - 1.4rem);}.egg_tip_list { font-size: 1.2rem; max-width: 18rem; line-height: 2rem; color: var(--themeColor);}.egg_tip_list .egg_tip_btn { padding: 0.2rem 0.4rem; background: #f1f1f1; color: #333;}.egg_tip_list .egg_tip_btn:disabled { opacity: 0.5; background: #ccc;}.egg_study_item { display: flex; justify-content: center; padding-top: 0.5rem;}.egg_study_item .egg_study_btn { background: var(--themeColor); padding: 0.8rem 2.4rem; font-size: 1.4rem; border-radius: 1rem; color: white; transition: 80ms ease;}.egg_study_item .egg_study_btn:not(.loading):active { opacity: 0.8;}.egg_study_item .egg_study_btn.loading { animation: fade 2s ease infinite alternate;}.egg_study_item .egg_study_btn:disabled { background: #ccc;}.egg_tip_wrap { position: fixed; left: 0; top: 0; z-index: 999999; width: 100%; height: 100%; pointer-events: none;}.egg_tip_wrap * { padding: 0; margin: 0; box-sizing: border-box; outline: none; border: none;}.egg_tip_wrap .egg_tip { position: absolute; bottom: 2rem; left: 2rem; padding: 1.2rem 1.4rem; border: none; border-radius: 1rem; background: var(--themeColor); color: white; font-size: 1.4rem; transition: 200ms ease; opacity: 0; transform: scale(0.9) translateY(1rem);}.egg_tip_wrap .egg_tip.active { opacity: 1; transform: scale(1) translateY(0);}.egg_tip_wrap .egg_tip.active.delay { opacity: 0.5;}.egg_tip_wrap .egg_tip .egg_countdown { display: inline-block; color: var(--themeColor); background: white; border-radius: 0.5rem; padding: 0.2rem 0.4rem; font-weight: bold; margin-left: 0.4rem; font-size: 1.2rem;}.egg_frame_wrap { position: fixed; left: 0; top: 0; z-index: 999; width: 100%; height: 100%; visibility: visible;}.egg_frame_wrap * { padding: 0; margin: 0; box-sizing: border-box; outline: none; border: none;}.egg_frame_wrap.hide { visibility: hidden;}.egg_frame_wrap.hide .egg_frame_mask,.egg_frame_wrap.hide .egg_frame_content_wrap { opacity: 0;}.egg_frame_wrap.hide .egg_frame_content_wrap { transform: scale(0);}.egg_frame_mask { background: #00000030; width: 100%; height: 100%; opacity: 1; transition: 200ms ease;}.egg_frame_content_wrap { position: absolute; width: 80%; height: 80%; top: 10%; left: 10%; display: flex; flex-direction: column; transition: 200ms ease; border-radius: 1rem; background: #ffffffe6; backdrop-filter: blur(1rem); overflow: hidden; transform: scale(1);}.egg_frame_content_wrap.max { top: 0; left: 0; width: 100%; height: 100%; border-radius: 0;}.egg_frame_content_wrap .egg_frame_controls_wrap { width: 100%; display: flex; justify-content: space-between; align-items: center; box-sizing: border-box;}.egg_frame_controls_wrap .egg_frame_title { padding: 1rem 2rem; font-size: 1.6rem;}.egg_frame_controls .egg_frame_btn { outline: none; border: none; background: none; padding: 1rem 2rem; transition: 80ms ease; cursor: pointer; color: #333; font-size: 1.8rem;}.egg_frame_controls .egg_frame_btn:active { opacity: 0.8;}.egg_frame_wrap .egg_frame_content { width: 100%; flex-grow: 1; border-top: 0.1rem solid #ccc; min-height: 40rem; min-width: 30rem; background: white;}.egg_frame_content .egg_frame { width: 100%; height: 100%; outline: none; border: none;}.egg_time_input { display: inline-flex; align-items: center; justify-content: center;}.egg_time_input .egg_hour_wrap,.egg_time_input .egg_minute_wrap { width: 4rem;}.egg_time_input .egg_separator { padding: 0 0.5rem; font-size: 1.5rem;}.egg_settings_item { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; overflow: hidden; border-radius: 1rem;}.egg_settings_item .egg_settings { display: inline-flex; flex-direction: column; font-size: 1.4rem; background: white; border-radius: 1rem; overflow: hidden; width: 100%; height: 100%; pointer-events: all; transform: translateX(100%); transition: transform 300ms ease; padding-top: 1rem;}.egg_settings_item .egg_settings.active { transform: translateX(0);}.egg_settings .egg_settings_label { padding-bottom: 1rem; user-select: none;}.egg_settings_item .egg_settings_version_wrap { padding: 1rem 2rem 0 2rem; display: flex; align-items: center; justify-content: space-between;}.egg_settings_version_wrap .egg_settings_version { color: #999; display: flex; align-items: center;}.egg_settings_version .egg_settings_version_detail { color: #24292f; font-size: 1.6rem; width: 1.6rem; height: 1.6rem; margin-left: 0.4rem;}.egg_settings_item .egg_settings_theme_wrap { padding: 1rem 2rem 0 2rem;}.egg_settings_theme_wrap .egg_settings_theme_colors { display: flex; align-items: center; justify-content: space-between;}.egg_settings_theme_color_wrap .egg_settings_theme_color { border-radius: 50%; width: 1.6rem; height: 1.6rem; background: currentColor;}.egg_settings .egg_settings_read_time_wrap,.egg_settings .egg_settings_watch_time_wrap { padding: 1rem 2rem 0 2rem; display: flex; justify-content: space-between; align-items: center;}.egg_settings_read_time_wrap .egg_settings_label,.egg_settings_watch_time_wrap .egg_settings_label { padding: 0.5rem 0;}.egg_settings_read_time_wrap .egg_select,.egg_settings_watch_time_wrap .egg_select { width: 6rem;}.egg_settings .egg_settings_token_wrap { padding: 1rem 2rem 0 2rem;}.egg_settings_token_wrap .egg_settings_token_input { outline: none; border: 0.1rem solid #eee; padding: 1rem; background: white; border-radius: 0.2rem; width: 100%; box-sizing: border-box; color: #ccc;}.egg_settings_token_wrap .egg_settings_token_input.active { color: #333;}.egg_settings_token_input::placeholder { color: #ccc;}.egg_settings .egg_settings_submit_btn_wrap { text-align: right; padding-top: 1rem; display: none;}.egg_settings .egg_settings_submit_btn_wrap.active { display: block;}.egg_settings_submit_btn_wrap .egg_settings_submit_btn { outline: none; border: 0.1rem solid #eee; padding: 0.5rem 1rem; text-align: center; background: white; border-radius: 0.2rem; cursor: pointer;}.egg_settings_submit_btn_wrap .egg_settings_submit_btn:active { background: #eee;}.egg_schedule { height: 100%; display: flex; flex-direction: column;}.egg_schedule_time_wrap { padding: 1rem 2rem; border-bottom: 0.1rem solid #eee;}.egg_schedule_time .egg_schedule_label { padding-bottom: 1rem; user-select: none;}.egg_schedule_time .egg_schedule_time_input_wrap { display: flex; justify-content: space-between; align-items: center;}.egg_schedule_time_input_wrap .egg_schedule_add_btn { outline: none; border: 0.1rem solid #eee; padding: 0.5rem 1rem; text-align: center; background: white; border-radius: 0.2rem; cursor: pointer;}.egg_schedule_time_input_wrap .egg_schedule_add_btn:active { background: #eee;}.egg_schedule_list { height: 100%; overflow: auto;}.egg_schedule_list .egg_schedule_item { display: flex; justify-content: space-between; padding: 0.5rem 1.5rem; font-size: 1.4rem; border-bottom: 0.1rem solid #eee;}.egg_schedule_list::-webkit-scrollbar { width: 0.4rem; background: white; border-radius: 0.2rem;}.egg_schedule_list::-webkit-scrollbar-thumb { background: #ccc; border-radius: 0.2rem;}.egg_schedule_detail_time_wrap { display: flex; align-items: center;}.egg_schedule_detail_time_wrap.inactive { color: #ccc;}.egg_schedule_detail_time_wrap .egg_schedule_detail_icon { padding-right: 0.4rem; display: flex; color: #ccc;}.egg_schedule_detail_del_wrap .egg_schedule_del_btn { outline: none; padding: 1rem; text-align: center; background: white; border-radius: 0.2rem; font-size: 1.4rem; cursor: pointer; color: #ccc;}.egg_schedule_detail_del_wrap .egg_schedule_del_btn:hover { color: #333;}.egg_schedule_detail_del_wrap .egg_schedule_del_btn:active { color: #eee;}.egg_schedule_list .egg_schedule_list_none { width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; color: #ccc;}.egg_schedule_list_none .egg_icon { font-size: 2.5rem;}.egg_schedule_list_none_text { padding-top: 1rem;}.egg_select { position: relative;}.egg_select .egg_select_input { outline: none; border: 0.1rem solid #eee; padding: 0.8rem; text-align: center; background: white; border-radius: 0.2rem; display: inline-block; width: 100%; box-sizing: border-box;}.egg_select .egg_select_input::placeholder { color: #ccc;}.egg_select_list { max-height: 12rem; border-radius: 0 0 0.2rem 0.2rem; box-shadow: 0 0.1rem 0.1rem 0.1rem #eee; background: white; user-select: none; transition: 100ms ease; scrollbar-width: thin; overflow: auto; opacity: 1; z-index: 9; width: 100%; position: absolute;}.egg_select_list.hide { opacity: 0; visibility: hidden;}.egg_select_list::-webkit-scrollbar { width: 0.4rem; background: white; border-radius: 0.2rem;}.egg_select_list::-webkit-scrollbar-thumb { background: #ccc; border-radius: 0.2rem;}.egg_select_list .egg_select_item { padding: 0.6rem 1rem; border-bottom: 0.1rem solid #eee; cursor: pointer; color: #333; transition: 300ms ease; text-align: center;}.egg_select_list .egg_select_item.selected { font-weight: bold; background: #f6f6f6;}.egg_select_list .egg_select_item.active { background: #eee;}.egg_select_list .egg_select_item:hover { background: #eee;}';
GM_addStyle(css); load((href) => href.match(URL_CONFIG.home), () => { initLogo(); log('进入主页面!'); initThemeColor(); initTaskConfig(); initSettings(); initFontSize(); initMainListener(); renderTip(); renderPanel(); renderFrame(); }); load((href) => href === GM_getValue('readingUrl'), async () => { log('进入文章选读页面!'); initThemeColor(); initSettings(); initFontSize(); initMaxRead(); initChildListener(); renderTip(); handleNews(); }); load((href) => href === GM_getValue('watchingUrl'), async () => { log('进入视听学习页面!'); initThemeColor(); initSettings(); initFontSize(); initMaxWatch(); initChildListener(); renderTip(); handleVideo(); }); load((href) => href === URL_CONFIG.examPractice, async () => { log('进入每日答题页面!'); initThemeColor(); initSettings(); initFontSize(); initChildListener(); renderTip(); await renderExamBtn(); doingExam(ExamType.PRACTICE); }); load((href) => href.includes(URL_CONFIG.examPaper), async () => { log('进入专项练习页面!'); initThemeColor(); initSettings(); initFontSize(); initChildListener(); renderTip(); await renderExamBtn(); doingExam(ExamType.PAPER); return; });
function initLogo() { console.log(`%c tech-study.js %c ${version} `, 'background:dodgerblue;color:white;font-size:15px;border-radius:4px 0 0 4px;padding:2px 0;', 'background:black;color:gold;font-size:15px;border-radius:0 4px 4px 0;padding:2px 0;'); }
function initTaskConfig() { try { const configTemp = JSON.parse(GM_getValue('taskConfig')); if (configTemp && Array.isArray(configTemp)) { if (configTemp.length === taskConfig.length) { taskConfig.forEach((task, i) => { task.active = configTemp[i].active; }); return; } } } catch (e) { } }
function initSettings() { try { const settingsTemp = JSON.parse(GM_getValue('studySettings')); if (settingsTemp && Array.isArray(settingsTemp)) { if (settingsTemp.length === settings.length) { for (const i in settingsTemp) { settings[i] = settingsTemp[i]; } } return; } } catch (e) { } }
function initFontSize() { const moblie = hasMobile(); if (moblie) { const meta = $$('meta[name=viewport]')[0]; if (meta) { meta.content = 'initial-scale=0, user-scalable=yes'; } const scale = ~~(window.innerWidth / window.outerWidth) || 1; document.documentElement.style.setProperty('--scale', String(scale)); } }
function initMaxRead() { try { const maxReadTemp = GM_getValue('maxRead'); if (maxReadTemp) { maxRead.value = maxReadTemp; } } catch (error) { } }
function initMaxWatch() { try { const maxWatchTemp = GM_getValue('maxWatch'); if (maxWatchTemp) { maxWatch.value = maxWatchTemp; } } catch (error) { } }
function initThemeColor() { try { watch(themeColor, () => { document.documentElement.style.setProperty('--themeColor', themeColor.value); }); const themeColorTemp = GM_getValue('themeColor'); if (themeColorTemp) { themeColor.value = themeColorTemp; } } catch (error) { } }
function renderTip() { const tipWrap = createElementNode('div', undefined, { class: 'egg_tip_wrap', }); mountElement(tipWrap); }
async function renderExamBtn() { const titles = await $_('.title'); if (titles.length) { titles[0].parentNode?.insertBefore(ExamBtn().ele, titles[0].nextSibling); } }
async function renderPanel() { const panel = Panel(); mountElement(panel); }
function renderFrame() { const frame = Frame(); mountElement(frame); }
async function getAnswer(question) { const data = { txt_name: md5(question), password: '', }; try { const params = new URLSearchParams(data); const res = await fetch(API_CONFIG.answerSearch, { method: 'POST', mode: 'cors', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: params.toString(), }); if (res.ok) { const result = await res.json(); const { data, status } = result; if (status !== 0) { const answerList = JSON.parse(data.txt_content); const answers = answerList[0].content.split(/[;\s]/); return answers; } } } catch (error) { } return []; }
async function saveAnswer(question, answer) { try { const content = JSON.stringify([{ title: md5(question), content: answer }]); const data = { txt_name: md5(question), txt_content: content, password: '', v_id: '', }; const params = new URLSearchParams(data); const res = await fetch(API_CONFIG.answerSave, { method: 'POST', mode: 'cors', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: params.toString(), }); if (res.ok) { const data = await res.json(); return data; } } catch (error) { } }
async function getNewsList() { const randNum = ~~(Math.random() * API_CONFIG.todayNews.length); try { const res = await fetch(API_CONFIG.todayNews[randNum], { method: 'GET', }); if (res.ok) { const data = await res.json(); return data; } } catch (err) { } }
async function getVideoList() { const randNum = ~~(Math.random() * API_CONFIG.todayVideos.length); try { const res = await fetch(API_CONFIG.todayVideos[randNum], { method: 'GET', }); if (res.ok) { const data = await res.json(); return data; } } catch (err) { } }
async function getExamPaper(pageNo) { const url = `${API_CONFIG.paperList}?pageSize=50&pageNo=${pageNo}`; try { const res = await fetch(url, { method: 'GET', credentials: 'include', }); if (res.ok) { const data = await res.json(); const paperJson = decodeURIComponent(escape(window.atob(data.data_str.replace(/-/g, '+').replace(/_/g, '/')))); const paper = JSON.parse(paperJson); return paper; } } catch (err) { return []; } return []; }
async function generateQRCode() { try { const res = await fetch(API_CONFIG.generateQRCode, { method: 'GET', mode: 'cors', }); if (res.ok) { const data = await res.json(); if (data.success) { return data.result; } } } catch (error) { } }
async function loginWithQRCode(qrCode) { try { const params = new URLSearchParams({ qrCode, goto: 'https://oa.xuexi.cn', pdmToken: '', }); const res = await fetch(API_CONFIG.loginWithQRCode, { method: 'POST', mode: 'cors', credentials: 'include', headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', }, body: params.toString(), }); if (res.ok) { const data = await res.json(); return data; } } catch (error) { } }
async function getSign() { try { const res = await fetch(API_CONFIG.sign, { method: 'GET', mode: 'cors', credentials: 'include', }); if (res.ok) { const data = await res.json(); if (data.ok) { return data.data.sign; } } } catch (error) { } }
async function secureCheck(data) { try { const params = new URLSearchParams(data); const url = `${API_CONFIG.secureCheck}?${params}`; const res = await fetch(url, { method: 'GET', mode: 'cors', credentials: 'include', }); if (res.ok) { const data = await res.json(); return data.success; } } catch (error) { } return false; }
async function pushPlus(token, title, content, template, toToken) { try { const body = { token, title, content, template, }; if (toToken) { body.to = toToken; } const res = await fetch(API_CONFIG.push, { method: 'POST', mode: 'cors', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(body), }); if (res.ok) { const data = await res.json(); return data; } } catch (error) { } }
async function getUserInfo() { try { const res = await fetch(API_CONFIG.userInfo, { method: 'GET', credentials: 'include', }); if (res.ok) { const { data } = await res.json(); return data; } } catch (err) { } }
async function getTotalScore() { try { const res = await fetch(API_CONFIG.totalScore, { method: 'GET', credentials: 'include', }); if (res.ok) { const { data } = await res.json(); const { score } = data; return score; } } catch (err) { } }
async function getTodayScore() { try { const res = await fetch(API_CONFIG.todayScore, { method: 'GET', credentials: 'include', }); if (res.ok) { const { data } = await res.json(); const { score } = data; return score; } } catch (err) { } }
async function getTaskList() { try { const res = await fetch(API_CONFIG.taskList, { method: 'GET', credentials: 'include', }); if (res.ok) { const { data } = await res.json(); const { taskProgress } = data; return taskProgress; } } catch (err) { } }
const maxNewsNum = 6;
const maxVideoNum = 6;
const maxRefreshCount = 10;
const autoRefreshQRCodeInterval = 100000;
const muted = true;
const URL_CONFIG = { home: /^https\:\/\/www\.xuexi\.cn(\/(index\.html)?)?$/, homeOrigin: 'https://www.xuexi.cn', examPractice: 'https://pc.xuexi.cn/points/exam-practice.html', examPaper: 'https://pc.xuexi.cn/points/exam-paper-detail.html', };
const API_CONFIG = { userInfo: 'https://pc-api.xuexi.cn/open/api/user/info', totalScore: 'https://pc-proxy-api.xuexi.cn/delegate/score/get', todayScore: 'https://pc-proxy-api.xuexi.cn/delegate/score/today/query', taskList: 'https://pc-proxy-api.xuexi.cn/delegate/score/days/listScoreProgress?sence=score&deviceType=2', todayNews: [ 'https://www.xuexi.cn/lgdata/35il6fpn0ohq.json', 'https://www.xuexi.cn/lgdata/1ap1igfgdn2.json', 'https://www.xuexi.cn/lgdata/vdppiu92n1.json', 'https://www.xuexi.cn/lgdata/152mdtl3qn1.json', ], todayVideos: [ 'https://www.xuexi.cn/lgdata/525pi8vcj24p.json', 'https://www.xuexi.cn/lgdata/11vku6vt6rgom.json', 'https://www.xuexi.cn/lgdata/2qfjjjrprmdh.json', 'https://www.xuexi.cn/lgdata/3o3ufqgl8rsn.json', 'https://www.xuexi.cn/lgdata/591ht3bc22pi.json', 'https://www.xuexi.cn/lgdata/1742g60067k.json', 'https://www.xuexi.cn/lgdata/1novbsbi47k.json', ], paperList: 'https://pc-proxy-api.xuexi.cn/api/exam/service/paper/pc/list', answerSave: 'https://a6.qikekeji.com/txt/data/save', answerSearch: 'https://a6.qikekeji.com/txt/data/detail', push: 'https://www.pushplus.plus/send', generateQRCode: 'https://login.xuexi.cn/user/qrcode/generate', loginWithQRCode: 'https://login.xuexi.cn/login/login_with_qr', sign: 'https://pc-api.xuexi.cn/open/api/sns/sign', secureCheck: 'https://pc-api.xuexi.cn/login/secure_check', qrcode: 'https://api.qrserver.com/v1/create-qr-code', };
const version = '1.7.0';
var TaskType; (function (TaskType) { TaskType[TaskType["LOGIN"] = 0] = "LOGIN"; TaskType[TaskType["READ"] = 1] = "READ"; TaskType[TaskType["WATCH"] = 2] = "WATCH"; TaskType[TaskType["PRACTICE"] = 3] = "PRACTICE"; })(TaskType || (TaskType = {}));
var SettingType; (function (SettingType) { SettingType[SettingType["AUTO_START"] = 0] = "AUTO_START"; SettingType[SettingType["SAME_TAB"] = 1] = "SAME_TAB"; SettingType[SettingType["SILENT_RUN"] = 2] = "SILENT_RUN"; SettingType[SettingType["SCHEDULE_RUN"] = 3] = "SCHEDULE_RUN"; SettingType[SettingType["RANDOM_EXAM"] = 4] = "RANDOM_EXAM"; SettingType[SettingType["REMOTE_PUSH"] = 5] = "REMOTE_PUSH"; })(SettingType || (SettingType = {}));
var TaskStatusType; (function (TaskStatusType) { TaskStatusType[TaskStatusType["LOADING"] = 0] = "LOADING"; TaskStatusType[TaskStatusType["LOADED"] = 1] = "LOADED"; TaskStatusType[TaskStatusType["START"] = 2] = "START"; TaskStatusType[TaskStatusType["PAUSE"] = 3] = "PAUSE"; TaskStatusType[TaskStatusType["FINISH"] = 4] = "FINISH"; })(TaskStatusType || (TaskStatusType = {}));
let currentSub;
const subscription = new WeakMap();
const proxyMap = new WeakMap();
const trackRef = (target) => { if (!currentSub) { return; } let subList = subscription.get(target); if (!subList) { subList = new Map(); const subkeyList = new Set(); subkeyList.add(currentSub); subList.set('value', subkeyList); subscription.set(target, subList); return; } let subkeyList = subList.get('value'); if (!subkeyList) { subkeyList = new Set(); subkeyList.add(currentSub); subList.set('value', subkeyList); subscription.set(target, subList); return; } subkeyList.add(currentSub); };
function triggerRef(target, newVal, oldVal) { const subList = subscription.get(target); if (!subList) { return; } let subkeyList = subList.get('value'); if (!subkeyList) { return; } for (const fn of subkeyList) { if (fn instanceof Function) { fn(newVal, oldVal); } } }
const track = (target, key) => { if (!currentSub) { return; } const proxyTarget = proxyMap.get(target); if (!proxyTarget) { return; } let subList = subscription.get(target); if (!subList) { subList = new Map(); const subkeyList = new Set(); subkeyList.add(currentSub); subList.set(key, subkeyList); subscription.set(target, subList); return; } let subkeyList = subList.get(key); if (!subkeyList) { subkeyList = new Set(); subkeyList.add(currentSub); subList.set(key, subkeyList); subscription.set(target, subList); return; } subkeyList.add(currentSub); };
function trigger(target, key, newVal, oldVal) { const proxyTarget = proxyMap.get(target); if (!proxyTarget) { return; } const subList = subscription.get(target); if (!subList) { return; } let subkeyList = subList.get(key); if (!subkeyList) { return; } for (const fn of subkeyList) { fn(newVal, oldVal); } }
var ReactiveFlags; (function (ReactiveFlags) { ReactiveFlags["IS_REF"] = "_isRef"; ReactiveFlags["IS_SHALLOW"] = "_isShallow"; ReactiveFlags["IS_REACTIVE"] = "_isReactive"; ReactiveFlags["IS_READONLY"] = "_isReadonly"; })(ReactiveFlags || (ReactiveFlags = {}));
class Ref { _isShallow = false; _isRef = true; _value; value; constructor(val, shallow = false) { const _this = this; this._isShallow = shallow; if (val && typeof val === 'object' && shallow) { const reactiveVal = reactive(val); this._value = reactiveVal; this.value = reactiveVal; } else { this._value = val; this.value = val; } Object.defineProperty(this, 'value', { get() { trackRef(this); return _this._value; }, set(newVal) { const oldVal = this._value; if (oldVal !== newVal) { _this._value = newVal; triggerRef(this, newVal, oldVal); } }, }); } toJSON() { return this._value; } }
const isRef = (v) => { return !!(v && v[ReactiveFlags.IS_REF]); };
const isShallow = (v) => { return !!(v && v[ReactiveFlags.IS_SHALLOW]); };
const createRef = (rawVal, shallow) => { return new Ref(rawVal, shallow); };
const unref = (val) => { return (isRef(val) ? val.value : val); };
const ref = (value) => { return isRef(value) ? value : createRef(value, true); };
const shallowRef = (value) => { return isRef(value) ? value : createRef(value, false); };
const createReactiveHandlers = (isReadonly, isShallow) => { return { get: createGetters(isReadonly, isShallow), set: createSetters(isReadonly, isShallow), }; };
const createGetters = (isReadonly, isShallow) => { return function get(target, key, receiver) { if (key === ReactiveFlags.IS_REACTIVE) { return !isReadonly; } if (key === ReactiveFlags.IS_READONLY) { return isReadonly; } if (key === ReactiveFlags.IS_SHALLOW) { return isShallow; } const res = Reflect.get(target, key, receiver); if (!isReadonly) { track(target, key); } if (isShallow) { return res; } if (isRef(res)) { return res.value; } if (res && typeof res === 'object') { if (res instanceof Element) { return res; } return isReadonly ? readonly(res) : reactive(res); } return res; }; };
const createSetters = (readonly, shallow) => { return function set(target, key, newVal, receiver) { if (readonly) { return false; } const oldVal = target[key]; if (isReadonly(oldVal) && isRef(oldVal) && !isRef(newVal)) { return false; } if (!shallow) { if (isRef(oldVal) && !isRef(newVal)) { oldVal.value = newVal; return true; } } const res = Reflect.set(target, key, newVal, receiver); if (Array.isArray(target) && key === 'length') { trigger(target, key, newVal, oldVal); return res; } if (oldVal !== newVal) { trigger(target, key, newVal, oldVal); } return res; }; };
const createReactiveObj = (target, isReadonly, shallow) => { const existingProxy = proxyMap.get(target); if (existingProxy) { return existingProxy; } const proxy = new Proxy(target, createReactiveHandlers(isReadonly, shallow)); proxyMap.set(target, proxy); return proxy; };
const isReactive = (val) => { return !!(val && val[ReactiveFlags.IS_REACTIVE]); };
const createReactive = (target) => { return createReactiveObj(target, false, false); };
const shallowReactive = (target) => { return createReactiveObj(target, false, true); };
const isReadonly = (val) => { return !!(val && val[ReactiveFlags.IS_READONLY]); };
const createReadonly = (target) => { return createReactiveObj(target, true, false); };
const shallowReadonly = (target) => { return createReactiveObj(target, true, true); };
const isProxy = (val) => { return isReactive(val) || isReadonly(val); };
const reactive = (target) => { return createReactive(target); };
const readonly = (target) => { return createReadonly(target); };
const watch = (source, callback, immediate = false) => { immediate && callback(unref(source), unref(source)); if (Array.isArray(source) && source.every((s) => isRef(s))) { for (const i in source) { if (isProxy(source[i])) { watch(source[i], () => { const res = source.map((s) => unref(s)); callback(res, res); }); } } watch(() => source.map((s) => unref(s)), callback); return; } if (source instanceof Function) { watch(watchEffectRef(source), (n, o) => { callback(unref(n), unref(o)); }); return; } if (isProxy(source)) { for (const key in source) { currentSub = () => { callback(source, source); }; const subSource = source[key]; currentSub = undefined; watch(subSource, () => { callback(source, source); }); } return; } if (isRef(source)) { if (isProxy(source.value)) { watch(source.value, () => { callback(unref(source), unref(source)); }); } currentSub = callback; trackRef(source); currentSub = undefined; return; } };
const watchEffect = (callback) => { currentSub = callback; callback(); currentSub = undefined; };
const watchRef = (source, callback) => { const effectRes = shallowRef(callback()); watch(source, () => (effectRes.value = unref(callback()))); return effectRes; };
const watchEffectRef = (callback) => { const effectRes = shallowRef(undefined); watchEffect(() => (effectRes.value = unref(callback()))); return effectRes; };
function createElementNode(tagName, props, attrs, children, options) { let beforemount = ref(false); let mounted = ref(false); const { onCreated, beforeCreat, onMounted, beforeMount } = options || {}; const subscribe = (e) => { const { onMounted, beforeMount } = e; if (beforeMount) { watch(beforemount, () => { if (beforemount.value) { beforeMount(); return; } }, true); } if (onMounted) { watch(mounted, () => { if (mounted.value) { onMounted(); return; } }, true); } }; const unsubscribe = (e) => { }; beforeCreat && beforeCreat(); const ele = document.createElement(tagName); handleProps(ele, props); handleAttributes(ele, attrs, subscribe, unsubscribe); handleChildren(ele, children, subscribe, unsubscribe); const collectBeforeMount = () => { beforemount.value = true; beforeMount && beforeMount(); }; const collectOnMounted = () => { mounted.value = true; onMounted && onMounted(); }; onCreated && onCreated(); return { ele, beforeMount: collectBeforeMount, onMounted: collectOnMounted }; }
function createNSElementNode(tagName, props, attrs, children, options) { let beforemount = ref(false); let mounted = ref(false); const { onCreated, beforeCreat, onMounted, beforeMount } = options || {}; const subscribe = (e) => { const { onMounted, beforeMount } = e; if (beforeMount) { watch(beforemount, () => { if (beforemount.value) { beforeMount(); return; } }, true); } if (onMounted) { watch(mounted, () => { if (mounted.value) { onMounted(); return; } }, true); } }; const unsubscribe = (e) => { }; beforeCreat && beforeCreat(); const ns = 'http://www.w3.org/2000/svg'; const ele = document.createElementNS(ns, tagName); handleProps(ele, props); handleAttributes(ele, attrs, subscribe, unsubscribe); handleChildren(ele, children, subscribe, unsubscribe); const collectBeforeMount = () => { beforemount.value = true; beforeMount && beforeMount(); }; const collectOnMounted = () => { mounted.value = true; onMounted && onMounted(); }; onCreated && onCreated(); return { ele, beforeMount: collectBeforeMount, onMounted: collectOnMounted }; }
function handleProps(ele, props) { for (const key in props) { if (isRef(props[key])) { const refVal = props[key]; watchEffect(() => (ele[key] = refVal.value)); continue; } ele[key] = props[key]; } }
function handleAttributes(ele, attrs, subscribe, unsubscribe) { if (attrs) { for (const key in attrs) { handleAttribute(ele, key, attrs[key], subscribe, unsubscribe); } } }
function handleEventOptions(option) { if (option.length) { const options = { capture: option.includes('capture'), once: option.includes('once'), passive: option.includes('passive'), }; return options; } }
function handleAttribute(ele, key, value, subscribe, unsubscribe) { const formatKey = key.toLowerCase(); if (formatKey.startsWith('on')) { const [event] = formatKey.match(/(?<=on).*/); if (event) { const [eventType, ...option] = event.split('_'); const options = handleEventOptions(option); if (isRef(value)) { const refVal = value; const refListener = watchRef(refVal, () => refVal.value ? (e) => { option.includes('prevent') && e.preventDefault(); option.includes('stop') && e.stopPropagation(); const callback = refVal.value; callback(e); } : undefined); refListener.value && ele.addEventListener(eventType, refListener.value, options); watch(refListener, (newVal, oldVal) => { oldVal && ele.removeEventListener(eventType, oldVal); newVal && ele.addEventListener(eventType, newVal, options); }); return; } if (value instanceof Function) { ele.addEventListener(eventType, value, options); } } return; } const specificAttrs = ['checked', 'selected', 'disabled', 'enabled']; if (specificAttrs.includes(formatKey)) { if (isRef(value)) { const refVal = value; watchEffect(() => { if (refVal.value) { ele.setAttribute(formatKey, ''); } else { ele.removeAttribute(formatKey); } }); return; } if (value) { ele.setAttribute(formatKey, ''); } else { ele.removeAttribute(formatKey); } return; } if (key === 'ref') { if (isRef(value)) { const refVal = value; subscribe && subscribe({ onMounted() { refVal.value = ele; }, }); return; } if (value instanceof Function) { const refFn = value; subscribe && subscribe({ onMounted() { refFn(ele); }, }); return; } return; } if (key.startsWith('xlink:')) { const attrNS = 'http://www.w3.org/1999/xlink'; if (value) { ele.setAttributeNS(attrNS, key, value); } else { ele.removeAttributeNS(attrNS, key); } return; } if (key && isRef(value)) { const refVal = value; watchEffect(() => { ele.setAttribute(key, refVal.value); }); return; } if (key) { ele.setAttribute(key, value); } }
function handleChildren(ele, children, subscribe, unsubscribe) { if (isRef(children)) { const comment = document.createComment(''); watch(children, async (newEle, oldEle) => { if (!newEle && oldEle) { if (oldEle instanceof Promise) { const oldEleRes = await oldEle; if (oldEleRes) { oldEleRes.forEach((ele) => { unsubscribe && unsubscribe(ele); }); } } if (!(oldEle instanceof Promise)) { oldEle.forEach((ele) => { unsubscribe && unsubscribe(ele); }); } ele.replaceChildren(comment); return; } if (newEle) { if (oldEle) { if (oldEle instanceof Promise) { const oldEleRes = await oldEle; if (oldEleRes) { oldEleRes.forEach((ele) => { unsubscribe && unsubscribe(ele); }); } } if (!(oldEle instanceof Promise)) { oldEle.forEach((ele) => { unsubscribe && unsubscribe(ele); }); } } if (newEle instanceof Promise) { const newEleRes = await newEle; if (newEleRes) { const eles = newEleRes.map((v) => { if (v.beforeMount || v.onMounted) { subscribe && subscribe(v); } return v.ele; }); ele.replaceChildren(createElementBlock(eles)); } return; } const eles = newEle.map((v) => { if (v.beforeMount || v.onMounted) { subscribe && subscribe(v); } return v.ele; }); ele.replaceChildren(createElementBlock(eles)); return; } }); if (children.value instanceof Promise) { ele.appendChild(comment); children.value.then((childrenEle) => { if (childrenEle) { const eles = childrenEle.map((v) => { if (v.beforeMount || v.onMounted) { subscribe && subscribe(v); } return v.ele; }); ele.replaceChildren(createElementBlock(eles)); } }); return; } if (children.value) { const eles = children.value.map((v) => { if (v.beforeMount || v.onMounted) { subscribe && subscribe(v); } return v.ele; }); ele.appendChild(createElementBlock(eles)); return; } ele.appendChild(comment); return; } if (children instanceof Promise) { const comment = document.createComment(''); ele.appendChild(comment); children.then((childEle) => { if (childEle) { const { beforeMount, onMounted } = childEle; if (beforeMount || onMounted) { subscribe && subscribe(childEle); } comment.replaceWith(childEle.ele); } }); return; } if (Array.isArray(children)) { const resChildren = []; for (const i in children) { const child = children[i]; if (isRef(child)) { const comment = document.createComment(''); watch(child, async (newEle, oldEle) => { if (!newEle && oldEle) { if (oldEle instanceof Promise) { const oldEleRes = await oldEle; if (oldEleRes) { handleChangeElement(newEle, oldEleRes, comment, subscribe, unsubscribe); } return; } handleChangeElement(newEle, oldEle, comment, subscribe, unsubscribe); return; } if (newEle && !oldEle) { if (newEle instanceof Promise) { const newEleRes = await newEle; if (newEleRes) { handleChangeElement(newEleRes, oldEle, comment, subscribe, unsubscribe); } return; } handleChangeElement(newEle, oldEle, comment, subscribe, unsubscribe); return; } if (newEle && oldEle) { if (newEle instanceof Promise && oldEle instanceof Promise) { const newEleRes = await newEle; const oldEleRes = await oldEle; handleChangeElement(newEleRes, oldEleRes, comment, subscribe, unsubscribe); return; } if (newEle instanceof Promise && !(oldEle instanceof Promise)) { const newEleRes = await newEle; handleChangeElement(newEleRes, oldEle, comment, subscribe, unsubscribe); return; } if (!(newEle instanceof Promise) && oldEle instanceof Promise) { const oldEleRes = await oldEle; handleChangeElement(newEle, oldEleRes, comment, subscribe, unsubscribe); return; } if (!(oldEle instanceof Promise) && !(newEle instanceof Promise)) { handleChangeElement(newEle, oldEle, comment, subscribe, unsubscribe); return; } } }); if (child.value instanceof Promise) { resChildren[i] = { ele: comment }; child.value.then((childEle) => { if (childEle) { const { beforeMount, onMounted } = childEle; if (beforeMount || onMounted) { subscribe && subscribe(childEle); } comment.replaceWith(childEle.ele); } }); continue; } if (child.value) { const { beforeMount, onMounted, ele } = child.value; resChildren[i] = { ele, beforeMount, onMounted }; continue; } resChildren[i] = { ele: comment }; continue; } if (child instanceof Promise) { const comment = document.createComment(''); resChildren[i] = { ele: comment }; child.then((childEle) => { if (childEle) { const { beforeMount, onMounted } = childEle; if (beforeMount || onMounted) { subscribe && subscribe(childEle); } comment.replaceWith(childEle.ele); } }); continue; } if (child) { const { beforeMount, onMounted, ele } = child; resChildren[i] = { ele, beforeMount, onMounted }; } } const eles = resChildren.map((v) => { if (v.beforeMount || v.onMounted) { subscribe && subscribe(v); } return v.ele; }); ele.appendChild(createElementBlock(eles)); return; } if (children) { const { beforeMount, onMounted } = children; if (beforeMount || onMounted) { subscribe && subscribe(children); } ele.appendChild(children.ele); return; } return; }
function handleChangeElement(newEle, oldEle, comment, subscribe, unsubscribe) { if (newEle && oldEle) { const { beforeMount, onMounted } = newEle; if (beforeMount || onMounted) { subscribe && subscribe(newEle); } oldEle.ele.replaceWith(newEle.ele); return; } if (newEle && !oldEle) { const { beforeMount, onMounted } = newEle; if (beforeMount || onMounted) { subscribe && subscribe(newEle); } comment.replaceWith(newEle.ele); return; } if (!newEle && oldEle) { unsubscribe && unsubscribe(oldEle); oldEle.ele.replaceWith(comment); return; } }
function createTextNode(text, options) { const { onCreated, beforeCreat, onMounted, beforeMount } = options || {}; beforeCreat && beforeCreat(); if (isRef(text)) { const refVal = text; const ele = document.createTextNode(''); watchEffect(() => { ele.data = refVal.value; }); onCreated && onCreated(); return { ele, beforeMount, onMounted }; } onCreated && onCreated(); return { ele: document.createTextNode(String(text)), beforeMount, onMounted }; }
function mountElement(eleOptions, parent = document.body) { const { ele, beforeMount, onMounted } = eleOptions; if (ele) { beforeMount && beforeMount(); parent.appendChild(ele); onMounted && onMounted(); } }
function $$(selector, parent = document) { return Array.from(parent.querySelectorAll(selector)); }
function $_(selector, parent = document, timeout) { return new Promise((resolve) => { const timer = setInterval(() => { const selectors = Array.from(parent.querySelectorAll(selector)); if (selectors.length) { clearInterval(timer); resolve(selectors); } }, 10); if (timeout) { setTimeout(() => { clearInterval(timer); resolve([]); }, timeout); } }); }
function createElementBlock(eles) { const fragment = document.createDocumentFragment(); for (const i in eles) { fragment.appendChild(eles[i]); } return fragment; }
function log(...text) { printColor('dodgerblue', ...text); }
function error(...text) { printColor('red', ...text); }
function info(...text) { printColor('yellow', ...text); }
function printColor(color, ...text) { const textFormatted = text .map((t) => (typeof t === 'object' ? JSON.stringify(t) : String(t))) .join(' '); console.log(`%c[${formatDateTime()}] %c${textFormatted}`, '', `color: ${color}`); }
function getProgressHTML(title, percent) { const progressHTML = `<div style=" display: flex; align-items: center; justify-content: space-between; padding: 1px 0; " > <span>${title}</span> <span>${getHighlightHTML(percent)} %</span> </div> <div style=" background: white; border-radius: 10px; height: 10px; border: 1px solid #eee; flex-shrink: 1; " > <div style=" background: linear-gradient(to left, #188fff80, #1890ff); height: 100%; width: ${percent}%; border-radius: 10px; " ></div> </div>`; return progressHTML; }
function getHighlightHTML(text) { const highlightHTML = `<span style="color: #1890ff">${text}</span>`; return highlightHTML; }
function getImgHTML(src) { return ` <div style="padding: 10px 0"> <div style=" display: flex; justify-content: center; align-items: center; padding: 20px; background: #f7f7f7; border-radius: 10px; " > <img src="${src}" style="width:200px;height:200px;" /> </div> </div> `; }
function createModal(options) { const { title, subTitle = '', to = '用户', content, type, from = 'tech-study.js', } = options; let contentText = ''; if (Array.isArray(content)) { contentText = content.map((ct) => `<div>${ct}</div>`).join(''); } else { contentText = content; } const dateTime = formatDateTime(); let typeHTML = ''; if (type && type.length) { if (type === 'info') { typeHTML = ` <svg viewBox="64 64 896 896" style="color: #1890ff; width: 18px; height: 18px" fill="currentColor" aria-hidden="true" > <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z" ></path> </svg>`; } if (type === 'warn') { typeHTML = ` <svg viewBox="64 64 896 896" style="color: #faad14; width: 18px; height: 18px" fill="currentColor" aria-hidden="true" > <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z" ></path> </svg> `; } if (type === 'success') { typeHTML = ` <svg viewBox="64 64 896 896" style="color: #52c41a; width: 18px; height: 18px" fill="currentColor" aria-hidden="true" > <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z" ></path> </svg> `; } if (type === 'fail') { typeHTML = ` <svg viewBox="64 64 896 896" style="color: #ff4d4f; width: 18px; height: 18px" fill="currentColor" aria-hidden="true" > <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" ></path> </svg> `; } } const typeWrap = ` <span style=" padding-right: 5px; display: flex; justify-content: center; align-items: center; " > ${typeHTML} </span> `; const baseHTML = ` <div style=" padding: 5px; display: flex; justify-content: center; align-items: center; " > <div style=" background: #ffffff; box-shadow: 1px 1px 8px -1px #dadada; padding: 5px 10px; border-radius: 5px; width: 100%; " > <div style=" display: flex; justify-content: space-between; padding: 5px; border-bottom: 1px solid #eee; " > <div style="display: flex; justify-content: center; align-items: center"> ${typeWrap} <span style="padding-left: 5px; font-size: 18px">${title}</span> </div> <div style="font-size: 16px; color: #999">${subTitle}</div> </div> <div></div> <div style="padding:10px 5px; font-size: 16px; min-height: 80px"> <div> ${getHighlightHTML(to)}, 你好! </div> <div style="line-height: 28px;">${contentText}</div> </div> <div style=" font-size: 14px; padding: 5px; border-top: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; " > <div style="color: #999">${dateTime}</div> <div> <span>来自</span> <span style="color: #1890ff; padding-left: 1px">${from}</span> </div> </div> </div> </div> `; return baseHTML; }
async function pushMessage(options) { const { title, content, template, fromToken, toToken } = options; const res = await pushPlus(fromToken, title, content, template, toToken); return res; }
async function pushModal(options, fromToken, toToken) { const html = createModal(options); const res = await pushMessage({ title: '消息提示', content: html, fromToken, toToken, template: 'html', }); if (res && res.code === 200) { return res; } return; }
function createRandomPoint(bounds) { const { x, y, width, height } = bounds; const randX = x + Math.random() * width * 0.5 + width * 0.25; const randY = y + Math.random() * height * 0.5 + height * 0.25; return { x: randX, y: randY, }; }
function createRandomPath(start, end, steps) { const minDeltaX = (end.x - start.x) / steps; const maxDeltaY = (end.y - start.y) / steps; const path = []; path.push(start); for (let i = 0; i < steps; i++) { const x = path[i].x + Math.random() * 5 + minDeltaX; const y = path[i].y + Math.random() * 5 * Math.pow(-1, ~~(Math.random() * 2 + 1)) + maxDeltaY; path.push({ x, y, }); } return path; }
function generateNumAsChar() { return (~~(Math.random() * 10)).toString(); }
function generateUpperAsChar() { return String.fromCharCode(~~(Math.random() * 26) + 65); }
function generateLowerAsChar() { return String.fromCharCode(~~(Math.random() * 26) + 97); }
function generateMix(length = 6) { const randomText = []; const typeGenerator = [ generateNumAsChar, generateUpperAsChar, generateLowerAsChar, ]; if (length) { for (let i = 0; i < length; i++) { const randomIndex = ~~(Math.random() * typeGenerator.length); randomText.push(typeGenerator[randomIndex]()); } } return randomText.join(''); }
function formatDateNum(num) { return num < 10 ? `0${num}` : `${num}`; }
function formatDateTime(time = Date.now()) { const date = new Date(time); const s = date.getSeconds(); const min = date.getMinutes(); const h = date.getHours(); const d = date.getDate(); const m = date.getMonth() + 1; const y = date.getFullYear(); const dateText = [y, m, d].map(formatDateNum).join('-'); const timeText = [h, min, s].map(formatDateNum).join(':'); const dateTimeText = `${dateText} ${timeText}`; return dateTimeText; }
const formatTime = (time = Date.now()) => { const date = new Date(time); const s = date.getSeconds(); const min = date.getMinutes(); const h = date.getHours(); const timeText = [h, min, s].map(formatDateNum).join(':'); return timeText; };
function isLate({ hour, minute }) { const date = new Date(); const h = date.getHours(); const min = date.getMinutes(); return h > hour || (h === hour && min >= minute); }
function isNow({ hour, minute }) { const date = new Date(); const h = date.getHours(); const min = date.getMinutes(); const s = date.getSeconds(); return h === hour && min === minute && s === 0; }
function setCookie(name, value, expires, domain) { const date = new Date(); date.setTime(date.getTime() + expires); document.cookie = `${name}=${value};expires=${date.toUTCString()};path=/;domain=${domain}`; }
function getCookie(name) { const strCookies = document.cookie; const cookieText = strCookies.split(';'); for (const i in cookieText) { const item = cookieText[i].split('='); if (item[0].trim() === name) { return item[1].trim(); } } return null; }
function delCookie(name, domain) { const value = getCookie(name); if (value !== null) { setCookie(name, '', -1, domain); } }
function debounce(callback, delay) { let timer = -1; return function (...args) { if (timer !== -1) { clearTimeout(timer); } timer = setTimeout(() => { callback.apply(this, args); }, delay); }; }
function hasMobile() { let isMobile = false; if (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i)) { log('移动端'); isMobile = true; } if (document.body.clientWidth < 800) { log('小尺寸设备端'); isMobile = true; } return isMobile; }
function sleep(time) { let timeDelay = Number(time); if (!Number.isInteger(timeDelay)) { timeDelay = 1000; } timeDelay += Math.random() * 500 - 250; return new Promise((resolve) => { setTimeout(() => { resolve(undefined); }, timeDelay); }); }
function studyPauseLock(callback) { return new Promise((resolve) => { const pauseStudy = GM_getValue('pauseStudy') || false; if (pauseStudy) { const doing = setInterval(() => { const pauseStudy = GM_getValue('pauseStudy') || false; if (!pauseStudy) { clearInterval(doing); log('学习等待结束!'); if (callback && callback instanceof Function) { callback(true); } resolve(true); return; } if (callback && callback instanceof Function) { callback(false); } log('学习等待...'); }, 500); return; } resolve(true); }); }
function load(match, callback) { const { href } = window.location; window.addEventListener('load', () => { if (match instanceof Function) { match(href) && callback(); return; } if (typeof match === 'boolean') { match && callback(); return; } if (href.match(match)) { callback(); return; } }); }
const href = window.location.href;
const taskConfig = reactive([ { title: '登录', currentScore: 0, dayMaxScore: 0, need: 0, status: false, tip: '每日首次登录积1分。', percent: 0, score: 0, active: true, immutable: true, type: TaskType.LOGIN, }, { title: '文章选读', currentScore: 0, dayMaxScore: 0, need: 0, status: false, tip: '每有效阅读一篇文章积1分,上限6分。有效阅读文章累计1分钟积1分,上限6分。每日上限积12分。', percent: 0, score: 0, active: true, immutable: false, type: TaskType.READ, }, { title: '视听学习', currentScore: 0, dayMaxScore: 0, need: 0, status: false, tip: '每有效一个音频或观看一个视频积1分,上限6分。有效收听音频或观看视频累计1分钟积1分,上限6分。每日上限积12分。', percent: 0, score: 0, active: true, immutable: false, type: TaskType.WATCH, }, { title: '每日答题', currentScore: 0, dayMaxScore: 0, need: 0, status: false, tip: '每组答题每答对1道积1分。每日上限积5分。', percent: 0, score: 0, active: true, immutable: false, type: TaskType.PRACTICE, }, ]);
const settings = reactive([false, false, false, false, false, false]);
const totalScore = ref(0);
const todayScore = ref(0);
const userinfo = reactive({ nick: '', avatar: '', });
const taskStatus = ref(TaskStatusType.LOADING);
const examPause = ref(false);
const login = ref(!!getCookie('token'));
const id = ref('');
const scheduleList = shallowReactive([]);
const pushToken = ref('');
const refreshCount = ref(0);
const frame = reactive({ title: '', show: false, exist: false, closed: true, ele: undefined, src: '', });
const page = ref(undefined);
const loginQRCodeShow = ref(false);
const maxRead = ref(100);
const maxWatch = ref(120);
const running = ref(false);
const themeColor = ref('#fa3333');
var ExamType; (function (ExamType) { ExamType[ExamType["PRACTICE"] = 0] = "PRACTICE"; ExamType[ExamType["PAPER"] = 1] = "PAPER"; })(ExamType || (ExamType = {}));
function getNextButton() { return new Promise((resolve) => { const timer = setInterval(() => { const nextAll = $$('.ant-btn').filter((next) => next.innerText); if (nextAll.length) { clearInterval(timer); if (nextAll.length === 2) { resolve(nextAll[1]); return; } resolve(nextAll[0]); } }, 500); }); }
function handleSlideVerify() { return new Promise(async (resolve) => { const mask = $$('#nc_mask')[0]; if (mask && getComputedStyle(mask).display !== 'none') { createTip('等待滑动验证'); mask.style.zIndex = '999'; const track = (await $_('.nc_scale', undefined, 3000))[0]; const slide = (await $_('.btn_slide', undefined, 3000))[0]; await sleep(2000); const rectTrack = track.getBoundingClientRect(); const rectSlide = slide.getBoundingClientRect(); const window = unsafeWindow; const start = createRandomPoint(rectSlide); const end = { x: rectTrack.x + rectTrack.width, y: rectTrack.y + rectTrack.height / 2, }; const path = createRandomPath(start, end, 10); const mobile = hasMobile(); if (mobile) { slide.style.touchAction = 'none'; const touchstartTouch = new Touch({ identifier: 0, target: slide, clientX: path[0].x, clientY: path[0].y, }); const touchstartList = [touchstartTouch]; const touchstart = new TouchEvent('touchstart', { targetTouches: touchstartList, touches: touchstartList, changedTouches: touchstartList, view: window, bubbles: true, }); slide.dispatchEvent(touchstart); for (const i in path) { const touchmoveTouch = new Touch({ identifier: 0, target: slide, clientX: path[i].x, clientY: path[i].y, }); const touchmoveList = [touchmoveTouch]; const touchmove = new TouchEvent('touchmove', { targetTouches: touchmoveList, touches: touchmoveList, changedTouches: touchmoveList, view: window, bubbles: true, }); slide.dispatchEvent(touchmove); await sleep(10); } const touchendTouch = new Touch({ identifier: 0, target: slide, clientX: path[path.length - 1].x, clientY: path[path.length - 1].y, }); const touchendList = [touchendTouch]; const touchend = new TouchEvent('touchend', { targetTouches: [], touches: [], changedTouches: touchendList, view: window, bubbles: true, }); slide.dispatchEvent(touchend); } else { const mousedown = new MouseEvent('mousedown', { clientX: path[0].x, clientY: path[0].y, bubbles: true, view: window, }); slide.dispatchEvent(mousedown); for (const i in path) { const mousemove = new MouseEvent('mousemove', { clientX: path[i].x, clientY: path[i].y, bubbles: true, view: window, }); slide.dispatchEvent(mousemove); await sleep(10); } const mouseup = new MouseEvent('mouseup', { clientX: path[path.length - 1].x, clientY: path[path.length - 1].y, bubbles: true, view: window, }); slide.dispatchEvent(mouseup); } createTip('滑动验证完成!'); const timer = setInterval(() => { const mask = $$('#nc_mask')[0]; if (!mask || getComputedStyle(mask).display === 'none') { log('滑动验证成功!'); createTip('滑动验证成功!'); clearInterval(timer); resolve(true); return; } resolve(false); log('滑动验证失败!'); createTip('滑动验证失败!'); }, 1000); return; } resolve(true); }); }
function handleChoiceBtn(answers) { const allBtns = $$('.q-answer'); if (answers.length && allBtns.length) { return answers.every((answer) => { if (answer && answer.length) { let minLengthChoice; allBtns.forEach((choice) => { const choiceText = choice.innerText.trim(); const unsignedChoiceText = choiceText.replaceAll(/[、,,。 ]/g, ''); const unsignedAnswer = answer.replaceAll(/[、,,。 ]/g, ''); if (choiceText === answer || choiceText.includes(answer) || answer.includes(choiceText) || unsignedChoiceText.includes(unsignedAnswer)) { if (minLengthChoice) { if (minLengthChoice.innerText.length > choiceText.length) { minLengthChoice = choice; } } else { minLengthChoice = choice; } } }); if (minLengthChoice) { if (!minLengthChoice.classList.contains('chosen')) { minLengthChoice.click(); } return true; } } return false; }); } return false; }
function handleSingleChoiceRand() { const allBtns = $$('.q-answer'); if (allBtns.length) { const index = ~~(Math.random() * allBtns.length); const randBtn = allBtns[index]; if (!randBtn.classList.contains('chosen')) { randBtn.click(); } } }
function handleMutiplyChoiceRand() { const allBtns = $$('.q-answer'); if (allBtns.length) { allBtns.forEach((allBtn) => { if (!allBtn.classList.contains('chosen')) { allBtn.click(); } }); } }
const handleBlankInput = (answers) => { const blanks = $$('.blank'); if (blanks.length && answers.length) { if (answers.length === blanks.length) { return answers.every((answer, i) => { if (answer && answer.length) { const inputEvent = new Event('input', { bubbles: true, }); blanks[i].setAttribute('value', answer); blanks[i].dispatchEvent(inputEvent); return true; } return false; }); } if (blanks.length === 1 && answers.length > 1) { const answer = answers.join(''); if (answer && answer.length) { const inputEvent = new Event('input', { bubbles: true, }); blanks[0].setAttribute('value', answer); blanks[0].dispatchEvent(inputEvent); return true; } } } return false; };
async function handleBlankInputRand() { const blanks = $$('.blank'); if (blanks.length) { const inputEvent = new Event('input', { bubbles: true, }); blanks.forEach((blank) => { blank.setAttribute('value', '答案'); blank.dispatchEvent(inputEvent); }); } }
function examPauseLock(callback) { return new Promise((resolve) => { const pauseStudy = (GM_getValue('pauseStudy') || false); if (pauseStudy) { examPause.value = true; } if (examPause.value) { createTip('已暂停, 手动开启自动答题! ', 10); const doing = setInterval(() => { if (!examPause.value) { clearInterval(doing); log('答题等待结束!'); if (callback && callback instanceof Function) { createTip('已开启, 自动答题!'); callback(true); } resolve(true); return; } if (callback && callback instanceof Function) { callback(false); } log('答题等待...'); }, 500); return; } resolve(true); }); }
async function doingExam(type) { let nextButton; let nextText; let shouldSaveAnswer = false; while (true) { await sleep(2500); await examPauseLock(); nextButton = await getNextButton(); nextText = nextButton.innerText.replaceAll(' ', ''); const finish = ['再练一次', '再来一组', '查看解析']; if (finish.includes(nextButton.innerText)) { break; } $$('.tips')[0]?.click(); const allTips = $$('.line-feed font[color]'); const answers = allTips.map((tip) => tip.innerText.trim()); const question = $$('.q-body')[0].innerText; await sleep(1500); await examPauseLock(); const allBtns = $$('.q-answer'); const blanks = $$('input[type=text][class=blank]'); const questionType = ($$('.q-header')[0].innerText.substring(0, 3)); await examPauseLock(); switch (questionType) { case '填空题': { if (answers.length) { const res = handleBlankInput(answers); if (res) { break; } } createTip('答案异常, 尝试网络题库获取!'); log('正在获取答案...'); const answersNetwork = await getAnswer(question); log(`获取答案${answersNetwork.length ? '成功' : '失败'}!`, { question, answersNetwork, }); if (answersNetwork.length) { const res = handleBlankInput(answersNetwork); if (res) { break; } } if (type === ExamType.PRACTICE || settings[SettingType.RANDOM_EXAM]) { log('答案不存在, 随机作答!'); createTip('答案不存在, 随机作答!'); await handleBlankInputRand(); } else { const res = await pushModal({ title: '学习推送', to: userinfo.nick, content: '答题存在异常, 已暂停答题!', type: 'fail', }, pushToken.value); createTip(`学习推送${res ? '成功' : '失败'}!`); examPause.value = true; shouldSaveAnswer = true; } break; } case '多选题': { if (answers.length) { const choicesText = allBtns.map((btn) => btn.innerText); const choicesContent = choicesText .map((choiceText) => choiceText.split(/[A-Z]./)[1].trim()) .join(''); const blanks = question.match(/()/g); if ((blanks && allBtns.length === blanks.length) || question === choicesContent || allBtns.length === 2) { allBtns.forEach((choice) => { if (!choice.classList.contains('chosen')) { choice.click(); } }); break; } if (allBtns.length >= answers.length) { const res = handleChoiceBtn(answers); if (res) { break; } } } createTip('答案异常, 尝试网络题库获取!'); log('正在获取答案...'); const answersNetwork = await getAnswer(question); log(`获取答案${answersNetwork.length ? '成功' : '失败'}!`, { question, answersNetwork, }); if (answersNetwork.length) { const res = handleChoiceBtn(answersNetwork); if (res) { break; } } if (type === ExamType.PRACTICE || settings[SettingType.RANDOM_EXAM]) { log('答案不存在, 随机作答!'); createTip('答案不存在, 随机作答!'); await handleMutiplyChoiceRand(); } else { const res = await pushModal({ title: '学习推送', to: userinfo.nick, content: '答题存在异常, 已暂停答题!', type: 'fail', }, pushToken.value); createTip(`学习推送${res ? '成功' : '失败'}!`); examPause.value = true; shouldSaveAnswer = true; } break; } case '单选题': { if (answers.length) { if (answers.length === 1) { const res = handleChoiceBtn(answers); if (res) { break; } } else { const seperator = [ '', ' ', ',', ';', ',', '、', '-', '|', '+', '/', ]; const answersLike = seperator .map((s) => answers.join(s).trim()) .filter((answer) => answer.length); if (answersLike.length) { const res = answersLike.some((answer) => { return handleChoiceBtn([answer]); }); if (res) { break; } } } } createTip('答案异常, 尝试网络题库获取!'); log('正在获取答案...'); const answersNetwork = await getAnswer(question); log(`获取答案${answersNetwork.length ? '成功' : '失败'}!`, { question, answersNetwork, }); if (answersNetwork.length) { if (answersNetwork.length === 1) { const res = handleChoiceBtn(answersNetwork); if (res) { break; } } else { const seperator = ['', ' ']; const answersLike = seperator.map((s) => answers.join(s)); if (answersLike.every((answer) => answer.length)) { const res = answersLike.some((answer) => { return handleChoiceBtn([answer]); }); if (res) { break; } } } } if (type === ExamType.PRACTICE || settings[SettingType.RANDOM_EXAM]) { log('答案不存在, 随机作答!'); createTip('答案不存在, 随机作答!'); await handleSingleChoiceRand(); } else { const res = await pushModal({ title: '学习推送', to: userinfo.nick, content: '答题存在异常, 已暂停答题!', type: 'fail', }, pushToken.value); createTip(`学习推送${res ? '成功' : '失败'}!`); examPause.value = true; shouldSaveAnswer = true; } break; } } await examPauseLock(); nextButton = await getNextButton(); nextText = nextButton.innerText.replaceAll(' ', ''); if (shouldSaveAnswer) { const answers = []; if (questionType === '填空题') { blanks.forEach((blank) => { answers.push(blank.value); }); } if (questionType === '单选题' || questionType === '多选题') { allBtns.forEach((choice) => { if (choice.classList.contains('chosen')) { const answerTemp = choice.innerText; const [, answer] = answerTemp.split('.'); if (answer && answer.length) { answers.push(answer); } } }); } const answer = answers.join(';'); if (answer.length) { log('正在上传答案...'); const res = await saveAnswer(question, answer); log(`上传答案${res ? '成功' : '失败'}!`, { question, answer }); } shouldSaveAnswer = false; } if (nextText === '确定') { nextButton.click(); await sleep(2000); await examPauseLock(); const answerBox = $$('.answer')[0]; if (answerBox) { const answerTemp = answerBox.innerText; const [, answerText] = answerTemp.split(':'); if (answerText && answerText.length) { const answer = answerText.replaceAll(' ', ';'); log('正在上传答案...'); const res = await saveAnswer(question, answer); log(`上传答案${res ? '成功' : '失败'}!`, { question, answer }); } } } nextButton = await getNextButton(); nextText = nextButton.innerText.replaceAll(' ', ''); if (nextText === '下一题' || nextText === '完成' || nextText === '交卷') { await sleep(2500); nextButton.click(); } await handleSlideVerify(); } handleCloseTaskWin(); }
async function doExamPractice() { await studyPauseLock(); log('正在每日答题...'); createTip('正在每日答题'); const url = URL_CONFIG.examPractice; await waitTaskWin(url, '每日答题'); createTip('完成每日答题!'); await sleep(1500); await refreshScoreInfo(); await refreshTaskList(); if (taskConfig[TaskType.PRACTICE].active && !taskConfig[TaskType.PRACTICE].status) { log('任务未完成, 继续每日答题!'); createTip('任务未完成, 继续每日答题!'); await doExamPractice(); } }
async function doExamPaper() { running.value = true; log('正在专项练习...'); createTip('正在专项练习'); const examPaperId = await findExamPaper(); if (examPaperId) { const url = `${URL_CONFIG.examPaper}?id=${examPaperId}`; log(`链接: ${url}`); await waitTaskWin(url, '专项练习'); createTip('完成专项练习!'); running.value = false; if (settings[SettingType.SAME_TAB]) { frame.exist = false; } return; } running.value = false; createTip('专项练习均已完成!'); }
async function initExam() { const data = await getExamPaper(1); if (data) { await sleep(3000); return data.totalPageCount; } }
async function findExamPaper() { const total = await initExam(); let current = 1; log(`正在寻找的专项练习...`); createTip(`正在寻找的专项练习...`); while (current <= total && current) { const data = await getExamPaper(current); if (data) { const examPapers = data.list; for (const i in examPapers) { if (examPapers[i].status !== 2) { return examPapers[i].id; } } current += 1; await sleep(3000); } else { break; } } }
function initMainListener() { window.addEventListener('message', (msg) => { const { data } = msg; if (data.id === id.value && data.closed) { closeFrame(); return; } }); }
function initChildListener() { window.addEventListener('message', (msg) => { const { data } = msg; if (data.id && !data.closed) { id.value = data.id; log(`初始化窗口 ID: ${id.value}`); return; } }); }
async function openFrame(url, title) { frame.src = url; await $_('.egg_frame'); if (frame.ele) { id.value = generateMix(10); frame.closed = false; frame.title = title || ''; await waitFrameLoaded(frame.ele); frame.ele.contentWindow?.postMessage({ id: id.value, closed: false }, url); return true; } return false; }
function closeFrame() { log(`关闭窗口 ID: ${id.value}`); frame.show = false; frame.closed = true; frame.title = ''; frame.src = ''; }
function handleCloseFrame() { window.parent.postMessage({ id: id.value, closed: true }, URL_CONFIG.homeOrigin); }
function waitFrameClose() { return new Promise((resolve) => { const timer = setInterval(() => { if (frame.closed) { clearInterval(timer); resolve(true); } }, 100); }); }
function waitFrameLoaded(iframe) { return new Promise((resolve) => { iframe.addEventListener('load', () => { resolve(true); }); }); }
function openWin(url) { return GM_openInTab(url, { active: true, insert: true, setParent: true, }); }
function closeWin() { page.value && page.value.close(); }
function handleCloseWin() { try { window.opener = window; const win = window.open('', '_self'); win?.close(); top?.close(); } catch (e) { } }
function waitWinClose(newPage) { return new Promise((resolve) => { newPage.onclose = () => { resolve(undefined); }; }); }
function closeTaskWin() { if (settings[SettingType.SAME_TAB] && id.value) { closeFrame(); return; } closeWin(); }
function handleCloseTaskWin() { if (settings[SettingType.SAME_TAB] && id.value) { handleCloseFrame(); return; } handleCloseWin(); }
async function waitTaskWin(url, title) { if (settings[SettingType.SAME_TAB]) { frame.exist = true; frame.show = !settings[SettingType.SILENT_RUN]; const res = await openFrame(url, title); if (res) { await waitFrameClose(); } return; } page.value = openWin(url); await waitWinClose(page.value); }
let refreshTimer = -1;
let tryLoginTimer = -1;
async function getQRCode() { log('正在生成登录二维码...'); const qrCode = await generateQRCode(); if (qrCode) { log('生成登录二维码成功!'); const url = `https://login.xuexi.cn/login/qrcommit?showmenu=false&code=${qrCode}&appId=dingoankubyrfkttorhpou`; return { code: qrCode, src: `${API_CONFIG.qrcode}?data=${encodeURIComponent(url)}`, url, }; } log('生成登录二维码失败!'); }
async function checkQRCode(code) { log('尝试用二维码登录...'); const res = await loginWithQRCode(code); if (res) { const { data, code, success } = res; if (success && data) { return data; } if (code === '11019') { return; } } return new Promise((resolve) => { clearTimeout(tryLoginTimer); tryLoginTimer = setTimeout(async () => { resolve(await checkQRCode(code)); }, 1000); }); }
async function tryLogin(checkCode) { log('正在获取签名...'); const sign = await getSign(); if (sign) { const uuid = crypto.randomUUID(); const [, code] = checkCode.split('='); const state = `${sign}${uuid}`; const res = await secureCheck({ code, state }); return res; } }
async function handleLogin() { clearInterval(refreshTimer); refreshTimer = setInterval(() => { handleLogin(); }, autoRefreshQRCodeInterval); if (refreshCount.value >= maxRefreshCount) { createTip('超过最大重试次数, 登录失败!'); refreshCount.value = 0; loginQRCodeShow.value = false; if (settings[SettingType.REMOTE_PUSH]) { const res = await pushModal({ title: '登录推送', content: '超过最大重试次数, 登录失败!', type: 'fail', }, pushToken.value); createTip(`登录推送${res ? '成功' : '失败'}!`); } return; } const imgWrap = $$('.egg_login_img_wrap')[0]; const img = $$('.egg_login_img', imgWrap)[0]; if (imgWrap && img) { log('刷新登录二维码!'); refreshCount.value++; const qrCode = await getQRCode(); if (qrCode) { const { src, code, url } = qrCode; img.src = src; loginQRCodeShow.value = true; if (settings[SettingType.REMOTE_PUSH]) { const imgWrap = getImgHTML(src); const aWrap = ` <div> 或在浏览器 <a href="dtxuexi://appclient/page/study_feeds?url=${encodeURIComponent(url)}" style="text-decoration: none" >${getHighlightHTML('打开学习强国APP')}</a > </div> `; const res = await pushModal({ title: '登录推送', content: ['扫一扫, 登录学习强国!', aWrap, imgWrap], type: 'info', }, pushToken.value); createTip(`登录推送${res ? '成功' : '失败'}!`); } const checkCode = await checkQRCode(code); if (checkCode) { const loginRes = await tryLogin(checkCode); if (loginRes) { clearInterval(refreshTimer); loginQRCodeShow.value = false; log('登录成功!'); createTip('登录成功!'); login.value = true; await refreshUserInfo(); await refreshScoreInfo(); await refreshTaskList(); if (settings[SettingType.REMOTE_PUSH]) { const res = await pushModal({ title: '登录推送', to: userinfo.nick, content: [ '学习强国, 登录成功!', `当天积分: ${getHighlightHTML(todayScore.value)} 分`, `总积分: ${getHighlightHTML(totalScore.value)} 分`, ...taskConfig.map((task) => getProgressHTML(task.title, task.percent)), ], type: 'success', }, pushToken.value); createTip(`登录推送${res ? '成功' : '失败'}!`); } } return; } log('登录二维码失效!'); handleLogin(); } } }
function handleLogout() { delCookie('token', '.xuexi.cn'); closeFrame(); frame.exist = false; login.value = false; userinfo.nick = ''; userinfo.avatar = ''; totalScore.value = 0; todayScore.value = 0; taskConfig.forEach((task) => { task.percent = 0; }); taskStatus.value = TaskStatusType.LOADING; log('退出登录'); }
let news = [];
let videos = [];
async function handleNews() { const sections = await $_('section', undefined, 5000); const section = sections[0]; if (!(section && section.innerText.includes('系统正在维护中'))) { reading(0); return; } log('未找到文章!'); createTip('未找到文章!'); handleCloseTaskWin(); }
async function handleVideo() { const videos = await $_('video', undefined, 10000); const video = videos[0]; const playBtn = $$('.prism-play-btn')[0]; if (video && playBtn) { video.muted = muted; log('正在尝试播放视频...'); const timeout = setTimeout(() => { log('视频播放超时!'); createTip('视频播放超时!'); handleCloseTaskWin(); }, 20000); video.addEventListener('canplay', () => { log('正在尝试播放视频...'); if (video.paused) { video.play(); if (video.paused) { playBtn.click(); } } if (!video.paused) { clearTimeout(timeout); reading(1); } }); return; } log('未找到视频!'); handleCloseTaskWin(); }
async function reading(type) { let time = 30; if (type === 0) { const sections = $$('section'); const maxTextCount = Math.max(...sections.map((s) => s.innerText.length), 200); const predictTime = ~~((60 * maxTextCount) / 1000); time = Math.min(predictTime, maxRead.value); } if (type === 1) { const video = $$('video')[0]; const predictTime = ~~video.duration; time = Math.min(predictTime, maxWatch.value); } time = time - ~~(Math.random() * 10) + 5; const firstTime = time - (~~(Math.random() * 4) + 4); const secendTime = ~~(Math.random() * 4) + 8; const window = unsafeWindow; const tip = createTip('距离关闭页面还剩', time, true, async (time) => { await studyPauseLock((flag) => { if (type === 1) { const video = $$('video')[0]; if (video.paused === !flag) { return; } video[flag ? 'play' : 'pause'](); } }); if (time === firstTime) { window.scrollTo(0, 400); const scroll = new Event('scroll', { bubbles: true, }); document.dispatchEvent(scroll); const mousemove = new MouseEvent('mousemove', { bubbles: true, }); document.dispatchEvent(mousemove); const click = new Event('click', { bubbles: true, }); document.dispatchEvent(click); } if (time === secendTime) { const scrollLength = document.body.scrollHeight / 2; window.scrollTo(0, scrollLength); const scroll = new Event('scroll', { bubbles: true, }); document.dispatchEvent(scroll); const mousemove = new MouseEvent('mousemove', { bubbles: true, }); document.dispatchEvent(mousemove); const click = new Event('click', { bubbles: true, }); document.dispatchEvent(click); } }); await tip.waitCountDown(); handleCloseTaskWin(); }
async function getNews() { const need = taskConfig[TaskType.READ].need < maxNewsNum ? taskConfig[TaskType.READ].need : maxNewsNum; log(`剩余 ${need} 个新闻`); const data = await getNewsList(); if (data && data.length) { let i = 0; const latestItems = data.slice(0, 100); const currentYear = new Date().getFullYear().toString(); while (i < need) { const randomIndex = ~~(Math.random() * latestItems.length); const item = latestItems[randomIndex]; if (item.publishTime.startsWith(currentYear) && item.type === 'tuwen') { news[i] = item; i++; } } } else { news = []; } }
async function getVideos() { const need = taskConfig[TaskType.WATCH].need < maxVideoNum ? taskConfig[TaskType.WATCH].need : maxVideoNum; log(`剩余 ${need} 个视频`); const data = await getVideoList(); if (data && data.length) { let i = 0; const latestItems = data.slice(0, 100); const currentYear = new Date().getFullYear().toString(); while (i < need) { const randomIndex = ~~(Math.random() * latestItems.length); const item = latestItems[randomIndex]; if (item.publishTime.startsWith(currentYear) && (item.type === 'shipin' || item.type === 'juji')) { videos[i] = item; i++; } } } else { videos = []; } }
async function readNews() { await getNews(); for (const i in news) { if (!taskConfig[TaskType.READ].active) { return; } await studyPauseLock(); log(`正在阅读第 ${Number(i) + 1} 个新闻...`); createTip(`正在阅读第 ${Number(i) + 1} 个新闻`); const { url } = news[i]; GM_setValue('readingUrl', url); await waitTaskWin(url, '文章选读'); GM_setValue('readingUrl', null); createTip(`完成阅读第 ${Number(i) + 1} 个新闻!`); await sleep(1500); await refreshScoreInfo(); await refreshTaskList(); if (taskConfig[TaskType.READ].active && taskConfig[TaskType.READ].status) { break; } } if (!taskConfig[TaskType.READ].active) { return; } if (taskConfig[TaskType.READ].active && !taskConfig[TaskType.READ].status) { log('任务未完成, 继续阅读新闻!'); createTip('任务未完成, 继续阅读新闻!'); await readNews(); } }
async function watchVideo() { await getVideos(); for (const i in videos) { if (!taskConfig[TaskType.WATCH].active) { return; } await studyPauseLock(); log(`正在观看第 ${Number(i) + 1} 个视频...`); createTip(`正在观看第 ${Number(i) + 1} 个视频`); const { url } = videos[i]; GM_setValue('watchingUrl', url); await waitTaskWin(url, '视听学习'); GM_setValue('watchingUrl', null); createTip(`完成观看第 ${Number(i) + 1} 个视频!`); await sleep(1500); await refreshScoreInfo(); await refreshTaskList(); if (taskConfig[TaskType.WATCH].active && taskConfig[TaskType.WATCH].status) { break; } } if (!taskConfig[TaskType.WATCH].active) { return; } if (taskConfig[TaskType.WATCH].active && !taskConfig[TaskType.WATCH].status) { log('任务未完成, 继续观看视频!'); createTip('任务未完成, 继续观看看视频!'); await watchVideo(); } }
let scheduleTimer = -1;
async function refreshScheduleTask() { clearInterval(scheduleTimer); if (!login.value) { const restList = scheduleList.filter((s) => !isLate(s)); if (restList.length) { const rest = restList[0]; log(`已设置 ${rest.time} 的定时任务!`); createTip(`已设置 ${rest.time} 的定时任务!`); let time = 0; const interval = 10; scheduleTimer = setInterval(() => { if (!(time++ % interval)) { log('定时刷新正在运行...'); } if (isNow(rest)) { clearInterval(scheduleTimer); log(`执行 ${rest.time} 的定时任务!`); createTip(`执行 ${rest.time} 的定时任务!`); handleLogin(); } }, 1000); } } }
function clearScheduleTask() { clearInterval(scheduleTimer); }
function createTip(text, delay = 2, countShow = false, callback) { const tipWrap = $$('.egg_tip_wrap')[0]; const tips = $$('.egg_tip'); if (tips.length) { tips.forEach((t) => t.delay()); } const delayCount = ref(delay); const textContent = ref(text); const show = ref(false); const delayShow = ref(false); let destroyed = false; let done = false; const tip = Tip({ text: textContent, count: delayCount, show, delayShow, countShow: ref(countShow), callback: async (count) => { callback && (await callback(count)); if (delayShow.value && count === delay) { delayShow.value = false; } if (count <= 0) { done = true; operate.destroy(); } }, }); const operate = { destroy() { if (!destroyed) { operate.hide(); destroyed = true; return new Promise((resolve) => { setTimeout(() => { tip.ele.remove(); resolve(undefined); }, 300); }); } }, hide() { if (!destroyed) { show.value = false; } }, show() { if (!destroyed) { return new Promise((resolve) => { setTimeout(() => { show.value = true; resolve(undefined); }, 300); }); } }, setText(text) { if (!destroyed) { textContent.value = text; } }, waitCountDown() { return new Promise((resolve) => { const timer = setInterval(() => { if (done) { clearInterval(timer); resolve(true); } }, 100); }); }, delay() { if (!destroyed) { delayShow.value = true; delayCount.value += 2; } }, }; Object.assign(tip.ele, operate); mountElement(tip, tipWrap); operate.show(); return operate; }
async function refreshUserInfo() { if (!login.value) { throw new Error('用户未登录!'); } if (userinfo.nick) { return true; } log('加载用户信息...'); const res = await getUserInfo(); if (res) { const { avatarMediaUrl = '', nick: nickRes } = res; if (nickRes) { userinfo.nick = nickRes; userinfo.avatar = avatarMediaUrl; return true; } } log('加载用户信息失败!'); return false; }
async function refreshScoreInfo() { if (!login.value) { throw new Error('用户未登录!'); } log('加载分数信息...'); const totalScoreRes = await getTotalScore(); const todayScoreRes = await getTodayScore(); if (Number.isInteger(totalScoreRes) && Number.isInteger(todayScoreRes)) { totalScore.value = totalScoreRes; todayScore.value = todayScoreRes; return true; } log('加载分数信息失败!'); return false; }
async function refreshTaskList() { if (!login.value) { throw new Error('用户未登录!'); } log('加载任务进度...'); const taskProgress = await getTaskList(); if (taskProgress) { taskConfig[TaskType.LOGIN].currentScore = taskProgress[2].currentScore; taskConfig[TaskType.LOGIN].dayMaxScore = taskProgress[2].dayMaxScore; taskConfig[TaskType.LOGIN].need = taskProgress[2].dayMaxScore - taskProgress[2].currentScore; taskConfig[TaskType.READ].currentScore = taskProgress[0].currentScore; taskConfig[TaskType.READ].dayMaxScore = taskProgress[0].dayMaxScore; taskConfig[TaskType.READ].need = taskProgress[0].dayMaxScore - taskProgress[0].currentScore; taskConfig[TaskType.WATCH].currentScore = taskProgress[1].currentScore; taskConfig[TaskType.WATCH].dayMaxScore = taskProgress[1].dayMaxScore; taskConfig[TaskType.WATCH].need = taskProgress[1].dayMaxScore - taskProgress[1].currentScore; taskConfig[TaskType.PRACTICE].currentScore = taskProgress[3].currentScore; taskConfig[TaskType.PRACTICE].dayMaxScore = taskProgress[3].dayMaxScore; taskConfig[TaskType.PRACTICE].need = taskProgress[3].dayMaxScore; for (const i in taskConfig) { const { currentScore, dayMaxScore } = taskConfig[i]; const rate = Number(((100 * currentScore) / dayMaxScore).toFixed(1)); taskConfig[i].percent = rate; taskConfig[i].score = currentScore; taskConfig[i].status = rate === 100; } return; } await sleep(2000); refreshTaskList(); return; } function Tip({ text, count, show, delayShow, countShow, callback, }) { return createElementNode('div', undefined, { class: watchRef([show, delayShow], () => `egg_tip${show.value ? (delayShow.value ? ' active delay' : ' active') : ''}`), }, [ createElementNode('span', undefined, { class: 'egg_text', }, createTextNode(text)), watchEffectRef(() => countShow.value ? createElementNode('span', undefined, { class: 'egg_countdown', }, createTextNode(watchEffectRef(() => `${count.value}s`))) : undefined), ], { onMounted() { const countDown = async () => { await callback(count.value); if (!count.value) { show.value = false; return; } count.value--; setTimeout(countDown, 1000); }; countDown(); }, }); }
function Hr({ text }) { return createElementNode('div', undefined, { class: 'egg_hr_wrap', }, [ createElementNode('div', undefined, { class: 'egg_hr' }), createElementNode('div', undefined, { class: 'egg_hr_title' }, createTextNode(text)), createElementNode('div', undefined, { class: 'egg_hr' }), ]); } function Select({ data, maxlength, placeholder = '', onchange, onblur, value, keep, }) { const selectData = reactive(data.map((v) => ({ selected: false, active: false, ele: undefined, ...v }))); const focus = ref(false); const input = shallowRef(undefined); const list = shallowRef(undefined); const valueRef = ref(''); value && watch(value, () => { const item = selectData.find((v) => v.value === value.value); valueRef.value = item ? item.label : ''; if (!item) { selectData.forEach((v) => (v.selected = false)); list.value && (list.value.scrollTop = 0); } }, true); return createElementNode('div', undefined, { class: 'egg_select', }, [ createElementNode('input', { value: valueRef }, { class: 'egg_select_input', type: 'text', placeholder, maxlength, ref: input, onfocus() { if (list.value && input.value) { focus.value = true; if (input.value.value && valueRef.value) { const index = selectData.findIndex((v) => v.label === valueRef.value); if (index + 1) { list.value.scrollTop = selectData[index].ele?.offsetTop || 0; selectData.forEach((v, i) => (v.selected = i === index)); } return; } } }, oninput() { if (list.value && input.value) { const { value } = input.value; if (value) { const index = selectData.findIndex((v) => v.label.includes(value)); if (index + 1) { list.value.scrollTop = selectData[index].ele?.offsetTop || 0; selectData.forEach((v, i) => { v.active = i === index; v.active && setTimeout(() => { v.active = false; }, 300); }); } return; } selectData.forEach((v) => (v.active = v.selected = false)); list.value.scrollTop = 0; } }, onblur() { if (list.value && input.value) { const item = selectData.find((v) => v.selected); if (item || !input.value.value) { setTimeout(() => { focus.value = false; }, 100); } if (item && input.value.value !== item.label) { input.value.value = item.label; } if (!item && keep) { input.value.value = valueRef.value; } onblur && onblur(item ? { label: item.label, value: item.value } : undefined); } }, }), createElementNode('div', undefined, { class: watchEffectRef(() => `egg_select_list${focus.value ? '' : ' hide'}`), ref: list, }, selectData.map((v, index) => createElementNode('div', undefined, { class: watchRef(() => [v.selected, v.active], () => `egg_select_item${v.selected ? ' selected' : v.active ? ' active' : ''}`), ref: (e) => (v.ele = e), onclick: debounce(() => { if (valueRef.value !== v.label) { onchange && onchange({ label: v.label, value: v.value }); selectData.forEach((v, i) => { v.selected = i === index; v.selected && (valueRef.value = v.label); }); } focus.value = false; }, 300), }, createTextNode(v.label)))), ]); }
function ExamBtn() { return createElementNode('button', undefined, { class: watchEffectRef(() => `egg_exam_btn${examPause.value ? ' manual' : ''}`), type: 'button', onclick: () => { examPause.value = !examPause.value; }, }, createTextNode(watchEffectRef(() => `${examPause.value ? '开启自动答题' : '关闭自动答题'}`))); }
function Frame() { const max = ref(false); return createElementNode('div', undefined, { class: watchEffectRef(() => `egg_frame_wrap${frame.show ? '' : ' hide'}`), }, watchRef(() => [login.value, settings[SettingType.SAME_TAB]], () => { if (login.value && settings[SettingType.SAME_TAB]) { return [ createElementNode('div', undefined, { class: 'egg_frame_mask' }), createElementNode('div', undefined, { class: watchEffectRef(() => `egg_frame_content_wrap ${max.value ? ' max' : ''}`), }, [ createElementNode('div', undefined, { class: 'egg_frame_controls_wrap' }, [ createElementNode('div', undefined, { class: 'egg_frame_title', }), createElementNode('div', undefined, { class: 'egg_frame_controls', }, [ createElementNode('button', undefined, { class: 'egg_frame_btn', type: 'button', title: '隐藏', onclick: debounce(() => { frame.show = false; }, 300), }, createNSElementNode('svg', undefined, { viewBox: '0 0 1024 1024', class: 'egg_icon', }, createNSElementNode('path', undefined, { d: 'M863.7 552.5H160.3c-10.6 0-19.2-8.6-19.2-19.2v-41.7c0-10.6 8.6-19.2 19.2-19.2h703.3c10.6 0 19.2 8.6 19.2 19.2v41.7c0 10.6-8.5 19.2-19.1 19.2z', }))), createElementNode('button', undefined, { class: 'egg_frame_btn', type: 'button', title: '缩放', onclick: debounce(() => { max.value = !max.value; }, 300), }, createNSElementNode('svg', undefined, { viewBox: '0 0 1024 1024', class: 'egg_icon', }, createNSElementNode('path', undefined, { d: 'M609.52 584.92a35.309 35.309 0 0 1 24.98-10.36c9.37 0 18.36 3.73 24.98 10.36l189.29 189.22-0.07-114.3 0.57-6.35c3.25-17.98 19.7-30.5 37.9-28.85 18.2 1.65 32.12 16.92 32.09 35.2v200.23c-0.05 1.49-0.19 2.97-0.42 4.45l-0.21 1.13c-0.22 1.44-0.55 2.85-0.99 4.24l-0.57 1.62-0.56 1.41a34.163 34.163 0 0 1-7.62 11.36l2.12-2.4-0.14 0.14-0.92 1.06-1.06 1.2-0.57 0.57-0.56 0.57a36.378 36.378 0 0 1-16.23 8.39l-3.53 0.5-4.02 0.35h-199.6l-6.35-0.63c-16.73-3.06-28.9-17.63-28.93-34.64l0.56-6.35c3.07-16.76 17.67-28.93 34.71-28.92l114.29-0.14-189.07-189.1-4.09-4.94c-9.71-14.01-8.01-32.95 4.02-45.02z m-162.06 0c12.06 12.05 13.78 30.99 4.09 45.01l-4.09 4.94-189.15 189.08 114.3 0.14c17.04-0.01 31.65 12.17 34.71 28.92l0.57 6.35c-0.03 17.01-12.19 31.58-28.92 34.64l-6.35 0.63H173.09l-4.23-0.42-3.39-0.49a36.38 36.38 0 0 1-17.36-9.52l-1.06-1.13-0.98-1.13 0.98 1.06-1.97-2.26 0.85 1.06-0.42-0.56a35.137 35.137 0 0 1-3.74-5.64l-1.13-2.68a34.71 34.71 0 0 1-2.11-7.33l-0.28-1.13c-0.21-1.47-0.33-2.96-0.36-4.45V659.78c-0.03-18.28 13.89-33.55 32.09-35.2 18.2-1.65 34.65 10.87 37.9 28.85l0.57 6.35-0.07 114.36 189.29-189.22c13.77-13.77 36.11-13.77 49.88 0h-0.09z m-74.71-471.71l6.35 0.57c16.76 3.06 28.93 17.67 28.92 34.71l-0.63 6.35c-3.07 16.76-17.67 28.93-34.71 28.92l-114.3 0.14 189.15 189.08 4.09 4.94c10.26 15.02 7.42 35.37-6.55 47.01-13.98 11.63-34.51 10.74-47.42-2.07L208.29 233.71l0.07 114.3-0.57 6.35c-3.25 17.98-19.7 30.5-37.9 28.85-18.2-1.65-32.12-16.92-32.09-35.2V147.78c0-1.55 0.14-3.03 0.35-4.51l0.21-1.13c0.24-1.44 0.59-2.85 1.06-4.23a34.97 34.97 0 0 1 8.68-14.39l-2.12 2.4-0.42 0.57 1.55-1.84-0.99 1.06 0.92-0.98 2.26-2.33c3.04-2.73 6.52-4.92 10.3-6.49l2.82-1.06c3.45-1.07 7.04-1.62 10.65-1.62l-3.6 0.14h0.49l1.48-0.14h201.31z m512.91 0l1.41 0.14h0.42c2.43 0.29 4.84 0.79 7.19 1.48l2.82 1.06 2.61 1.2 3.04 1.76c2.09 1.33 4.03 2.89 5.78 4.66l1.13 1.2 0.78 0.98 0.21 0.14 0.49 0.64 2.33 3.17c2.35 3.83 3.98 8.07 4.8 12.49l0.21 1.13c0.21 1.48 0.35 2.96 0.35 4.44v200.37c-0.16 18.13-14.03 33.19-32.08 34.83-18.06 1.64-34.42-10.67-37.83-28.48l-0.57-6.35V233.65L659.54 422.87c-12.9 12.95-33.56 13.91-47.59 2.2-14.04-11.71-16.81-32.2-6.38-47.22l4.02-4.86 189.22-189.08-114.29-0.14c-17.06 0.04-31.71-12.14-34.78-28.92l-0.63-6.35c-0.01-17.04 12.16-31.65 28.93-34.71l6.35-0.57h201.27z m0 0', }))), createElementNode('button', undefined, { class: 'egg_frame_btn', type: 'button', title: '关闭', onclick: debounce(() => { closeFrame(); }, 300), }, createNSElementNode('svg', undefined, { viewBox: '0 0 1024 1024', class: 'egg_icon', }, createNSElementNode('path', undefined, { d: 'M453.44 512L161.472 220.032a41.408 41.408 0 0 1 58.56-58.56L512 453.44 803.968 161.472a41.408 41.408 0 0 1 58.56 58.56L570.56 512l291.968 291.968a41.408 41.408 0 0 1-58.56 58.56L512 570.56 220.032 862.528a41.408 41.408 0 0 1-58.56-58.56L453.44 512z', }))), ]), ]), createElementNode('div', undefined, { class: 'egg_frame_content', }, watchEffectRef(() => frame.src ? [ createElementNode('iframe', undefined, { class: 'egg_frame', src: frame.src, ref(ele) { frame.ele = ele; }, }, undefined), ] : undefined)), ], { onMounted() { watch(() => [ taskStatus.value, running.value, settings[SettingType.SAME_TAB], settings[SettingType.SILENT_RUN], ], () => { if (settings[SettingType.SAME_TAB] && (taskStatus.value === TaskStatusType.START || taskStatus.value === TaskStatusType.PAUSE || running.value)) { frame.show = !settings[SettingType.SILENT_RUN]; } }); }, }), ]; } }), { onMounted() { watch(() => [login.value, settings[SettingType.SAME_TAB]], () => login.value && (settings[SettingType.SAME_TAB] ? closeWin() : closeFrame())); }, }); }
function LoginItem() { return watchEffectRef(() => { return login.value ? undefined : createElementNode('div', undefined, { class: 'egg_login_item', }, [ createElementNode('button', undefined, { type: 'button', class: 'egg_login_btn', onclick: debounce(async () => { handleLogin(); }, 300), }, createTextNode('扫码登录')), createElementNode('div', undefined, { class: watchEffectRef(() => `egg_login_img_wrap${loginQRCodeShow.value ? ' active' : ''}`), }, createElementNode('img', undefined, { class: 'egg_login_img', })), ], { onMounted() { watch(() => settings[SettingType.SCHEDULE_RUN], () => { if (!settings[SettingType.SCHEDULE_RUN]) { handleLogin(); } }, true); }, }); }); }
function InfoItem() { return watchEffectRef(() => { if (login.value) { return createElementNode('div', undefined, { class: 'egg_info_item', }, [ createElementNode('div', undefined, { class: 'egg_userinfo' }, [ createElementNode('div', undefined, { class: 'egg_avatar' }, watchEffectRef(() => { return [ userinfo.avatar ? createElementNode('img', undefined, { src: userinfo.avatar, class: 'egg_avatar_img', }) : createElementNode('div', undefined, { class: 'egg_avatar_nick', }, createTextNode(watchEffectRef(() => userinfo.nick.substring(1, 3)))), ]; })), createElementNode('div', undefined, { class: 'egg_nick' }, createTextNode(watchEffectRef(() => userinfo.nick))), ]), createElementNode('button', undefined, { type: 'button', class: 'egg_login_btn', onclick: debounce(() => { handleLogout(); }, 300), }, createTextNode('退出')), ], { onMounted() { refreshUserInfo(); }, }); } }); }
function ScoreItem() { return watchEffectRef(() => { if (login.value) { const scoreShow = ref(false); return createElementNode('div', undefined, { class: 'egg_score_item', }, createElementNode('div', undefined, { class: 'egg_scoreinfo' }, [ createElementNode('div', undefined, { class: 'egg_totalscore', }, [ createTextNode('总积分'), createElementNode('span', undefined, undefined, createTextNode(totalScore)), ]), createElementNode('div', undefined, { class: 'egg_todayscore', }, [ createElementNode('button', undefined, { type: 'button', class: 'egg_todayscore_btn', title: '查看分数详情', onclick: debounce(() => { scoreShow.value = !scoreShow.value; }, 300), onblur: () => { scoreShow.value = false; }, }, [ createTextNode('当天分数'), createElementNode('span', undefined, undefined, createTextNode(todayScore)), createNSElementNode('svg', undefined, { viewBox: '0 0 1024 1024', class: 'egg_icon', }, createNSElementNode('path', undefined, { d: 'M332.16 883.84a40.96 40.96 0 0 0 58.24 0l338.56-343.04a40.96 40.96 0 0 0 0-58.24L390.4 140.16a40.96 40.96 0 0 0-58.24 58.24L640 512l-307.84 314.24a40.96 40.96 0 0 0 0 57.6z', })), createElementNode('div', undefined, { class: watchEffectRef(() => `egg_score_details${scoreShow.value ? '' : ' hide'}`), }, [ createElementNode('div', undefined, { class: 'egg_score_title' }, [ createNSElementNode('svg', undefined, { viewBox: '0 0 1024 1024', class: 'egg_icon', }, [ createNSElementNode('path', undefined, { d: 'M314.81 304.01h415.86v58.91H314.81zM314.81 440.24h415.86v58.91H314.81z', }), createNSElementNode('path', undefined, { d: 'M814.8 892.74h-8.64l-283.51-182-283.51 182h-8.64A69.85 69.85 0 0 1 160.72 823V188.22a69.85 69.85 0 0 1 69.77-69.77H814.8a69.85 69.85 0 0 1 69.77 69.77V823a69.85 69.85 0 0 1-69.77 69.74zM230.5 177.35a10.87 10.87 0 0 0-10.86 10.86V823a10.86 10.86 0 0 0 5 9.11l298.01-191.42 298.06 191.38a10.86 10.86 0 0 0 5-9.11V188.22a10.87 10.87 0 0 0-10.86-10.86z', }), ]), createElementNode('div', undefined, { class: 'egg_score_title_text', }, createTextNode('积分详情')), ]), ...taskConfig.map((task) => createElementNode('div', undefined, { class: 'egg_score_item' }, [ createTextNode(task.title), createElementNode('span', undefined, { class: 'egg_score_detail', }, createTextNode(watchEffectRef(() => task.score))), ])), ]), ]), ]), ]), { onMounted() { refreshScoreInfo(); }, }); } }); }
function NomalItem({ title, tip, checked, onchange, }) { return createElementNode('div', undefined, { class: 'egg_setting_item' }, [ createElementNode('div', undefined, { class: 'egg_label_wrap' }, [ createElementNode('label', undefined, { class: 'egg_task_title' }, [ createTextNode(title), createElementNode('span', undefined, { class: 'egg_detail', title: tip, }, createTextNode('i')), ]), ]), createElementNode('input', undefined, { title: tip, class: 'egg_switch', type: 'checkbox', checked, onchange, }), ]); }
function TaskItem({ title, tip, checked, percent, currentScore, dayMaxScore, onchange, immutable, }) { return createElementNode('div', undefined, { class: 'egg_task_item', }, [ createElementNode('div', undefined, { class: 'egg_label_wrap' }, [ createElementNode('div', undefined, { class: 'egg_task_title_wrap' }, [ createElementNode('div', undefined, { class: 'egg_task_title' }, createTextNode(title)), createElementNode('div', undefined, { class: 'egg_task_progress_wrap' }, [ createElementNode('div', undefined, { class: 'egg_task_current', }, createTextNode(currentScore)), createElementNode('div', undefined, { class: 'egg_task_max', }, createTextNode(watchEffectRef(() => `/${dayMaxScore.value}`))), ]), ]), createElementNode('div', undefined, { class: 'egg_progress' }, [ createElementNode('div', undefined, { class: 'egg_track' }, createElementNode('div', undefined, { class: 'egg_bar', style: watchEffectRef(() => `width: ${percent.value}%;`), })), ]), ]), createElementNode('input', undefined, { title: tip, class: 'egg_switch', type: 'checkbox', checked, onchange, disabled: immutable, }), ]); }
function TaskList() { const handleTaskChange = (e, type, title) => { const { checked } = e.target; if (taskConfig[type].active !== checked) { taskConfig[type].active = checked; GM_setValue('taskConfig', JSON.stringify(taskConfig)); createTip(`${title} ${checked ? '打开' : '关闭'}!`); } }; watch(login, async () => { if (login.value) { await refreshTaskList(); if (taskConfig.some((task) => task.active && !task.status)) { GM_setValue('pauseStudy', false); taskStatus.value = TaskStatusType.LOADED; return; } taskStatus.value = TaskStatusType.FINISH; } }, true); return createElementNode('div', undefined, { class: 'egg_task_list', }, taskConfig.map((label) => label.immutable ? TaskItem({ title: label.title, tip: label.tip, checked: watchEffectRef(() => label.active), percent: watchEffectRef(() => label.percent), currentScore: watchEffectRef(() => label.currentScore), dayMaxScore: watchEffectRef(() => label.dayMaxScore), onchange: debounce((e) => { handleTaskChange(e, label.type, label.title); }, 300), immutable: label.immutable, }) : TaskItem({ title: label.title, tip: label.tip, checked: watchEffectRef(() => label.active), percent: watchEffectRef(() => label.percent), currentScore: watchEffectRef(() => label.currentScore), dayMaxScore: watchEffectRef(() => label.dayMaxScore), onchange: debounce((e) => { handleTaskChange(e, label.type, label.title); }, 300), immutable: label.immutable, }))); }
function TaskBtn() { return watchEffectRef(() => { if (login.value) {
async function study() { createTip('开始学习!'); await studyPauseLock(); if (taskConfig[TaskType.READ].active && !taskConfig[TaskType.READ].status) { log('任务一: 文章选读'); createTip('任务一: 文章选读'); await studyPauseLock(); await readNews(); } log('任务一: 文章选读已完成!'); if (taskConfig[TaskType.WATCH].active && !taskConfig[TaskType.WATCH].status) { log('任务二: 视听学习'); createTip('任务二: 视听学习'); await studyPauseLock(); await watchVideo(); } log('任务二: 视听学习已完成!'); if (taskConfig[TaskType.PRACTICE].active && !taskConfig[TaskType.PRACTICE].status) { log('任务三: 每日答题'); createTip('任务三: 每日答题'); await studyPauseLock(); await doExamPractice(); } log('任务三: 每日答题已完成!'); }
function pauseTask() { GM_setValue('pauseStudy', true); taskStatus.value = TaskStatusType.PAUSE; }
function continueTask() { GM_setValue('pauseStudy', false); taskStatus.value = TaskStatusType.START; }
async function startTask() { if (taskConfig.some((task) => task.active && !task.status)) { taskStatus.value = TaskStatusType.START; try { await study(); if (settings[SettingType.SAME_TAB]) { closeFrame(); frame.exist = false; } } catch (err) { if (err instanceof Error) { createTip(err.message); error(err.message); return; } createTip(err); error(err); } } taskStatus.value = TaskStatusType.FINISH; log('已完成'); createTip('完成学习!'); if (settings[SettingType.REMOTE_PUSH]) { const res = await pushModal({ title: '学习推送', to: userinfo.nick, content: [ '学习强国, 学习完成!', `当天积分: ${getHighlightHTML(todayScore.value)} 分`, `总积分: ${getHighlightHTML(totalScore.value)} 分`, ...taskConfig.map((task) => getProgressHTML(task.title, task.percent)), ], type: 'success', }, pushToken.value); createTip(`学习推送${res ? '成功' : '失败'}!`); } } let flag = false; watch(() => [taskStatus.value, settings[SettingType.AUTO_START]], async () => { if (!flag && taskStatus.value === TaskStatusType.LOADED) { if (settings[SettingType.AUTO_START]) { flag = true; const tip = createTip('即将自动开始任务', 5, true); await tip.waitCountDown(); if (settings[SettingType.AUTO_START] && taskStatus.value !== TaskStatusType.START) { createTip('自动开始任务'); startTask(); return; } flag = false; createTip('已取消自动开始任务!'); } } }); taskConfig.forEach((task) => { watch(() => [task.active], () => { if (taskStatus.value === TaskStatusType.FINISH) { if (task.active && !task.status) { taskStatus.value = TaskStatusType.LOADED; } } }); }); return createElementNode('div', undefined, { class: 'egg_study_item' }, createElementNode('button', undefined, { class: watchEffectRef(() => `egg_study_btn${taskStatus.value === TaskStatusType.START ? ' loading' : ''}`), type: 'button', disabled: watchRef(() => [running.value, taskStatus.value], () => running.value || taskStatus.value === TaskStatusType.LOADING || taskStatus.value === TaskStatusType.FINISH), onclick: watchEffectRef(() => taskStatus.value === TaskStatusType.LOADED ? debounce(startTask, 300) : taskStatus.value === TaskStatusType.START ? debounce(pauseTask, 300) : taskStatus.value === TaskStatusType.PAUSE ? debounce(continueTask, 300) : undefined), }, createTextNode(watchEffectRef(() => `${taskStatus.value === TaskStatusType.LOADING ? '等待中' : taskStatus.value === TaskStatusType.LOADED ? '开始学习' : taskStatus.value === TaskStatusType.START ? '正在学习, 点击暂停' : taskStatus.value === TaskStatusType.PAUSE ? '继续学习' : taskStatus.value === TaskStatusType.FINISH ? '已完成' : ''}`)))); } }); }
function ScheduleList() { return createElementNode('div', undefined, { class: 'egg_schedule_list' }, watchEffectRef(() => { return scheduleList.length ? scheduleList.map((schedule) => createElementNode('div', undefined, { class: 'egg_schedule_item' }, [ createElementNode('div', undefined, { class: `egg_schedule_detail_time_wrap${isLate(schedule) ? ' inactive' : ''}`, }, [ createElementNode('div', undefined, { class: 'egg_schedule_detail_icon', }, createNSElementNode('svg', undefined, { viewBox: '0 0 1024 1024', class: 'egg_icon', }, [ createNSElementNode('path', undefined, { d: 'M810.137703 213.860762c-164.388001-164.4187-431.887404-164.4187-596.277452 0-164.417677 164.388001-164.417677 431.889451 0 596.278475 164.390048 164.417677 431.890474 164.417677 596.277452 0C974.557426 645.750213 974.557426 378.248763 810.137703 213.860762zM767.347131 767.345596c-140.797723 140.829446-369.927237 140.797723-510.693238 0-140.828422-140.797723-140.828422-369.895515 0-510.708588 140.767024-140.783397 369.896538-140.813073 510.693238 0C908.14383 397.420405 908.14383 626.578572 767.347131 767.345596z', }), createNSElementNode('path', undefined, { d: 'M721.450824 521.495258 515.404028 521.495258l0.028653-227.948619c0-15.124466-12.362562-27.458375-27.501354-27.458375s-27.443026 12.33391-27.443026 27.458375l0 235.115855c0 0.835018-1.013073 20.48659 12.094456 34.459836 8.331759 8.809643 20.038382 13.288654 35.148521 13.288654l213.720569 0.031722c15.140839 0 27.472702-12.304234 27.472702-27.474748C748.922503 533.887496 736.620315 521.584286 721.450824 521.495258z', }), ])), createElementNode('div', undefined, { class: 'egg_schedule_detail_time' }, createTextNode(schedule.time)), ]), createElementNode('div', undefined, { class: 'egg_schedule_detail_del_wrap' }, [ createElementNode('button', undefined, { class: 'egg_schedule_del_btn', onclick: debounce(() => { if (!settings[SettingType.SCHEDULE_RUN]) { createTip('未开启定时刷新!'); return; } const index = scheduleList.findIndex((s) => s === schedule); scheduleList.splice(index, 1); GM_setValue('scheduleList', JSON.stringify(scheduleList)); refreshScheduleTask(); }, 300), }, createNSElementNode('svg', undefined, { viewBox: '0 0 1024 1024', class: 'egg_icon', }, [ createNSElementNode('path', undefined, { d: 'M896.22 896.22c14.262-14.263 11.263-40.449-6.583-58.295L230.473 178.76c-17.847-17.847-44.105-20.846-58.295-6.583-14.263 14.19-11.264 40.448 6.583 58.295l659.164 659.164c17.846 17.846 44.032 20.845 58.294 6.582', }), createNSElementNode('path', undefined, { d: 'M172.178 896.22c-14.263-14.263-11.264-40.449 6.583-58.295L837.925 178.76c17.846-17.847 44.032-20.846 58.294-6.583 14.263 14.19 11.264 40.448-6.582 58.295L230.4 889.637c-17.847 17.846-44.105 20.845-58.295 6.582', }), ])), ]), ])) : [ createElementNode('div', undefined, { class: 'egg_schedule_list_none' }, [ createNSElementNode('svg', undefined, { viewBox: '0 0 1024 1024', class: 'egg_icon', }, [ createNSElementNode('path', undefined, { d: 'M238.1 520.5c-17.6 0-31.9-14.3-31.9-31.9 0-17.6 14.3-31.9 31.9-31.9h293c17.6 0 31.9 14.3 31.9 31.9 0 17.6-14.3 31.9-31.9 31.9h-293zM238.1 733.6c-17.6 0-31.9-14.3-31.9-31.9s14.3-31.9 31.9-31.9h186.5c17.6 0 31.9 14.3 31.9 31.9s-14.3 31.9-31.9 31.9H238.1zM241.6 314.9c-17.6 0-31.9-14.3-31.9-31.9s14.3-31.9 31.9-31.9h426.1c17.6 0 31.9 14.3 31.9 31.9 0 17.5-14.3 31.7-31.8 31.9H241.6z', }), createNSElementNode('path', undefined, { d: 'M160 926.6c-46.9 0-85.1-38.2-85.1-85.1V149.1c0-46.9 38.2-85.1 85.1-85.1h586c46.9 0 85.1 38.2 85.1 85.1v297.4c0 17.6-14.3 31.9-31.9 31.9-17.6 0-31.9-14.3-31.9-31.9V149.1c0-11.8-9.6-21.4-21.4-21.4H160c-11.8 0-21.4 9.6-21.4 21.4v692.4c0 11.8 9.6 21.4 21.4 21.4h304.5c17.5 0 31.8 14.2 31.9 31.8 0 17.6-14.3 31.8-31.9 31.8H160z', }), createNSElementNode('path', undefined, { d: 'M917.2 959.9c-8.5 0-16.5-3.3-22.5-9.3l-78.5-78.5-5.3-0.5-0.6 0.4c-31.7 21.6-68.7 33-107 33-105.2 0-190.8-85.6-190.8-190.8s85.6-190.8 190.8-190.8c105.2 0 190.8 85.6 190.8 190.8 0 38.2-11.4 75.2-33 107l-0.4 0.6 0.5 5.3 78.5 78.5c6 6 9.3 14 9.3 22.5s-3.4 16.5-9.4 22.5c-5.9 6-13.9 9.3-22.4 9.3zM703.4 587c-70.1 0-127.2 57.1-127.2 127.2s57.1 127.2 127.2 127.2 127.2-57.1 127.2-127.2S773.6 587 703.4 587z', }), ]), createElementNode('div', undefined, { class: 'egg_schedule_list_none_text', }, createTextNode('暂无定时任务')), ]), ]; })); }
function TimeInput({ hour, minute, onchange, }) { const hours = new Array(24).fill(undefined).map((v, i) => ({ value: i, label: formatDateNum(i), })); const minutes = new Array(60).fill(undefined).map((v, i) => ({ value: i, label: formatDateNum(i), })); const valueRef = watchEffectRef(() => { const h = hours.find((h) => h.value === hour.value); const min = minutes.find((min) => min.value === minute.value); return { hour: h ? h.value : -1, minute: min ? min.value : -1, }; }); return createElementNode('div', undefined, { class: 'egg_time_input' }, [ createElementNode('div', undefined, { class: 'egg_hour_wrap' }, [ Select({ data: hours, placeholder: '00', maxlength: 2, value: hour, onchange({ value }) { valueRef.value.hour = value; onchange && onchange(valueRef.value); }, onblur(res) { if (!res) { valueRef.value.hour = -1; onchange && onchange(valueRef.value); } }, }), ]), createElementNode('span', undefined, { class: 'egg_separator' }, createTextNode(':')), createElementNode('div', undefined, { class: 'egg_minute_wrap' }, [ Select({ data: minutes, placeholder: '00', maxlength: 2, value: minute, onchange({ value }) { valueRef.value.minute = value; onchange && onchange(valueRef.value); }, onblur(res) { if (!res) { valueRef.value.minute = -1; onchange && onchange(valueRef.value); } }, }), ]), ]); }
function SettingsPanel({ show }) { let token = ''; let hour = ref(-1); let minute = ref(-1); return createElementNode('div', undefined, { class: watchEffectRef(() => `egg_settings${show.value ? ' active' : ''}`), }, [ createElementNode('div', undefined, { class: 'egg_settings_version_wrap' }, [ createElementNode('div', undefined, { class: 'egg_settings_label' }, createTextNode('版本信息')), createElementNode('div', undefined, { class: 'egg_settings_version', }, [ createTextNode(`v${version}`), createElementNode('a', undefined, { class: 'egg_settings_version_detail', title: 'GitHub Xu22Web/tech-study-js', href: 'https://github.com/Xu22Web/tech-study-js', }, createNSElementNode('svg', undefined, { viewBox: '0 0 16 16', class: 'egg_icon', }, createNSElementNode('path', undefined, { d: 'M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z', }))), ]), ]), createElementNode('div', undefined, { class: 'egg_settings_theme_wrap' }, [ createElementNode('div', undefined, { class: 'egg_settings_label' }, createTextNode('主题预设')), createElementNode('div', undefined, { class: 'egg_settings_theme_colors' }, [ { value: '#fa3333', title: '强国红', detail: 'XueXi Red', code: 'none', }, { value: '#bb2649', title: '非凡洋红', detail: 'Viva Magenta', code: '18-1750', }, { value: '#35548a', title: '经典蓝', detail: 'Classic Blue', code: '19-4052', }, { value: '#f36f63', title: '活珊瑚橘', detail: 'Living Coral', code: '16-1546', }, { value: '#6d5b97', title: '紫外光色', detail: 'Ultra Violet', code: '18-3838', }, { value: '#86af49', title: '草木绿', detail: 'Greenery', code: '15-0343', }, { value: '#fc8bab', title: 'B站粉', detail: 'Bilibili Pink', code: 'none', }, { value: '#056de8', title: '知乎蓝', detail: 'Zhihu Blue', code: 'none', }, ].map((color) => createElementNode('div', undefined, { class: 'egg_settings_theme_color_wrap', }, createElementNode('button', undefined, { class: 'egg_settings_theme_color', type: 'button', style: watchEffectRef(() => `color: ${color.value};${themeColor.value === color.value ? '' : ` box-shadow: 0rem 0.4rem 0.1rem 0.1rem ${color.value}30;`}`), title: color.title, onclick: debounce(() => { if (themeColor.value !== color.value) { themeColor.value = color.value; GM_setValue('themeColor', themeColor.value); } }, 300), })))), ]), createElementNode('div', undefined, { class: 'egg_settings_read_time_wrap', }, [ createElementNode('div', undefined, { class: 'egg_settings_label' }, createTextNode('最大文章时长')), Select({ data: [ { label: '40s', value: 40, }, { label: '60s', value: 60, }, { label: '80s', value: 80, }, { label: '100s', value: 100, }, ], value: maxRead, placeholder: '100s', maxlength: 4, keep: true, onchange({ value }) { createTip('最大文章时长 已保存!'); maxRead.value = value; GM_setValue('maxRead', value); }, }), ], { onMounted() { try { const maxReadTemp = GM_getValue('maxRead'); if (maxReadTemp) { maxRead.value = maxReadTemp; } } catch (e) { } }, }), createElementNode('div', undefined, { class: 'egg_settings_watch_time_wrap', }, [ createElementNode('div', undefined, { class: 'egg_settings_label' }, createTextNode('最大视听时长')), Select({ data: [ { label: '40s', value: 40, }, { label: '60s', value: 60, }, { label: '80s', value: 80, }, { label: '100s', value: 100, }, { label: '120s', value: 120, }, ], value: maxWatch, placeholder: '120s', maxlength: 4, keep: true, onchange({ value }) { createTip('最大视听时长 已保存!'); maxWatch.value = value; GM_setValue('maxWatch', value); }, }), ], { onMounted() { try { const maxWatchTemp = GM_getValue('maxWatch'); if (maxWatchTemp) { maxWatch.value = maxWatchTemp; } } catch (e) { } }, }), watchEffectRef(() => settings[SettingType.REMOTE_PUSH] ? createElementNode('div', undefined, { class: 'egg_settings_token_wrap' }, [ createElementNode('div', undefined, { class: 'egg_settings_token' }, [ createElementNode('div', undefined, { class: 'egg_settings_label' }, createTextNode('我的 token')), createElementNode('input', undefined, { class: 'egg_settings_token_input', placeholder: '用户 token', maxlength: 32, value: pushToken.value, onfocus: (e) => { const input = e.target; input.classList.add('active'); const btnWrap = $$('.egg_settings_submit_btn_wrap')[0]; btnWrap.classList.add('active'); }, onblur: (e) => { const input = e.target; const value = input.value.trim(); if (/^[0-9a-z]{32}$/.test(value)) { token = value; input.value = value; } else { token = ''; } input.classList.remove('active'); setTimeout(() => { const btnWrap = $$('.egg_settings_submit_btn_wrap')[0]; btnWrap.classList.remove('active'); input.value = pushToken.value; }, 200); }, }), ]), createElementNode('div', undefined, { class: 'egg_settings_submit_btn_wrap' }, createElementNode('button', undefined, { class: 'egg_settings_submit_btn', onclick: debounce(() => { createTip('用户 token 已保存!'); if (token !== pushToken.value) { pushToken.value = token; GM_setValue('pushToken', token); } }, 300), }, createTextNode('保存'))), ]) : undefined), watchEffectRef(() => settings[SettingType.SCHEDULE_RUN] ? createElementNode('div', undefined, { class: 'egg_schedule' }, [ createElementNode('div', undefined, { class: 'egg_schedule_time_wrap' }, [ createElementNode('div', undefined, { class: 'egg_schedule_time' }, [ createElementNode('div', undefined, { class: 'egg_schedule_label' }, createTextNode('设置时间')), createElementNode('div', undefined, { class: 'egg_schedule_time_input_wrap' }, [ TimeInput({ hour, minute, onchange({ hour: h, minute: min }) { hour.value = h; minute.value = min; }, }), createElementNode('button', undefined, { class: 'egg_schedule_add_btn', onclick: debounce(() => { if (!settings[SettingType.SCHEDULE_RUN]) { createTip('未开启定时刷新!'); return; } if (hour.value === -1 || minute.value === -1) { createTip('时间格式不符合要求!'); return; } const exists = scheduleList.find((schedule) => schedule.hour === hour.value && schedule.minute === minute.value); if (exists) { createTip('设置定时任务重复!'); return; } createTip('设置定时任务成功!'); scheduleList.push({ hour: hour.value, minute: minute.value, time: `${formatDateNum(hour.value)}:${formatDateNum(minute.value)}`, }); scheduleList.sort((a, b) => a.hour === b.hour ? a.minute - b.minute : a.hour - b.hour); GM_setValue('scheduleList', JSON.stringify(scheduleList)); hour.value = -1; minute.value = -1; const inputs = $$('.egg_time_input input'); inputs.forEach((i) => (i.value = '')); refreshScheduleTask(); }, 300), }, createNSElementNode('svg', undefined, { viewBox: '0 0 1024 1024', class: 'egg_icon', }, createNSElementNode('path', undefined, { d: 'M801.171 483.589H544V226.418c0-17.673-14.327-32-32-32s-32 14.327-32 32v257.171H222.83c-17.673 0-32 14.327-32 32s14.327 32 32 32H480v257.17c0 17.673 14.327 32 32 32s32-14.327 32-32v-257.17h257.171c17.673 0 32-14.327 32-32s-14.327-32-32-32z', }))), ]), ]), ]), ScheduleList(), ]) : undefined), ], { onMounted() { watch(() => settings[SettingType.REMOTE_PUSH], () => { if (settings[SettingType.REMOTE_PUSH]) { try { const tokenTemp = GM_getValue('pushToken'); if (tokenTemp) { pushToken.value = tokenTemp; } } catch (e) { } } }, true); watch(() => settings[SettingType.SCHEDULE_RUN], () => { if (settings[SettingType.SCHEDULE_RUN]) { try { const scheduleTemp = JSON.parse(GM_getValue('scheduleList')); if (scheduleTemp && Array.isArray(scheduleTemp)) { for (const i in scheduleTemp) { scheduleList[i] = scheduleTemp[i]; } } } catch (e) { } refreshScheduleTask(); return; } clearScheduleTask(); }, true); }, }); }
function Panel() { const runLabels = [ { title: '自动开始', tip: '启动时, 自动开始任务, 在倒计时结束前自动开始可随时取消; 如果在自动开始前手动开始任务, 此次自动开始将取消', type: SettingType.AUTO_START, }, { title: '同屏任务', tip: '运行任务时,所有任务均在当前页面以弹窗方式运行', type: SettingType.SAME_TAB, }, { title: '静默运行', tip: '同屏任务时, 不显示任务弹窗静默运行', type: SettingType.SILENT_RUN, }, { title: '定时刷新', tip: '定时刷新页面,重新进行任务,此功能需要长时间占用浏览器', type: SettingType.SCHEDULE_RUN, }, ]; const examLabels = [ { title: '随机作答', tip: '无答案时, 随机选择或者填入答案, 不保证正确', type: SettingType.RANDOM_EXAM, }, ]; const pushLabels = [ { title: '远程推送', tip: '利用 pushplus 推送, 将登录二维码直接推送到微信公众号', type: SettingType.REMOTE_PUSH, }, ]; const handleSettingsChange = (e, type, title) => { const { checked } = e.target; if (settings[type] !== checked) { settings[type] = checked; GM_setValue('studySettings', JSON.stringify(settings)); createTip(`${title} ${checked ? '打开' : '关闭'}!`); } }; const scheduleShow = ref(false); const panelShow = ref(false); return createElementNode('div', undefined, { class: `egg_panel_wrap${hasMobile() ? ' mobile' : ''}`, }, createElementNode('div', undefined, { class: watchEffectRef(() => `egg_panel${panelShow.value ? ' hide' : ''}`), }, [ LoginItem(), InfoItem(), ScoreItem(), Hr({ text: '任务' }), TaskList(), Hr({ text: '运行' }), createElementNode('div', undefined, { class: 'egg_run_list' }, runLabels.map((label) => { return NomalItem({ title: label.title, tip: label.tip, checked: settings[label.type], onchange: debounce((e) => { handleSettingsChange(e, label.type, label.title); }, 300), }); })), Hr({ text: '答题' }), createElementNode('div', undefined, { class: 'egg_exam_list' }, examLabels.map((label) => { return NomalItem({ title: label.title, tip: label.tip, checked: settings[label.type], onchange: debounce((e) => { handleSettingsChange(e, label.type, label.title); }, 300), }); })), Hr({ text: '推送' }), createElementNode('div', undefined, { class: 'egg_push_list' }, pushLabels.map((label) => { return NomalItem({ title: label.title, tip: label.tip, checked: settings[label.type], onchange: debounce((e) => { handleSettingsChange(e, label.type, label.title); }, 300), }); })), Hr({ text: '提示' }), createElementNode('div', undefined, { class: 'egg_tip_list' }, [ createTextNode('专项练习已被移除, 如需使用, 请点击'), createElementNode('button', undefined, { class: 'egg_tip_btn', type: 'button', onclick: debounce(doExamPaper, 300), disabled: watchRef(() => [running.value, taskStatus.value], () => running.value || taskStatus.value === TaskStatusType.START || taskStatus.value === TaskStatusType.PAUSE), }, createTextNode('去完成')), ]), createElementNode('div', undefined, { class: 'egg_btns_wrap', }, [ createElementNode('button', undefined, { class: watchRef(() => [frame.exist, frame.show], () => `egg_frame_show_btn${!frame.exist || frame.show ? ' hide' : ''}`), title: '窗口', type: 'button', onclick: debounce(() => { frame.show = true; }, 300), }, createNSElementNode('svg', undefined, { viewBox: '0 0 1024 1024', class: 'egg_icon', }, createNSElementNode('path', undefined, { d: 'M836.224 106.666667h-490.666667a85.589333 85.589333 0 0 0-85.333333 85.333333V256h-64a85.589333 85.589333 0 0 0-85.333333 85.333333v490.666667a85.589333 85.589333 0 0 0 85.333333 85.333333h490.666667a85.589333 85.589333 0 0 0 85.333333-85.333333V768h64a85.589333 85.589333 0 0 0 85.333333-85.333333V192a85.589333 85.589333 0 0 0-85.333333-85.333333z m-132.266667 725.333333a20.138667 20.138667 0 0 1-21.333333 21.333333h-490.666667a20.138667 20.138667 0 0 1-21.333333-21.333333V341.333333a20.138667 20.138667 0 0 1 21.333333-21.333333h494.933334a20.138667 20.138667 0 0 1 21.333333 21.333333v490.666667z m153.6-149.333333a20.138667 20.138667 0 0 1-21.333333 21.333333h-64V341.333333a85.589333 85.589333 0 0 0-85.333333-85.333333h-362.666667V192a20.138667 20.138667 0 0 1 21.333333-21.333333h490.666667a20.138667 20.138667 0 0 1 21.333333 21.333333z', }))), createElementNode('button', undefined, { class: 'egg_panel_show_btn', title: '面板', type: 'button', onclick: debounce(() => { panelShow.value = !panelShow.value; }, 300), }, createNSElementNode('svg', undefined, { viewBox: '0 0 1024 1024', class: 'egg_icon', }, createNSElementNode('path', undefined, { d: 'M332.16 883.84a40.96 40.96 0 0 0 58.24 0l338.56-343.04a40.96 40.96 0 0 0 0-58.24L390.4 140.16a40.96 40.96 0 0 0-58.24 58.24L640 512l-307.84 314.24a40.96 40.96 0 0 0 0 57.6z', }))), createElementNode('button', undefined, { class: watchEffectRef(() => `egg_settings_show_btn${scheduleShow.value ? ' active' : ''}`), title: '设置', type: 'button', onclick: debounce(() => { scheduleShow.value = !scheduleShow.value; }, 300), }, createNSElementNode('svg', undefined, { viewBox: '0 0 1024 1024', class: 'egg_icon', }, [ createNSElementNode('path', undefined, { d: 'M7.25325 705.466473a503.508932 503.508932 0 0 0 75.26742 121.391295 95.499302 95.499302 0 0 0 93.211173 31.07039 168.59902 168.59902 0 0 1 114.526906 16.257763 148.487566 148.487566 0 0 1 71.052444 83.456515 91.163899 91.163899 0 0 0 75.989987 61.538643 578.053784 578.053784 0 0 0 148.969278 0A91.163899 91.163899 0 0 0 662.380873 957.642436a148.487566 148.487566 0 0 1 72.256723-83.456515 168.59902 168.59902 0 0 1 114.406478-16.257763 95.61973 95.61973 0 0 0 93.331601-31.07039 503.508932 503.508932 0 0 0 75.267419-121.391295 84.29951 84.29951 0 0 0-18.545892-94.897163 138.251197 138.251197 0 0 1 0-197.140426 84.29951 84.29951 0 0 0 18.545892-94.897163 503.508932 503.508932 0 0 0-75.869559-121.391295 95.499302 95.499302 0 0 0-93.211173-31.070391A168.59902 168.59902 0 0 1 734.637596 149.812272a148.848849 148.848849 0 0 1-72.256723-83.456515A91.163899 91.163899 0 0 0 586.631741 4.817115a581.907476 581.907476 0 0 0-148.969277 0A91.163899 91.163899 0 0 0 361.311193 66.355757a148.848849 148.848849 0 0 1-71.413728 83.456515 168.59902 168.59902 0 0 1-114.406478 16.257763 95.378874 95.378874 0 0 0-93.3316 31.070391A503.508932 503.508932 0 0 0 7.25325 318.531721a84.29951 84.29951 0 0 0 18.545893 94.897163 140.057615 140.057615 0 0 1 41.30676 98.509999 140.057615 140.057615 0 0 1-41.30676 98.630427A84.29951 84.29951 0 0 0 7.25325 705.466473z m929.462315-349.240828a219.901294 219.901294 0 0 0 0 312.028615c0.842995 0.842995 2.649413 3.010697 1.806418 5.057971a427.398517 427.398517 0 0 1-63.104205 101.520696 9.513802 9.513802 0 0 1-9.032091 2.167702 255.547944 255.547944 0 0 0-173.777418 24.928569 231.823653 231.823653 0 0 0-111.275354 130.302957 6.984817 6.984817 0 0 1-6.021394 4.937543 492.790851 492.790851 0 0 1-126.328837 0 6.984817 6.984817 0 0 1-6.021394-4.937543 231.823653 231.823653 0 0 0-111.275353-130.302957 255.668372 255.668372 0 0 0-120.427872-30.468252 258.919924 258.919924 0 0 0-52.747408 5.539683 9.513802 9.513802 0 0 1-9.03209-2.167702 427.398517 427.398517 0 0 1-63.104205-101.520696c-0.842995-2.047274 0.963423-4.214976 1.806418-5.057971a221.82814 221.82814 0 0 0 64.910623-156.556233 221.707712 221.707712 0 0 0-65.512762-155.713238c-0.842995-0.842995-2.649413-3.010697-1.806418-5.057971a427.398517 427.398517 0 0 1 63.104205-101.520696 9.393374 9.393374 0 0 1 8.911662-2.167701 255.7888 255.7888 0 0 0 173.897847-24.92857 231.823653 231.823653 0 0 0 111.275353-130.302957 6.984817 6.984817 0 0 1 6.021394-4.937543 492.790851 492.790851 0 0 1 126.328837 0 6.984817 6.984817 0 0 1 6.021394 4.937543 231.823653 231.823653 0 0 0 111.275354 130.302957 255.547944 255.547944 0 0 0 173.777418 24.92857 9.513802 9.513802 0 0 1 9.032091 2.167701 423.063113 423.063113 0 0 1 62.983777 101.520696c0.963423 2.047274-0.842995 4.214976-1.68599 5.057971z', }), createNSElementNode('path', undefined, { d: 'M512.086889 305.766366a206.292944 206.292944 0 1 0 206.172516 206.172517 206.413372 206.413372 0 0 0-206.172516-206.172517z m123.197713 206.172517a123.197713 123.197713 0 1 1-123.197713-123.077285 123.318141 123.318141 0 0 1 123.197713 123.077285z', }), ])), createElementNode('button', undefined, { class: 'egg_settings_reset_btn', title: '重置', type: 'button', onclick: debounce(() => { GM_setValue('taskConfig', null); GM_setValue('studySettings', null); GM_setValue('maxRead', null); GM_setValue('maxWatch', null); GM_setValue('themeColor', null); location.reload(); }, 300), }, createNSElementNode('svg', undefined, { viewBox: '0 0 1024 1024', class: 'egg_icon', }, [ createNSElementNode('path', undefined, { d: 'M943.8 484.1c-17.5-13.7-42.8-10.7-56.6 6.8-5.7 7.3-8.5 15.8-8.6 24.4h-0.4c-0.6 78.3-26.1 157-78 223.3-124.9 159.2-356 187.1-515.2 62.3-31.7-24.9-58.2-54-79.3-85.9h77.1c22.4 0 40.7-18.3 40.7-40.7v-3c0-22.4-18.3-40.7-40.7-40.7H105.5c-22.4 0-40.7 18.3-40.7 40.7v177.3c0 22.4 18.3 40.7 40.7 40.7h3c22.4 0 40.7-18.3 40.7-40.7v-73.1c24.2 33.3 53 63.1 86 89 47.6 37.3 101 64.2 158.9 79.9 55.9 15.2 113.5 19.3 171.2 12.3 57.7-7 112.7-24.7 163.3-52.8 52.5-29 98-67.9 135.3-115.4 37.3-47.6 64.2-101 79.9-158.9 10.2-37.6 15.4-76 15.6-114.6h-0.1c-0.3-11.6-5.5-23.1-15.5-30.9zM918.7 135.2h-3c-22.4 0-40.7 18.3-40.7 40.7V249c-24.2-33.3-53-63.1-86-89-47.6-37.3-101-64.2-158.9-79.9-55.9-15.2-113.5-19.3-171.2-12.3-57.7 7-112.7 24.7-163.3 52.8-52.5 29-98 67.9-135.3 115.4-37.3 47.5-64.2 101-79.9 158.8-10.2 37.6-15.4 76-15.6 114.6h0.1c0.2 11.7 5.5 23.2 15.4 30.9 17.5 13.7 42.8 10.7 56.6-6.8 5.7-7.3 8.5-15.8 8.6-24.4h0.4c0.6-78.3 26.1-157 78-223.3 124.9-159.2 356-187.1 515.2-62.3 31.7 24.9 58.2 54 79.3 85.9h-77.1c-22.4 0-40.7 18.3-40.7 40.7v3c0 22.4 18.3 40.7 40.7 40.7h177.3c22.4 0 40.7-18.3 40.7-40.7V175.8c0.1-22.3-18.2-40.6-40.6-40.6z', }), ])), ]), TaskBtn(), createElementNode('div', undefined, { class: 'egg_settings_item' }, [ SettingsPanel({ show: scheduleShow }), ]), ])); }
|