Cloze-Test Generator

Generate cloze tests for both Japanese and English texts, from any highlighted text on any site.

目前為 2016-09-20 提交的版本,檢視 最新版本

// ==UserScript==
// @name           Cloze-Test Generator
// @description    Generate cloze tests for both Japanese and English texts, from any highlighted text on any site.
// @namespace		https://gf.qytechs.cn/en/users/3656-kaiko
// @require https://gf.qytechs.cn/scripts/23318-tinysegmenter/code/TinySegmenter.js?version=148172
// @version        1.0
// @grant		 GM_registerMenuCommand
// @include	*
// ==/UserScript==
GM_registerMenuCommand("Run a Cloze Test", getSelectionText);

var styled = false;
var refreshTest = false;
var prevText;

function getSelectionText() {
	//Make sure we only have one quiz instance at a time
	var sanityCheck = document.getElementsByClassName("cloze-container-popup");
	
	//Get our selected text
	var text = "";
	if (sanityCheck.length === 0){
		if (window.getSelection) {
			text = window.getSelection().toString();
		} else if (document.selection && document.selection.type != "Control") {
			text = document.selection.createRange().text;
		}
		refreshTest = false;
	}
	
	//Restart the process if we change difficulty during run-time
	//After changing difficulty, see if we have already run the quiz first, and re-run the test to avoid duplicate test overlays.
	var currentDifficulty = localStorage.getItem("kaiko_clozeTestCurDif");
	var previousDifficulty = localStorage.getItem("kaiko_clozeTestPrevDif");
	//Restart!
	if (currentDifficulty !== null && previousDifficulty !== null){
		if (currentDifficulty !== previousDifficulty && sanityCheck.length > 0) {
			refreshTest = true;
			if (prevText !== undefined){
				text = prevText;
			}
			localStorage.setItem("kaiko_clozeTestPrevDif", currentDifficulty);
		}
	}
	
	//Start a new process
	if (text !== ""){
		if (sanityCheck.length === 0 || refreshTest === true) {
			//Store our selected text, in case difficulty is changed later
			prevText = text;
			
			//Style the quiz popup overlay
			if (!styled){
				var cssNode = document.createElement('style');
				cssNode.innerHTML = '.clozetitle,.clozeTestOptions{font-family:Verdana,Geneva,sans-serif}.cloze-container-popup,.cloze-popup{z-index:99999 !important;top:0 !important;right:0 !important;bottom:0 !important;left:0 !important}.cloze-container-popup{position:fixed;background:rgba(0,0,0,.85) !important}.cloze-popup{width:60% !important;height:80% !important;background:#2C3E50 !important;color:#fff !important;position:absolute !important;margin:auto !important;padding:20px !important;font-size:18px !important;overflow-y:scroll !important;line-height:30px !important;display:table-cell !important;vertical-align:middle !important;text-align:justify !important;}.clozetitle,.clozeTestOptions{background-color:#34495E !important}.clozetitle{text-indent:50px !important;letter-spacing:6px !important;color:#fff !important;font-size:25px !important;text-align:center !important;padding:15px !important;margin:0 0 10px !important}.clozeTestOptions{padding:5px !important;display:block !important}.content{height:100% !important;position:relative !important}.textareaform{display:inline !important;background-color:#395168 !important;box-shadow:none !important;color:#fff !important;resize:none !important;font-family:Meiryo-UI;border:1px solid #000 !important;padding:1px !important;margin:0 5px -4px !important;min-height:20px !important;max-height:20px !important;max-width:125px !important;}.closebtn,.resetbtn{text-transform:none !important; box-shadow:none !important; line-height:1 !important; cursor:pointer !important; color:#fff !important;border:2px solid #0C8698 !important}.btnbg,.closebtn{font-family:Verdana,Geneva,sans-serif}@-moz-document url-prefix(){.textareaform{margin:0 5px 4px !important;max-height:22px !important}}.btnbg{background-color:#34495E !important;padding:10px !important;bottom:0 !important;display:block !important}.closebtn,.closebtn:hover,.resetbtn,.resetbtn:hover{background:#00AEC8 !important;text-decoration:none !important}.closebtn{font-size:20px !important;padding:5px !important}.resetbtnbg{position:absolute !important;display:inline !important;right:15px !important}.resetbtn{font-size:15px !important;padding:5px !important}a.copyright:active,a.copyright:link,a.copyright:visited{color:#d30 !important;text-decoration:none !important}a.copyright:hover{text-decoration:underline !important}.clozeTestForm{margin:5px !important; line-height:2 !important;font-family:"ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro",Osaka, "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic", sans-serif; font-weight:normal !important;}.clozeTestForm .input{display:table-cell !important; position:relative !important;}.clozeTestDifficultyDrpdwn{background:#fff !important;color:#000 !important;width:auto !important;height:auto !important;padding:0 !important;font-size:14px !important;line-height:1 !important;border:0 !important;border-radius:0 !important;}';
				document.body.appendChild(cssNode);
				styled = true;
			}
			
			//Remove the old test instance if difficulty changed
			if (refreshTest) {
				closeQuiz();
			}
			
			//Start segmentation
			var segmenter = new TinySegmenter();
			var segs = segmenter.segment(text);
			
			// Make the output cleaner by separating the output at specific grammar points, and combining verb auxiliaries
			var illegalChars = ['を', 'へ', 'に', 'と', 'の', 'は', 'が', 'も', 'こと', 'から', 'まで', 'とき', 'など','ために','ための', '「', '」', 'ず', 'っ', '!', '?', 'しか', 'だけ', 'や', 'か', '、', '。', 'で', 'では', '.', '!', '?', '"', "'", '’', '(', ')', '(', ')', 'and', 'the', 'in', 'to', 'a', 'from', 'when', 'only', 'just', 'of', 'for', 'is', ' ', 'with', 'at', ':', ':', '=', '='];
			//Char Codes:
			//Hiragana: >= 12353 && <= 12435
			//Katakana: >= 12443 && <= 12532
			for (var i = 0; i < segs.length; ++i){
				if(illegalChars.indexOf(segs[i]) === -1){
					if (segs[i+1] && illegalChars.indexOf(segs[i+1]) === -1){
						if (segs[i+1] && segs[i].charCodeAt(segs[i].length-1) >= 12353 && segs[i].charCodeAt(segs[i].length-1) <= 12435 && segs[i+1].charCodeAt(0) >= 12353 && segs[i+1].charCodeAt(0) <= 12435){
							segs[i] += segs[i+1];
							segs.splice(i+1, 1);
							i = i-1;
						}
						else if(segs[i+1] && segs[i+1].length == 1){
							segs[i] += segs[i+1];
							segs.splice(i+1, 1);
							i = i-1;
						}
					}
				}
				//Accepted combinations: のを, ことを, への, までの, からの, とても, して, って, った
				//i, i+1
				else if(segs[i+1] && segs[i] == 'の' && segs[i+1] == 'を' || segs[i] == 'こと' && segs[i+1] == 'を' || segs[i] == 'へ' && segs[i+1] == 'の' ||
						segs[i] == 'まで' && segs[i+1] == 'の' || segs[i] == 'から' && segs[i+1] == 'の' || segs[i] == 'と' && segs[i+1].startsWith('ても') || segs[i].endsWith('し') && segs[i+1].startsWith('て') || segs[i].endsWith('っ') && segs[i+1].startsWith('て') || segs[i].endsWith('っ') && segs[i+1].startsWith('た')){
					segs[i] += segs[i+1];
					segs.splice(i+1, 1);
					i = i-1;
				}
				//Accepted combinations: 子どもたち, ず-ending-kanji-starting words (ie: verbs?)
				//-1, i, i+1
				else if (segs[i-1] == '子ども' && segs[i] == 'も' && segs[i+1] == 'たち' || /^[\u4e00-\u9faf]+$/.test(segs[i-1]) && segs[i+1].endsWith('ず') === -1){
					segs[i] = segs[i-1] + segs[i] + segs[i+1];
					segs.splice(i-1, 1);
					segs.splice(i, 1);
					i = i-2;
				}
				//Accepted combinations (with prior term): など, とか, ず
				//i-1, i
				else if (segs[i] == 'など' || segs[i] == 'とか' || segs[i] == 'ず'){
					segs[i] = segs[i-1] + segs[i];
					segs.splice(i-1, 1);
					segs.splice(i, 1);
					i = i-1;
				}
			}
			
			// Cloze-Test by a factor of every X destignated item
			// 'Remove X number of items every Y number of items'?
			// (set the value for a few at a time, and adjust for-loop placement)
			var skippedChars = ['、', '。', '「', '"', '”', '’','?','!', '」', 'こと', 'から', 'まで', 'とき', 'など', 'ことが', 'のは', 'のが', 'ことは', 'では', 'という', 'ても', 'ために', 'ための', 'and', 'the', 'in', 'to', 'a', 'from', 'when', 'only', 'just', 'of', 'for', 'is',' ', 'with', 'at', '(', ')'];
			var factorOfItemToChange;
			if (currentDifficulty && factorOfItemToChange === undefined){
				factorOfItemToChange = currentDifficulty;
				localStorage.setItem("kaiko_clozeTestPrevDif", factorOfItemToChange);
			} else {
				factorOfItemToChange = 3;
				localStorage.setItem("kaiko_clozeTestPrevDif", factorOfItemToChange);
				localStorage.setItem("kaiko_clozeTestCurDif", factorOfItemToChange);
			}
			//Create an array to store our answers in.
			var selectedEntries = [];
			var colLen;
			var engCount = 0;
			var nonEngCount = 0;
			var segJoin = "";
			for (var i = 0; i < segs.length; ++i) {
				//Check if the text is mostly English or not
				if (/[a-z]/i.test(segs[i])){
					++engCount;
				} else {
					++nonEngCount;
				}
				// If the modulus of the index + 1 (it's order in non-index notation) is 0
				// (divides perfectly by the value passed in with no remainder)
				if ((i + 1) % factorOfItemToChange === 0) {
					//Exclude banned terms
					if(skippedChars.indexOf(segs[i]) === -1 && segs[i].length != 1){
						colLen = segs[i].length *2.25;
						selectedEntries.push(segs[i] + '_-' + i + '-_');
						segs[i] = '<textarea class="textareaform" rows="1" cols="' + colLen + '" id="' + segs[i] + '_-' + i + '-_' + '"></textarea>';
					}
					else if (segs[i+1] && skippedChars.indexOf(segs[i+1]) === -1 && segs[i+1].length != 1){
						colLen = segs[i+1].length *2.25;
						selectedEntries.push(segs[i+1] + '_-' + i + '-_');
						segs[i+1] = '<textarea class="textareaform" rows="1" cols="' + colLen + '" id="' + segs[i+1] + '_-' + i + '-_' + '"></textarea>';
						i = i+1;
					}
				}
			}
			
			//Set out join character between text
			if (engCount >= nonEngCount){
				segJoin = " ";
			}
			//Store our IDs (answers) of each textarea to compare later.
			localStorage.setItem('kaiko_clozeTestAnswers', JSON.stringify(selectedEntries));
			//Create the popup cloze test.
			window.document.body.innerHTML += '<div class="cloze-container-popup"><div class="cloze-popup"><div class="clozetitle">CLOZE TEST</div>' + '<div class="clozeTestOptions">Difficulty: Remove every <select id="clozeTestDifficultyDrpdwn" class="clozeTestDifficultyDrpdwn"><option><option value="2">2nd<option value="3">3rd<option value="4">4th<option value="5">5th<option value="6">6th<option value="7">7th<option value="8">8th<option value="9">9th<option value="10">10th</select> word (+1 if grammar particle) <div class="resetbtnbg"><button id="resetbtn" class="resetbtn">Reset Form</button></div> </div>' + '<p id="clozeTestForm" class="clozeTestForm">' + segs.join(segJoin) + '</p><div class="btnbg"><center><button id="checkAnswers" class="closebtn">Show Answers</button> <button id="closebtn" class="closebtn">Close</button><br /><a href="https://kainokage.wordpress.com" target="_new" class="copyright">Made With Love</a> ・ <a href="https://gf.qytechs.cn/en/scripts/18228-automatically-hide-ruby-based-furigana" target="_new" class="copyright">Furigana Issues?</a></center></div></div></div>';
			//Add eventListeners for GUI functions
			document.getElementById("clozeTestDifficultyDrpdwn").addEventListener("change", changeDifficulty);
			document.getElementById("resetbtn").addEventListener("click", resetForm);
			document.getElementById("checkAnswers").addEventListener("click", checkAnswers);
			document.getElementById("closebtn").addEventListener("click", closeQuiz);
		}
	}
	return text;
}

