国家中小学智慧教育平台助手

《也许同类型中最好用?》系列 - 一个基于 JavaScript 的国家中小学智慧教育平台助手,支持免登录下载教材,美化顶栏,页脚增加一言,登录后还能增强预览!

Εγκατάσταση αυτού του κώδικαΒοήθεια
Κώδικας προτεινόμενος από τον δημιιουργό

Μπορεί, επίσης, να σας αρέσει ο κώδικας (改)网盘直链下载助手.

Εγκατάσταση αυτού του κώδικα
// ==UserScript==
// @name              国家中小学智慧教育平台助手
// @namespace         github.com/hmjz100
// @version           0.8
// @description       《也许同类型中最好用?》系列 - 一个基于 JavaScript 的国家中小学智慧教育平台助手,支持免登录下载教材,美化顶栏,页脚增加一言,登录后还能增强预览!
// @author            hmjz100
// @match             *://*.basic.smartedu.cn/*
// @match             *://*.zxx.edu.cn/*
// @icon              https://basic.smartedu.cn/favicon.ico
// @license           MIT
// @grant             GM_getResourceText
// @grant             GM.xmlHttpRequest
// @connect           v1.hitokoto.cn
// @connect           s-file-1.ykt.cbern.com.cn
// @connect           s-file-2.ykt.cbern.com.cn
// @connect           s-file-3.ykt.cbern.com.cn
// @require           https://unpkg.com/jquery@3.6.3/dist/jquery.min.js
// @require           https://unpkg.com/sweetalert2@11/dist/sweetalert2.all.min.js
// @resource Swal     https://unpkg.com/sweetalert2@11/dist/sweetalert2.min.css
// @resource SwalDark https://unpkg.com/@sweetalert2/theme-dark@5.0.15/dark.min.css
// ==/UserScript==

