// ==UserScript==
// @name quizii上传试卷工具——补完试卷
// @namespace http://tampermonkey.net/
// @version 1.2.2
// @description 预传试卷框架后,逐题更新
// @author jin junwei
// @match http://121.42.229.71:8200/overlook?q=*
// @match http://121.42.229.71:8100/typesetting/item/*
// @match http://121.42.229.71:8100/correction/item/*
// @grant none
// ==/UserScript==
'use strict';
// 纠错系统
// http://121.42.229.71:8100/correction/item/*
(function() {
if( !location.href.startsWith("http://121.42.229.71:8100/correction/item/") ){ return; }
// 悬浮按钮的容器
let referenceElement=function (referenceElement){
let newElement = document.createElement('div');
newElement.innerHTML = '<span></span>';
newElement = newElement.firstChild
// 插入元素
referenceElement.parentElement.insertBefore(newElement, referenceElement.nextElementSibling);
return newElement;
}(document.querySelector('#remove'));
// 悬浮按钮,导入补完试卷
let newElement = document.createElement('div');
newElement.innerHTML = '<button class="btn btn-info" title="油猴脚本">修复选项中缺少题号错误</button>';
newElement = newElement.firstChild
referenceElement.appendChild(newElement); // 插入到页面
// 功能脚本
newElement.onclick = onclick;
async function onclick() {
if( !/选项中 9.和\d+之间缺少题号/.test( document.querySelector("#parse_result").textContent ) ){
alert("当前错误类型不是选项中缺少题号,请手动改正");
return;
}
let textarea = document.querySelector("#stem_data")
// 在a-d前添加字母"z",修复所有可能的错误;
// 在补完习题时,需要配合本脚本删除字母"z",
textarea.value =textarea.value.replace(/(?<=\[\[img\]\]{"src": "\w*\d)(?=[a-d]\d)/g, "z");
// 点击 test
document.querySelector("#test").click();
}
})();
// http://121.42.229.71:8200/overlook?q=
(function() {
if( !location.href.startsWith("http://121.42.229.71:8200/overlook?q=") ){return}
// 悬浮按钮的容器
let referenceElement=function (referenceElement){
let newElement = document.createElement('div');
newElement.innerHTML = '<span></span>';
newElement = newElement.firstChild
// 插入元素
referenceElement.parentElement.insertBefore(newElement, referenceElement.nextElementSibling);
return newElement;
}(document.querySelector('#item-status-stats'));
// 悬浮按钮,导入补完试卷
let newElement = document.createElement('div');
newElement.innerHTML = '<div class="btn btn-success btn-sm" title="油猴脚本,导入预测试卷对应的补完文件,*.quizii补完.json.txt">补完试卷</div>';
newElement = newElement.firstChild
referenceElement.appendChild(newElement); // 插入到页面
// 功能脚本
newElement.onclick = onclick;
async function onclick() {
async function waitTabFocus(){
await new Promise(r => setTimeout(r, 2000));
while(!document.hasFocus()) {
await new Promise(r => setTimeout(r, 200));
}
}
// 习题列表
const exElementList = [...document.querySelectorAll("q")]
// 逐题修改习题
let isFirst=true
for(let i=0; i<exElementList.length; i++){
let exEl = exElementList[i]
if(exEl.innerText.startsWith("【预传习题】")){
let search = "?total="+exElementList.length+"&idx="+i+"&pId="+location.search.split("q=")[1].split("&")[0];
if (isFirst){
search = search + "&load=true";
isFirst=false;
}
let href = exEl.parentElement.nextElementSibling.children[1].href+search;
let iframeElement = openInNewTab(href);
await waitTabFocus();
}
}
}
function openInNewTab(url) {
var win = window.open(url, '_blank');
win.focus();
}
})();
// http://121.42.229.71:8100/typesetting/item/*
// 导入"*.quizii补完.json.txt"
(function() {
if( !location.href.startsWith("http://121.42.229.71:8100/typesetting/item/") ){return;}
if( !location.search.startsWith("?total=") ){return;}
if( document.getElementById("show-dups") ){
insertButtonPreviewFirstSimilarity();
}
insertButtonSubmitAndClose()
const idx = parseInt(location.search.split("idx=")[1].split("&")[0]);
const total = parseInt(location.search.split("total=")[1].split("&")[0]);
const pId = location.search.split("pId=")[1].split("&")[0];
if(location.search.indexOf("load=true")>-1){
const element = openFileInput(idx,total,pId);
element.click();
//document.body.removeChild(element);
return
}else{
const exData = checkAndGetExData(idx, total, pId);
updateEx(exData);
}
function checkAndGetExData(idx, total, pId){
const paperData = JSON.parse(localStorage.getItem("paperData"));
// check, todo
if(paperData.pId!==pId){
alert("试卷id不一致,请重新载入*.quizii补完.json.txt文件!");
return;
}
if(paperData.exDataList.length!==total){
alert("习题总数不一致,请检查习题数!");
return;
}
console.log("读取了试卷习题数据,pId="+paperData.pId+"; 保存time="+Date(paperData.time))
const exData = paperData['exDataList'][idx];
return exData;
}
function getImageDict(question_html){
}
async function updateEx(exData) {
while(!editor || !editor.edit.doc.body.innerText.startsWith("【预传习题】")) {
await new Promise(r => setTimeout(r, 300));
}
const previewEl = document.querySelector("#preview");
while(previewEl.disabled) {
await new Promise(r => setTimeout(r, 200));
}
// await waitElementCreated(editor.edit.doc, "q");
// 替换图片
const imgMap = new Map(
[...editor.edit.doc.querySelectorAll("img")].map(el=>{
// 上传预传试卷后,由于图片src中含"abcd"等字母,会出现“97和98之间缺少序号”的错误,
// 如“1a1c1”,在单个字母“a”或“c”前后插入字母“z”可以避免出现这个错误,如“1za1c1”
// 在这里需要删除这些额外添加的字母"z".
el.src = el.src.replace(/z/g, "");
el.dataset.keSrc = el.dataset.keSrc.replace(/z/g,"");
let imgMark = el.previousSibling.wholeText.trim();
if( /【预传图片\-\w+\-\d+】/.test(imgMark) ){
let width = imgMark.replace("】","").split("-").pop();
el.style["width"]=width+"px";
el.width=width;
}
let imgHtml = JSON.stringify(el.outerHTML);
imgHtml = imgHtml.substring(1,imgHtml.length-1);
return [imgMark, imgHtml];
})
);
let exString = JSON.stringify(exData);
// exString = exString.replace(/\\n/g,"\n") // 换行
imgMap.forEach((v, k)=>{
exString = exString.replace(new RegExp(k+"/","gm"),v);
});
exData=JSON.parse(exString);
// 解答题 题干
const stemEl = editor.edit.doc.querySelector("q > div > stem")
if(stemEl){stemEl.innerHTML = exData.stem;}
const subqEls = [...editor.edit.doc.querySelectorAll("q subq")];
if(subqEls.length!==exData.qs.length){alert("问题个数不一致!");return;}
subqEls.forEach((subqEl,i)=>{
const qData = exData.qs[i];
// 选择题、填空题、子问题 题干
const subStemEl =subqEl.querySelector("stem");
if(subStemEl){subStemEl.innerHTML = qData.desc;}
// 选择题,选项
const subOptEls = [...subqEl.querySelectorAll("opt > span:nth-child(2)")];
if(subOptEls.length!==qData.opts.length){alert("选项个数不一致!");return;}
subOptEls.forEach((el,j)=>{
el.innerHTML = qData.opts[j];
});
// 答案、解析
aeditors[i].edit.doc.body.innerHTML = qData.ans;
eeditors[i].edit.doc.body.innerHTML = qData.exp;
});
const btnEl = document.querySelector("#preview");
// const btnEl = document.querySelector("#submit");
while(btnEl.disabled) {
await new Promise(r => setTimeout(r, 200));
}
btnEl.click();
btnEl.disabled = true;
// 完成后关闭页面
console.log("等待保存完成,并且关闭页面")
while(btnEl.disabled) {
await new Promise(r => setTimeout(r, 200));
}
// close();
}
function insertButtonSubmitAndClose(){
const liEl = document.createElement('li');
const element = document.createElement('a');
element.innerText="保存并关闭"
liEl.appendChild(element)
let paperEl = document.querySelector("#typesetting_pager > li:nth-child(2)")
if (!paperEl){
paperEl = document.querySelector("#typesetting_pager > li:nth-child(1)")
}
paperEl.parentElement.insertBefore(liEl, paperEl.nextElementSibling);
element.onclick = async function (){
const btnEl = document.querySelector("#submit");
btnEl.click();
await new Promise(r => setTimeout(r, 200));
close();
};
return element
}
function insertButtonPreviewFirstSimilarity(){
const liEl = document.createElement('li');
const element = document.createElement('a');
element.innerText="复制并预览首个相似题"
liEl.appendChild(element)
let paperEl = document.querySelector("#typesetting_pager > li:nth-child(2)")
if (!paperEl){
paperEl = document.querySelector("#typesetting_pager > li:nth-child(1)")
}
paperEl.parentElement.insertBefore(liEl, paperEl.nextElementSibling);
element.onclick = previewFirstSimilarity;
return element
}
async function waitElementCreated(parentEl, selector){
let el;
while(!(el=parentEl.querySelector(selector))) {
// console.log(document.querySelector(selector).innerHTML);
await new Promise(r => setTimeout(r, 100));
}
return el
}
function previewFirstSimilarity() {
let qEl = document.querySelector("#q-dups q");
if( !qEl ){
alert("请点击左下角的Similarities按钮")
return;
}
qEl = qEl.cloneNode(true)
// mathjax unrender
qEl.querySelectorAll(".MathJax").forEach(el=>el.remove());
qEl.querySelectorAll("script")
.forEach(el=>el.innerText = "\\( "+htmlEntities(el.innerText) +" \\)")
editor.html(qEl.outerHTML);
for (var i = 0; i < qnum; ++i) {
let subqEl = qEl.querySelectorAll("subq")[i]
if( subqEl.querySelector(".answer .dd") ){
aeditors[i].html(subqEl.querySelector(".answer .dd").innerHTML);
}
if( subqEl.querySelector(".exp .dd") ){
eeditors[i].html(subqEl.querySelector(".exp .dd").innerHTML);
}
}
qEl=undefined;
document.querySelector("#preview").click()
};
function htmlEntities(str) {
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
}
function openFileInput(idx, total, pId){
const element = document.createElement('input');
element.setAttribute('type', 'file' );
element.setAttribute('accept', '.json.txt');
//element.style.display = 'none';
//document.body.appendChild(element);
const paperEl = document.querySelector("#mathright > h4 > div.pull-right > span")
paperEl.parentElement.insertBefore(element, paperEl.nextElementSibling);
element.onchange = function (event) {
const filename = paperEl.innerText + ".quizii补完.json.txt"
if(filename !== event.target.files[0].name) {
alert("文件名不匹配,请导入:"+filename);
return;
}
const reader = new FileReader();
reader.readAsText(event.target.files[0]);
reader.onload = function(event){
// 习题列表
console.log("读取了文件:"+filename);
const exDataList = JSON.parse(event.target.result);
// 拆分解答题的子问题
exDataList.filter(o=> o.type===1003 )
.forEach(splitSubq)
console.log("拆分了解答题的子问题");
// 保存到localStorage
localStorage.setItem("paperData", JSON.stringify({pId:pId, time:Date.now(), exDataList:exDataList}));
console.log("exDataList保存到了localStorage的paperData");
// callback
const exData = checkAndGetExData(idx, total, pId);
updateEx(exData);
}
};
return element
}
function splitSubq(exObj){
// 拆分子问题(1)...(2)...
let preQObj = exObj.qs[0];
exObj.qs.length = 0;
let i = 1;
while(true){
let separator = "("+(i++)+")";
if(preQObj.desc.indexOf(separator)<0){
if(exObj.qs.length === 0){exObj.qs.push(preQObj);}
return exObj;
}
let qObj = {desc:"", ans:"", exp:"",opts:[],context:""};
[preQObj.desc, qObj.desc] = splitOnce(preQObj.desc, separator);
[preQObj.ans, qObj.ans] = splitOnce(preQObj.ans, separator);
[preQObj.exp, qObj.exp] = splitOnce(preQObj.exp, separator);
// 防止丢失不规范的内容
if( exObj.qs.length === 0 ){
if( qObj.exp.length ===0 ) {
qObj.exp = preQObj.exp
}
if( qObj.ans.length ===0 ) {
qObj.ans = preQObj.ans
}
}
exObj.qs.push(qObj);
preQObj = qObj
}
}
function splitOnce(str, separator){
let splitAt = str.indexOf(separator);
if (splitAt===-1){return [str, ""];}
return [str.substring(0,splitAt), str.substring(splitAt+separator.length)];
}
})();