<![CDATA[Node Nexus]]> false false true true false ./tech_savvy/images/spaceship-image.png ./tech_savvy/images/sjw.png 25 1 false VARIABLE_EDITOR game true false VARIABLE_EDITOR others true false VARIABLE_EDITOR targets true false VARIABLE_EDITOR bullets true false VARIABLE_EDITOR robotHand true false VARIABLE_EDITOR questions true false VARIABLE_EDITOR otherGameObjects true false VARIABLE_EDITOR Var Table 2 true false VARIABLE_EDITOR height true false CODE_EDITOR adjustVariablesWithRespectToSizeOfContainer true false CODE_EDITOR pages true false changePage('gameIntroduction')); gameIntroBtn.addEventListener('click', () => changePage('gameplayInstructions')); gameplayInstructionsBtn.addEventListener('click', function() { if (!isLoadingStage) { isLoadingStage = true; // show loading spinner document.querySelector('.smooth.spinner').classList.remove('d-none'); document.getElementById('overlay').classList.remove('d-none'); setTimeout(() => { numberOfTargets = 3; // update the HTMLView Element to hold new number of "child" elements initialiseNewStage(numberOfTargets); _play(); }, 2000); // the function MUST be called some time after initialising the cluster of nodes (targets) to // prevent error of not being able to find the new targets in the DOM after initialising them. // therefore, we call the function below 50ms later. // if we call it much later, the user can see the color and type of clusters change mid way // while playing and we don't want that. setTimeout(() => { randomiseTargetClusterImages(); currentPage = "gameScreen"; // display instructions showInstructionsToHitNumberOfTargets(3); _view.backgroundMusic.play(); isLoadingStage = false; document.querySelector('.smooth.spinner').classList.add('d-none'); document.getElementById('overlay').classList.add('d-none'); setTimeout(() => { hideInstructionsToHitNumberOfTargets(); }, 10000); }, 2050); document.querySelector('#gameScreen').style.zIndex = 0; } // due to difficulties in implementing a double click event handler for firing bullets // I used a single click event handler to simulate a double click event. function handleSingleClick(event) { const currentTime = new Date().getTime(); const timeElapsed = currentTime - lastClickTime; if (timeElapsed <= doubleClickDelay) { clearTimeout(clickTimeout); handleDoubleClick(event); } else { clickTimeout = setTimeout(() => { handleSingleClickAction(event); }, doubleClickDelay); } lastClickTime = currentTime; } function handleSingleClickAction(event) { // Perform the action for a single click } // Define the double-click action function function handleDoubleClick(event) { event.stopPropagation(); if (canFireBullet) { // limited number of laser nodes given each stage. _println("===attempt to fire laser node from robot hand==="); const availableBulletIndex = y.findIndex(x => x == 2); allBulletsAreExhausted = y.every(x => x > 2); // only allow bullet to be fired if condition below is true. if (!allBulletsAreExhausted) { _println('are bullets exhausted? '+ allBulletsAreExhausted); _println('=====bullet fires!====='); _println("bullet index: " + availableBulletIndex + " is fired."); if (!isMuted) _view.fireLaserNodeSound.play(); // shoot available bullet! x[availableBulletIndex] = xc; vy[availableBulletIndex] = 18; shouldShowBullets[availableBulletIndex] = true; } // updating remaining laser nodes let currentShownNumberOfLaserNodes = document.getElementById('number-of-remaining-nodes').innerText; if (parseInt(currentShownNumberOfLaserNodes) > 0) { document.getElementById('number-of-remaining-nodes').innerText = `${parseInt(currentShownNumberOfLaserNodes) - 1}`; } } } // Get the target element and add the click event listener const targetElement1 = document.getElementById('gameScreen'); targetElement1.addEventListener('click', handleSingleClick); const targetElement2 = document.getElementById('objectImageDesktop'); targetElement2.addEventListener('click', handleSingleClick); }); ]]> CODE_EDITOR robotHand true false { let isDragging = false; const draggable = document.getElementById('objectImageDesktop'); console.log(draggable); draggable.addEventListener('mousedown', (e) => { isDragging = true; console.log('mousedown'); let startX = e.clientX; let startY = e.clientY; const moveHandler = (e) => { const deltaX = e.clientX - startX; const deltaY = e.clientY - startY; draggable.style.left = `${draggable.offsetLeft + deltaX}px`; draggable.style.top = `${draggable.offsetTop + deltaY}px`; startX = e.clientX; startY = e.clientY; }; const upHandler = () => { isDragging = false; // Remove event listeners when dragging is done document.removeEventListener('mousemove', moveHandler); document.removeEventListener('mouseup', upHandler); }; // Attach event listeners for mousemove and mouseup document.addEventListener('mousemove', moveHandler); document.addEventListener('mouseup', upHandler); }); // Double click event draggable.addEventListener('dblclick', (e) => { if (!isDragging) { // Handle your double click logic here alert('Double click!'); } }); }, 2000); ]]> CODE_EDITOR robothandfirebullet true false { isDragging = true; startPosX = e.clientX; startPosY = e.clientY; offsetX = draggableElement.offsetLeft; offsetY = draggableElement.offsetTop; draggableElement.classList.add("dragging"); }); document.addEventListener("mousemove", (e) => { if (!isDragging) return; const deltaX = e.clientX - startPosX; const newPosX = offsetX + deltaX; draggableElement.style.left = `${newPosX}px`; }); document.addEventListener("mouseup", () => { if (!isDragging) return; isDragging = false; draggableElement.classList.remove("dragging"); }); function getDraggableElementXPosition() { const draggableElement = document.getElementById("draggable"); const draggableElementRect = draggableElement.getBoundingClientRect(); return draggableElementRect.left + (draggableElementRect.width / 2); } draggableElement.addEventListener("dblclick", () => { // Call your double click handler function here hideInstructionsToHitNumberOfTargets(); // limited number of laser nodes given each stage. _println("===attempt to fire laser node from robot hand==="); const availableBulletIndex = y.findIndex(x => x == 2); allBulletsAreExhausted = y.every(x => x > 2); // only allow bullet to be fired if condition below is true. if (!allBulletsAreExhausted) { _println('are bullets exhausted? '+ allBulletsAreExhausted); _println('=====bullet fires!====='); _println("bullet index: " + availableBulletIndex + " is fired."); if (!isMuted) _view.fireLaserNodeSound.play(); // shoot available bullet! (note: bullet position is in pixel units) x[availableBulletIndex] = getDraggableElementXPosition(); vy[availableBulletIndex] = 600; shouldShowBullets[availableBulletIndex] = true; } // updating remaining laser nodes let currentShownNumberOfLaserNodes = document.getElementById('number-of-remaining-nodes').innerText; if (parseInt(currentShownNumberOfLaserNodes) > 0) { document.getElementById('number-of-remaining-nodes').innerText = `${parseInt(currentShownNumberOfLaserNodes) - 1}`; } }); ]]> CODE_EDITOR alert true false { slideContainer.style.bottom = '-100%'; }); const userAgent = navigator.userAgent; console.log('User-Agent String:', userAgent); // Check if the User-Agent string contains specific keywords if (userAgent.match(/Android/i)) { console.log('This user is using an Android device.'); slideContainer.style.display = 'block'; slideContainer.style.bottom = '0'; } else if (userAgent.match(/iPhone|iPad|iPod/i)) { console.log('This user is using an iOS device (iPhone, iPad, or iPod).'); slideContainer.style.display = 'block'; slideContainer.style.bottom = '0'; } else if (userAgent.match(/Windows/i)) { console.log('This user is using a Windows device.'); } else if (userAgent.match(/Macintosh/i)) { console.log('This user is using a Macintosh (Mac) device.'); } else { console.log('The user agent does not match any known device/OS.'); } ]]> ODE_EDITOR Evol Page true false EVENT_EDITOR bounceFromLeft true false CROSSING_EVENT BISECTION 200 true EVENT_EDITOR bounceFromRight true false CROSSING_EVENT BISECTION 200 true 0 && distanceToRightWall < 0.5) { bounceFromRightIndex = i; // remember index of particle to bounce off wall. return 0; } } return 1.0; ]]> EVENT_EDITOR bounceFromTop true false CROSSING_EVENT BISECTION 200 true 0 && distanceToTopWall < 0.5) { bounceFromTopIndex = i; // remember index of particle to bounce off wall. return 0; } } return 1.0; ]]> EVENT_EDITOR BounceFromBottom true false CROSSING_EVENT BISECTION 200 true EVENT_EDITOR returnFromLeft true false CROSSING_EVENT BISECTION 100 true 0 && (XPos[i] - 1) > boxWidth) { returnFromLeftIndex = i; // remember index of particle to bounce off wall. // _println('particle: '+returnFromLeftIndex+' exited the screen on the right'); return 0; } } return 1.0; ]]> EVENT_EDITOR returnFromRight true false CROSSING_EVENT BISECTION 100 true EVENT_EDITOR returnFromTop true false CROSSING_EVENT BISECTION 100 true EVENT_EDITOR returnFromBottom true false CROSSING_EVENT BISECTION 100 true 0 && (YPos[i] - 2) > boxHeight) { returnFromBottomIndex = i; // _println('particle: '+returnFromBottomIndex+' exited the screen on the top!'); return 0; } } return 1.0; ]]> EVENT_EDITOR automaticallyFireBullets false false CROSSING_EVENT BISECTION 100 true EVENT_EDITOR automaticallyLoopBullets false false STATE_EVENT BISECTION 100 true EVENT_EDITOR disappearedBullets true false STATE_EVENT BISECTION 100 true 15 && shouldShowBullets[i] == true) { // save index of bullet to be hidden hideBulletIndex = i; return 0; } } return 1; ]]> accumulator + (currentValue == true ? 1 : 0), 0); _println('areTargetsAlive array: '); _println(areTargetsAlive); _println("number of cluster targets left: "+numberOfTargetsLeft); if (remainingLaserNodesLeft < numberOfTargetsLeft) { _println("remainingLaserNodesLeft < numberOfTargetsLeft"); document.getElementById('gameScreen').classList.add('add-overlay'); if (gameLevel == 1) { ranOutOfLaserNodes1 = true; } if (gameLevel == 2) { ranOutOfLaserNodes2 = true; } if (gameLevel == 3) { ranOutOfLaserNodes3 = true; } if (gameLevel == 4) { ranOutOfLaserNodes4 = true; } if (gameLevel == 5) { ranOutOfLaserNodes5 = true; } } } ]]> EVENT_EDITOR aimLine false false CROSSING_EVENT BISECTION 100 true bullet == false) && currentPage == "gameScreen") { shouldShowAimLine = true; return 0; } else { shouldShowAimLine = false; return 0; } return 1.0; ]]> EVENT_EDITOR CollisionOfTargetsWithBullets true false CROSSING_EVENT BISECTION 100 true x == false)) { _println('all clusters destroyed'); _println('current game level: '+gameLevel); hideInstructionsToHitNumberOfTargets() _pause(); shouldShowRoboHand = false; shouldShowQuestion = true; } ]]> t dt vy[i] XPosRateOfChange[i] YPosRateOfChange[i] changeInRotationAngle RungeKutta 10000 0.00001 false false false 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 InitialiseTargetObjects true false LIBRARY_EDITOR test true false LIBRARY_EDITOR progressBar true false = 1 && level < 100) { progressLevel.classList.remove("emptybar"); progressLevel.classList.remove("fullbar"); } else { progressLevel.classList.remove("emptybar"); progressLevel.classList.add("fullbar"); } } ]]> LIBRARY_EDITOR randomNumber true false LIBRARY_EDITOR hitNumberOfTargetsInstructions true false to fire!"; instructions.appendChild(howToFireBulletInstruction); shouldShowAnimatedClick = true; } instructions.classList.add('d-visible'); instructions.style.margin = '0'; } function hideInstructionsToHitNumberOfTargets() { let instructions = document.getElementById('stageInstructions'); instructions.classList.remove('d-visible'); shouldShowAnimatedClick = false; } ]]> LIBRARY_EDITOR sound true false { // console.log(voice.name, voice.lang) //}) //debug // Queue this utterance. window.speechSynthesis.speak(msg); } ]]> LIBRARY_EDITOR changeOrientation true false LIBRARY_EDITOR togglefullscreen true false HTML_VIEW_EDITOR HtmlView Page true false 0 0 0 800 600 true Elements.Panel ]]> Elements.Panel ]]> Elements.Panel ]]> Elements.Panel ]]> Elements.Panel false Elements.Panel false Elements.WrappedPanel false Elements.PlottingPanel Elements.Image2D Elements.ImageSet2D false Elements.Group2D Elements.Image2D false Elements.Group2D Elements.ImageSet2D Elements.Image2D x == 2); allBulletsAreExhausted = y.every(x => x > 2); // only allow bullet to be fired if condition below is true. if (!allBulletsAreExhausted) { _println('are bullets exhausted? '+ allBulletsAreExhausted); _println('=====bullet fires!====='); _println("bullet index: " + availableBulletIndex + " is fired."); if (!isMuted) _view.fireLaserNodeSound.play(); // shoot available bullet! x[availableBulletIndex] = xc; vy[availableBulletIndex] = 18; shouldShowBullets[availableBulletIndex] = true; } // updating remaining laser nodes let currentShownNumberOfLaserNodes = document.getElementById('number-of-remaining-nodes').innerText; if (parseInt(currentShownNumberOfLaserNodes) > 0) { document.getElementById('number-of-remaining-nodes').innerText = `${parseInt(currentShownNumberOfLaserNodes) - 1}`; } } ]]> Elements.Image2D false Elements.Panel Elements.Panel false Elements.Panel Elements.Panel A.
A type of social media filter
A red cross that indicates an incorrect answer.]]>
img').classList.add("d-visible"); document.querySelector('#question1 #optionA').classList.add("display-incorrect-option"); // show correct answer document.querySelector('#question1 #optionB > img').classList.add("d-visible"); document.querySelector('#question1 #optionB').classList.add("display-incorrect-option"); updateProgressBar(25); // save answer in memory for updating summary in moodle at the end of the interactive const optionA = "A type of social media filter"; summary["Q1"]["choice"] = optionA; // send score and choice to moodle moodle.startQuestion("Q1"); moodle.addQuestionHistory(optionA); moodle.awardQuestionMarks(1); moodle.endQuestion(); setTimeout(() => { gameLevel += 1; numberOfTargets = 5; // update the HTMLView Element to hold new number of "child" elements initialiseNewStage(numberOfTargets); _play(); } , 4000); // the function MUST be called some time after initialising the cluster of nodes (targets) to // prevent error of not being able to find the new targets in the DOM after initialising them. // therefore, we call the function below 50ms later. // if we call it much later, the user can see the color and type of clusters change mid way // while playing and we don't want that. setTimeout(() => { randomiseTargetClusterImages(); setTimeout(() => { showInstructionsToHitNumberOfTargets(5); }, 1500); setTimeout(() => { hideInstructionsToHitNumberOfTargets(); }, 10750); } , 2050); ]]>
Elements.Panel B.
A manipulated video or image created using artificial intelligence
A green tick that indicates a correct answer.]]>
img').classList.add("d-visible"); // 'cross' logo to indicate incorrect document.querySelector('#question1 #optionB').classList.add("display-incorrect-option"); // blur out background for incorrect answer updateProgressBar(25); // save answer in memory for updating summary in moodle at the end of the interactive const optionB = "A manipulated video or image created using artificial intelligence"; summary["Q1"]["choice"] = optionB; // send score and choice to moodle moodle.startQuestion("Q1"); moodle.addQuestionHistory(optionB); moodle.awardQuestionMarks(2); moodle.endQuestion(); setTimeout(() => { gameLevel += 1; numberOfTargets = 5; // update the HTMLView Element to hold new number of "child" elements initialiseNewStage(numberOfTargets); _play(); } , 4000); // the function MUST be called some time after initialising the cluster of nodes (targets) to // prevent error of not being able to find the new targets in the DOM after initialising them. // therefore, we call the function below 50ms later. // if we call it much later, the user can see the color and type of clusters change mid way // while playing and we don't want that. setTimeout(() => { randomiseTargetClusterImages(); setTimeout(() => { showInstructionsToHitNumberOfTargets(5); }, 1500); setTimeout(() => { hideInstructionsToHitNumberOfTargets(); }, 10750); } , 2050);]]>
Elements.Panel C.
A style of internet meme
A red cross that indicates an incorrect answer. ]]>
img').classList.add("d-visible"); document.querySelector('#question1 #optionC').classList.add("display-incorrect-option"); // show correct answer document.querySelector('#question1 #optionB > img').classList.add("d-visible"); document.querySelector('#question1 #optionB').classList.add("display-correct-option"); updateProgressBar(25); const optionC = "A style of internet meme"; // save answer in memory for updating summary in moodle at the end of the interactive summary["Q1"]["choice"] = optionC; // send score and choice to moodle moodle.startQuestion("Q1"); moodle.addQuestionHistory(optionC); moodle.awardQuestionMarks(1); moodle.endQuestion(); setTimeout(() => { gameLevel += 1; numberOfTargets = 5; // update the HTMLView Element to hold new number of "child" elements initialiseNewStage(numberOfTargets); _play(); } , 4000); // the function MUST be called some time after initialising the cluster of nodes (targets) to // prevent error of not being able to find the new targets in the DOM after initialising them. // therefore, we call the function below 50ms later. // if we call it much later, the user can see the color and type of clusters change mid way // while playing and we don't want that. setTimeout(() => { randomiseTargetClusterImages(); setTimeout(() => { showInstructionsToHitNumberOfTargets(5); }, 1500); setTimeout(() => { hideInstructionsToHitNumberOfTargets(); }, 10750); } , 4050); ]]>
false Elements.Panel Elements.Panel false Elements.Panel Elements.Panel A.
Blockchain technology
A red cross that indicates an incorrect answer. ]]>
img').classList.add("d-visible"); // 'cross' logo to indicate incorrect document.querySelector('#question2 #optionA2').classList.add("display-incorrect-option"); // blur out background for incorrect answer // show correct answer document.querySelector('#question2 #optionC2 > img').classList.add("d-visible");// show correct logo document.querySelector('#question2 #optionC2').classList.add("display-correct-option"); // highlight correct answer updateProgressBar(50); const optionA = "Blockchain technology"; // save answer in memory for updating summary in moodle at the end of the interactive summary["Q2"]["choice"] = optionA; // send score and choice to moodle moodle.startQuestion("Q2"); moodle.addQuestionHistory(optionA); moodle.awardQuestionMarks(1); moodle.endQuestion(); setTimeout(() => { gameLevel += 1; numberOfTargets = 7; // update the HTMLView Element to hold new number of "child" elements initialiseNewStage(numberOfTargets); _play(); } , 4000); // the function MUST be called some time after initialising the cluster of nodes (targets) to // prevent error of not being able to find the new targets in the DOM after initialising them. // therefore, we call the function below 50ms later. // if we call it much later, the user can see the color and type of clusters change mid way // while playing and we don't want that. setTimeout(() => { randomiseTargetClusterImages(); setTimeout(() => { showInstructionsToHitNumberOfTargets(7); }, 1500); setTimeout(() => { hideInstructionsToHitNumberOfTargets(); }, 10750); } , 4050); ]]>
Elements.Panel B.
Quantum Computing
A red cross that indicates an incorrect answer. ]]>
img').classList.add("d-visible"); // 'cross' logo to indicate incorrect document.querySelector('#question2 #optionB2').classList.add("display-incorrect-option"); // blur out background for incorrect answer // show correct answer document.querySelector('#question2 #optionC2 > img').classList.add("d-visible");// show correct logo document.querySelector('#question2 #optionC2').classList.add("display-correct-option"); // highlight correct answer updateProgressBar(50); const optionB = "Quantum Computing"; // save answer in memory for updating summary in moodle at the end of the interactive summary["Q2"]["choice"] = optionB; // send score and choice to moodle moodle.startQuestion("Q2"); moodle.addQuestionHistory(optionB); moodle.awardQuestionMarks(1); moodle.endQuestion(); setTimeout(() => { gameLevel += 1; numberOfTargets = 7; // update the HTMLView Element to hold new number of "child" elements initialiseNewStage(numberOfTargets); _play(); } , 4000); // the function MUST be called some time after initialising the cluster of nodes (targets) to // prevent error of not being able to find the new targets in the DOM after initialising them. // therefore, we call the function below 50ms later. // if we call it much later, the user can see the color and type of clusters change mid way // while playing and we don't want that. setTimeout(() => { randomiseTargetClusterImages(); setTimeout(() => { showInstructionsToHitNumberOfTargets(7); }, 1500); setTimeout(() => { hideInstructionsToHitNumberOfTargets(); }, 10750); } , 4050); ]]>
Elements.Panel C.
Artificial Intelligence
A green tick that indicates a correct answer.]]>
img').classList.add("d-visible");// show correct logo document.querySelector('#question2 #optionC2').classList.add("display-correct-option"); // highlight correct answer updateProgressBar(50); const optionB = "Artificial Intelligence"; // save answer in memory for updating summary in moodle at the end of the interactive summary["Q2"]["choice"] = optionB; // send score and choice to moodle moodle.startQuestion("Q2"); moodle.addQuestionHistory(optionB); moodle.awardQuestionMarks(2); moodle.endQuestion(); setTimeout(() => { gameLevel += 1; numberOfTargets = 7; // update the HTMLView Element to hold new number of "child" elements initialiseNewStage(numberOfTargets); _play(); } , 4000); // the function MUST be called some time after initialising the cluster of nodes (targets) to // prevent error of not being able to find the new targets in the DOM after initialising them. // therefore, we call the function below 50ms later. // if we call it much later, the user can see the color and type of clusters change mid way // while playing and we don't want that. setTimeout(() => { randomiseTargetClusterImages(); setTimeout(() => { showInstructionsToHitNumberOfTargets(7); }, 1500); setTimeout(() => { hideInstructionsToHitNumberOfTargets(); }, 10750); } , 4050); ]]>
false Elements.Panel Elements.Panel false Elements.Panel Elements.Panel A.
Misinformation and fake news
A green tick that indicates a correct answer.]]>
img').classList.add("d-visible"); document.querySelector('#question3 #optionA3').classList.add("display-incorrect-option"); updateProgressBar(75); const optionA = "Misinformation and fake news"; // save answer in memory for updating summary in moodle at the end of the interactive summary["Q3"]["choice"] = optionA; // send score and choice to moodle moodle.startQuestion("Q3"); moodle.addQuestionHistory(optionA); moodle.awardQuestionMarks(2); moodle.endQuestion(); setTimeout(() => { gameLevel += 1; numberOfTargets = 9; // update the HTMLView Element to hold new number of "child" elements initialiseNewStage(numberOfTargets); _play(); } , 4000); // the function MUST be called some time after initialising the cluster of nodes (targets) to // prevent error of not being able to find the new targets in the DOM after initialising them. // therefore, we call the function below 50ms later. // if we call it much later, the user can see the color and type of clusters change mid way // while playing and we don't want that. setTimeout(() => { randomiseTargetClusterImages(); setTimeout(() => { showInstructionsToHitNumberOfTargets(9); }, 1500); setTimeout(() => { hideInstructionsToHitNumberOfTargets(); }, 10750); } , 4050); ]]>
Elements.Panel B.
Enhanced security measures
A red cross that indicates an incorrect answer. ]]>
img').classList.add("d-visible"); // 'cross' logo to indicate incorrect document.querySelector('#question3 #optionB3').classList.add("display-incorrect-option"); // blur out background for incorrect answer // show correct answer document.querySelector('#question3 #optionA3 > img').classList.add("d-visible");// show correct logo document.querySelector('#question3 #optionA3').classList.add("display-correct-option"); // highlight correct answer updateProgressBar(75); const optionB = "Enhanced security measures"; // save answer in memory for updating summary in moodle at the end of the interactive summary["Q3"]["choice"] = optionB; // send score and choice to moodle moodle.startQuestion("Q3"); moodle.addQuestionHistory(optionB); moodle.awardQuestionMarks(1); moodle.endQuestion(); setTimeout(() => { gameLevel += 1; numberOfTargets = 9; // update the HTMLView Element to hold new number of "child" elements initialiseNewStage(numberOfTargets); _play(); } , 4000); // the function MUST be called some time after initialising the cluster of nodes (targets) to // prevent error of not being able to find the new targets in the DOM after initialising them. // therefore, we call the function below 50ms later. // if we call it much later, the user can see the color and type of clusters change mid way // while playing and we don't want that. setTimeout(() => { randomiseTargetClusterImages(); setTimeout(() => { showInstructionsToHitNumberOfTargets(9); }, 1500); setTimeout(() => { hideInstructionsToHitNumberOfTargets(); }, 10750); } , 4050); ]]>
Elements.Panel C.
Improved video editing techniques
A red cross that indicates an incorrect answer. ]]>
img').classList.add("d-visible"); // 'cross' logo to indicate incorrect document.querySelector('#question3 #optionC3').classList.add("display-incorrect-option"); // blur out background for incorrect answer // show correct answer document.querySelector('#question3 #optionA3 > img').classList.add("d-visible");// show correct logo document.querySelector('#question3 #optionA3').classList.add("display-correct-option"); // highlight correct answer updateProgressBar(75); const optionC = "Improved video editing techniques"; // save answer in memory for updating summary in moodle at the end of the interactive summary["Q3"]["choice"] = optionC; // send score and choice to moodle moodle.startQuestion("Q3"); moodle.addQuestionHistory(optionC); moodle.awardQuestionMarks(1); moodle.endQuestion(); setTimeout(() => { gameLevel += 1; numberOfTargets = 9; // update the HTMLView Element to hold new number of "child" elements initialiseNewStage(numberOfTargets); _play(); } , 4000); // the function MUST be called some time after initialising the cluster of nodes (targets) to // prevent error of not being able to find the new targets in the DOM after initialising them. // therefore, we call the function below 50ms later. // if we call it much later, the user can see the color and type of clusters change mid way // while playing and we don't want that. setTimeout(() => { randomiseTargetClusterImages(); setTimeout(() => { showInstructionsToHitNumberOfTargets(9); }, 1500); setTimeout(() => { hideInstructionsToHitNumberOfTargets(); }, 10750); } , 4050); ]]>
false Elements.Panel Elements.Panel false Elements.Panel Elements.Panel A.
By analyzing the body language and facial expressions of the subjects
A red cross that indicates an incorrect answer. ]]>
img').classList.add("d-visible"); document.querySelector('#question4 #optionA4').classList.add("display-incorrect-option"); // show correct answer document.querySelector('#question4 #optionC4 > img').classList.add("d-visible");// show correct logo document.querySelector('#question4 #optionC4').classList.add("display-correct-option"); // highlight correct answer updateProgressBar(100); const optionA = "By analyzing the body language and facial expressions of the subjects"; // save answer in memory for updating summary in moodle at the end of the interactive summary["Q4"]["choice"] = optionA; // send score and choice to moodle moodle.startQuestion("Q4"); moodle.addQuestionHistory(optionA); moodle.awardQuestionMarks(1); moodle.endQuestion(); generateSummaryListOnMoodle(); setTimeout(() => { gameLevel += 1; numberOfTargets = 12; // update the HTMLView Element to hold new number of "child" elements initialiseNewStage(numberOfTargets); _play(); } , 4000); // the function MUST be called some time after initialising the cluster of nodes (targets) to // prevent error of not being able to find the new targets in the DOM after initialising them. // therefore, we call the function below 50ms later. // if we call it much later, the user can see the color and type of clusters change mid way // while playing and we don't want that. setTimeout(() => { shouldShowRoboHand = false; shouldShowQuestion = false; shouldShowAimLine = false; // currentPage = "gameEnd"; document.getElementById('question5').style.display = "none"; document.getElementById('gameEnd').style.display = "block"; _view.backgroundMusic.pause(); } , 4000); function generateSummaryListOnMoodle() { let summaryList = "Chosen answers:\n"; for (const question in summary) { summaryList += `${question}: ${summary[question]['choice']}\n`; } // send the summary list results to moodle moodle.resetQuestionHistory("Summary"); moodle.questionInstantMark("Summary", summaryList); } ]]>
Elements.Panel B.
By checking the source and credibility of the content
A red cross that indicates an incorrect answer.]]>
img').classList.add("d-visible"); document.querySelector('#question4 #optionB4').classList.add("display-incorrect-option"); // show correct answer document.querySelector('#question4 #optionC4 > img').classList.add("d-visible");// show correct logo document.querySelector('#question4 #optionC4').classList.add("display-correct-option"); // highlight correct answer updateProgressBar(100); const optionB = "By checking the source and credibility of the content"; // save answer in memory for updating summary in moodle at the end of the interactive summary["Q4"]["choice"] = optionB; // send score and choice to moodle moodle.startQuestion("Q4"); moodle.addQuestionHistory(optionB); moodle.awardQuestionMarks(1); moodle.endQuestion(); generateSummaryListOnMoodle(); setTimeout(() => { shouldShowRoboHand = false; shouldShowQuestion = false; shouldShowAimLine = false; // currentPage = "gameEnd"; document.getElementById('question5').style.display = "none"; document.getElementById('gameEnd').style.display = "block"; _view.backgroundMusic.pause(); } , 4000); function generateSummaryListOnMoodle() { let summaryList = "Chosen answers:\n"; for (const question in summary) { summaryList += `${question}: ${summary[question]['choice']}\n`; } // send the summary list results to moodle moodle.resetQuestionHistory("Summary"); moodle.questionInstantMark("Summary", summaryList); } ]]>
Elements.Panel C.
By looking for subtle visual anomalies or inconsistencies
A green tick that indicates a correct answer.]]>
img').classList.add("d-visible");// show correct logo document.querySelector('#question4 #optionC4').classList.add("display-correct-option"); // highlight correct answer updateProgressBar(100); const optionC = "By looking for subtle visual anomalies or inconsistencies"; // save answer in memory for updating summary in moodle at the end of the interactive summary["Q4"]["choice"] = optionC; // send score and choice to moodle moodle.startQuestion("Q4"); moodle.addQuestionHistory(optionC); moodle.awardQuestionMarks(2); moodle.endQuestion(); generateSummaryListOnMoodle(); setTimeout(() => { shouldShowRoboHand = false; shouldShowQuestion = false; shouldShowAimLine = false; // currentPage = "gameEnd"; document.getElementById('question5').style.display = "none"; document.getElementById('gameEnd').style.display = "block"; _view.backgroundMusic.pause(); } , 4000); function generateSummaryListOnMoodle() { let summaryList = "Chosen answers:\n"; for (const question in summary) { summaryList += `${question}: ${summary[question]['choice']}\n`; } // send the summary list results to moodle moodle.resetQuestionHistory("Summary"); moodle.questionInstantMark("Summary", summaryList); } ]]>
false Elements.Panel Elements.Panel false Elements.Panel Elements.Panel A.
Avoid sharing videos or images from unknown sources
A green tick that indicates a correct answer. ]]>
img').classList.add("d-visible"); document.querySelector('#question5 #optionA5').classList.add("display-incorrect-option"); // blur out background for incorrect answer updateProgressBar(100); const optionA = "Avoid sharing videos or images from unknown sources"; // save answer in memory for updating summary in moodle at the end of the interactive summary["Q5"]["choice"] = optionA; // send score and choice to moodle moodle.startQuestion("Q5"); moodle.addQuestionHistory(optionA); moodle.awardQuestionMarks(2); moodle.endQuestion(); generateSummaryListOnMoodle(); setTimeout(() => { shouldShowRoboHand = false; shouldShowQuestion = false; shouldShowAimLine = false; // currentPage = "gameEnd"; document.getElementById('question5').style.display = "none"; document.getElementById('gameEnd').style.display = "block"; _view.backgroundMusic.pause(); } , 4000); function generateSummaryListOnMoodle() { let summaryList = "Chosen answers:\n"; for (const question in summary) { summaryList += `${question}: ${summary[question]['choice']}\n`; } // send the summary list results to moodle moodle.resetQuestionHistory("Summary"); moodle.questionInstantMark("Summary", summaryList); } ]]>
Elements.Panel B.
Enhance your own video editing skills
A red cross that indicates an incorrect answer. ]]>
img').classList.add("d-visible"); document.querySelector('#question5 #optionB5').classList.add("display-incorrect-option"); // blur out background for incorrect answer // show correct answer document.querySelector('#question5 #optionA5 > img').classList.add("d-visible");// show correct logo document.querySelector('#question5 #optionA5').classList.add("display-correct-option"); // highlight correct answer updateProgressBar(100); const optionB = "Enhance your own video editing skills"; // save answer in memory for updating summary in moodle at the end of the interactive summary["Q5"]["choice"] = optionB; // send score and choice to moodle moodle.startQuestion("Q5"); moodle.addQuestionHistory(optionB); moodle.awardQuestionMarks(1); moodle.endQuestion(); generateSummaryListOnMoodle(); setTimeout(() => { shouldShowRoboHand = false; shouldShowQuestion = false; shouldShowAimLine = false; // currentPage = "gameEnd"; document.getElementById('question5').style.display = "none"; document.getElementById('gameEnd').style.display = "block"; _view.backgroundMusic.pause(); } , 4000); function generateSummaryListOnMoodle() { let summaryList = "Chosen answers:\n"; for (const question in summary) { summaryList += `${question}: ${summary[question]['choice']}\n`; } // send the summary list results to moodle moodle.resetQuestionHistory("Summary"); moodle.questionInstantMark("Summary", summaryList); } ]]>
Elements.Panel C.
Share deepfake content to raise awareness
A red cross that indicates an incorrect answer. ]]>
img').classList.add("d-visible"); document.querySelector('#question5 #optionC5').classList.add("display-incorrect-option"); // blur out background for incorrect answer // show correct answer document.querySelector('#question5 #optionA5 > img').classList.add("d-visible");// show correct logo document.querySelector('#question5 #optionA5').classList.add("display-correct-option"); // highlight correct answer updateProgressBar(100); const optionC = "Share deepfake content to raise awareness"; // save answer in memory for updating summary in moodle at the end of the interactive summary["Q5"]["choice"] = optionC; // send score and choice to moodle moodle.startQuestion("Q5"); moodle.addQuestionHistory(optionB); moodle.awardQuestionMarks(1); moodle.endQuestion(); generateSummaryListOnMoodle(); setTimeout(() => { shouldShowRoboHand = false; shouldShowQuestion = false; shouldShowAimLine = false; // currentPage = "gameEnd"; document.getElementById('question5').style.display = "none"; document.getElementById('gameEnd').style.display = "block"; _view.backgroundMusic.pause(); } , 4000); function generateSummaryListOnMoodle() { let summaryList = "Chosen answers:\n"; for (const question in summary) { summaryList += `${question}: ${summary[question]['choice']}\n`; } // send the summary list results to moodle moodle.resetQuestionHistory("Summary"); moodle.questionInstantMark("Summary", summaryList); } ]]>
Elements.Panel
progress checkmark
progress checkmark
progress checkmark
]]>
Elements.Panel ]]> Elements.Panel Elements.Audio Elements.Audio Elements.Audio Elements.Panel ]]> Elements.Panel ]]> Elements.Panel Elements.Panel ]]> Elements.Panel

For an enhanced experience, using a laptop is recommended.

]]>
Elements.Panel ]]> Elements.Panel ]]> Elements.Panel ]]> Elements.Panel ]]> Elements.Panel ]]>