Merge branch 'master' of https://git.xkjq.uk/ross/rts
This commit is contained in:
Vendored
+6
-3
@@ -1,8 +1,11 @@
|
||||
{
|
||||
"editor.autoIndent": "keep",
|
||||
"javascript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": false,
|
||||
"javascript.format.insertSpaceAfterKeywordsInControlFlowStatements": false,
|
||||
"javascript.format.insertSpaceAfterKeywordsInControlFlowStatements": true,
|
||||
"javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false,
|
||||
"javascript.format.enable": false,
|
||||
"liveServer.settings.port": 5502
|
||||
"javascript.format.enable": true,
|
||||
"liveServer.settings.port": 5502,
|
||||
"javascript.format.insertSpaceAfterConstructor": true,
|
||||
"javascript.format.insertSpaceBeforeFunctionParenthesis": false,
|
||||
"javascript.format.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces": false
|
||||
}
|
||||
+9
-11
@@ -17,15 +17,12 @@
|
||||
<div id="content">
|
||||
<div class="content-panel">
|
||||
<div class="nav-bar">
|
||||
<button id="review-button" class="navigation nav-right"><b>☰</b></button>
|
||||
<button id="nav-submit-button" class="submit-button navigation nav-right"><b>submit</b></button>
|
||||
<!--<button id="options-button" class="navigation nav-right"
|
||||
title="click to load new packet or manage saved answers">options</button>-->
|
||||
<button id="review-overlay-button" class="navigation nav-right"
|
||||
<button id="btn-review" class="navigation nav-right"><b>☰</b></button>
|
||||
<button id="btn-submit-nav" class="submit-button navigation nav-right"><b>submit</b></button>
|
||||
<button id="btn-review-overlay" class="navigation nav-right"
|
||||
title="click to finish exam and review answers">review</button>
|
||||
<button id="fullscreen-overlay-button" class="navigation nav-right"
|
||||
<button id="btn-fullscreen-overlay" class="navigation nav-right"
|
||||
title="click to toggle fullscreen">fullscreen</button>
|
||||
<!--<button id="logout-button" class="navigation nav-right">logout</button>-->
|
||||
<div class="app-name nav-left">RTS</div>
|
||||
<div id="timer" title="click to pause"></div>
|
||||
<div class="exam-name" title="currently loaded packet"></div>
|
||||
@@ -161,7 +158,7 @@
|
||||
<div class="dialog-text">
|
||||
Allocated time has ended. You can review or continue.
|
||||
</div>
|
||||
<button id="time-up-review-button" class="navigation dialog-review">Review</button>
|
||||
<button id="btn-time-up-review" class="navigation dialog-review">Review</button>
|
||||
<button id="time-up-continue-button" class="navigation dialog-yes">Continue</button>
|
||||
<button id="dialog-submit-button" class="submit-button navigation dialog-review hidden"><b>submit</b></button>
|
||||
</div>
|
||||
@@ -172,7 +169,7 @@
|
||||
Candidate number: <input type="number" id="candidate-number" name="candidate" required size="10"><br />
|
||||
Passcode: <input type="text" id="passcode" name="passcode" required size="10"><br />
|
||||
</div>
|
||||
<button id="login-button" class="navigation dialog-yes">Login</button>
|
||||
<button id="btn-login" class="navigation dialog-yes">Login</button>
|
||||
</div>
|
||||
|
||||
<div id="loading" class="fullscreen-overlay">
|
||||
@@ -210,16 +207,17 @@
|
||||
<div id="database-error" class="fullscreen-overlay">
|
||||
</div>
|
||||
|
||||
<script src="lib/loglevel.min.js"></script>
|
||||
<script src="lib/uuidv4.min.js" type="module"></script>
|
||||
<script src="lib/jquery-3.4.1.min.js" type="text/javascript"></script>
|
||||
<script src="lib/jq-ajax-progress.min.js" type="text/javascript"></script>
|
||||
<script src="lib/easytimer.min.js" type="module"></script>
|
||||
<script src="lib/hammer.js"></script>
|
||||
<script src="https://unpkg.com/cornerstone-core/dist/cornerstone.js"></script>
|
||||
<script src="lib/cornerstone.js"></script>
|
||||
<script src="lib/dicomParser.min.js"></script>
|
||||
<script src="lib/cornerstoneMath.min.js"></script>
|
||||
<!-- <script src="lib/cornerstoneTools.min.js"></script> -->
|
||||
<script src="https://www.unpkg.com/cornerstone-tools@5.2.0/dist/cornerstoneTools.js"></script>
|
||||
<script src="lib/cornerstoneTools.js"></script>
|
||||
<script src="lib/cornerstoneWebImageLoader.min.js"></script>
|
||||
<script src="lib/cornerstoneWADOImageLoader.js"></script>
|
||||
<script src="lib/cornerstone-base64-image-loader.umd.js"></script>
|
||||
|
||||
+3
-3
@@ -138,7 +138,7 @@ export function submissionError(data, answer_json, exam_details) {
|
||||
)}</p></span></div>`
|
||||
);
|
||||
|
||||
$("#nav-submit-button").prependTo("#submit-error-overlay");
|
||||
$("#btn-submit-nav").prependTo("#submit-error-overlay");
|
||||
|
||||
$("#submit-error-overlay").height(docHeight);
|
||||
|
||||
@@ -180,11 +180,11 @@ export function getQuestion(url, question_number, question_total) {
|
||||
});
|
||||
}
|
||||
|
||||
export function postSavedAnswer(type, qid, answer, e, db_object, db) {
|
||||
export function postSavedAnswer(type, qid, answer, e, db_object, db, submit_url) {
|
||||
console.debug("post", type, qid, answer, e)
|
||||
return $.ajax({
|
||||
type: "POST",
|
||||
url: config.question_answer_submit_url,
|
||||
url: submit_url,
|
||||
data: JSON.stringify({
|
||||
qid: `${type}/${qid}`,
|
||||
answer: answer,
|
||||
|
||||
+184
-141
@@ -5,6 +5,37 @@ import * as interact from "./interact.js";
|
||||
import * as config from "./config.js";
|
||||
// const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
log.setDefaultLevel("warn")
|
||||
|
||||
|
||||
let URLS = {};
|
||||
|
||||
// Generate urls base on base_url
|
||||
if (config.base_url != "" && config.base_url != undefined) {
|
||||
URLS.base_url = config.base_url;
|
||||
URLS.exam_query_url = config.base_url + "exam/json";
|
||||
URLS.exam_submit_url = config.base_url + "exam/submit";
|
||||
URLS.exam_results_url = config.base_url + "cid/";
|
||||
URLS.question_feedback_url = config.base_url + "feedback/";
|
||||
URLS.question_answer_submit_url = config.base_url + "feedback/answer";
|
||||
URLS.login_url = config.base_url + "accounts/login/?next=/rts";
|
||||
URLS.logout_url = config.base_url + "accounts/logout/?next=/rts";
|
||||
}
|
||||
|
||||
// Override individual urls
|
||||
let urls_to_check = ["exam_query_url", "exam_submit_url",
|
||||
"exam_results_url", "question_feedback_url",
|
||||
"quetion_answer_submit_url", "login_url", "logout_url"]
|
||||
|
||||
for (let url of urls_to_check) {
|
||||
if (config[url] != "" && config[url] != undefined) {
|
||||
URLS[url] = config[url];
|
||||
}
|
||||
}
|
||||
|
||||
log.debug(URLS)
|
||||
|
||||
|
||||
let exam_details = {
|
||||
aid: null,
|
||||
cid: "",
|
||||
@@ -80,18 +111,13 @@ question_db.version(1).stores({
|
||||
saved_exams: "&eid, type, exam_mode, name, order, time, exam_json_id",
|
||||
});
|
||||
|
||||
const user_db = new Dexie("user_database");
|
||||
user_db.version(1).stores({
|
||||
user: "cid, passcode",
|
||||
});
|
||||
|
||||
retrievePacketList();
|
||||
|
||||
try {
|
||||
refreshDatabaseSettings();
|
||||
refreshDatabaseSettings();
|
||||
}
|
||||
catch {
|
||||
console.log("unable to check database settings")
|
||||
log.warn("unable to check database settings")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,42 +130,42 @@ console.log("unable to check database settings")
|
||||
async function retrievePacketList() {
|
||||
let url = "packets/packets.json";
|
||||
|
||||
//console.debug(config.exam_query_url);
|
||||
let users = await user_db.user.toArray();
|
||||
console.debug(users)
|
||||
log.debug(`Load users from database...`)
|
||||
//let users = await user_db.user.toArray();
|
||||
//log.debug(`available users "${users}"`)
|
||||
let cid = localStorage.getItem("cid.cid")
|
||||
let passcode = localStorage.getItem("cid.passcode")
|
||||
|
||||
if (users.length > 0) {
|
||||
global_cid = users[0].cid;
|
||||
global_passcode = users[0].passcode;
|
||||
|
||||
//$(".exam-wrapper").toggle();
|
||||
if (cid != null && passcode != null) {
|
||||
global_cid = cid;
|
||||
global_passcode = passcode;
|
||||
|
||||
let logout = $("<span>[x]</span>")
|
||||
logout.click(() => {
|
||||
user_db.user.clear().then(() => {
|
||||
window.location = window.location.pathname;
|
||||
return;
|
||||
});
|
||||
localStorage.removeItem("cid.cid");
|
||||
localStorage.removeItem("cid.passcode");
|
||||
window.location = window.location.pathname;
|
||||
})
|
||||
|
||||
if (config.exam_results_url != "" && config.exam_results_url != undefined) {
|
||||
let url = `${config.exam_results_url}${global_cid}/${global_passcode}`;
|
||||
if (URLS.exam_results_url != "" && URLS.exam_results_url != undefined) {
|
||||
let url = `${URLS.exam_results_url}${global_cid}/${global_passcode}`;
|
||||
log.debug(`Setting exam results url to ${URLS.exam_results_url}`);
|
||||
|
||||
$("#options-link")
|
||||
.empty()
|
||||
.append(
|
||||
`<a href="${url}" target="_blank"><div class="packet-button">Results and answers</div></a>`
|
||||
);
|
||||
} else {
|
||||
log.debug("No exams results url set in config.");
|
||||
}
|
||||
|
||||
$("#candidate-details").append(`Current: CID ${global_cid} / Passcode ${global_passcode} `).append(logout);
|
||||
} else {
|
||||
|
||||
|
||||
log.debug("Try and set CID / Passcode from current URL")
|
||||
let items = window.location.search.substr(1).split("&");
|
||||
if (items.length == 2) {
|
||||
let cid = "";
|
||||
let passcode = "";
|
||||
for (let index = 0; index < items.length; index++) {
|
||||
const item = items[index];
|
||||
let s = item.split("=");
|
||||
@@ -152,52 +178,55 @@ async function retrievePacketList() {
|
||||
}
|
||||
|
||||
};
|
||||
console.debug(cid, passcode)
|
||||
|
||||
user_db.user.add({
|
||||
cid: cid,
|
||||
passcode: passcode
|
||||
})
|
||||
log.debug("CID and passcode extracted from URL", cid, passcode)
|
||||
log.debug("adding to local db")
|
||||
localStorage.setItem("cid.cid", cid)
|
||||
localStorage.setItem("cid.passcode", passcode)
|
||||
log.debug("reload page")
|
||||
location.reload();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
if (config.exam_query_url != undefined) {
|
||||
log.debug("Try to set exam query url")
|
||||
if (URLS.exam_query_url != undefined) {
|
||||
|
||||
if (global_cid == null) {
|
||||
url = config.exam_query_url;
|
||||
url = URLS.exam_query_url;
|
||||
} else {
|
||||
url = `${config.exam_query_url}/${global_cid}/${global_passcode}`;
|
||||
url = `${URLS.exam_query_url}/${global_cid}/${global_passcode}`;
|
||||
}
|
||||
}
|
||||
log.debug(`url: {url}`)
|
||||
} catch (e) {
|
||||
//
|
||||
log.debug("Unable to set exam query url", e)
|
||||
log.debug("this will default to packets/packets.json")
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
cache: false,
|
||||
url: url,
|
||||
success: function(data) {
|
||||
if (data.hasOwnProperty("exams")) {
|
||||
loadExamList(data);
|
||||
} else {
|
||||
loadPacketList(data);
|
||||
}
|
||||
},
|
||||
error: function(httpObj, textStatus) {
|
||||
console.debug(httpObj);
|
||||
if (httpObj.status == 401 || httpObj.status == 404) {
|
||||
$.notify("Unable to login", "error");
|
||||
$("#options-panel").show();
|
||||
$("#candidate-details").addClass("invalid-login");
|
||||
}
|
||||
},
|
||||
})
|
||||
dataType: "json",
|
||||
cache: false,
|
||||
url: url,
|
||||
success: function(data) {
|
||||
if (data.hasOwnProperty("exams")) {
|
||||
loadExamList(data);
|
||||
} else {
|
||||
loadPacketList(data);
|
||||
}
|
||||
},
|
||||
error: function(httpObj, textStatus) {
|
||||
console.debug(httpObj);
|
||||
if (httpObj.status == 401 || httpObj.status == 404) {
|
||||
$.notify("Unable to login", "error");
|
||||
$("#options-panel").show();
|
||||
$("#candidate-details").addClass("invalid-login");
|
||||
}
|
||||
},
|
||||
})
|
||||
.done(function() {})
|
||||
.fail(function() {
|
||||
log.debug("Unable to load packets / exams, try loading '/packets' (legacy)")
|
||||
$.getJSON("packets", function(data) {
|
||||
if (data.hasOwnProperty("exams")) {
|
||||
loadExamList(data);
|
||||
@@ -205,8 +234,7 @@ async function retrievePacketList() {
|
||||
loadPacketList(data);
|
||||
}
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
console.debug("No packet list available");
|
||||
//showLoginDialog();
|
||||
console.warn("No packet list available");
|
||||
});
|
||||
})
|
||||
.always(function() {});
|
||||
@@ -219,8 +247,10 @@ async function retrievePacketList() {
|
||||
* @param {JSON} data - json containing available exams
|
||||
*/
|
||||
async function loadExamList(data) {
|
||||
log.debug(`Load exam list: ${data}`)
|
||||
log.debug("Loading saved sessions")
|
||||
let sessions = await db.session.toArray().catch(function(error) {
|
||||
console.debug("Error loading session", error);
|
||||
log.debug("Error loading session", error);
|
||||
$("#database-error").text(
|
||||
"Error loading the database, schema has probably changed and needs updating. You will probably need to delete the local database."
|
||||
);
|
||||
@@ -233,8 +263,11 @@ async function loadExamList(data) {
|
||||
$("#database-error").append(delete_button);
|
||||
$("#database-error").show();
|
||||
});
|
||||
log.debug(sessions)
|
||||
|
||||
//Display user info if it exists
|
||||
// If a user is loggged into the server it any exam responses
|
||||
// should have the details embedded
|
||||
if (data.hasOwnProperty("user") && data.user) {
|
||||
$("#user").append(`User: ${data.user}`);
|
||||
//$(".exam-wrapper").show();
|
||||
@@ -250,10 +283,9 @@ async function loadExamList(data) {
|
||||
|
||||
let logout = $("<span>[x]</span>")
|
||||
logout.click(() => {
|
||||
user_db.user.clear().then(() => {
|
||||
window.location = "/accounts/logout/";
|
||||
return;
|
||||
});
|
||||
localStorage.removeItem("cid.cid");
|
||||
localStorage.removeItem("cid.passcode");
|
||||
window.location = URLS.logout_url;
|
||||
})
|
||||
$("#user").append(logout);
|
||||
}
|
||||
@@ -261,75 +293,91 @@ async function loadExamList(data) {
|
||||
|
||||
let exams_started = [];
|
||||
let exams_completed = [];
|
||||
sessions.forEach((s) => {
|
||||
log.debug("Check status of saved exams")
|
||||
for (const s of sessions) {
|
||||
log.debug(`checking status of`, s)
|
||||
if (s.status == "active") {
|
||||
exams_started.push(s.packet);
|
||||
} else {
|
||||
exams_completed.push(s.packet);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Wouldn't we be better terminating ealier in this situation
|
||||
let exam_list = [];
|
||||
if (data != null) {
|
||||
exam_list = data.exams;
|
||||
}
|
||||
|
||||
// exam_generated_map stores the exam and its json ID to
|
||||
// enable invalidation when the question is updated on the server
|
||||
let exam_generated_map = {};
|
||||
$("#packet-list").empty();
|
||||
//exam_list.sort((a, b) => (a.name > b.name) ? 1 : -1).forEach(function (exam) {
|
||||
for (let index = 0; index < exam_list.length; index++) {
|
||||
const exam = exam_list[index];
|
||||
console.debug(exam)
|
||||
let name = exam["name"];
|
||||
let url = exam["url"];
|
||||
let eid = exam["eid"];
|
||||
let exam_json_id = exam["exam_json_id"];
|
||||
|
||||
log.debug("Loop available exams")
|
||||
for (const exam of exam_list) {
|
||||
log.debug(exam)
|
||||
const name = exam["name"];
|
||||
const url = exam["url"];
|
||||
const eid = exam["eid"];
|
||||
const exam_json_id = exam["exam_json_id"];
|
||||
|
||||
let question_json_id_hash = {};
|
||||
|
||||
// In the case of multiquestion exams (longs) we
|
||||
// check each question json_id in addition
|
||||
if (exam.hasOwnProperty("multi_question_json")) {
|
||||
question_json_id_hash = exam["multi_question_json"];
|
||||
}
|
||||
exam_generated_map[eid] = [exam_json_id, question_json_id_hash];
|
||||
|
||||
let c = "";
|
||||
// The exam against stored session data
|
||||
let item_class = "";
|
||||
if (exams_started.indexOf(name) > -1) {
|
||||
c = " session-started";
|
||||
item_class = " session-started";
|
||||
}
|
||||
if (exams_completed.indexOf(name) > -1) {
|
||||
c = " session-completed";
|
||||
item_class = " session-completed";
|
||||
}
|
||||
|
||||
if (!exam.exam_active) {
|
||||
c = c + " inactive";
|
||||
item_class = item_class + " inactive";
|
||||
}
|
||||
|
||||
// Place the exam under the relavant subheading
|
||||
// by defailt they will be under the packet section
|
||||
let target_list = "packet-list";
|
||||
// if in exam mode we place them under the exam section
|
||||
if (exam.exam_mode) {
|
||||
target_list = "exam-list";
|
||||
}
|
||||
|
||||
let list;
|
||||
if (exam.type != undefined) {
|
||||
// If the relevant subheading exists target_it it
|
||||
if ($(`.${target_list}.${exam.type}`).length) {
|
||||
list = $(`.${target_list}.${exam.type}`);
|
||||
// or create and add it
|
||||
} else {
|
||||
list = $(`<div class='${target_list} ${exam.type}'><span class='packet-list-title'>${exam.type}</span><br/></div>`);
|
||||
$(`#${target_list}`).append(list);
|
||||
|
||||
}
|
||||
// if the packet / exam type is not defined just add it at the bottom
|
||||
// TODO: do we actually want this?
|
||||
} else {
|
||||
list = $("#packet-list");
|
||||
}
|
||||
|
||||
// Add the button that is used to load the packet/exam
|
||||
$(list).append(
|
||||
$(
|
||||
`<div class='packet-button${c}' data-eid='${eid}' title='Load packet'></div>`
|
||||
`<div class='packet-button${item_class}' data-eid='${eid}' title='Load packet'></div>`
|
||||
)
|
||||
.text(name)
|
||||
.click(function() {
|
||||
loadPacketFromAjax(url, eid, exam_json_id);
|
||||
})
|
||||
.text(name)
|
||||
.click(function() {
|
||||
loadPacketFromAjax(url, eid, exam_json_id);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -444,8 +492,8 @@ async function loadExamList(data) {
|
||||
return a.dataset.eid > b.dataset.eid ?
|
||||
1 :
|
||||
a.dataset.eid < b.dataset.eid ?
|
||||
-1 :
|
||||
0;
|
||||
-1 :
|
||||
0;
|
||||
})
|
||||
.appendTo(".packet-list.rapid");
|
||||
$(".packet-list.anatomy div")
|
||||
@@ -453,8 +501,8 @@ async function loadExamList(data) {
|
||||
return a.dataset.eid > b.dataset.eid ?
|
||||
1 :
|
||||
a.dataset.eid < b.dataset.eid ?
|
||||
-1 :
|
||||
0;
|
||||
-1 :
|
||||
0;
|
||||
})
|
||||
.appendTo(".packet-list.anatomy");
|
||||
$(".packet-list.long div")
|
||||
@@ -462,8 +510,8 @@ async function loadExamList(data) {
|
||||
return a.dataset.eid > b.dataset.eid ?
|
||||
1 :
|
||||
a.dataset.eid < b.dataset.eid ?
|
||||
-1 :
|
||||
0;
|
||||
-1 :
|
||||
0;
|
||||
})
|
||||
.appendTo(".packet-list.long");
|
||||
|
||||
@@ -540,18 +588,18 @@ async function loadPacketList(data) {
|
||||
}
|
||||
list.append(
|
||||
$(`<div class='packet-button${c}' title='Load packet'></div>`)
|
||||
.text(packet)
|
||||
.click(function() {
|
||||
loadPacketFromAjax("packets/" + packet, packet);
|
||||
})
|
||||
.append(
|
||||
$(
|
||||
`<div class='save-button' title='Download packet for offline use (or to save bandwidth)'><a href='packets/${packet}' download='${packet}'>💾</a></div>`
|
||||
).click(function(evt) {
|
||||
//console.debug("packets/" + packet);
|
||||
evt.stopPropagation();
|
||||
.text(packet)
|
||||
.click(function() {
|
||||
loadPacketFromAjax("packets/" + packet, packet);
|
||||
})
|
||||
)
|
||||
.append(
|
||||
$(
|
||||
`<div class='save-button' title='Download packet for offline use (or to save bandwidth)'><a href='packets/${packet}' download='${packet}'>💾</a></div>`
|
||||
).click(function(evt) {
|
||||
//console.debug("packets/" + packet);
|
||||
evt.stopPropagation();
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
$("#options-panel").show();
|
||||
@@ -600,19 +648,19 @@ function loadPacketFromAjax(path, eid, exam_json_id) {
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url: path,
|
||||
cache: browser_cache,
|
||||
progress: function(e) {
|
||||
if (e.lengthComputable) {
|
||||
var completedPercentage = Math.round((e.loaded * 100) / e.total);
|
||||
dataType: "json",
|
||||
url: path,
|
||||
cache: browser_cache,
|
||||
progress: function(e) {
|
||||
if (e.lengthComputable) {
|
||||
var completedPercentage = Math.round((e.loaded * 100) / e.total);
|
||||
|
||||
$("#progress").html(
|
||||
`${completedPercentage}%<br/>${helper.formatBytes(e.total)}`
|
||||
);
|
||||
}
|
||||
},
|
||||
})
|
||||
$("#progress").html(
|
||||
`${completedPercentage}%<br/>${helper.formatBytes(e.total)}`
|
||||
);
|
||||
}
|
||||
},
|
||||
})
|
||||
.done(function(data) {
|
||||
setUpPacket(data, path);
|
||||
$("#options-panel").hide();
|
||||
@@ -933,7 +981,7 @@ function setUpQuestions(load_previous) {
|
||||
//question_type = questions[Object.keys(questions)[0]].type;
|
||||
|
||||
if (exam_details.exam_mode) {
|
||||
$("#options-button, #review-overlay-button").hide();
|
||||
$("#options-button, #btn-review-overlay").hide();
|
||||
} else {
|
||||
$(".submit-button").hide();
|
||||
}
|
||||
@@ -1728,7 +1776,7 @@ $(".submit-button").click(function(evt) {
|
||||
interact.submitAnswers(exam_details, db, config);
|
||||
});
|
||||
|
||||
$("#review-button").click(function(evt) {
|
||||
$("#btn-review").click(function(evt) {
|
||||
$(".question-list-panel").toggle();
|
||||
});
|
||||
|
||||
@@ -1737,7 +1785,7 @@ $("#options-button").click(function(evt) {
|
||||
$("#options-panel").toggle();
|
||||
});
|
||||
|
||||
$("#review-overlay-button").click(function(evt) {
|
||||
$("#btn-review-overlay").click(function(evt) {
|
||||
if (review_mode == true) {
|
||||
reviewQuestions();
|
||||
} else {
|
||||
@@ -1745,7 +1793,7 @@ $("#review-overlay-button").click(function(evt) {
|
||||
}
|
||||
});
|
||||
|
||||
$("#fullscreen-overlay-button").click(function(evt) {
|
||||
$("#btn-fullscreen-overlay").click(function(evt) {
|
||||
if (document.fullscreen == false) {
|
||||
document.documentElement.requestFullscreen();
|
||||
} else {
|
||||
@@ -1753,13 +1801,13 @@ $("#fullscreen-overlay-button").click(function(evt) {
|
||||
}
|
||||
});
|
||||
|
||||
$("#finish-exam, #time-up-review-button").click(function(evt) {
|
||||
$("#finish-exam, #btn-time-up-review").click(function(evt) {
|
||||
review_mode = true;
|
||||
reviewQuestions();
|
||||
$.modal.close();
|
||||
});
|
||||
|
||||
$("#time-up-continue-button").click(function(evt) {
|
||||
$("#btn-time-up-continue").click(function(evt) {
|
||||
$.modal.close();
|
||||
});
|
||||
|
||||
@@ -1872,10 +1920,8 @@ function reviewQuestions() {
|
||||
if (question_type == "long") {
|
||||
$("#review-answer-table").append(
|
||||
$(
|
||||
`<tr class='review-table-heading' data-qid=${
|
||||
n + 1
|
||||
} title='Click to load question ${
|
||||
n + 1
|
||||
`<tr class='review-table-heading' data-qid=${n + 1
|
||||
} title='Click to load question ${n + 1
|
||||
}'><td colspan=2>Question ${n + 1}</td></tr>`
|
||||
).click(() => {
|
||||
loadQuestion(n);
|
||||
@@ -1884,8 +1930,7 @@ function reviewQuestions() {
|
||||
);
|
||||
// $("#review-answer-table").append(`<tr id='review-answer-${qid}'><td class='review-user-answer-cell'></td><td class='review-model-answer-cell'></td></tr>`);
|
||||
$("#review-answer-table").append(
|
||||
`<tr class='answer-sub' data-qid=${
|
||||
n + 1
|
||||
`<tr class='answer-sub' data-qid=${n + 1
|
||||
}><td>Your answers</td><td>Model answers</td></tr>`
|
||||
);
|
||||
|
||||
@@ -1926,8 +1971,7 @@ function reviewQuestions() {
|
||||
model_answers[title.toLowerCase()];
|
||||
|
||||
$("#review-answer-table").append(
|
||||
`<tr class='' data-qid=${
|
||||
n + 1
|
||||
`<tr class='' data-qid=${n + 1
|
||||
}><td>${user_ans}</td><td>${model_ans}</td></tr>`
|
||||
);
|
||||
});
|
||||
@@ -1938,8 +1982,8 @@ function reviewQuestions() {
|
||||
return a.dataset.qid > b.dataset.qid ?
|
||||
1 :
|
||||
a.dataset.qid < b.dataset.qid ?
|
||||
-1 :
|
||||
0;
|
||||
-1 :
|
||||
0;
|
||||
})
|
||||
.appendTo("#review-answer-table");
|
||||
return;
|
||||
@@ -2158,8 +2202,8 @@ function reviewQuestions() {
|
||||
return a.dataset.qid > b.dataset.qid ?
|
||||
1 :
|
||||
a.dataset.qid < b.dataset.qid ?
|
||||
-1 :
|
||||
0;
|
||||
-1 :
|
||||
0;
|
||||
})
|
||||
.appendTo("#review-answer-list");
|
||||
});
|
||||
@@ -2260,7 +2304,8 @@ function markAnswer(qid, current_question, n) {
|
||||
saved_answer,
|
||||
e,
|
||||
saved_answer_object,
|
||||
db
|
||||
db,
|
||||
URLS.question_answer_submit_url
|
||||
);
|
||||
})
|
||||
);
|
||||
@@ -2337,7 +2382,7 @@ function addFeedback(current_question, qid) {
|
||||
|
||||
$(".answer-panel").append(
|
||||
$(
|
||||
`<div class='feedback-link'><a href=${config.question_feedback_url}${current_question.type}/${qid} target=”_blank”>Submit question feedback</a></div>`
|
||||
`<div class='feedback-link'><a href=${URLS.question_feedback_url}${current_question.type}/${qid} target=”_blank”>Submit question feedback</a></div>`
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -2384,8 +2429,8 @@ function compareString(a, b) {
|
||||
function answerInArray(arr, ans) {
|
||||
return (
|
||||
arr
|
||||
.map((v) => v.toLowerCase().replace(/\s/g, ""))
|
||||
.includes(ans.toLowerCase().replace(/\s/g, "")) == true
|
||||
.map((v) => v.toLowerCase().replace(/\s/g, ""))
|
||||
.includes(ans.toLowerCase().replace(/\s/g, "")) == true
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2470,23 +2515,19 @@ $("#btn-candidate-login").click(function(evt) {
|
||||
});
|
||||
|
||||
$("#btn-user-login").click(function(evt) {
|
||||
window.location = "/accounts/login/?next=/rts"
|
||||
window.location = URLS.login_url;
|
||||
//$(".exam-wrapper").toggle();
|
||||
});
|
||||
|
||||
|
||||
|
||||
$("#login-button").click(function(evt) {
|
||||
$("#btn-login").click(function(evt) {
|
||||
global_cid = parseInt($("#candidate-number").val());
|
||||
global_passcode = $("#passcode").val();
|
||||
|
||||
user_db.user.clear().then(() => {
|
||||
user_db.user.add({
|
||||
cid: global_cid,
|
||||
passcode: global_passcode
|
||||
})
|
||||
location.reload();
|
||||
});
|
||||
localStorage.setItem("cid.cid", global_cid)
|
||||
localStorage.setItem("cid.passcode", global_passcode)
|
||||
location.reload();
|
||||
$.modal.close();
|
||||
});
|
||||
|
||||
@@ -2516,7 +2557,7 @@ $(".start-packet-button").click(function(evt) {
|
||||
timer.addEventListener("targetAchieved", function(e) {
|
||||
if (exam_details.exam_mode == true) {
|
||||
$(
|
||||
"#dialog-submit-button, #time-up-review-button, #time-up-continue-button"
|
||||
"#btn-dialog-submit, #btn-time-up-review, #btn-time-up-continue"
|
||||
).toggle();
|
||||
$("#time-up-dialog .dialog-text").text(
|
||||
"Allocated time has ended. Click to submit answers."
|
||||
@@ -2674,7 +2715,8 @@ $("#btn-reset-local").click(function(evt) {
|
||||
if (r == true) {
|
||||
delete_answer_db();
|
||||
delete_question_db();
|
||||
delete_user_db();
|
||||
localStorage.removeItem("cid.cid")
|
||||
localStorage.removeItem("cid.passcode")
|
||||
} else {}
|
||||
});
|
||||
|
||||
@@ -2800,6 +2842,7 @@ $(document).on("saveSessionEvent", {}, (evt, review) => {
|
||||
saveSession(review);
|
||||
});
|
||||
|
||||
// Disable right click
|
||||
$(document).contextmenu(() => {
|
||||
return false;
|
||||
})
|
||||
|
||||
+1989
-1504
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
/*! loglevel - v1.8.1 - https://github.com/pimterry/loglevel - (c) 2022 Tim Perry - licensed MIT */
|
||||
!function(a,b){"use strict";"function"==typeof define&&define.amd?define(b):"object"==typeof module&&module.exports?module.exports=b():a.log=b()}(this,function(){"use strict";function a(a,b){var c=a[b];if("function"==typeof c.bind)return c.bind(a);try{return Function.prototype.bind.call(c,a)}catch(b){return function(){return Function.prototype.apply.apply(c,[a,arguments])}}}function b(){console.log&&(console.log.apply?console.log.apply(console,arguments):Function.prototype.apply.apply(console.log,[console,arguments])),console.trace&&console.trace()}function c(c){return"debug"===c&&(c="log"),typeof console!==i&&("trace"===c&&j?b:void 0!==console[c]?a(console,c):void 0!==console.log?a(console,"log"):h)}function d(a,b){for(var c=0;c<k.length;c++){var d=k[c];this[d]=c<a?h:this.methodFactory(d,a,b)}this.log=this.debug}function e(a,b,c){return function(){typeof console!==i&&(d.call(this,b,c),this[a].apply(this,arguments))}}function f(a,b,d){return c(a)||e.apply(this,arguments)}function g(a,b,c){function e(a){var b=(k[a]||"silent").toUpperCase();if(typeof window!==i&&m){try{return void(window.localStorage[m]=b)}catch(a){}try{window.document.cookie=encodeURIComponent(m)+"="+b+";"}catch(a){}}}function g(){var a;if(typeof window!==i&&m){try{a=window.localStorage[m]}catch(a){}if(typeof a===i)try{var b=window.document.cookie,c=b.indexOf(encodeURIComponent(m)+"=");-1!==c&&(a=/^([^;]+)/.exec(b.slice(c))[1])}catch(a){}return void 0===l.levels[a]&&(a=void 0),a}}function h(){if(typeof window!==i&&m){try{return void window.localStorage.removeItem(m)}catch(a){}try{window.document.cookie=encodeURIComponent(m)+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC"}catch(a){}}}var j,l=this;b=null==b?"WARN":b;var m="loglevel";"string"==typeof a?m+=":"+a:"symbol"==typeof a&&(m=void 0),l.name=a,l.levels={TRACE:0,DEBUG:1,INFO:2,WARN:3,ERROR:4,SILENT:5},l.methodFactory=c||f,l.getLevel=function(){return j},l.setLevel=function(b,c){if("string"==typeof b&&void 0!==l.levels[b.toUpperCase()]&&(b=l.levels[b.toUpperCase()]),!("number"==typeof b&&b>=0&&b<=l.levels.SILENT))throw"log.setLevel() called with invalid level: "+b;if(j=b,!1!==c&&e(b),d.call(l,b,a),typeof console===i&&b<l.levels.SILENT)return"No console available for logging"},l.setDefaultLevel=function(a){b=a,g()||l.setLevel(a,!1)},l.resetLevel=function(){l.setLevel(b,!1),h()},l.enableAll=function(a){l.setLevel(l.levels.TRACE,a)},l.disableAll=function(a){l.setLevel(l.levels.SILENT,a)};var n=g();null==n&&(n=b),l.setLevel(n,!1)}var h=function(){},i="undefined",j=typeof window!==i&&typeof window.navigator!==i&&/Trident\/|MSIE /.test(window.navigator.userAgent),k=["trace","debug","info","warn","error"],l=new g,m={};l.getLogger=function(a){if("symbol"!=typeof a&&"string"!=typeof a||""===a)throw new TypeError("You must supply a name when creating a logger.");var b=m[a];return b||(b=m[a]=new g(a,l.getLevel(),l.methodFactory)),b};var n=typeof window!==i?window.log:void 0;return l.noConflict=function(){return typeof window!==i&&window.log===l&&(window.log=n),l},l.getLoggers=function(){return m},l.default=l,l});
|
||||
Reference in New Issue
Block a user