<![CDATA[21CC Navigator Future Self Quiz_Primary_Version]]> false false true true false ./future_self_quiz/css/styles.css ]]> ./future_self_quiz/images/sjw_ejss.png ; DESCRIPTION_EDITOR setago true false _default_ setago false ]]> 20 1 false VARIABLE_EDITOR global true false VARIABLE_EDITOR layout true false VARIABLE_EDITOR lookang true false CODE_EDITOR viewport true false var metaTag=document.createElement('meta'); metaTag.name = "viewport"; metaTag.content = "width=device-width, initial-scale=1.0" document.getElementsByTagName('head')[0].appendChild(metaTag); ]]> CODE_EDITOR cover true false btn.addEventListener('click', () => { showPage("Future Ready Skills Quiz"); isScrollerShown = true; _view._update(); })); ]]> CODE_EDITOR scroll true false { scrollUp(); }; scrollUpbutton.addEventListener('click', scrollUp); scrollUpbutton.addEventListener('touchstart', scrollUp); const scrollDownbutton = document.getElementById('scroller-down-btn'); let scrollDownIntervalId; let holdDownDuration = 100; // Interval in milliseconds const callbackFunctionForScrollDown = () => { scrollDown(); }; scrollDownbutton.addEventListener('click', scrollDown); scrollDownbutton.addEventListener('touchstart', scrollDown); //const stopScrollDownInterval = () => { // clearInterval(scrollDownIntervalId); //}; //scrollDownbutton.addEventListener('mouseup', stopScrollDownInterval); //scrollDownbutton.addEventListener('mouseleave', stopScrollDownInterval); //scrollDownbutton.addEventListener('touchend', () => { /// const container = document.getElementById('control'); // container.scrollTo(container.scrollX + 25 , container.scrollY + 25); //}); ]]> CODE_EDITOR computedone true false LIBRARY_EDITOR futureSkillsDocumentation true false LIBRARY_EDITOR MoodleDataAnalyticsLibrary true false 0; } startQuestion(questionName) { /* * This function should be called whenever the student first sees the question, * and the student cannot start answering any other question. The question may be subsequently * ended by calling endQuestion function. * * Parameters: * questionName: string * * return value: * undefined */ _view._addInteraction(this._nullFunction, { action:"questionStart", name:questionName }, { element:"questionLib", property:"value" } ); this._debugPrint("Start question: " + questionName); this._questionLib.stack.push(questionName); } endQuestion() { /* * This function should be called whenever the student submits/finalises their answer for the question. * * No Parameters * * return value: * undefined */ if (this._questionLib.stack.length > 0) { const questionName = this._questionLib.stack.pop(); this._debugPrint("End question: " + questionName); _view._addInteraction(this._nullFunction, { action:"questionEnd", name:questionName }, { element: "questionLib", property: "value" } ); } } // for assessment.json history addQuestionHistory(history, questionName=null) { /* * Adds an entry to the question history to the specified question, or to the pending question by default. * If adding to the question history of the pending question, it also flushes the history. (which makes Moodle able to read the history) * * Parameters: * history: string, questionName?: string * * return value: * undefined * * questioName: * This is the name of the question that the history will be added to. If this parameter is not set, by default, the history entry * will be added to the pending question. */ if (questionName === null && this.isQuestionStarted()) { questionName = this._getCurrentQuestion(); } if (!(questionName in this._questionLib.history)) { this._debugPrint("Create question history for " + questionName); this._questionLib.history[questionName] = []; } if (this.debugMode) { console.log("Push \"" + history + "\" to question history for " + questionName); } this._questionLib.history[questionName].push(history); this._flushQuestionHistory(questionName); } _flushQuestionHistory(questionName) { if (questionName === this._getCurrentQuestion()) { const outputHistory = this._getQuestionHistory(questionName); _view._addInteraction(this._nullFunction, outputHistory, { property: "historyFor" + questionName, element: "questionLib" } ); } } _getQuestionHistory(questionName) { if (questionName in this._questionLib.history) { return this._questionLib.history[questionName].join("\n"); } else { _debugPrint("No question \"" + questionName + "\" exists"); return ""; } } onAnswer(answer, isCorrect=false, history=answer, questionName=null) { /* * Indicates that the student made an attempt to answer the question, * writes the history, and adds a log for the submitted answer. * However, it does not give marks in itself (that is done by awardQuestionMarks) * * Parameters: * answer: String * A string representing what the student chose as the answer to the question. * This can be used in an advanced assessment.json generator to check the answers on the server side. * * isCorrect?: Boolean * Whether the student's answer is correct (defaults to false) * * history: String * A textual representation of what the student chose as the answer to the question. * By default, this is the same as the value given in the parameter answer. * This parameter controls what the teacher will see when they hover their cursor over the student's marks for this question. * * questionName?: String * The name of the question the student is giving an answer to. * By default, this is the pending question. * However, this can be overridden using this parameter to write the answer history to some other question. */ if (questionName === null && this.isQuestionStarted()) { questionName = this._questionLib.stack[this._questionLib.stack.length - 1]; } if (questionName !== null) { const explainer = Object.create(null); explainer[true] = " ✅"; explainer[false] = " ❌"; this.addQuestionHistory(history + explainer[isCorrect], questionName); if (questionName === this._getCurrentQuestion()) { _view._addInteraction(this._nullFunction, {name:questionName, answer:answer, isCorrect:isCorrect, action:"questionAnswer"}, {property: "answer", element:"questionLib"}); } } } awardQuestionMarks(marks=1) { /* * Awards a number of marks to the student for the pending question. * * Parameters: * marks?: int * * The number of marks to award to the student. By default, this value is 1. */ if (this.isQuestionStarted()) { const questionName = this._getCurrentQuestion(); this._questionLib.questionMarksAwarded[questionName] = marks; _view._addInteraction( this._nullFunction, this._questionLib.questionMarksAwarded[questionName], { element: "questionLib", property: "awardMarkFor"+questionName } ); } } resetQuestionMarks(questionName) { /* * Reset the awarded marks for the indicated question to 0 the next time the question is started. * * Parameters: * questionName: string * * return value: * undefined * * questionName: * The name of the question to reset. */ this._questionLib.questionMarksAwarded[questionName] = 0; } questionInstantMark(questionName, message) { /* * A convenience function to start a question, award a mark, (optionally) add a history entry, and end the question. * This function should be called whenever a student answers a question correctly. * It instantly submits the correct answer to the Moodle server, and gives the student a mark. * For most cases, this function would suffice. * * Parameters: * questionName: string, message?: string * * return value: * undefined * * questionName: * These values are the names of the questions that will be displayed on the analytics page on Moodle, * and they'll be needed when you make the assessment.json file so please remember to note down what * values you use for the name of each question. * * message: * What Moodle will display when the teacher hovers their mouse over the student's mark for a particular question. */ this.startQuestion(questionName); this._debugPrint("" + message); if (message) { this.addQuestionHistory(message); } else { this._flushQuestionHistory(questionName); } this.awardQuestionMarks(); this.endQuestion(); } questionAppendHistory(questionName, message) { /* * Adds a history entry to the given question. * * Parameters: * questionName: string, message?: string * * return value: * undefined * * questionName: * The name of the question to append the history entry to. * * message: * The history entry to add to the question. */ if (!(questionName in this._questionLib.questionMarksAwarded)) { this._questionLib.questionMarksAwarded[questionName] = 0; } let shouldPushQuestion = this._getCurrentQuestion() !== questionName; if (shouldPushQuestion) { this.startQuestion(questionName); } this.awardQuestionMarks(this._questionLib.questionMarksAwarded[questionName]) this.addQuestionHistory(message); if (shouldPushQuestion) { this.endQuestion(); } } resetQuestionHistory(questionName) { /* * Clear/reset the history of the indicated question. * * Parameters: * questionName: string * * return value: * undefined * * questionName: * The name of the question to reset. */ this._questionLib.history[questionName] = []; } resetQuestion(questionName) { /* * A convenience function to reset the marks and history of an indicated question. */ this.resetQuestionHistory(questionName); this.resetQuestionMarks(questionName); } } let moodle = new MoodleDataAnalyticsLibrary(); ]]> LIBRARY_EDITOR fullscreen true false LIBRARY_EDITOR questions true false { const radios = question.querySelectorAll('input[type="radio"]'); radios.forEach(radio => { if (radio.checked) { numberOfAnswers++; } }); }); return numberOfQuestions == numberOfAnswers; } function firstUnansweredQuestionIndex () { // returns the index of the first question that is unanswered // the questions are indexed beginning from 1 const questions = document.querySelectorAll(".options"); let index = -1; for (let i = 0; i < questions.length; i++) { const radios = questions[i].querySelectorAll('input[type="radio"]'); let isAnswered = false; for (let j = 0; j < radios.length; j++) { if (radios[j].checked) { isAnswered = true; } } if (!isAnswered) { index = i; break; } } return index + 1; } ]]> LIBRARY_EDITOR showpage true false LIBRARY_EDITOR radiobutton true false qn.name == 'Q'+index)[0]['points'] = questionPointsScored; futureSkills[futureSkill]['associatedQuestions'].filter(qn => qn.name == 'Q'+index)[0]['choice'] = userSelection; // update score to moodle (analytics) moodle.startQuestion('Q'+index); moodle.addQuestionHistory(userSelection); moodle.awardQuestionMarks(questionPointsScored); moodle.endQuestion(); // Display strengths and improvement lists to users on the page if (isAllScoresSet()) { showPage("Well Done"); isScrollerShown = false; } } // helper functions for this page function sanitiseAnswer(str) { // note, this won't work once the id of the html element changes let data = str.split("_")[1].split(".")[0]; return addSpaceBeforeUppercaseExceptFirst(data); } function addSpaceBeforeUppercaseExceptFirst(inputString) { let modifiedString = inputString[0]; // Initialize with the first character for (let i = 1; i < inputString.length; i++) { // Check if the current character is uppercase if (inputString[i] === inputString[i].toUpperCase()) { modifiedString += ' '; // Add a space before uppercase characters } modifiedString += inputString[i]; // Add the current character } return modifiedString; } ]]> LIBRARY_EDITOR progressBar true false { const radios = container.querySelectorAll('input[type="radio"]'); radios.forEach(radio => { if (radio.checked) { percentage+= x; } }); }); return Math.round(percentage); } function updateProgressBar(level) { var percentage = document.getElementById("percentage"); var progressLevel = document.getElementById("progressLevel"); progressLevel.style.width = level + "%"; percentage.innerText = level + "%"; percentage.style.marginLeft = level + "%"; if (level == 100) { percentage.style.transform = "translateX(-50px)"; } else if (level <= 99 && level >= 10) { percentage.style.transform = "translateX(-35px)"; } else if (level <= 99 && level >= 4) { percentage.style.transform = "translateX(-26px)"; } else if (level < 4 && level >= 1) { percentage.style.transform = "translateX(-10px)"; } if (level < 100) { progressLevel.classList.remove("fullbar"); } else { progressLevel.classList.add("fullbar"); } } ]]> LIBRARY_EDITOR finalCompute true false { setTimeout(resolve, seconds * 1000); // 1000 milliseconds (1 second) }); } async function finalCompute () { var passingmark = 0.67; var maxmarks = 6; var strengthList = []; var improvementList = []; // here is where the program decides if a student's skill falls under a strength or improvement list for (const futureSkill in futureSkills) { const skillInfo = futureSkills[futureSkill]; if (skillInfo['totalPoints'] > passingmark * (maxmarks * skillInfo['numberOfQuestions'])) { strengthList.push(skillInfo['titleCased']); } else { improvementList.push(skillInfo['titleCased']); } } // display the strength and improvement list accordingly based on the evaluated scores if (strengthList.length > 0) { left21CC = "Your Strengths

