diff options
Diffstat (limited to 'modules/zelfoverhoor')
-rw-r--r-- | modules/zelfoverhoor/.gitignore | 4 | ||||
-rw-r--r-- | modules/zelfoverhoor/docent.html | 57 | ||||
-rw-r--r-- | modules/zelfoverhoor/docent.js | 173 | ||||
-rw-r--r-- | modules/zelfoverhoor/index.html | 15 | ||||
-rw-r--r-- | modules/zelfoverhoor/notfound.html | 12 | ||||
-rw-r--r-- | modules/zelfoverhoor/qs.html | 64 | ||||
-rw-r--r-- | modules/zelfoverhoor/qs.js | 76 | ||||
-rw-r--r-- | modules/zelfoverhoor/style.css | 3 | ||||
-rw-r--r-- | modules/zelfoverhoor/zelfoverhoor.js | 189 |
9 files changed, 593 insertions, 0 deletions
diff --git a/modules/zelfoverhoor/.gitignore b/modules/zelfoverhoor/.gitignore new file mode 100644 index 0000000..948b29c --- /dev/null +++ b/modules/zelfoverhoor/.gitignore @@ -0,0 +1,4 @@ +accounts.json +questiondb.json +questionsets.json +userlists.json diff --git a/modules/zelfoverhoor/docent.html b/modules/zelfoverhoor/docent.html new file mode 100644 index 0000000..224384d --- /dev/null +++ b/modules/zelfoverhoor/docent.html @@ -0,0 +1,57 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<title>Zelfoverhoor: Docent (<!--###NAME###-->)</title> +<link rel="stylesheet" href="/zelfoverhoor/style.css"> +<script src="/zelfoverhoor/docent/docent.js"></script> +<style> +table { + border-collapse: collapse; +} +td, th { + border: 1px #333 solid; + padding: 4px; +} +.invisible { + display: none; +} +#newqsetform { + border: 1px #333 solid; + padding: 4px; +} +.newquestioncontainer { + margin-bottom: 10px; +} +.newquestioncontainer > span { + margin-right: 5px; +} +.newquestioncontainer textarea { + width: 300px; + height: 60px; +} +</style> +</head> +<body> +<h1>Zelfoverhoor: Docent (<!--###NAME###-->)</h1> +<h2>Vragensets</h2> +<table> + <thead><tr><th>ID</th><th>Naam</th><th>Beschrijving</th><th>#vragen</th></tr></thead> + <tbody id="qsets"></tbody> +</table> +<br> +<input type="button" id="newqsetvisible" onclick="doNewQSet()" value="Nieuwe vragenset maken"> +<br> +<div id="newqsetform" class="invisible"> + <h2>Nieuwe vragenset</h2> + <b>Naam</b>:<br><input type="text" id="newqsetname" size="40"><br><br> + <b>Beschrijving</b>:<br><textarea id="newqsetdescr" style="width:300px;height:60px"></textarea><br><br> + <div id="newquestions"></div> + <input type="button" onclick="addNewQuestion()" value="Nog een vraag!"><br> + <br> + <div style="background-color:#d8d8d8;padding:10px;display:inline-block"> + <input type="button" onclick="submitQSet()" value="Vragenset klaar"> + </div> +</div> +</body> +</html> diff --git a/modules/zelfoverhoor/docent.js b/modules/zelfoverhoor/docent.js new file mode 100644 index 0000000..7987351 --- /dev/null +++ b/modules/zelfoverhoor/docent.js @@ -0,0 +1,173 @@ +var questionsets=null; + +function getQuestionSets(){ + var xhr=new XMLHttpRequest(); + xhr.onreadystatechange=function(){ + if(xhr.readyState==4){ + questionsets=JSON.parse(xhr.responseText); + updateQuestionSetsList(); + } + }; + xhr.responseType="text"; + xhr.open("GET","/zelfoverhoor/docent/sets"); + xhr.send(); +} + +function updateQuestionSetsList(){ + var tbody=document.getElementById("qsets"); + clearElement(tbody); + if(questionsets.length==0){ + var tr=document.createElement("tr"); + var td=document.createElement("td"); + td.appendChild(document.createTextNode("Nog geen vragensets...")); + td.setAttribute("colspan","4"); + tr.appendChild(td); + tbody.appendChild(tr); + } + for(var i=0;i<questionsets.length;i++){ + var tr=document.createElement("tr"); + td=document.createElement("td"); + var a=document.createElement("a"); + a.setAttribute("href",location.origin+"/zelfoverhoor/qs/"+questionsets[i].id); + a.setAttribute("target","_blank"); + a.innerHTML=questionsets[i].id; + td.appendChild(a); + tr.appendChild(td); + td=document.createElement("td"); + td.appendChild(document.createTextNode(questionsets[i].name)); + tr.appendChild(td); + td=document.createElement("td"); + td.appendChild(document.createTextNode(questionsets[i].description)); + tr.appendChild(td); + td=document.createElement("td"); + td.appendChild(document.createTextNode(questionsets[i].questions.length)); + tr.appendChild(td); + td=document.createElement("td"); + var input=document.createElement("input"); + input.setAttribute("type","button"); + input.setAttribute("value","verwijder"); + input.addEventListener("click",(function(id,name){return function(){ + if(confirm("Weet je zeker dat je de vragenset '"+name+"' wilt verwijderen?")){ + var xhr=new XMLHttpRequest(); + xhr.onreadystatechange=function(){ + if(xhr.readyState==4){ + if(xhr.status==200){ + getQuestionSets(); + } else if(xhr.status==404){ + alert("Vragenset kon niet worden gevonden... Misschien even de pagina herladen?"); + } else { + alert("Onbekende error: "+xhr.responseText); + } + } + }; + xhr.open("POST","/zelfoverhoor/docent/deleteset"); + xhr.send(id); + } + };})(questionsets[i].id,questionsets[i].name)); + td.appendChild(input); + tr.appendChild(td); + tbody.appendChild(tr); + } +} + +function clearElement(el){ + var c; + while((c=el.lastChild))el.removeChild(c); +} + +function doNewQSet(){ + document.getElementById("newqsetname").value=""; + document.getElementById("newqsetdescr").value=""; + clearElement("newquestions"); + document.getElementById("newqsetform").classList.remove("invisible"); + document.getElementById("newqsetvisible").classList.add("invisible"); + addNewQuestion(); +} + +function closeQSetForm(){ + document.getElementById("newqsetform").classList.add("invisible"); + document.getElementById("newqsetvisible").classList.remove("invisible"); +} + +function addNewQuestion(){ + var parent=document.getElementById("newquestions"); + + var div=document.createElement("div"); + div.classList.add("newquestioncontainer"); + + var span=document.createElement("span"); + span.setAttribute("style","display:inline-block"); + var b=document.createElement("b"); + b.appendChild(document.createTextNode("Vraag:")); + span.appendChild(b); + span.appendChild(document.createElement("br")); + var qta=document.createElement("textarea"); + span.appendChild(qta); + div.appendChild(span); + + span=document.createElement("span"); + span.setAttribute("style","display:inline-block"); + b=document.createElement("b"); + b.appendChild(document.createTextNode("Antwoord:")); + span.appendChild(b); + span.appendChild(document.createElement("br")); + span.appendChild(document.createElement("textarea")); + div.appendChild(span); + + span=document.createElement("span"); + var input=document.createElement("input"); + input.setAttribute("type","button"); + input.setAttribute("value","verwijder vraag"); + input.setAttribute("style","vertical-align:50px"); + input.addEventListener("click",function(){ + if(confirm("Weet je zeker dat je de volgende vraag wil verwijderen?\n"+qta.value)){ + parent.removeChild(div); + } + }); + span.appendChild(input); + div.appendChild(span); + + parent.appendChild(div); +} + +function submitQSet(){ + var name=document.getElementById("newqsetname").value.trim(); + var description=document.getElementById("newqsetdescr").value.trim(); + if(name==""||description==""){ + alert("Naam en beschrijving zijn nodig."); + return; + } + + var div=document.getElementById("newquestions"); + var ch=div.children,nq=ch.length; + var questions=[]; + for(var i=0;i<nq;i++){ + var tas=ch[i].getElementsByTagName("textarea"); + var q={"q": tas[0].value.trim(), "a": tas[1].value.trim()}; + if(q.q==""||q.a==""){ + alert("Een van de vragen of antwoorden is leeg; graag even vullen of de vraag verwijderen."); + return; + } + questions.push(q); + } + + var obj={"name": name, "description": description, "questions": questions}; + + var xhr=new XMLHttpRequest(); + xhr.onreadystatechange=function(){ + if(xhr.readyState==4){ + if(xhr.status==200){ + getQuestionSets(); + closeQSetForm(); + } else { + alert("Vragenset lijkt niet succesvol toegevoegd te zijn...\n"+xhr.responseText); + } + } + }; + xhr.open("POST","/zelfoverhoor/docent/addset"); + xhr.send(JSON.stringify(obj)); +} + +window.addEventListener("load",function(){ + getQuestionSets(); +}); diff --git a/modules/zelfoverhoor/index.html b/modules/zelfoverhoor/index.html new file mode 100644 index 0000000..d8f951f --- /dev/null +++ b/modules/zelfoverhoor/index.html @@ -0,0 +1,15 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<title>Zelfoverhoor</title> +<link rel="stylesheet" href="/zelfoverhoor/style.css"> +</head> +<body> +<div style="float:right"> + <a href="/zelfoverhoor/docent">(docent)</a> +</div> +<h1>Zelfoverhoor</h1> +<p>Welkom bij Zelfoverhoor! Als het goed is heb je van de docent een link van de vorm <b>https://tomsmeding.com/zelfoverhoor/qs/abcdef</b> gekregen. Daarmee kom je op de juiste pagina terecht. Hier is dus verder weinig te vinden. ;)</p> +</body> +</html> diff --git a/modules/zelfoverhoor/notfound.html b/modules/zelfoverhoor/notfound.html new file mode 100644 index 0000000..8d79c10 --- /dev/null +++ b/modules/zelfoverhoor/notfound.html @@ -0,0 +1,12 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<title>Zelfoverhoor: niet gevonden</title> +<link rel="stylesheet" href="/zelfoverhoor/style.css"> +</head> +<body> +<h1>Zelfoverhoor</h1> +Die vragenset kon niet worden gevonden. Misschien heb je de link niet goed gekopieerd? +</body> +</html> diff --git a/modules/zelfoverhoor/qs.html b/modules/zelfoverhoor/qs.html new file mode 100644 index 0000000..32c9099 --- /dev/null +++ b/modules/zelfoverhoor/qs.html @@ -0,0 +1,64 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<title>Zelfoverhoor: <!--###ID###--></title> +<link rel="stylesheet" href="/zelfoverhoor/style.css"> +<script> +// Gefeliciteerd, je hebt de antwoorden gevonden. +// De vraag is echter, wil je die gebruiken? :) +// Deze overhoor-applicatie is er immers om JEZELF te verbeteren... +var questionset=/*###QUESTIONSET###*/; +</script> +<script src="/zelfoverhoor/qs.js"></script> +<style> +#qsetdescr { + margin-top: 5px; + display: inline-block; + background-color: #eee; + padding: 5px; +} +#qcontainer { + border: 1px #666 solid; + border-radius: 10px; + padding: 10px; + display: inline-block; +} +#question, #answer { + display: inline-block; + background-color: #eef; + border-radius: 10px; + padding: 10px; + margin-top: 5px; +} +/* HACK */ +#qcontainer.invisible, .invisible { + display: none; +} +</style> +</head> +<body> +<h1>Zelfoverhoor: vragenset "<span id="qsetname"></span>"</h1> +<b>Beschrijving:</b><br> +<div id="qsetdescr"></div><br> +<br><br> +<div id="qcontainer"> + <b>Vraag:</b><br> + <div id="question"></div><br> + <br> + <input type="button" id="showAnswerButton" onclick="showAnswer()" value="Laat antwoord zien!"> + <div id="answercontainer" class="invisible"> + <b>Antwoord:</b><br> + <div id="answer"></div><br> + <input type="button" onclick="advance(true)" value="Goed!"><span style="margin-left:20px"></span> + <input type="button" onclick="advance(false)" value="Fout"> + </div> +</div> +<div id="rescontainer"> + <b>Je hebt alle vragen gehad!</b><br> + <p>Je had <span id="numcorrect"></span> vragen goed van de in totaal <span id="numtotal"></span> vragen.</p> + <p id="allcorrectp"><b>Gefeliciteerd met je prestatie! :D</b></p> + <input type="button" onclick="startQuiz()" value="Nog een keer?"> +</div> +</body> +</html> diff --git a/modules/zelfoverhoor/qs.js b/modules/zelfoverhoor/qs.js new file mode 100644 index 0000000..cc68af9 --- /dev/null +++ b/modules/zelfoverhoor/qs.js @@ -0,0 +1,76 @@ +var setname=questionset.name; +var setdescription=questionset.description; +var questions=questionset.questions; + +var currentidx=0; +var numcorrect=0; + +function clearElement(el){ + while(el.lastChild)el.removeChild(el.lastChild); +} + +function shuffle(a){ + var j,v,i; + for(var i=a.length-1;i>0;i--){ + j=Math.floor(Math.random()*(i+1)); + v=a[i]; a[i]=a[j]; a[j]=v; + } +} + +function startQuiz(){ + currentidx=0; + numcorrect=0; + showCurrent(); +} + +function showCurrent(){ + document.getElementById("qcontainer").classList.remove("invisible"); + document.getElementById("rescontainer").classList.add("invisible"); + document.getElementById("answercontainer").classList.add("invisible"); + document.getElementById("showAnswerButton").classList.remove("invisible"); + + var qdiv=document.getElementById("question"); + clearElement(qdiv); + qdiv.appendChild(document.createTextNode(questions[currentidx].q)); +} + +function showAnswer(){ + document.getElementById("answercontainer").classList.remove("invisible"); + document.getElementById("showAnswerButton").classList.add("invisible"); + + var adiv=document.getElementById("answer"); + clearElement(adiv); + adiv.appendChild(document.createTextNode(questions[currentidx].a)); +} + +function finishQuiz(){ + document.getElementById("qcontainer").classList.add("invisible"); + document.getElementById("rescontainer").classList.remove("invisible"); + document.getElementById("numcorrect").innerHTML=numcorrect; + document.getElementById("numtotal").innerHTML=questions.length; + if(numcorrect==questions.length){ + document.getElementById("allcorrectp").classList.remove("invisible"); + } else { + document.getElementById("allcorrectp").classList.add("invisible"); + } +} + +function advance(corr){ + if(corr)numcorrect++; + currentidx++; + if(currentidx<questions.length){ + showCurrent(); + } else { + finishQuiz(); + } +} + +window.addEventListener("load",function(){ + document.getElementById("qsetname").appendChild(document.createTextNode(setname)); + document.getElementById("qsetdescr").appendChild(document.createTextNode(setdescription)); + if(questions.length==0){ + alert("Deze lijst heeft geen vragen, dus er kan weinig overhoord worden, eerlijk gezegd..."); + return; + } + startQuiz(); +}); diff --git a/modules/zelfoverhoor/style.css b/modules/zelfoverhoor/style.css new file mode 100644 index 0000000..a326381 --- /dev/null +++ b/modules/zelfoverhoor/style.css @@ -0,0 +1,3 @@ +body { + font-family: sans-serif; +} diff --git a/modules/zelfoverhoor/zelfoverhoor.js b/modules/zelfoverhoor/zelfoverhoor.js new file mode 100644 index 0000000..c55f0e0 --- /dev/null +++ b/modules/zelfoverhoor/zelfoverhoor.js @@ -0,0 +1,189 @@ +var cmn=require("../$common.js"), + fs=require("fs"); + +var moddir; + +var accounts=require("./accounts.json"); + +// {<id>: {q: "question", a: "answer"}} +var questiondb={}; +// {<id>: {id, name, description, questions: [<question_id>]}} +var questionsets={}; +// {<username>: [<set_id>]} +var userlists={}; + +function uniqidstr(validp){ + for(var len=6;;len++){ + var str=""; + for(var i=0;i<len;i++)str+=String.fromCharCode(97+Math.random()*26|0); + if(validp(str))return str; + } +} + +var persistDB=(function(){ + var qdbCache=null,qsetsCache=null,ulCache=null; + return function persistDB(){ + var s=JSON.stringify(questiondb); + if(s!=qdbCache){ + qdbCache=s; + fs.writeFile(moddir+"/questiondb.json",JSON.stringify(questiondb),function(err){ + if(err)console.log("write questiondb.json:",err); + }); + } + s=JSON.stringify(questionsets); + if(s!=qsetsCache){ + qsetsCache=s; + fs.writeFile(moddir+"/questionsets.json",JSON.stringify(questionsets),function(err){ + if(err)console.log("write questionsets.json:",err); + }); + } + s=JSON.stringify(userlists); + if(s!=ulCache){ + ulCache=s; + fs.writeFile(moddir+"/userlists.json",JSON.stringify(userlists),function(err){ + if(err)console.log("write userlists.json:",err); + }); + } + }; +})(); + +function shuffle(a){ + var j,v,i; + for(var i=a.length-1;i>0;i--){ + j=Math.floor(Math.random()*(i+1)); + v=a[i]; a[i]=a[j]; a[j]=v; + } +} + +module.exports=function(app,io,_moddir){ + moddir=_moddir; + + try { + questiondb=JSON.parse(fs.readFileSync(moddir+"/questiondb.json")); + questionsets=JSON.parse(fs.readFileSync(moddir+"/questionsets.json")); + userlists=JSON.parse(fs.readFileSync(moddir+"/userlists.json")); + } catch(e){ + console.log("error on reading zelfoverhoor db"); + questiondb={}; + questionsets={}; + userlists={}; + persistDB(); + } + + app.get("/zelfoverhoor",function(req,res){ + res.sendFile(moddir+"/index.html"); + }); + + app.get("/zelfoverhoor/qs/:id",function(req,res){ + var qset=questionsets[req.params.id]; + if(qset==null){ + res.status(404).sendFile(moddir+"/notfound.html"); + return; + } + var list=[]; + for(var i=0;i<qset.questions.length;i++){ + list.push(questiondb[qset.questions[i]]); + } + var resset={"id": qset.id, "name": qset.name, "description": qset.description, "questions": list}; + shuffle(resset.questions); + fs.readFile(moddir+"/qs.html",function(err,data){ + if(err)throw err; + res.send(String(data) + .replace("<!--###ID###-->",req.params.id) + .replace("/*###QUESTIONSET###*/",JSON.stringify(resset))); + }); + }); + + app.use(["/zelfoverhoor/docent","/zelfoverhoor/docent/*"],cmn.authgen(accounts)); + app.get("/zelfoverhoor/docent",function(req,res){ + fs.readFile(moddir+"/docent.html",function(err,data){ + if(err)throw err; + res.send(String(data) + .replace(/<!--###NAME###-->/g,req.authuser)); + }); + }); + app.get("/zelfoverhoor/docent/sets",function(req,res){ + if(userlists[req.authuser]==null){ + userlists[req.authuser]=[]; + } + var ul=userlists[req.authuser]; + var list=[]; + for(var i=0;i<ul.length;i++){ + list.push(questionsets[ul[i]]); + } + res.send(JSON.stringify(list)); + }); + app.post("/zelfoverhoor/docent/addset",function(req,res){ + var qset; + try { + qset=JSON.parse(req.body); + } catch(e){ + res.status(400).send("Invalid json received"); + return; + } + if(!qset.name||typeof qset.name!="string"|| + !qset.description||typeof qset.description!="string"|| + !qset.questions||!Array.isArray(qset.questions)){ + res.status(400).send("Invalid data received"); + return; + } + var i; + for(i=0;i<qset.questions.length;i++){ + if(!qset.questions[i].q||typeof qset.questions[i].q!="string"|| + !qset.questions[i].a||typeof qset.questions[i].a!="string"){ + res.status(400).send("Invalid question data received"); + return; + } + } + var ids=[],id; + for(i=0;i<qset.questions.length;i++){ + id=uniqidstr(function(s){return questiondb[s]==null;}); + questiondb[id]={"q": qset.questions[i].q, "a": qset.questions[i].a}; + ids.push(id); + } + var setid=uniqidstr(function(s){return questionsets[s]==null;}); + questionsets[setid]={"id": setid, "name": qset.name, "description": qset.description, "questions": ids}; + if(userlists[req.authuser]==null){ + userlists[req.authuser]=[]; + } + userlists[req.authuser].push(setid); + persistDB(); + res.send(setid); + }); + app.post("/zelfoverhoor/docent/deleteset",function(req,res){ + var setid=req.body; + if(!setid||typeof setid!="string"){ + res.status(400).send("Invalid id received"); + return; + } + if(!userlists[req.authuser]){ + userlists[req.authuser]=[]; + } + var ul=userlists[req.authuser]; + var found=false; + for(var i=0;i<ul.length;i++){ + if(ul[i]==setid){ + ul.splice(i,1); + found=true; + break; + } + } + if(!found){ + res.status(404).send("ID not found"); + return; + } + persistDB(); + res.send(); + }); + + app.get("/zelfoverhoor/docent/docent.js",function(req,res){ + res.sendFile(moddir+"/docent.js"); + }); + + app.get("/zelfoverhoor/qs.js",function(req,res){ + res.sendFile(moddir+"/qs.js"); + }); + app.get("/zelfoverhoor/style.css",function(req,res){ + res.sendFile(moddir+"/style.css"); + }); +}; |