// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-3.0-or-later /* * Stairway.js: a simple JavaScript privacy quiz * Copyright © 2021 luca0N! * * This file is part of Stairway.js. * * Stairway.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Stairway.js is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Stairway.js. If not, see . * * E-mail contact: . */ /* * 2021-05-27 * by luca0N! */ let quiz, quizEl, mappedIds = []; // Attach setUpStrings() to onReady() in order to execute the main code as soon as possible. This overwrites onReady(). onReady = setUpStrings; /** * Replaces dummy strings with localized messages. * @since 2021-05-27 */ function setUpStrings(){ // Hide JavaScript warning message. document.getElementById('javascript-warning').style['display'] = 'none'; let warning = document.getElementById('warning'); warning.style['display'] = 'unset'; // Get JSON file which contains all of the challenges required. // FIXME: remove hardcoded quiz name. sendGetRequest('sample.en.json', function(text){ quiz = JSON.parse(text); // Set up UI elements based on quiz metadata. let quizTitle = document.getElementById('quiz-title'); quizTitle.innerText = quiz.title; quizTitle.style['display'] = 'unset'; let quizAuthor = document.getElementById('quiz-author'); quizAuthor.innerText = quiz.publisher; quizAuthor.style['display'] = 'unset'; let contents = quiz.contents; quizEl = document.getElementById('quiz'); // quiz div element. for (let x = 0; x < contents.length; x++){ //console.log(contents[x].title); let itemDiv = document.createElement('div'); itemDiv.className = 'section-question'; let itemTitle = document.createElement('p'); itemTitle.innerText = contents[x].title; itemTitle.className = 'question'; itemDiv.appendChild(itemTitle); mappedIds[x] = []; if (contents[x].type === 'multiple' || contents[x].type === 'single'){ // Generate a checkbox for each choice. for (let y = 0; y < contents[x].choices.length; y++){ if (contents[x].choices.length != contents[x].ids.length){ console.error('Unable to add choice #' + (y + 1) + ' for question #' + (x + 1) + ': ' + contents[x].choices.length > contents[x].ids.length ? 'missing ID(s)' : 'extra ID(s) found'); continue; } let questionInput = document.createElement('input'); questionInput.type = contents[x].type === 'multiple' ? 'checkbox' : 'radio'; if (contents[x].type === 'single') questionInput.name = 'sec.' + x + '.chg'; // choice group let currentId = 'sec.' + x + '.ch.' + y; // section, choice questionInput.id = currentId; let questionLabel = document.createElement('label'); questionLabel.htmlFor = currentId; questionLabel.innerText = contents[x].choices[y]; mappedIds[x][y] = contents[x].ids[y]; itemDiv.append(questionInput); itemDiv.append(questionLabel); if (y < contents[x].choices.length) itemDiv.append(document.createElement('br')); // Add a line break if this is not the last question. } if (contents[x].type === 'single'){ // This is a 'single' choice type question. // Add a button that clears the current question selection. let questionClear = document.createElement('button'); questionClear.innerText = 'Clear'; questionClear.onclick = function() { for (let y = 0; y < contents[x].choices.length; y++) // Iterate through all input tags. document.getElementById('sec.' + x + '.ch.' + y).checked = false; // Uncheck it. } itemDiv.append(questionClear); // Append the button to the item dit. } } else console.warn('Warning: unknown type "' + contents[x].type + '" for question #' + (x + 1)); quizEl.appendChild(itemDiv); } // Add the finish button. let finish = document.createElement('button'); finish.innerText = 'Finish!'; finish.onclick = function(){ // Calculate the results. let score = 0; // Retrieve choices. let contents = quiz.contents; let choices = []; for (let x = 0; x < contents.length; x++){ choices[x] = []; for (let y = 0; y < contents[x].choices.length; y++){ let currentEl = document.getElementById('sec.' + x + '.ch.' + y); // current choice element // Is it checked? if (currentEl.checked){ // Get the ID choices[x].push(mappedIds[x][y]); score += quiz.reports[mappedIds[x][y]].points; } } } // Calculate the grade let grade; if (score > quiz.threshold['a.min']) grade = 's'; else if (score > quiz.threshold['b.min']) grade = 'a' else if (score > quiz.threshold['c.min']) grade = 'b' else if (score > quiz.threshold['d.min']) grade = 'c' else if (score > quiz.threshold['e.min']) grade = 'd' else if (score > quiz.threshold['b.min']) grade = 'e'; else grade = 'f'; // Display the grade X badge element (unset its 'none' display value) document.getElementById('badge-' + grade).style['display'] = 'unset'; // Generate report elements. let reports = document.getElementById('reports'); // Clear the reports element. reports.innerHTML = ''; for (let x = 0; x < choices.length; x++) for (let y = 0; y < choices[x].length; y++){ let report = quiz.reports[choices[x][y]]; let reportEl = document.createElement('div'); reportEl.className = 'section-report section-report-' + report.type; let title = document.createElement('p'); title.className = 'report-title'; // Create the report type span. let type = document.createElement('span'); // Make sure the first letter is uppercase. let typeName = report.type[0].toUpperCase(); typeName += report.type.substring(1); type.innerText = typeName; type.className = 'report-type report-' + report.type; title.append(type); let titleSpan = document.createElement('span'); titleSpan.innerText = report.title; title.appendChild(titleSpan); reportEl.appendChild(title); type.after(' '); // add a whitespace after this span so it doesn't look too close to the report title. // For each line break in the report description, // create a separate paragraph. let paragraphs = report.description.split('\n'); for (let z = 0; z < paragraphs.length; z++){ let p = document.createElement('p'); p.innerText = paragraphs[z]; reportEl.appendChild(p); } // Add an advisory paragraph if it exists. if (report.advisory != null && report.advisory != undefined){ let p = document.createElement('p'); p.innerText = report.advisory; p.className = 'advisory'; reportEl.appendChild(p); } reports.appendChild(reportEl); } // Set the grade message (if it exists. let gme = document.getElementById('grade-message'); // Grade Message Element // Check if this quiz contains custom grade messages. if (quiz.gradeMessages != undefined && quiz.gradeMessages != null){ let gmt, gmto = quiz.gradeMessages['grade-' + grade]; // Check if there is a grade message for the current grade. if (gmto !== undefined && gmto !== null){ gmt = gmto.split('\n'); for (let i = 0; i < gmt.length; i++){ let line = gmt[i]; let p = document.createElement('p'); p.innerText = line; gme.appendChild(p); } } } // Hide quiz and then show the results. quizEl.style['display'] = 'none'; document.getElementById('results').style['display'] = 'unset'; // Jump to the top of the results. document.location = '#results'; } quizEl.appendChild(finish); warning.style['display'] = 'none'; }); } // @license-end