您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
预传试卷框架后,逐题更新
当前为
// ==UserScript== // @name quizii上传试卷工具——补完试卷 // @namespace http://tampermonkey.net/ // @version 1.2.8 // @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 GM_setValue // @grant GM_getValue // @grant GM_setClipboard // ==/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; } if( !document.querySelector("#book-content").textContent.includes("【预传习题】") ){ 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 && Date.now()<GM_getValue("closeBefore", 0) ){ GM_setValue("closeBefore", 0); close(); 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(); GM_setValue("closeBefore", Date.now()+3000); // 载入新页面时关闭 // 小标题的最后一题不会自动刷新页面 setTimeout(close, 200); }; 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); const filename = paperEl.innerText + ".quizii补完.json.txt" GM_setClipboard(filename); // 复制文件名到粘贴板 element.onchange = function (event) { 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)]; } })();