236 lines
8.1 KiB
JavaScript
236 lines
8.1 KiB
JavaScript
// @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 <https://www.gnu.org/licenses/>.
|
|
*
|
|
* E-mail contact: <luca0n@luca0n.com>.
|
|
*/
|
|
|
|
/*
|
|
* 2021-05-27
|
|
* by luca0N! <https://www.luca0n.com>
|
|
*/
|
|
|
|
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
|