" + strengthList.join("
").replaceAll("&", "&"); var leftPanel = document.getElementById("Left"); leftPanel.innerHTML = left21CC; } else { giveEncouragement = true; // used to populate some encouraging words instead of empty container } if (improvementList.length > 0) { right21CC = "What You Can Work On

" + improvementList.join("
").replaceAll("&", "&"); var rightPanel = document.getElementById("Right"); rightPanel.innerHTML = right21CC; } else { givePraise = true; // used to populate praise instead of empty container } // send the summary list results to moodle var summary = "Strengths:\n" + strengthList.join("\n") + "\n\nAreas for Improvement:\n" + improvementList.join("\n"); moodle.resetQuestionHistory("Summary"); moodle.questionInstantMark("Summary", summary); } // Warning: // Do not use this function to update all the scores for the 24 questions at the moment async function sendAllQuizAnswersToMoodle() { for (const skill in futureSkills) { let questions = futureSkills[skill]['associatedQuestions']; for (const question of questions) { let { name, choice, points } = question; _println(name); _println(choice); _println(points); startQuestion(name); addQuestionHistory(choice); awardQuestionMarks(points); endQuestion(); await sleep(1.5); } } } ]]>
LIBRARY_EDITOR computedone true false 0) { countattempt += 1; } } } ]]>
HTML_VIEW_EDITOR HtmlView true false 0 0 0 800 600 true true Elements.Panel Elements.Panel ]]> Elements.Panel ]]> true Elements.Panel Elements.Panel ]]> Elements.Label Elements.Panel Elements.Panel
0%
]]>
true Elements.Panel true Elements.Panel true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label true Elements.Panel Elements.Label false Elements.Panel Elements.Label false Elements.Panel Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.RadioButton Elements.Label Elements.Panel ]]> Elements.Panel Well Done! --> ]]> false Elements.Panel Elements.Label false Elements.Panel false Elements.Panel Your Strengths

That’s ok!
This will be a great 
opportunity to strengthen 
your 21CCs!

]]>
Elements.Panel false Elements.Panel Elements.Panel Elements.Panel Elements.Panel What You Can Work On
That’s great!
You may not have any 21CCs 
to improve on at the moment. 
But, do keep up with your 
strengths.
Take this opportunity to keep 
working on them!
]]>
false Elements.Panel Elements.Panel Brought to you by
Ministry of Education Singapore
GovTech.

]]>
Elements.Panel © Classroom of the Future
MOE ]]>
false Elements.Panel Elements.Button Elements.Button alert("Oops! You missed a question!"), 1000); } } else { // keep scrolling down div.scrollTo( { top: div.scrollTop + div.clientHeight, behavior: 'smooth' } ); // enable 'BACK' button let button = document.querySelector("#back_button"); button.disabled = false; // update appearance of button when enabled button.classList.remove('btn-is-disabled'); } function hasScrolledToBottom(container) { return container.clientHeight + container.scrollTop == container.scrollHeight ? true : false; } function toast(sentence, duration) { const message = document.querySelector(".toast"); message.classList.add("toast--show"); message.innerText = sentence; setTimeout(() => { message.classList.remove("toast--show"); }, duration); } ]]>