由于不希望学生可以复制粘贴代码,这样会失去实测的目的,但偶尔自己也需要复制粘贴测试学生提供的代码,于是用篡改猴组件给前端页面注入JS脚本来实现这个功能。由于代码编辑器是高级的monaco[1],所以需要使用monaco的API来设置代码,以保持数据的一致性,用法也是常规的editor->model->get/setValue。唯一需要知道的是如何引用已经存在的editor,凭借经验,大多数会放在window对象下,于是盲猜是window.monaco或者window.editor。最终确定是window.monaco.editor。注意:在篡改猴的JS脚本中,需要使用unsafeWindow来访问正常的window对象。
// ==UserScript==
// @name 清览题库|清览测验复制粘贴
// @namespace https://app.qingline.net/student/examing*
// @version 0.2
// @description 自动屏蔽弹窗;允许复制题目;允许复制代码;允许粘贴代码
// @author tpu01yzx
// @connect *
// @match https://app.qingline.net/student/examing*
// @match https://app.qingline.net/student/training?exam_id=*
// @run-at document-end
// @license MIT
// @grant unsafeWindow
// ==/UserScript==
(function () {
console.log('清览题库|清览测验复制粘贴 => start');
'use strict';
window.addEventListener('load', function () {
setTimeout(() => {
init();
}, 1000);
});
function getQuesText() {
const $ = (s) => [...document.querySelectorAll(s)];
const head = $(".stem")[0]
.textContent
.trim()
.replace(/^([0-9]+)/, "")
.replace(/[。?]/, "")
.replace(/(\s+?)/g, " ");
const body = $(".info_content")[0].textContent.trim();
return head + "\n" + body;
}
function getCodeText() {
return "//Copied from app.qingline.net \n" + document.getElementsByClassName("inputarea")[0].value;
}
function AddButton(p, t, func, tips, tips2) {
// 创建一个新的按钮元素
const newButton = document.createElement('button');
newButton.type = 'button';
//newButton.style.marginRight = "10px";
newButton.className = 'submit_btn ant-btn';
// 创建一个新的span元素并将其添加到按钮中
const newSpan = document.createElement('span');
newSpan.textContent = t;
newButton.appendChild(newSpan);
// 添加新按钮作为fixed_con元素的第一个子元素
p.insertBefore(newButton, p.firstChild);
//fixedCon.appendChild(newButton);
newButton.addEventListener('click', function () {
//const textToCopy = getQuesText();
//console.log(textToCopy);
//navigator.clipboard.writeText(textToCopy)
var msg;
try{
func();
msg = tips || "成功";
}catch(e){
msg = tips || "失败";
}
console.log('Text copied to clipboard');
// 创建一个新的元素来显示复制成功的消息
const copySuccessMessage = document.createElement('div');
copySuccessMessage.textContent = msg;
copySuccessMessage.style.position = 'fixed';
copySuccessMessage.style.bottom = '10px';
copySuccessMessage.style.right = '10px';
copySuccessMessage.style.padding = '10px';
copySuccessMessage.style.backgroundColor = '#3d64ff';
copySuccessMessage.style.color = 'white';
copySuccessMessage.style.borderRadius = '5px';
document.body.appendChild(copySuccessMessage);
// 一段时间后隐藏消息
setTimeout(function () {
copySuccessMessage.style.display = 'none';
}, 1000);
});
}
function init() {
console.log('清览题库|清览测验禁用复制和全屏 => init');
// 选择fixed_con元素
var fixedCon = document.getElementsByClassName("right_bottom_btns");
if(fixedCon == null || fixedCon.length <= 0) {
setTimeout(function() {
init();
}, 1000);
return;
}
fixedCon = fixedCon[0];
AddButton(fixedCon, '复制题目', function() {
const textToCopy = getQuesText();
//console.log(textToCopy);
navigator.clipboard.writeText(textToCopy);
}, '复制成功~');
AddButton(fixedCon, '复制代码', function() {
//const textToCopy = getCodeText();
const _window = unsafeWindow ? unsafeWindow : window
const textToCopy = _window.monaco.editor.getModels()[0].getValue();
//console.log(textToCopy);
navigator.clipboard.writeText(textToCopy);
}, '复制成功~');
AddButton(fixedCon, '粘贴代码', async function() {
const textToPaste = await navigator.clipboard.readText();
const _window = unsafeWindow ? unsafeWindow : window
_window.monaco.editor.getModels()[0].setValue(textToPaste);
//document.getElementsByClassName("inputarea")[0].value = textToPaste;
}, '粘贴成功~');
// 检查是否存在弹窗
const modal = document.querySelector('.ant-modal-body');
console.log(modal);
if (modal) {
const modalRoot = document.querySelector('.ant-modal-root');
if(modalRoot) modalRoot.remove();
}
}
})();
参考文献:
[1] https://microsoft.github.io/monaco-editor/
[2] https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.ITextModel.html