//Implement closeQuiz(), changeDifficulty(), checkAnswers() and resetForm().
function changeDifficulty () {
	var chosenDifficulty = document.getElementById("clozeTestDifficultyDrpdwn").value;
	localStorage.setItem("kaiko_clozeTestCurDif", chosenDifficulty);
	//Restart!
	var currentDifficulty = localStorage.getItem("kaiko_clozeTestCurDif");
	var previousDifficulty = localStorage.getItem("kaiko_clozeTestPrevDif");
	if (currentDifficulty !== previousDifficulty) {
		getSelectionText();
		return;
	}
}
function resetForm () {
	localStorage.setItem('kaiko_clozeTestPrevDif', 0);
	getSelectionText();
	return;
}
function checkAnswers () {
	var clozeTestAnswers = JSON.parse(localStorage.getItem("kaiko_clozeTestAnswers"));
	var clozeInput;
	var clozeInputValue;
	var clozeTestForm = document.getElementById(clozeTestForm);
	var chosenClozeTestAnswer;
	var styledChosenClozeTestAnswer;
	for (var i = 0; i < clozeTestAnswers.length; ++i){
		clozeInput = document.getElementById(clozeTestAnswers[i]);
		clozeInputValue = clozeInput.value;
		chosenClozeTestAnswer = clozeTestAnswers[i].replace(/_-(.*)-_/, "");
		if (clozeInputValue != chosenClozeTestAnswer){
			styledChosenClozeTestAnswer = "<span style=\"color:lightgreen\">" + chosenClozeTestAnswer+ "</span>";
			clozeInput.insertAdjacentHTML("afterend", styledChosenClozeTestAnswer);
		}
	}
	clozeTestAnswers = [];
	localStorage.setItem("kaiko_clozeTestAnswers", "");
}
function closeQuiz () {
	var containerPopup = document.getElementsByClassName("cloze-container-popup");
	containerPopup[0].parentNode.removeChild(containerPopup[0]);
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址