// ==UserScript== // @name LMSGuide辅助 // @version 1.3.2 // @description 要有远大的理想,也要有脚踏实地的本领 // @author 小钊 // @match *://10.90.53.11:5090/* // @homepageURL https://www.zxqblog.cn // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // ==/UserScript== (function() { 'use strict'; // 通用样式定义 // 复制到虚拟机的样式 const buttonStyle = ` .copy-vm-btn { position: absolute; right: 10px; top: 10px; padding: 5px 10px; background-color: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 12px; opacity: 0; transition: opacity 0.3s ease; z-index: 100; } .copy-vm-btn:hover { background-color: #45a049; } /* pre标签容器样式 */ .pre-container { position: relative; } /* 鼠标悬浮时显示按钮 */ .pre-container:hover .copy-vm-btn { opacity: 1; } `; const styleSheet = document.createElement("style"); styleSheet.textContent = buttonStyle; document.head.appendChild(styleSheet); // 设置页面的开关样式 const switchStyles = { position: 'relative', width: '50px', height: '24px', appearance: 'none', outline: 'none', borderRadius: '12px', backgroundColor: '#ccc', cursor: 'pointer', verticalAlign: 'middle' }; const sliderStyles = { position: 'absolute', top: '2px', left: '2px', width: '20px', height: '20px', borderRadius: '50%', backgroundColor: '#fff', transition: 'all 0.3s' }; // ------------------------------------- // 注册脚本菜单 GM_registerMenuCommand("设置", showSettingsPopup); // ------------------------------------- // 免责说明 window.addEventListener('load', function(event) { if (!GM_getValue('mianzeshenming',false)){ // 如果检测到没有同意协议则渲染免责协议窗口 document.body.innerHTML = ''; const mzsf = document.createElement('div'); Object.assign(mzsf.style, { position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', padding: '20px', backgroundColor: '#fff', borderRadius: '8px', boxShadow: '0 4px 8px rgba(0,0,0,0.2)', zIndex: '1000', width: '300px' }); // 标题与提示渲染 const title = document.createElement('h2'); title.textContent = '免责协议'; mzsf.appendChild(title); const tips = document.createElement('p'); tips.textContent = "请合理使用此脚本,造成一切损失与作者无关!同意协议后点击刷新进行页面刷新,如果不同意协议请删除此脚本并刷新网页" tips.style.marginTop = '10px'; tips.style.marginBottom = '10px' mzsf.appendChild(tips); // 开关渲染 // 复制到虚拟机开关 mzsf.appendChild(createSwitch('同意免责协议', 'mianzeshenming')); // 刷新按钮渲染 const sxButton = document.createElement('button'); sxButton.textContent = '刷新'; Object.assign(sxButton.style, { marginTop: '15px', padding: '8px 16px', backgroundColor: '#f44336', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' }); sxButton.onclick = function() { location.reload() }; mzsf.appendChild(sxButton); document.body.appendChild(mzsf); }else { // 否则渲染设置窗口 if (!GM_getValue("CopyToVM_Switch_Status",false) && !GM_getValue("Autoground_Switch_Status",false) && !GM_getValue("Select_Switch_Status",false) && !GM_getValue("NoOneShowsetting",false)) { // 如果所有的配置项均未配置,则显示配置窗口 GM_setValue("NoOneShowsetting", true); showSettingsPopup(); } } }) // ------------------------------------- // 函数部分 // 设置->创建开关 函数 function createSwitch(label, storageKey, onChange) { const container = document.createElement('div'); container.style.marginBottom = '15px'; const switchLabel = document.createElement('label'); switchLabel.textContent = label; switchLabel.style.marginRight = '10px'; switchLabel.style.fontWeight = 'normal'; const switchInput = document.createElement('input'); switchInput.type = 'checkbox'; Object.assign(switchInput.style, switchStyles); const slider = document.createElement('span'); Object.assign(slider.style, sliderStyles); switchInput.appendChild(slider); // 初始状态 const isChecked = GM_getValue(storageKey, false); switchInput.name = storageKey; switchInput.checked = isChecked; switchInput.style.backgroundColor = isChecked ? '#4CAF50' : '#ccc'; slider.style.transform = isChecked ? 'translateX(26px)' : 'translateX(0)'; // 事件处理 switchInput.addEventListener('change', function() { const checked = this.checked; this.style.backgroundColor = checked ? '#4CAF50' : '#ccc'; slider.style.transform = checked ? 'translateX(26px)' : 'translateX(0)'; GM_setValue(storageKey, checked); if (onChange) onChange(checked); }); container.appendChild(switchLabel); container.appendChild(switchInput); return container; } // 设置->主界面 函数 function showSettingsPopup() { // 确保之前的弹窗被移除 const existingPopup = document.querySelector('div[style*="position: fixed; top: 50%; left: 50%;"]'); if (existingPopup) { document.body.removeChild(existingPopup); } const popup = document.createElement('div'); Object.assign(popup.style, { position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', padding: '20px', backgroundColor: '#fff', borderRadius: '8px', boxShadow: '0 4px 8px rgba(0,0,0,0.2)', zIndex: '1000', width: '300px' }); // 标题与提示渲染 const title = document.createElement('h2'); title.textContent = '设置'; popup.appendChild(title); const tip_list = ["启动立马生效,关闭刷新生效","复制到虚拟机优先级高于复制到实体机"]; const tips = document.createElement('p'); tips.textContent = tip_list[0]; tips.id = "setting_tip"; tips.style.marginTop = '10px'; tips.style.marginBottom = '10px' popup.appendChild(tips); let tip_id = 1 const tip_initer = setInterval(() => { tip_id = tip_id==1 ? 0 : 1; document.getElementById("setting_tip").innerHTML = tip_list[tip_id]; }, 1000) // 开关渲染 // 复制到虚拟机开关 popup.appendChild(createSwitch('复制到虚拟机', 'CopyToVM_Switch_Status')); // 实验报告滚动 popup.appendChild(createSwitch('实验报告自动滚动到底', 'Autoground_Switch_Status')); // 移除复制限制 popup.appendChild(createSwitch('移除移除选择、复制、剪切操作的限制', 'Select_Switch_Status')); // 复制到实体机开关 popup.appendChild(createSwitch('复制到实体机', 'CopyToTrue_Switch_Status')); // 关闭按钮渲染 const closeButton = document.createElement('button'); closeButton.textContent = '关闭'; Object.assign(closeButton.style, { marginTop: '15px', padding: '8px 16px', backgroundColor: '#f44336', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' }); closeButton.onclick = function() { document.body.removeChild(popup); clearInterval(tip_initer) }; popup.appendChild(closeButton); document.body.appendChild(popup); } // 主功能->弹窗函数 function notification(text) { const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 10px 20px; background-color: #4CAF50; color: white; border-radius: 5px; z-index: 10000; `; notification.textContent = text; document.body.appendChild(notification); setTimeout(() => notification.remove(), 2000); } // 复制到虚拟机功能 -> 复制到虚拟机 函数 function sendToVirtualMachine(text) { if (GM_getValue("CopyToVM_Switch_Status",false)){ console.log("text"+text) const code = "code:"+text document.getElementById("tool_content").contentWindow.postMessage(code, "*"); }else if (GM_getValue("CopyToTrue_Switch_Status",false)) { mycopy(text); } return true } // 复制到实体机功能 -> 复制到实体机 函数 function mycopy(data) { const TEXT_PLAIN = "text/plain"; const TEXT_HTML = "text/html"; const COPY = "copy"; const opt = Object.prototype.toString; const execCopyCommand = (data) => { const textarea = document.createElement("textarea"); const handler = (event) => { event.preventDefault(); event.stopImmediatePropagation(); for (const [key, value] of Object.entries(data)) { event.clipboardData && event.clipboardData.setData(key, value); } }; textarea.addEventListener(COPY, handler, true); textarea.style.position = "fixed"; textarea.style.left = "-999999999px"; textarea.style.top = "-999999999px"; textarea.value = data[TEXT_PLAIN] || " "; document.body.appendChild(textarea); textarea.select(); document.execCommand("copy"); textarea.removeEventListener(COPY, handler); document.body.removeChild(textarea); } function isString(value) { return opt.call(value) === "[object String]"; } const isEmptyContent = (data) => { if (!data) return true; return isString(data) ? !data : !data[TEXT_PLAIN]; }; const params = isString(data) ? { [TEXT_PLAIN]: data } : data; const plainText = params[TEXT_PLAIN]; if (!plainText) return false; if (navigator.clipboard && window.ClipboardItem) { const dataItems = {}; for (const [key, value] of Object.entries(params)) { const blob = new Blob([value], { type: key }); dataItems[key] = blob; } navigator.clipboard.write([new ClipboardItem(dataItems)]).catch(() => { execCopyCommand(params); }); } else { execCopyCommand(params); } return true; } // ------------------------------------- // 定时器部分 // 页面加载时移除选择、复制、剪切操作的限制 setInterval(() => { if (GM_getValue("Select_Switch_Status",false)) { document.onselectstart = function(event) {return true;}; document.oncopy = function(event) {return true;}; document.oncut = function(event) {return true;}; var elements = document.querySelectorAll('[class^=" language-"]'); elements.forEach(function(element) {element.style.cssText = '';}); } }, 1000) // 复制到虚拟/虚拟机按钮渲染 setInterval(() => { if (GM_getValue("CopyToVM_Switch_Status",false)||GM_getValue("CopyToTrue_Switch_Status",false)){ const btn_type = GM_getValue("CopyToVM_Switch_Status") ? ["虚拟",'Vm'] : ["实体","Tr"]; document.querySelectorAll('pre[class^=" language-"]').forEach(pre => { // 元素存在且状态正确 if (pre.getAttribute('data-type-button')==btn_type[1]) return; // 元素存在且状态不正确 if (pre.getAttribute('data-type-button') != null && pre.getAttribute('data-type-button')!=btn_type[1]) { pre.setAttribute('data-type-button', btn_type[1]); const btns = document.getElementsByClassName('copy-vm-btn') Array.from(btns).forEach(btn=>{ btn.innerHTML = `复制到${btn_type[0]}机`; btn.onclick = function() { const success = sendToVirtualMachine(pre.textContent); if (success) {notification(`已复制到${btn_type[0]}机!`);} }; }) return }; // 元素不存在 console.log("不正确") const container = document.createElement('div'); container.className = 'pre-container'; pre.parentNode.insertBefore(container, pre); container.appendChild(pre); const button = document.createElement('button'); button.className = 'copy-vm-btn'; pre.setAttribute('data-type-button', btn_type[1]); button.textContent = `复制到${btn_type[0]}机` button.onclick = function() { const success = sendToVirtualMachine(pre.textContent); if (success) {notification(`已复制到${btn_type[0]}机!`);} }; container.appendChild(button); }); } }, 1000); // 点击实验报告滚动到最下面 setInterval(() => { if (GM_getValue("Autoground_Switch_Status",false)) { try { document.querySelector("#assignment-report").addEventListener('click', function() { const lastElement =document.getElementById("report_area_ifr").contentDocument.querySelector("#tinymce p:last-child") if (lastElement) { lastElement.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); } }); }catch {} } }, 1000) })();