Harmonize with web version, better style, fix for Firefox 45.9.0 ESR
authorBenjamin Auder <benjamin.auder@somewhere>
Mon, 12 Feb 2018 18:54:51 +0000 (19:54 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Mon, 12 Feb 2018 18:54:51 +0000 (19:54 +0100)
public/javascripts/assessment.js
public/javascripts/components/statements.js
public/javascripts/course.js
public/javascripts/courseList.js
public/javascripts/login.js
public/stylesheets/assessment.css
public/stylesheets/course.css
routes/assessments.js
views/assessment.pug
views/course.pug

index ad309d7..acce548 100644 (file)
@@ -12,7 +12,7 @@ function checkWindowSize()
        return window.innerWidth >= screen.width-3 && window.innerHeight >= screen.height-3;
 };
 
-let V = new Vue({
+new Vue({
        el: "#assessment",
        data: {
                assessment: assessment,
index 67aaf12..819d723 100644 (file)
@@ -48,6 +48,7 @@ Vue.component("statements", {
                                                                change: e => { this.answers.inputs[i][idx] = e.target.checked; },
                                                        },
                                                },
+                                               [ '' ] //to work in Firefox 45.9 ESR @ ENSTA...
                                        )
                                );
                                option.push(
index e0f2e37..8064eaf 100644 (file)
 // Use open mode for question banks: add setting "nbQuestions" to show nbQuestions
 // at random among active questions
 
-window.onload = function() {
-
-       V = new Vue({
-               el: '#course',
-               data: {
-                       display: "assessments", //or "students", or "grades" (admin mode)
-                       course: course,
-                       mode: "view", //or "edit" (some assessment)
-                       // assessment data:
-                       monitorPwd: "",
-                       newAssessment: { name: "" },
-                       assessmentArray: assessmentArray,
-                       assessmentIndex: 0, //current edited assessment index
-                       assessment: { }, //copy of assessment at editing index in array
-                       assessmentText: "", //questions in an assessment, in text format
-                       // grades data:
-                       settings: {
-                               totalPoints: 20,
-                               halfPoints: false,
-                               zeroSum: false,
-                       },
-                       group: 1, //for detailed grades tables
-                       grades: { }, //computed
+new Vue({
+       el: '#course',
+       data: {
+               display: "assessments", //or "students", or "grades" (admin mode)
+               course: course,
+               mode: "view", //or "edit" (some assessment)
+               // assessment data:
+               monitorPwd: "",
+               newAssessment: { name: "" },
+               assessmentArray: assessmentArray,
+               assessmentIndex: 0, //current edited assessment index
+               assessment: { }, //copy of assessment at editing index in array
+               assessmentText: "", //questions in an assessment, in text format
+               // grades data:
+               settings: {
+                       totalPoints: 20,
+                       halfPoints: false,
+                       zeroSum: false,
                },
-               mounted: function() {
-                       $('.modal').each( (i,elem) => {
-                               if (elem.id != "assessmentEdit")
-                                       $(elem).modal();
-                       });
-                       $('ul.tabs').tabs();
-                       $('#assessmentEdit').modal({
-                               complete: () => {
-                                       this.parseAssessment();
-                                       Vue.nextTick( () => {
-                                               $("#questionList").find("code[class^=language-]").each( (i,elem) => {
-                                                       Prism.highlightElement(elem);
-                                               });
-                                               MathJax.Hub.Queue(["Typeset",MathJax.Hub,"questionList"]);
+               group: 1, //for detailed grades tables
+               grades: { }, //computed
+       },
+       mounted: function() {
+               $('.modal').each( (i,elem) => {
+                       if (elem.id != "assessmentEdit")
+                               $(elem).modal();
+               });
+               $('ul.tabs').tabs();
+               $('#assessmentEdit').modal({
+                       complete: () => {
+                               this.parseAssessment();
+                               Vue.nextTick( () => {
+                                       $("#questionList").find("code[class^=language-]").each( (i,elem) => {
+                                               Prism.highlightElement(elem);
                                        });
-                               },
-                       });
-               },
-               methods: {
-                       // GENERAL:
-                       toggleDisplay: function(area) {
-                               if (this.display == area)
-                                       this.display = "";
-                               else
-                                       this.display = area;
-                       },
-                       studentList: function(group) {
-                               return this.course.students
-                                       .filter( s => { return group==0 || s.group == group; })
-                                       .map( s => { return Object.assign({}, s); }) //not altering initial array
-                                       .sort( (a,b) => { return a.name.localeCompare(b.name); })
-                       },
-                       // STUDENTS:
-                       uploadTrigger: function() {
-                               $("#upload").click();
-                       },
-                       upload: function(e) {
-                               let file = (e.target.files || e.dataTransfer.files)[0];
-                               Papa.parse(file, {
-                                       header: true,
-                                       skipEmptyLines: true,
-                                       complete: (results,file) => {
-                                               let students = [ ];
-                                               // Post-process: add group/number if missing
-                                               let number = 1;
-                                               results.data.forEach( d => {
-                                                       if (!d.group)
-                                                               d.group = 1;
-                                                       if (!d.number)
-                                                               d.number = number++;
-                                                       if (typeof d.number !== "string")
-                                                               d.number = d.number.toString();
-                                                       students.push(d);
-                                               });
-                                               $.ajax("/import/students", {
-                                                       method: "POST",
-                                                       data: {
-                                                               cid: this.course._id,
-                                                               students: JSON.stringify(students),
-                                                       },
-                                                       dataType: "json",
-                                                       success: res => {
-                                                               if (!res.errmsg)
-                                                                       this.course.students = students;
-                                                               else
-                                                                       alert(res.errmsg);
-                                                       },
-                                               });
-                                       },
+                                       MathJax.Hub.Queue(["Typeset",MathJax.Hub,"questionList"]);
                                });
                        },
-                       // ASSESSMENT:
-                       addAssessment: function() {
-                               if (!admin)
-                                       return;
-                               // modal, fill code and description
-                               let error = Validator.checkObject(this.newAssessment, "Assessment");
-                               if (!!error)
-                                       return alert(error);
-                               else
-                                       $('#newAssessment').modal('close');
-                               $.ajax("/add/assessment",
-                                       {
-                                               method: "GET",
+               });
+       },
+       methods: {
+               // GENERAL:
+               toggleDisplay: function(area) {
+                       if (this.display == area)
+                               this.display = "";
+                       else
+                               this.display = area;
+               },
+               studentList: function(group) {
+                       return this.course.students
+                               .filter( s => { return group==0 || s.group == group; })
+                               .map( s => { return Object.assign({}, s); }) //not altering initial array
+                               .sort( (a,b) => { return a.name.localeCompare(b.name); })
+               },
+               // STUDENTS:
+               uploadTrigger: function() {
+                       $("#upload").click();
+               },
+               upload: function(e) {
+                       let file = (e.target.files || e.dataTransfer.files)[0];
+                       Papa.parse(file, {
+                               header: true,
+                               skipEmptyLines: true,
+                               complete: (results,file) => {
+                                       let students = [ ];
+                                       // Post-process: add group/number if missing
+                                       let number = 1;
+                                       results.data.forEach( d => {
+                                               if (!d.group)
+                                                       d.group = 1;
+                                               if (!d.number)
+                                                       d.number = number++;
+                                               if (typeof d.number !== "string")
+                                                       d.number = d.number.toString();
+                                               students.push(d);
+                                       });
+                                       $.ajax("/import/students", {
+                                               method: "POST",
                                                data: {
-                                                       name: this.newAssessment.name,
-                                                       cid: course._id,
+                                                       cid: this.course._id,
+                                                       students: JSON.stringify(students),
                                                },
                                                dataType: "json",
                                                success: res => {
                                                        if (!res.errmsg)
-                                                       {
-                                                               this.newAssessment["name"] = "";
-                                                               this.assessmentArray.push(res);
-                                                       }
+                                                               this.course.students = students;
                                                        else
                                                                alert(res.errmsg);
                                                },
-                                       }
-                               );
-                       },
-                       materialOpenModal: function(id) {
-                               $("#" + id).modal("open");
-                               Materialize.updateTextFields(); //textareas, time field...
-                       },
-                       updateAssessment: function() {
-                               $.ajax("/update/assessment", {
-                                       method: "POST",
-                                       data: {assessment: JSON.stringify(this.assessment)},
+                                       });
+                               },
+                       });
+               },
+               // ASSESSMENT:
+               addAssessment: function() {
+                       if (!admin)
+                               return;
+                       // modal, fill code and description
+                       let error = Validator.checkObject(this.newAssessment, "Assessment");
+                       if (!!error)
+                               return alert(error);
+                       else
+                               $('#newAssessment').modal('close');
+                       $.ajax("/add/assessment",
+                               {
+                                       method: "GET",
+                                       data: {
+                                               name: this.newAssessment.name,
+                                               cid: course._id,
+                                       },
                                        dataType: "json",
                                        success: res => {
                                                if (!res.errmsg)
                                                {
-                                                       this.assessmentArray[this.assessmentIndex] = this.assessment;
-                                                       this.mode = "view";
+                                                       this.newAssessment["name"] = "";
+                                                       this.assessmentArray.push(res);
                                                }
                                                else
                                                        alert(res.errmsg);
                                        },
-                               });
-                       },
-                       deleteAssessment: function(assessment) {
-                               if (!admin)
-                                       return;
-                               if (confirm("Delete assessment '" + assessment.name + "' ?"))
-                               {
-                                       $.ajax("/remove/assessment",
-                                               {
-                                                       method: "GET",
-                                                       data: { qid: this.assessment._id },
-                                                       dataType: "json",
-                                                       success: res => {
-                                                               if (!res.errmsg)
-                                                                       this.assessmentArray.splice( this.assessmentArray.findIndex( item => {
-                                                                               return item._id == assessment._id;
-                                                                       }), 1 );
-                                                               else
-                                                                       alert(res.errmsg);
-                                                       },
-                                               }
-                                       );
                                }
-                       },
-                       toggleState: function(questionIndex) {
-                               // add or remove from activeSet of current assessment
-                               let activeIndex = this.assessment.activeSet.findIndex( item => { return item == questionIndex; });
-                               if (activeIndex >= 0)
-                                       this.assessment.activeSet.splice(activeIndex, 1);
-                               else
-                                       this.assessment.activeSet.push(questionIndex);
-                       },
-                       setAssessmentText: function() {
-                               let txt = "";
-                               this.assessment.questions.forEach( q => {
-                                       txt += q.wording + "\n";
-                                       q.options.forEach( (o,i) => {
-                                               let symbol = q.answer.includes(i) ? "+" : "-";
-                                               txt += symbol + " " + o + "\n";
-                                       });
-                                       txt += "\n"; //separate questions by new line
-                               });
-                               this.assessmentText = txt;
-                       },
-                       parseAssessment: function() {
-                               let questions = [ ];
-                               let lines = this.assessmentText.split("\n").map( L => { return L.trim(); })
-                               lines.push(""); //easier parsing
-                               let emptyQuestion = () => {
-                                       return {
-                                               wording: "",
-                                               options: [ ],
-                                               answer: [ ],
-                                               active: true, //default
-                                       };
-                               };
-                               let q = emptyQuestion();
-                               lines.forEach( L => {
-                                       if (L.length > 0)
+                       );
+               },
+               materialOpenModal: function(id) {
+                       $("#" + id).modal("open");
+                       Materialize.updateTextFields(); //textareas, time field...
+               },
+               updateAssessment: function() {
+                       $.ajax("/update/assessment", {
+                               method: "POST",
+                               data: {assessment: JSON.stringify(this.assessment)},
+                               dataType: "json",
+                               success: res => {
+                                       if (!res.errmsg)
                                        {
-                                               if (['+','-'].includes(L.charAt(0)))
-                                               {
-                                                       if (L.charAt(0) == '+')
-                                                               q.answer.push(q.options.length);
-                                                       q.options.push(L.slice(1).trim());
-                                               }
-                                               else if (L.charAt(0) == '*')
-                                               {
-                                                       // TODO: read current + next lines into q.answer (HTML, 1-elem array)
-                                               }
-                                               else
-                                                       q.wording += L + " "; //space required at line breaks, generally
+                                               this.assessmentArray[this.assessmentIndex] = this.assessment;
+                                               this.mode = "view";
                                        }
                                        else
-                                       {
-                                               // Flush current question (if any)
-                                               if (q.wording.length > 0)
-                                               {
-                                                       questions.push(q);
-                                                       q = emptyQuestion();
-                                               }
-                                       }
-                               });
-                               this.assessment.questions = questions;
-                       },
-                       actionAssessment: function(index) {
-                               if (admin)
-                               {
-                                       // Edit screen
-                                       this.assessmentIndex = index;
-                                       this.assessment = $.extend(true, {}, this.assessmentArray[index]);
-                                       this.setAssessmentText();
-                                       this.mode = "edit";
-                                       Vue.nextTick( () => {
-                                               $("#questionList").find("code[class^=language-]").each( (i,elem) => {
-                                                       Prism.highlightElement(elem);
-                                               });
-                                               MathJax.Hub.Queue(["Typeset",MathJax.Hub,"questionList"]);
-                                       });
-                               }
-                               else //external user: show assessment
-                                       this.redirect(this.assessmentArray[index].name);
-                       },
-                       redirect: function(assessmentName) {
-                               document.location.href = "/" + initials + "/" + course.code + "/" + assessmentName;
-                       },
-                       setPassword: function() {
-                               let hashPwd = Sha1.Compute(this.monitorPwd);
-                               let error = Validator.checkObject({password:hashPwd}, "Course");
-                               if (error.length > 0)
-                                       return alert(error);
-                               $.ajax("/set/password",
+                                               alert(res.errmsg);
+                               },
+                       });
+               },
+               deleteAssessment: function(assessment) {
+                       if (!admin)
+                               return;
+                       if (confirm("Delete assessment '" + assessment.name + "' ?"))
+                       {
+                               $.ajax("/remove/assessment",
                                        {
                                                method: "GET",
-                                               data: {
-                                                       cid: this.course._id,
-                                                       pwd: hashPwd,
-                                               },
+                                               data: { qid: this.assessment._id },
                                                dataType: "json",
                                                success: res => {
                                                        if (!res.errmsg)
-                                                               alert("Password saved!");
+                                                               this.assessmentArray.splice( this.assessmentArray.findIndex( item => {
+                                                                       return item._id == assessment._id;
+                                                               }), 1 );
                                                        else
                                                                alert(res.errmsg);
                                                },
                                        }
                                );
-                       },
-                       // NOTE: artifact required for Vue v-model to behave well
-                       checkBoxFixedId: function(i) {
-                               return "questionFixed" + i;
-                       },
-                       checkBoxActiveId: function(i) {
-                               return "questionActive" + i;
-                       },
-                       // GRADES:
-                       gradeSettings: function() {
-                               $("#gradeSettings").modal("open");
-                               Materialize.updateTextFields(); //total points field in grade settings overlap
-                       },
-                       download: function() {
-                               // Download (all) grades as a CSV file
-                               let data = [ ];
-                               this.studentList(0).forEach( s => {
-                                       let finalGrade = 0.;
-                                       let gradesCount = 0;
-                                       if (!!this.grades[s.number])
+                       }
+               },
+               toggleState: function(questionIndex) {
+                       // add or remove from activeSet of current assessment
+                       let activeIndex = this.assessment.activeSet.findIndex( item => { return item == questionIndex; });
+                       if (activeIndex >= 0)
+                               this.assessment.activeSet.splice(activeIndex, 1);
+                       else
+                               this.assessment.activeSet.push(questionIndex);
+               },
+               setAssessmentText: function() {
+                       let txt = "";
+                       this.assessment.questions.forEach( q => {
+                               txt += q.wording; //already ended by \n
+                               q.options.forEach( (o,i) => {
+                                       let symbol = q.answer.includes(i) ? "+" : "-";
+                                       txt += symbol + " " + o + "\n";
+                               });
+                               txt += "\n"; //separate questions by new line
+                       });
+                       this.assessmentText = txt;
+               },
+               parseAssessment: function() {
+                       let questions = [ ];
+                       let lines = this.assessmentText.split("\n").map( L => { return L.trim(); })
+                       lines.push(""); //easier parsing
+                       let emptyQuestion = () => {
+                               return {
+                                       wording: "",
+                                       options: [ ],
+                                       answer: [ ],
+                                       active: true, //default
+                               };
+                       };
+                       let q = emptyQuestion();
+                       lines.forEach( L => {
+                               if (L.length > 0)
+                               {
+                                       if (['+','-'].includes(L.charAt(0)))
                                        {
-                                               Object.keys(this.grades[s.number]).forEach( assessmentName => {
-                                                       s[assessmentName] = this.grades[s.number][assessmentName];
-                                                       if (_.isNumeric(s[assessmentName]) && !isNaN(s[assessmentName]))
-                                                       {
-                                                               finalGrade += s[assessmentName];
-                                                               gradesCount++;
-                                                       }
-                                                       if (gradesCount >= 1)
-                                                               finalGrade /= gradesCount;
-                                                       s["final"] = finalGrade; //TODO: forbid "final" as assessment name
-                                               });
+                                               if (L.charAt(0) == '+')
+                                                       q.answer.push(q.options.length);
+                                               q.options.push(L.slice(1).trim());
                                        }
-                                       data.push(s); //number,name,group,assessName1...assessNameN,final
-                               });
-                               let csv = Papa.unparse(data, {
-                                       quotes: true,
-                                       header: true,
-                               });
-                               let downloadAnchor = $("#download");
-                               downloadAnchor.attr("download", this.course.code + "_results.csv");
-                               downloadAnchor.attr("href", "data:text/plain;charset=utf-8," + encodeURIComponent(csv));
-                               this.$refs.download.click()
-                               //downloadAnchor.click(); //fails
-                       },
-                       showDetails: function(group) {
-                               this.group = group;
-                               $("#detailedGrades").modal("open");
-                       },
-                       groupList: function() {
-                               let maxGrp = 1;
-                               this.course.students.forEach( s => {
-                                       if (s.group > maxGrp)
-                                               maxGrp = s.group;
+                                       else if (L.charAt(0) == '*')
+                                       {
+                                               // TODO: read current + next lines into q.answer (HTML, 1-elem array)
+                                       }
+                                       else
+                                               q.wording += L + "\n";
+                               }
+                               else
+                               {
+                                       // Flush current question (if any)
+                                       if (q.wording.length > 0)
+                                       {
+                                               questions.push(q);
+                                               q = emptyQuestion();
+                                       }
+                               }
+                       });
+                       this.assessment.questions = questions;
+               },
+               actionAssessment: function(index) {
+                       if (admin)
+                       {
+                               // Edit screen
+                               this.assessmentIndex = index;
+                               this.assessment = $.extend(true, {}, this.assessmentArray[index]);
+                               this.setAssessmentText();
+                               this.mode = "edit";
+                               Vue.nextTick( () => {
+                                       $("#questionList").find("code[class^=language-]").each( (i,elem) => {
+                                               Prism.highlightElement(elem);
+                                       });
+                                       MathJax.Hub.Queue(["Typeset",MathJax.Hub,"questionList"]);
                                });
-                               return _.range(1,maxGrp+1);
-                       },
-                       grade: function(assessmentIndex, studentNumber) {
-                               if (!this.grades[assessmentIndex] || !this.grades[assessmentIndex][studentNumber])
-                                       return ""; //no grade yet
-                               return this.grades[assessmentIndex][studentNumber];
-                       },
-                       groupId: function(group, prefix) {
-                               return (prefix || "") + "group" + group;
-                       },
-                       togglePresence: function(number, index) {
-                               // UNIMPLEMENTED
-                               // TODO: if no grade (thus automatic 0), toggle "exempt" state on student for current exam
-                               // --> automatic update of grades view (just a few number to change)
-                       },
-                       computeGrades: function() {
-                               // UNIMPLEMENTED
-                               // TODO: compute all grades using settings (points, coefficients, bonus/malus...).
-                               // If some questions with free answers (open), display answers and ask teacher action.
-                               // TODO: need a setting for that too (by student, by exercice, by question)
-                       },
+                       }
+                       else //external user: show assessment
+                               this.redirect(this.assessmentArray[index].name);
                },
-       });
-
-};
+               redirect: function(assessmentName) {
+                       document.location.href = "/" + initials + "/" + course.code + "/" + assessmentName;
+               },
+               setPassword: function() {
+                       let hashPwd = Sha1.Compute(this.monitorPwd);
+                       let error = Validator.checkObject({password:hashPwd}, "Course");
+                       if (error.length > 0)
+                               return alert(error);
+                       $.ajax("/set/password",
+                               {
+                                       method: "GET",
+                                       data: {
+                                               cid: this.course._id,
+                                               pwd: hashPwd,
+                                       },
+                                       dataType: "json",
+                                       success: res => {
+                                               if (!res.errmsg)
+                                                       alert("Password saved!");
+                                               else
+                                                       alert(res.errmsg);
+                                       },
+                               }
+                       );
+               },
+               // NOTE: artifact required for Vue v-model to behave well
+               checkBoxFixedId: function(i) {
+                       return "questionFixed" + i;
+               },
+               checkBoxActiveId: function(i) {
+                       return "questionActive" + i;
+               },
+               // GRADES:
+               gradeSettings: function() {
+                       $("#gradeSettings").modal("open");
+                       Materialize.updateTextFields(); //total points field in grade settings overlap
+               },
+               download: function() {
+                       // Download (all) grades as a CSV file
+                       let data = [ ];
+                       this.studentList(0).forEach( s => {
+                               let finalGrade = 0.;
+                               let gradesCount = 0;
+                               if (!!this.grades[s.number])
+                               {
+                                       Object.keys(this.grades[s.number]).forEach( assessmentName => {
+                                               s[assessmentName] = this.grades[s.number][assessmentName];
+                                               if (_.isNumeric(s[assessmentName]) && !isNaN(s[assessmentName]))
+                                               {
+                                                       finalGrade += s[assessmentName];
+                                                       gradesCount++;
+                                               }
+                                               if (gradesCount >= 1)
+                                                       finalGrade /= gradesCount;
+                                               s["final"] = finalGrade; //TODO: forbid "final" as assessment name
+                                       });
+                               }
+                               data.push(s); //number,name,group,assessName1...assessNameN,final
+                       });
+                       let csv = Papa.unparse(data, {
+                               quotes: true,
+                               header: true,
+                       });
+                       let downloadAnchor = $("#download");
+                       downloadAnchor.attr("download", this.course.code + "_results.csv");
+                       downloadAnchor.attr("href", "data:text/plain;charset=utf-8," + encodeURIComponent(csv));
+                       this.$refs.download.click()
+                       //downloadAnchor.click(); //fails
+               },
+               showDetails: function(group) {
+                       this.group = group;
+                       $("#detailedGrades").modal("open");
+               },
+               groupList: function() {
+                       let maxGrp = 1;
+                       this.course.students.forEach( s => {
+                               if (s.group > maxGrp)
+                                       maxGrp = s.group;
+                       });
+                       return _.range(1,maxGrp+1);
+               },
+               grade: function(assessmentIndex, studentNumber) {
+                       if (!this.grades[assessmentIndex] || !this.grades[assessmentIndex][studentNumber])
+                               return ""; //no grade yet
+                       return this.grades[assessmentIndex][studentNumber];
+               },
+               groupId: function(group, prefix) {
+                       return (prefix || "") + "group" + group;
+               },
+               togglePresence: function(number, index) {
+                       // UNIMPLEMENTED
+                       // TODO: if no grade (thus automatic 0), toggle "exempt" state on student for current exam
+                       // --> automatic update of grades view (just a few number to change)
+               },
+               computeGrades: function() {
+                       // UNIMPLEMENTED
+                       // TODO: compute all grades using settings (points, coefficients, bonus/malus...).
+                       // If some questions with free answers (open), display answers and ask teacher action.
+                       // TODO: need a setting for that too (by student, by exercice, by question)
+               },
+       },
+});
index 238c1fc..020af55 100644 (file)
@@ -1,69 +1,65 @@
-window.onload = function() {
-
-       new Vue({
-               el: '#courseList',
-               data: {
-                       courseArray: courseArray,
-                       newCourse: {
-                               code: "",
-                               description: "",
-                       },
+new Vue({
+       el: '#courseList',
+       data: {
+               courseArray: courseArray,
+               newCourse: {
+                       code: "",
+                       description: "",
                },
-               mounted: function() {
-                       $('.modal').modal();
+       },
+       mounted: function() {
+               $('.modal').modal();
+       },
+       methods: {
+               redirect: function(code) {
+                       document.location.href = "/" + initials + "/" + code;
                },
-               methods: {
-                       redirect: function(code) {
-                               document.location.href = "/" + initials + "/" + code;
-                       },
-                       addCourse: function() {
-                               if (!admin)
-                                       return;
-                               // modal, fill code and description
-                               let error = Validator.checkObject({code:this.newCourse.code}, "Course");
-                               if (!!error)
-                                       return alert(error);
-                               else
-                                       $('#newCourse').modal('close');
-                               $.ajax("/add/course",
+               addCourse: function() {
+                       if (!admin)
+                               return;
+                       // modal, fill code and description
+                       let error = Validator.checkObject({code:this.newCourse.code}, "Course");
+                       if (!!error)
+                               return alert(error);
+                       else
+                               $('#newCourse').modal('close');
+                       $.ajax("/add/course",
+                               {
+                                       method: "GET",
+                                       data: this.newCourse,
+                                       dataType: "json",
+                                       success: res => {
+                                               if (!res.errmsg)
+                                               {
+                                                       this.newCourse["code"] = "";
+                                                       this.newCourse["description"] = "";
+                                                       this.courseArray.push(res);
+                                               }
+                                               else
+                                                       alert(res.errmsg);
+                                       },
+                               }
+                       );
+               },
+               deleteCourse: function(course) {
+                       if (!admin)
+                               return;
+                       if (confirm("Delete course '" + course.code + "' ?"))
+                               $.ajax("/remove/course",
                                        {
                                                method: "GET",
-                                               data: this.newCourse,
+                                               data: { cid: course._id },
                                                dataType: "json",
                                                success: res => {
                                                        if (!res.errmsg)
-                                                       {
-                                                               this.newCourse["code"] = "";
-                                                               this.newCourse["description"] = "";
-                                                               this.courseArray.push(res);
-                                                       }
+                                                               this.courseArray.splice( this.courseArray.findIndex( item => {
+                                                                       return item._id == course._id;
+                                                               }), 1 );
                                                        else
                                                                alert(res.errmsg);
                                                },
                                        }
                                );
                        },
-                       deleteCourse: function(course) {
-                               if (!admin)
-                                       return;
-                               if (confirm("Delete course '" + course.code + "' ?"))
-                                       $.ajax("/remove/course",
-                                               {
-                                                       method: "GET",
-                                                       data: { cid: course._id },
-                                                       dataType: "json",
-                                                       success: res => {
-                                                               if (!res.errmsg)
-                                                                       this.courseArray.splice( this.courseArray.findIndex( item => {
-                                                                               return item._id == course._id;
-                                                                       }), 1 );
-                                                               else
-                                                                       alert(res.errmsg);
-                                                       },
-                                               }
-                                       );
-                               },
-                       }
-       });
-
-};
+               }
+});
index 4f70e19..8752ba8 100644 (file)
@@ -1,86 +1,82 @@
-window.onload = function() {
-
-       const messages = {
-               "login": "Go",
-               "register": "Send",
-       };
+const messages = {
+       "login": "Go",
+       "register": "Send",
+};
 
-       const ajaxUrl = {
-               "login": "/sendtoken",
-               "register": "/register",
-       };
+const ajaxUrl = {
+       "login": "/sendtoken",
+       "register": "/register",
+};
 
-       const infos = {
-               "login": "Connection token sent. Check your emails!",
-               "register": "Registration complete! Please check your emails.",
-       };
+const infos = {
+       "login": "Connection token sent. Check your emails!",
+       "register": "Registration complete! Please check your emails.",
+};
 
-       const animationDuration = 300; //in milliseconds
+const animationDuration = 300; //in milliseconds
 
-       // Basic anti-bot measure: force at least N seconds between arrival on page, and register form validation:
-       const enterTime = Date.now();
+// Basic anti-bot measure: force at least N seconds between arrival on page, and register form validation:
+const enterTime = Date.now();
 
-       new Vue({
-               el: '#login',
-               data: {
-                       messages: messages,
-                       user: {
-                               name: "",
-                               email: "",
-                       },
-                       stage: "login", //or "register"
+new Vue({
+       el: '#login',
+       data: {
+               messages: messages,
+               user: {
+                       name: "",
+                       email: "",
                },
-               mounted: function() {
-                       // https://laracasts.com/discuss/channels/vue/vuejs-set-focus-on-textfield
-                       this.$refs.userEmail.focus();
+               stage: "login", //or "register"
+       },
+       mounted: function() {
+               // https://laracasts.com/discuss/channels/vue/vuejs-set-focus-on-textfield
+               this.$refs.userEmail.focus();
+       },
+       methods: {
+               toggleStage: function(stage) {
+                       let $form = $("#form");
+                       $form.fadeOut(animationDuration);
+                       setTimeout( () => {
+                               this.stage = stage;
+                               $form.show(0);
+                       }, animationDuration);
                },
-               methods: {
-                       toggleStage: function(stage) {
-                               let $form = $("#form");
-                               $form.fadeOut(animationDuration);
-                               setTimeout( () => {
-                                       this.stage = stage;
-                                       $form.show(0);
-                               }, animationDuration);
-                       },
-                       submit: function() {
-                               if (this.stage=="register")
+               submit: function() {
+                       if (this.stage=="register")
+                       {
+                               if (Date.now() - enterTime < 5000)
+                                       return;
+                       }
+                       let error = Validator.checkObject({email: this.user.email}, "User");
+                       if (!error && this.stage == "register")
+                               error = Validator.checkObject({name: this.user.name}, "User");
+                       let $dialog = $("#dialog");
+                       show($dialog);
+                       setTimeout(() => {hide($dialog);}, 3000);
+                       if (error.length > 0)
+                               return showMsg($dialog, "error", error);
+                       showMsg($dialog, "process", "Processing... Please wait");
+                       $.ajax(ajaxUrl[this.stage],
                                {
-                                       if (Date.now() - enterTime < 5000)
-                                               return;
-                               }
-                               let error = Validator.checkObject({email: this.user.email}, "User");
-                               if (!error && this.stage == "register")
-                                       error = Validator.checkObject({name: this.user.name}, "User");
-                               let $dialog = $("#dialog");
-                               show($dialog);
-                               setTimeout(() => {hide($dialog);}, 3000);
-                               if (error.length > 0)
-                                       return showMsg($dialog, "error", error);
-                               showMsg($dialog, "process", "Processing... Please wait");
-                               $.ajax(ajaxUrl[this.stage],
+                                       method: "GET",
+                                       data:
                                        {
-                                               method: "GET",
-                                               data:
+                                               email: encodeURIComponent(this.user.email),
+                                               name: encodeURIComponent(this.user.name), //may be unused
+                                       },
+                                       dataType: "json",
+                                       success: res => {
+                                               if (!res.errmsg)
                                                {
-                                                       email: encodeURIComponent(this.user.email),
-                                                       name: encodeURIComponent(this.user.name), //may be unused
-                                               },
-                                               dataType: "json",
-                                               success: res => {
-                                                       if (!res.errmsg)
-                                                       {
-                                                               this.user["name"] = "";
-                                                               this.user["email"] = "";
-                                                               showMsg($dialog, "info", infos[this.stage]);
-                                                       }
-                                                       else
-                                                               showMsg($dialog, "error", res.errmsg);
-                                               },
-                                       }
-                               );
-                       },
-               }
-       });
-
-};
+                                                       this.user["name"] = "";
+                                                       this.user["email"] = "";
+                                                       showMsg($dialog, "info", infos[this.stage]);
+                                               }
+                                               else
+                                                       showMsg($dialog, "error", res.errmsg);
+                                       },
+                               }
+                       );
+               },
+       }
+});
index 26d9d75..7677063 100644 (file)
@@ -28,6 +28,7 @@ button.sendAnswer {
 
 .question .wording {
        margin-bottom: 10px;
+       overflow: auto;
 }
 
 .question .option {
@@ -61,6 +62,10 @@ table.in-question {
 }
 
 table.in-question th, table.in-question td {
-       padding: 3px;
+       padding: 7px;
        border-bottom: 1px solid grey;
 }
+
+/*table { border: none; border-collapse: collapse; }*/
+table.in-question td { border-left: 1px solid grey; }
+table.in-question td:first-child { border-left: none; }
index c1c4a9b..178e824 100644 (file)
@@ -3,6 +3,11 @@ h4.title {
        background-color: lightgrey;
 }
 
+.idle {
+       background-color: #EFEFEF;
+       opacity: 0.5;
+}
+
 tr.assessment {
        cursor: pointer;
 }
@@ -33,6 +38,7 @@ tr.stats {
 
 .question .wording {
        margin-bottom: 10px;
+       overflow: auto;
 }
 
 .question .option {
@@ -62,6 +68,10 @@ table.in-question {
 }
 
 table.in-question th, table.in-question td {
-       padding: 3px;
+       padding: 7px;
        border-bottom: 1px solid grey;
 }
+
+/*table { border: none; border-collapse: collapse; }*/
+table.in-question td { border-left: 1px solid grey; }
+table.in-question td:first-child { border-left: none; }
index a107d7e..b7dcfd9 100644 (file)
@@ -11,9 +11,10 @@ const sanitizeHtml = require('sanitize-html');
 const sanitizeOpts = {
        allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'img', 'u' ]),
        allowedAttributes: {
-               img: [ 'src' ],
+               img: [ 'src','style' ],
                code: [ 'class' ],
                table: [ 'class' ],
+               div: [ 'style' ],
        },
 };
 
index d1f696f..9e8550f 100644 (file)
@@ -18,7 +18,7 @@ block content
                                        .center-align
                                                a.modal-action.modal-close.waves-effect.waves-light.btn-flat(href="#!") Ok
                .row
-                       .col.s12.m10.offset-m1.l8.offset-l2.xl6.offset-xl3
+                       .col.s12.m10.offset-m1
                                h4= assessment.name
                                #stage0(v-show="stage==0")
                                        .card
index 7aa6619..9de3570 100644 (file)
@@ -90,12 +90,12 @@ block content
                                                        tr.grade(v-for="student in studentList(group)")
                                                                td {{ student.number }}
                                                                td(v-for="(assessment,i) in assessmentArray" @click="togglePresence(student.number,i)")
-                                                                       {{ grade(i,student.number) }}
+                                                                       {{ grade(i,student.number) }}
                                .modal-footer
                                        .center-align
                                                a.modal-action.modal-close.waves-effect.waves-light.btn-flat(href="#!") Close
                .row(v-show="mode=='view'")
-                       .col.s12.m10.offset-m1.l8.offset-l2.xl6.offset-xl3
+                       .col.s12.m10.offset-m1
                                if teacher
                                        h4.title(@click="toggleDisplay('students')") Students
                                        .card(v-show="display=='students'")
@@ -128,7 +128,7 @@ block content
                                                                th #Questions
                                                                th Time
                                                tbody
-                                                       tr.assessment(v-for="(assessment,i) in assessmentArray" v-if="assessment.active"
+                                                       tr.assessment(v-for="(assessment,i) in assessmentArray" :class="{idle:!assessment.active}"
                                                                        @click.left="actionAssessment(i)" @contextmenu.prevent="deleteAssessment(assessment)")
                                                                td {{ assessment.name }}
                                                                td {{ assessment.mode }}
@@ -162,7 +162,7 @@ block content
                                                                        td(colspan="4") Stats: range= stdev= mean=
                if teacher
                        .row(v-show="mode=='edit'")
-                               .col.s12.m10.offset-m1.l8.offset-l2.xl6.offset-xl3
+                               .col.s12.m10.offset-m1
                                        h4 {{ assessment.name }}
                                        .card
                                                .center-align