(async function BasicEduDownload() {
	'use strict';

	/*
	防止代码因其他原因被执行多次
	这段代码出自 Via轻插件,作者谷花泰
	*/
	let key = encodeURIComponent('(改)B站成分检测器:主代码');
	if (window[key]) return;
	window[key] = true;
	console.log("【国家中小学智慧教育平台助手】即时\n运行中…")
	addDownStyle();

	// 默认收起侧栏
	waitForKeyElements('[class^="index-module_collapse_"]', (element) => {
		element.click();
	}, true)

	// 移除顶部客户端相关内容
	waitForKeyElements('[class^="index-module_download"]', (element) => {
		element.remove()
	}, true)

	// 给顶栏增加个时钟
	waitForKeyElements('[class^="index-module_menu-container_"]', (element) => {
		var timebar = $('<div class="index-module_tip"><img src="/img/wenxintishi.67f17cbe.png?x-bce-process=image/format,f_auto"/><span id="basicEduGreeting"></span>,现在是<span id="basicEduTime"></span>感谢您使用本脚本~</div>')
		if (element && element.length > 0) {
			element.after(timebar)
			window.setInterval(() => {
				timebar.find("#basicEduTime")[0].innerText = Time();
				timebar.find("#basicEduGreeting")[0].innerText = Greeting();
			}, 500);
		}
	}, true)

	// 给底栏增加个一言
	waitForKeyElements('#zxxcontent', (element) => {
		var poembar = $('<div class="index-module_tip"><img src="/img/wenxintishi.67f17cbe.png?x-bce-process=image/format,f_auto"/><span id="todayPoem"></span></div>')
		if (element && element.length > 0) {
			let text = poembar.find("#todayPoem")
			element.after(poembar)
			text.on('click', () => {
				if (text[0].innerText === "加载中……") return;
				text[0].innerText = "加载中……"
				text.css({ "cursor": "default" });
				Poem(text)
			})
			text[0].innerText = "加载中……"
			text.css({ "cursor": "default" });
			Poem(text)
		}
	}, true)

	// 免登录下载
	let contentType = new URL(location.href).searchParams.get("contentType") || '';
	let contentId = new URL(location.href).searchParams.get("contentId") || '';
	if (/^\/tchMaterial\/detail/.test(location.pathname) && contentType === 'assets_document' && contentId) {
		try {
			let data = await Promise.any(["1", "2", "3"].map(async base => {
				try {
					let data = await request({
						url: `https://s-file-${base}.ykt.cbern.com.cn/zxx/ndrv2/resources/tch_material/details/${contentId}.json`,
						headers: {
							"referer": "https://basic.smartedu.cn/",
							'Cache-Control': 'no-cache'
						}
					});
					console.log(data)
					let item = {
						id: data.id,
						title: data.global_title['zh-CN'],
						cover: data.custom_properties.thumbnails[0],
						url: `https://r${base}-ndr.ykt.cbern.com.cn/edu_product/esp/assets/${contentId}.pkg/pdf.pdf`,
					};
					return item
				} catch (error) {
					console.error('【国家中小学智慧教育平台助手】\n获取数据时发生错误:', error);
				}
			}));

			waitForKeyElements("iframe#pdfPlayerFirefox[src]", (element) => {
				let url = new URL(element[0].src);
				url.searchParams.set("file", item.url);
				element[0].src = url.toString();

				let button = $('<button class="index-module_pdf-fullscreen">网页全屏</button>')
				element.parent().parent().css({ "border-radius": "8px" })
				button.on('click', function () {
					let parent = element.parent().parent();
					if (!parent.hasClass('fullscreen')) {
						// 进入全屏模式
						parent.addClass('fullscreen').css({ "border-radius": "0" });
						$('body').addClass('no-scroll');
						button.text('退出全屏');
					} else {
						// 退出全屏模式
						parent.removeClass('fullscreen').css({ "border-radius": "8px" });
						$('body').removeClass('no-scroll');
						button.text('网页全屏');
					}
				});
				element.before(button)
			})

			// 点击登录提示中隐藏的取消按钮
			waitForKeyElements('button[type="button"].fish-btn[style]', async (element) => {
				element.click();
				let dialog = await Swal.fire({
					title: data.title,
					imageUrl: data.cover,
					imageHeight: 300,
					allowOutsideClick: false,
					showCloseButton: true,
					showDenyButton: true,
					confirmButtonText: '跳转',
					heightAuto: false,
					scrollbarPadding: false,
					denyButtonText: '关闭',
					html: `<div>点击“跳转”后将在新标签页打开PDF链接<br>部分浏览器支持点击按钮后支持直接预览教材<br>教材较大,如果支持预览请耐心等待加载完成<br>一般加载完成后会出现“下载”按钮<br>如果浏览器不支持预览,则会直接下载PDF</div>
				`
				});
				if (dialog.isConfirmed) {
					window.open(data.url, "_blank")
				}
			})
		} catch (error) {
			console.error('所有请求都失败了:', error);
		}
	}

	// 增强预览
	if (/^\/pdfjs\/.*\/web\/viewer.html/.test(location.pathname)) {
		let url = new URL(location.href);
		let file = url.searchParams.get("file");
		if (file) {
			let decodedFile = decodeURIComponent(file);
			let newFile = decodedFile.replace(/(r\d)-ndr-\w+/, '$1-ndr');
			url.searchParams.set("file", newFile);
			window.location.replace(url.toString());
		}
		// 取消隐藏预览PDF页面的隐藏功能(例如下载、打印等功能)
		waitForKeyElements('.visibleLargeView, .visibleMediumView, .visibleSmallView, .hiddenLargeView, .hiddenMediumView, .hiddenSmallView', function (element) {
			element.removeClass('visibleLargeView visibleMediumView visibleSmallView hiddenLargeView hiddenMediumView hiddenSmallView hidden');
			if (element.attr('hidden') !== undefined) {
				element.removeAttr('hidden');
			}
			if (element.attr('disabled') !== undefined) {
				element.removeAttr('disabled');
			}
		}, true);
	}

	var toast = Swal.mixin({
		toast: true,
		position: 'bottom-end',
		showConfirmButton: false,
		timer: 3500,
		timerProgressBar: true,
		showCloseButton: true,
		didOpen: (toast) => {
			toast.addEventListener('mouseenter', Swal.stopTimer);
			toast.addEventListener('mouseleave', Swal.resumeTimer);
		}
	});

	var message = {
		success: function (text) {
			toast.fire({ html: text, icon: 'success' });
		},
		error: function (text) {
			toast.fire({ html: text, icon: 'error' });
		},
		warning: function (text) {
			toast.fire({ html: text, icon: 'warning' });
		},
		info: function (text) {
			toast.fire({ html: text, icon: 'info' });
		},
		question: function (text) {
			toast.fire({ html: text, icon: 'question' });
		}
	};

	function Time() {
		function repair(i) {
			if (i >= 0 && i <= 9) {
				return "0" + i;
			} else {
				return i;
			}
		}
		var date = new Date();
		var year = date.getFullYear();
		var month = repair(date.getMonth() + 1);
		var day = repair(date.getDate());
		var hours = repair(date.getHours());
		var minute = repair(date.getMinutes());
		var second = repair(date.getSeconds());

		var curTime = year + "年 - " + month + "月 - " + day + "日 " + hours + "时 : " + minute + "分 : " + second + "秒";
		return curTime;
	}

	function Greeting() {
		var date = new Date();
		var hour = date.getHours();
		var greeting = '';

		if (hour >= 0 && hour <= 10) {
			greeting = '早上好';
		} else if (hour > 10 && hour <= 14) {
			greeting = '中午好';
		} else if (hour > 14 && hour <= 18) {
			greeting = '下午好';
		} else if (hour > 18 && hour <= 24) {
			greeting = '晚上好';
		}
		return greeting;
	}

	async function Poem(element) {
		try {
			let data = await request({
				data: "",
				url: "https://v1.hitokoto.cn/?c=i",
				headers: {
					"referer": "https://hitokoto.cn",
					'Cache-Control': 'no-cache'
				}
			})
			// 使用数据更新页面元素
			element[0].innerText = `「${data.hitokoto}」${data.from_who || ""}`;
			element.css({ "cursor": "pointer" });
		} catch (error) {
			// 处理错误情况
			console.error('获取诗词时发生错误:', error);
			element[0].innerText = '诗词加载失败';
			element.css({ "cursor": "pointer" });
		}
	}

	function request(option) {
		return new Promise((resolve, reject) => {
			GM.xmlHttpRequest({
				method: 'get',
				...option,
				onload: (response) => {
					let res = JSON.parse(response.responseText);
					resolve(res);
				},
				onerror: (error) => {
					reject(error);
				},
			});
		});
	}

	function addStyle(id, tag, css) {
		tag = tag || 'style';
		let doc = document, styleDom = doc.getElementById(id);
		if (styleDom) styleDom.remove();
		let style = doc.createElement(tag);
		style.rel = 'stylesheet';
		style.id = id;
		tag === 'style' ? style.innerHTML = css : style.href = css;
		doc.getElementsByTagName('body')[0].appendChild(style);
	}

	function addDownStyle() {
		let color = "#1e62ec";
		let swalcss = `
			.swal2-loader{display:none;align-items:center;justify-content:center;width:2.2em;height:2.2em;margin:0 1.875em;-webkit-animation:swal2-rotate-loading 1.5s linear 0s infinite normal;animation:swal2-rotate-loading 1.5s linear 0s infinite normal;border-width:.25em;border-style:solid;border-radius:100%;border-color:${color} transparent ${color} transparent }
			.swal2-styled.swal2-confirm{border:0;border-radius:.25em;background:initial;background-color:${color};color:#fff;font-size:1em}
			.swal2-styled.swal2-confirm:focus{box-shadow:0 0 0 3px ${color}80 }
			.swal2-timer-progress-bar{width:100%;height:.25em;background:${color}33 }
			.swal2-progress-steps .swal2-progress-step{z-index:20;flex-shrink:0;width:2em;height:2em;border-radius:2em;background:${color};color:#fff;line-height:2em;text-align:center}
			.swal2-progress-steps .swal2-progress-step.swal2-active-progress-step{background:${color} }
			.swal2-progress-steps .swal2-progress-step-line{z-index:10;flex-shrink:0;width:2.5em;height:.4em;margin:0 -1px;background:${color}}
		`
		let css = `
			[class^="index-module_header-wrap_"] [class^="index-module_header"] .theme-menu-normal {
				/*height: 95px !important;*/
				height: auto !important;
			}
			
			[class^="index-module_header-wrap_"] [class^="index-module_header"] .theme-menu-sticky {
				height: auto !important;
				-webkit-backdrop-filter: blur(15px);
				backdrop-filter: blur(15px);
				background: rgba(255, 255, 255, 0.8);
			}

			#basicEduTime{
				margin: 0 5px;
				color: ${color};
			}
			
			.index-module_tip {
				cursor: default;
				text-align: center;
				display: inline-flex;
				justify-content: center;
				margin: 10px 0;
				width: 100%;
			}

			.index-module_tip img {
				margin-right: 5px;
				width: 20px;
				height: 20px;
			}

			.index-module_pdf-fullscreen {
				background: #1e62ecf0;
				position: absolute;
				text-align: center;
				color: #fff;
				top: 20px;
				right: 30px;
				padding: 8px 25px;
				border-radius: 144.889px;
				border: 0;
			}

			.index-module_pdf-fullscreen:hover {
				background: #4079eff0;
			}

			div .fullscreen {
				position: fixed !important;
				top: 0 !important;
				left: 0 !important;
				width: 100% !important;
				height: 100% !important;
				z-index: 1000 !important;
			}

			.no-scroll {
        		overflow: hidden !important;
    		}
		`;
		// 先监听颜色方案变化 SweetAlert2-Default
		window.matchMedia('(prefers-color-scheme: dark)').addListener((e) => {
			if (e.matches) {
				// 切换到暗色主题
				addStyle('swal-pub-style', 'style', GM_getResourceText('SwalDark'));
			} else {
				// 切换到浅色主题
				addStyle('swal-pub-style', 'style', GM_getResourceText('Swal'));
			}
			addStyle('SweetAlert2-User', 'style', swalcss);
		});

		// 再修改主题 SweetAlert2-Default
		if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
			// 切换到暗色主题
			addStyle('swal-pub-style', 'style', GM_getResourceText('SwalDark'));
		} else {
			// 切换到浅色主题
			addStyle('swal-pub-style', 'style', GM_getResourceText('Swal'));
		}
		addStyle('SweetAlert2-User', 'style', swalcss);
		addStyle('BasicSmartEdu-User', 'style', css);
	}

	function waitForKeyElements(selectorTxt, actionFunction, bWaitOnce, iframeSelector) {
		var targetbadges, btargetsFound;
		if (typeof iframeSelector == "undefined")
			targetbadges = $(selectorTxt);
		else
			targetbadges = $(iframeSelector).contents()
				.find(selectorTxt);
		if (targetbadges && targetbadges.length > 0) {
			btargetsFound = true;
			targetbadges.each(function () {
				var jThis = $(this);
				var alreadyFound = jThis.data('alreadyFound') || false;
				if (!alreadyFound) {
					var cancelFound = actionFunction(jThis);
					if (cancelFound) {
						btargetsFound = false;
					} else {
						jThis.data('alreadyFound', true);
					}
				}
			});
		} else {
			btargetsFound = false;
		}
		var controlObj = waitForKeyElements.controlObj || {};
		var controlKey = selectorTxt.replace(/[^\w]/g, "_");
		var timeControl = controlObj[controlKey];
		if (btargetsFound && bWaitOnce && timeControl) {
			clearInterval(timeControl);
			delete controlObj[controlKey]
		} else {
			if (!timeControl) {
				timeControl = setInterval(function () {
					waitForKeyElements(selectorTxt, actionFunction, bWaitOnce, iframeSelector);
				}, 500);
				controlObj[controlKey] = timeControl;
			}
		}
		waitForKeyElements.controlObj = controlObj;
	}
})();