// Bind Listeners $("input[name='font-select']").change(function(){ let $choice = $(this).val() set_font($choice) }) $("input[name='font-size']").change(function(){ let $choice = $(this).val() set_font_size($choice) }) $("input[name='bg-select']").change(function(){ let $choice = $(this).val() set_bg_colour($choice) }) $(".bg-select-area").click(function(event){ $(this).find("input[name='bg-select']").prop("checked", true).change() }) $("#btn-toggle-navigator").click(function(event){ check_answered() update_navigator() if ($quiz_navigator.is(":hidden")) { if ($quiz_settings.is(":visible")) { toggle_settings = true $quiz_settings.fadeOut() } $quiz_render.fadeOut() $quiz_navigator.fadeIn() $(".navigator-text").fadeIn() $(".review-text").fadeOut() toggle_navigator = false $(window).scrollTop(0) } else { $quiz_navigator.fadeOut() if (toggle_settings) { $quiz_settings.fadeIn() $(window).scrollTop(0) toggle_settings = false } else { $quiz_render.fadeIn() $(window).scrollTop(0) } } event.preventDefault() }) $("#btn-toggle-settings").click(function(event){ if (($quiz_settings).is(":hidden")) { if ($quiz_navigator.is(":visible")) { toggle_navigator = true $quiz_navigator.fadeOut() } $quiz_render.fadeOut() $quiz_settings.fadeIn() $(window).scrollTop(0) toggle_settings = false } else { $quiz_settings.fadeOut() if (toggle_navigator) { $quiz_navigator.fadeIn() toggle_navigator = false $(window).scrollTop(0) } else { $quiz_render.fadeIn() $(window).scrollTop(0) } } event.preventDefault() }) $(".btn-quiz-return").click(function(event){ $quiz_navigator.fadeOut() $quiz_settings.fadeOut() $quiz_render.fadeIn() $(window).scrollTop(0) toggle_settings = false toggle_navigator = false event.preventDefault() }) $(".btn-dummy").click(function(event){ event.preventDefault() }) $("#navigator-container").on("click", ".q-navigator-button", function(event){ check_answered() update_navigator() current_question = parseInt($(this).prop("name")) $quiz_navigator.fadeOut() $quiz_render.fadeIn() $question_title.focus() $(window).scrollTop(0) toggle_navigator = false toggle_settings = false render_question() check_flag() event.preventDefault() }) $(".q-question-nav").click(function(event){ check_answered() update_navigator() if ($(this).prop("id") == "q-nav-next") { if (current_question < questions.length) { current_question ++ } } else if ($(this).prop("id") == "q-nav-prev") { if (current_question > 0) { current_question -- } } else if ($(this).hasClass("q-navigator-button")) { current_question = $(this).prop("name") $quiz_render.fadeIn() $quiz_navigator.fadeOut() toggle_navigator = false toggle_settings = false } render_question() check_flag() event.preventDefault() }) $("#q-nav-flag").click(function(event){ if (question_status[current_question] != 1) { question_status[current_question] = 1 $(this).removeClass().addClass("btn btn-warning") $(this).prop("title", "Question Flagged for revision. Click to un-flag.") } else { question_status[current_question] = 0 $(this).removeClass().addClass("btn btn-secondary") $(this).prop("title", "Question Un-Flagged. Click to flag for revision.") } window.localStorage.setItem('question_status', JSON.stringify(question_status)) update_navigator() event.preventDefault() }) $("#btn-start-quiz").click(function(event){ $.ajax({ url: `/api/questions/`, type: 'POST', data: JSON.stringify({'id': id}), contentType: "application/json", success: function(response) { $("#btn-start-quiz").fadeOut() $(".btn-quiz-return").fadeIn() $(".quiz-console").fadeIn() $("#quiz-settings").fadeOut() $("#quiz-navigator").fadeOut() $(".quiz-start-text").fadeOut() time_limit = response.time_limit start_time = response.start_time questions = response.questions total_questions = questions.length window.localStorage.setItem('questions', JSON.stringify(questions)) window.localStorage.setItem('start_time', JSON.stringify(start_time)) window.localStorage.setItem('time_limit', JSON.stringify(time_limit)) render_question() build_navigator() check_flag() if (time_limit != 'null' && time_limit != null) { $("#q-timer-widget").fadeIn() time_remaining = get_time_remaining() clock = setInterval(timer, 1000) } if (response.time_adjustment > 0) { const $alert = $("#alert-box") $alert.html( ` ` ) $alert.focus() } }, error: function(response) { error_response(response) } }) event.preventDefault() }) $("#quiz-question-options").on("change", ".quiz-option", function(event){ $name = parseInt($(this).prop("name")) $value = $(this).prop("value") answers[$name] = $value window.localStorage.setItem('answers', JSON.stringify(answers)) }) $("#q-review-answers").click(function(event){ check_answered() update_navigator() if ($quiz_navigator.is(":hidden")) { if ($quiz_settings.is(":visible")) { toggle_settings = true $quiz_settings.fadeOut() } $quiz_render.fadeOut() $quiz_navigator.fadeIn() $(".navigator-text").fadeOut() $(".review-text").fadeIn() toggle_navigator = false $(window).scrollTop(0) } else { $quiz_navigator.fadeOut() if (toggle_settings) { $quiz_settings.fadeIn() toggle_settings = false } else { $quiz_render.fadeIn() } } event.preventDefault() }) $(".quiz-button-submit").click(function(event){ let submission = { 'id': id, 'answers': answers } $.ajax({ url: `/api/submit/`, type: 'POST', data: JSON.stringify(submission), contentType: "application/json", success: function(response) { window.localStorage.clear() window.location.href = `/result/` }, error: function(response) { error_response(response) } }) event.preventDefault() }) // Functions function set_font(value = 'osdefault') { let font_styles = ['arial', 'comicsans', 'opendyslexic', 'tahoma', 'verdana'] for (let i = 0; i < font_styles.length; i ++) { if (font_styles[i] != value) { $("body").removeClass( `q-f-${font_styles[i]}` ) } } if (value != 'osdefault') { $("body").addClass(`q-f-${value}`) } display_settings['font-select'] = value window.localStorage.setItem('display_settings', JSON.stringify(display_settings)) $('input[name="font-select"][value="' + value + '"]').prop('checked', true) } function set_font_size(value = '14pt') { let font_sizes = ['12pt', '16pt', '18pt'] for (let i = 0; i < font_sizes.length; i ++) { if (font_sizes[i] != value) { $("body").removeClass( `q-f-${font_sizes[i]}` ) } } if (value != '14pt') { $("body").addClass(`q-f-${value}`) } display_settings['font-size'] = value window.localStorage.setItem('display_settings', JSON.stringify(display_settings)) $('input[name="font-size"][value="' + value + '"]').prop('checked', true) } function set_bg_colour(value = 'bg-light') { let backgrounds = ['bg-light', 'q-bg-light-1', 'q-bg-light-2', 'alert-primary', 'alert-secondary', 'alert-dark', 'bg-dark'] for (let i = 0; i < backgrounds.length; i ++) { if (backgrounds[i] != value) { $("body").removeClass(backgrounds[i]) if (backgrounds[i] == 'bg-dark') { $("body").removeClass('text-light') } if (backgrounds[i] == 'alert-primary' || backgrounds[i] == 'alert-secondary' || backgrounds[i] == 'alert-dark') { $("body").removeClass('text-dark') } } } $("body").addClass(value) if (value == 'bg-dark') { $("body").addClass('text-light') } if (value == 'alert-primary' || value == 'alert-secondary' || value == 'alert-dark') { $("body").addClass('text-dark') } display_settings['bg-select'] = value window.localStorage.setItem('display_settings', JSON.stringify(display_settings)) $('input[name="bg-select"][value="' + value + '"]').prop('checked', true) } function get_settings_from_storage() { let display_settings = window.localStorage.getItem('display_settings') if (display_settings != null) { return JSON.parse(display_settings) } return { 'font-select': 'osdefault', 'font-size': '14pt', 'bg-select': 'bg-light' } } function apply_settings(settings) { set_font(settings['font-select']) set_font_size(settings['font-size']) set_bg_colour(settings['bg-select']) } function render_question() { if (current_question == 0) { $nav_prev.addClass('disabled') } if (current_question == questions.length - 1) { $nav_next.addClass('disabled') } if ($nav_prev.hasClass('disabled') && current_question > 0) { $nav_prev.removeClass('disabled') } if ($nav_next.hasClass('disabled') && current_question < questions.length - 1) { $nav_next.removeClass('disabled') } var question = questions[current_question] let header_text = question.question_header var block_length = 0 if ('block_length' in question) { block_length = question['block_length'] } var block_q_no = 0 if ('block_q_no' in question) { block_q_no = question['block_q_no'] } let remaining_qs = (block_length - block_q_no).toString() if (block_length - block_q_no > 1) { remaining_qs += ' questions' } else { remaining_qs += ' question' } header_text = header_text.replace('', remaining_qs) $question_header.html(header_text) $question_text.html(question.text) $question_title.html(`Question ${current_question + 1} of ${ questions.length }.`) var q_no = question['q_no'] var options = question.options var options_output = '' for (let i = 0; i < options.length; i ++) { var add_checked = '' if (q_no in answers) { if (answers[q_no] == options[i][0]) { add_checked = 'checked' } } options_output += `
` } $question_options.html(options_output) let skipped = count_questions(-1) let answered = count_questions(2) let flagged = count_questions(1) $progress_skipped.prop('title', `Skipped: ${skipped}`) $progress_skipped.prop('aria-valuenow', skipped) $progress_skipped.css('width', `${skipped}%`) $skipped_count.text(`Skipped: ${skipped}`) if (skipped < 1) { $skipped_count.fadeOut() } else { $skipped_count.fadeIn() } $progress_flagged.prop('title', `Flagged: ${flagged}`) $progress_flagged.prop('aria-valuenow', flagged) $progress_flagged.css('width', `${flagged}%`) $flagged_count.text(`Flagged: ${flagged}`) if (flagged < 1) { $flagged_count.fadeOut() } else { $flagged_count.fadeIn() } $progress_answered.prop('title', `Answered: ${answered}`) $progress_answered.prop('aria-valuenow', answered) $progress_answered.css('width', `${answered}%`) $answered_count.text(`Answered: ${answered}`) if (answered < 1) { $answered_count.fadeOut() } else { $answered_count.fadeIn() } $question_title.focus() $(window).scrollTop(0) } function check_answered() { var question = questions[current_question] var name = question.q_no if (question_status[current_question] == 0 || question_status[current_question] == -1) { if (!$(`input[name='${name}']:checked`).val()) { question_status[current_question] = -1 } else { question_status[current_question] = 2 } window.localStorage.setItem('question_status', JSON.stringify(question_status)) } } function check_flag() { if (!(current_question in question_status)) { question_status[current_question] = 0 window.localStorage.setItem('question_status', JSON.stringify(question_status)) } switch (question_status[current_question]) { case -1: $nav_flag.removeClass().addClass('btn btn-danger progress-bar-striped') $nav_flag.prop("title", "Question Incomplete. Click to flag for revision.") break case 1: $nav_flag.removeClass().addClass('btn btn-warning') $nav_flag.prop("title", "Question Flagged for revision. Click to un-flag.") break case 2: $nav_flag.removeClass().addClass('btn btn-success') $nav_flag.prop("title", "Question Answered. Click to flag for revision.") break default: $nav_flag.removeClass().addClass('btn btn-secondary') $nav_flag.prop("title", "Question Un-Flagged. Click to flag for revision.") } } function build_navigator() { $nav_container.html('') var output = '' for (let i = 0; i < questions.length; i ++) { let add_class, add_href, add_status = '' switch (question_status[i]) { case -1: add_class = 'btn-danger progress-bar-striped' add_href = 'href="#"' add_status = 'Incomplete' break case 1: add_class = 'btn-warning' add_href = 'href="#"' add_status = 'Flagged' break case 2: add_class = 'btn-success' add_href = 'href="#"' add_status = 'Answered' break default: add_class = 'btn-secondary disabled' add_href = '' add_status = 'Unseen' } output += `Q${i + 1}` } $nav_container.html(output) } function update_navigator() { let button = $(`.q-navigator-button[name=${current_question}]`) if (current_question in question_status) { switch (question_status[current_question]) { case -1: button.removeClass().addClass("q-navigator-button btn btn-danger progress-bar-striped") button.prop("title", `Question ${current_question + 1}: Incomplete`) break case 1: button.removeClass().addClass("q-navigator-button btn btn-warning") button.prop("title", `Question ${current_question + 1}: Flagged`) break case 2: button.removeClass().addClass("q-navigator-button btn btn-success") button.prop("title", `Question ${current_question + 1}: Answered`) break default: button.removeClass().addClass("q-navigator-button btn btn-secondary disabled") button.prop("title", `Question ${current_question + 1}: Unseen`) } } } function start() { $("#btn-start-quiz").fadeOut() $(".btn-quiz-return").fadeIn() $(".quiz-console").fadeIn() $("#quiz-settings").fadeOut() $("#quiz-navigator").fadeOut() $(".quiz-start-text").fadeOut() questions = JSON.parse(window.localStorage.getItem('questions')) total_questions = questions.length start_time = window.localStorage.getItem('start_time') time_limit = window.localStorage.getItem('time_limit') let get_answers = window.localStorage.getItem('answers') if (get_answers != null) { answers = JSON.parse(get_answers) } let get_status = window.localStorage.getItem('question_status') if (get_status != null) { question_status = JSON.parse(get_status) } render_question() build_navigator() check_flag() if (time_limit != 'null' && time_limit != null) { $("#q-timer-widget").fadeIn() time_remaining = get_time_remaining() clock = setInterval(timer, 1000) } } function check_started() { let questions = window.localStorage.getItem('questions') let time_limit = window.localStorage.getItem('time_limit') let start_time = window.localStorage.getItem('start_time') if (questions != null && start_time != null && time_limit != null) { start() } } function get_time_remaining() { var end_time = new Date(time_limit).getTime() var _start_time = new Date().getTime() return end_time - _start_time } function timer() { var hours = Math.floor((time_remaining % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) var minutes = Math.floor((time_remaining % (1000 * 60 * 60)) / (1000 * 60)) var seconds = Math.floor((time_remaining % (1000 * 60)) / 1000) if (time_remaining > 0) { var timer_display = '' if (hours > 0) { timer_display = `${hours.toString()}:` } if (minutes > 0 || hours > 0) { if (minutes < 10) { timer_display += `0${minutes.toString()}:` } else { timer_display += `${minutes.toString()}:` } } if (seconds < 10) { timer_display += `0${seconds.toString()}` } else { timer_display += seconds.toString() } $timer.html(timer_display) time_remaining -= 1000 } else { $timer.html('Expired') clearInterval(clock) stop() } } function stop() { $quiz_render.fadeOut() $quiz_navigator.fadeOut() $quiz_timeout.fadeIn() $("#btn-toggle-navigator").addClass('disabled') $("#btn-toggle-settings").addClass('disabled') } function count_questions(status) { output = 0 for (let i = 0; i < Object.keys(question_status).length; i++) { key = Object.keys(question_status)[i] if (question_status[key] == status){ output ++ } } return output } // Variable Definitions const id = window.localStorage.getItem('id') var current_question = 0 var total_questions = 0 var question_status = {} var answers = {} var questions = [] var time_limit, start_time, time_remaining var display_settings = get_settings_from_storage() const $quiz_settings = $("#quiz-settings") const $quiz_navigator = $("#quiz-navigator") const $quiz_render = $("#quiz-render") const $quiz_timeout = $("#quiz-timeout") const $nav_flag = $("#q-nav-flag") const $nav_next = $("#q-nav-next") const $nav_prev = $("#q-nav-prev") const $nav_container = $("#navigator-container") const $timer = $("#q-timer-display") var clock var toggle_settings = false var toggle_navigator = false const $question_title = $("#quiz-question-title") const $question_header = $("#quiz-question-header") const $question_text = $("#quiz-question-text") const $question_options = $("#quiz-question-options") const $progress_skipped = $("#skipped-bar") const $progress_answered = $("#answered-bar") const $progress_flagged = $("#flagged-bar") const $skipped_count = $("#skipped-count") const $answered_count = $("#answered-count") const $flagged_count = $("#flagged-count") // Execution on Load apply_settings(display_settings) check_started()