';
/// Print all the questions
@@ -154,7 +152,7 @@
// Some hidden fields to trach what is going on.
echo '
';
echo '
';
- echo '
';
+ echo '
';
echo '
';
// Add a hidden field with questionids. Do this at the end of the form, so
diff --git a/mod/quiz/attemptlib.php b/mod/quiz/attemptlib.php
index 42c782c762d..30d66ce3141 100644
--- a/mod/quiz/attemptlib.php
+++ b/mod/quiz/attemptlib.php
@@ -273,7 +273,7 @@ class quiz {
/**
* @param integer $timenow the current time as a unix timestamp.
- * @return object and instance of the quiz_access_manager class for this quiz at this time.
+ * @return quiz_access_manager and instance of the quiz_access_manager class for this quiz at this time.
*/
public function get_access_manager($timenow) {
if (is_null($this->accessmanager)) {
@@ -763,12 +763,7 @@ class quiz_attempt extends quiz {
*/
public function get_html_head_contributions($page = 'all') {
global $PAGE;
- // The JS does important things like navigation and so must be initialised
- // as seen as possible, particularly if the page is loading slowly.
- $PAGE->requires->yui2_lib('dom');
- $PAGE->requires->yui2_lib('event');
- $PAGE->requires->js('/mod/quiz/quiz.js', true);
- get_html_head_contributions($this->get_question_ids($page), $this->questions, $this->states);
+ question_get_html_head_contributions($this->get_question_ids($page), $this->questions, $this->states);
}
/**
@@ -776,7 +771,7 @@ class quiz_attempt extends quiz {
* @param integer $questionid the question id.
*/
public function get_question_html_head_contributions($questionid) {
- get_html_head_contributions(array($questionid), $this->questions, $this->states);
+ question_get_html_head_contributions(array($questionid), $this->questions, $this->states);
}
/**
@@ -1129,7 +1124,7 @@ abstract class quiz_nav_panel_base {
public function get_contents() {
global $PAGE;
- $PAGE->requires->js_function_call('quiz_init_nav_flags');
+ $PAGE->requires->js_init_call('M.mod_quiz.nav.init', null, false, quiz_get_js_module());
$content = '';
if ($this->attemptobj->get_quiz()->showuserpicture) {
@@ -1168,7 +1163,6 @@ class quiz_attempt_nav_panel extends quiz_nav_panel_base {
$output = '';
$output .= '
' . get_string('endtest', 'quiz') . '';
$output .= $this->attemptobj->get_timer_html();
- $output .= html_writer::script(js_writer::function_call('quiz_init_attempt_nav'));
return $output;
}
}
diff --git a/mod/quiz/lang/en/quiz.php b/mod/quiz/lang/en/quiz.php
index 2f871ff102c..6541be044f8 100644
--- a/mod/quiz/lang/en/quiz.php
+++ b/mod/quiz/lang/en/quiz.php
@@ -357,6 +357,7 @@ $string['formulaerror'] = 'Formula errors!';
$string['fractionsaddwrong'] = 'The positive grades you have chosen do not add up to 100%
Instead, they add up to {$a}%
Do you want to go back and fix this question?';
$string['fractionsnomax'] = 'One of the answers should be 100%, so that it is
possible to get a full grade for this question.
Do you want to go back and fix this question?';
$string['fromfile'] = 'from file:';
+$string['functiondisabledbysecuremode'] = 'That functionality is currently disabled';
$string['functiontakesatleasttwo'] = 'The function {$a} must have at least two arguments';
$string['functiontakesnoargs'] = 'The function {$a} does not take any arguments';
$string['functiontakesonearg'] = 'The function {$a} must have exactly one argument';
diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php
index 85ea4bf02d5..0685c6b871a 100644
--- a/mod/quiz/locallib.php
+++ b/mod/quiz/locallib.php
@@ -1251,3 +1251,15 @@ function quiz_error($quiz, $errorcode, $a = null) {
function quiz_check_safe_browser() {
return strpos($_SERVER['HTTP_USER_AGENT'], "SEB") !== false;
}
+
+function quiz_get_js_module() {
+ return array(
+ 'name' => 'mod_quiz',
+ 'fullpath' => '/mod/quiz/module.js',
+ 'requires' => array('base', 'dom', 'event-delegate', 'event-key'),
+ 'strings' => array(
+ array('timesup', 'quiz'),
+ array('functiondisabledbysecuremode', 'quiz'),
+ ),
+ );
+}
\ No newline at end of file
diff --git a/mod/quiz/module.js b/mod/quiz/module.js
new file mode 100644
index 00000000000..24b59f0c4f0
--- /dev/null
+++ b/mod/quiz/module.js
@@ -0,0 +1,220 @@
+/*
+ * JavaScript library for the quiz module.
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ */
+
+M.mod_quiz = M.mod_quiz || {};
+
+M.mod_quiz.init_attempt_form = function(Y) {
+ Y.one('#responseform').setAttribute('autocomplete', 'off');
+ Y.on('key', function (e) {
+ if (!e.target.test('a') && !e.target.test('input[type=submit]') &&
+ !e.target.test('input[type=img]')) {
+ e.preventDefault();
+ }
+ }, '#responseform', 'press:13');
+ Y.on('submit', M.mod_quiz.timer.stop, '#responseform');
+}
+
+M.mod_quiz.init_review_form = function(Y) {
+ Y.all('.questionflagsavebutton').remove();
+
+ Y.on('key', function (e) {
+ if (!e.target.test('a') && !e.target.test('input[type=submit]') &&
+ !e.target.test('input[type=img]')) {
+ e.preventDefault();
+ }
+ }, '.questionflagsaveform', 'press:13');
+
+ Y.on('submit', function(e) { e.halt(); }, '.questionflagsaveform');
+}
+
+// Code for updating the countdown timer that is used on timed quizzes.
+M.mod_quiz.timer = {
+ // YUI object.
+ Y: null,
+
+ // Timestamp at which time runs out, according to the student's computer's clock.
+ endtime: 0,
+
+ // This records the id of the timeout that updates the clock periodically,
+ // so we can cancel.
+ timeoutid: null,
+
+ /**
+ * @param Y the YUI object
+ * @param timeleft, the time remaining, in seconds.
+ */
+ init: function(Y, timeleft) {
+ M.mod_quiz.timer.Y = Y;
+ M.mod_quiz.timer.endtime = new Date().getTime() + timeleft*1000;
+ M.mod_quiz.timer.update();
+ Y.one('#quiz-timer').setStyle('display', 'block');
+ },
+
+ /**
+ * Stop the timer, if it is running.
+ */
+ stop: function(e) {
+ if (M.mod_quiz.timer.timeoutid) {
+ clearTimeout(M.mod_quiz.timer.timeoutid);
+ }
+ },
+
+ /**
+ * Function to convert a number between 0 and 99 to a two-digit string.
+ */
+ two_digit: function(num) {
+ if (num < 10) {
+ return '0' + num;
+ } else {
+ return num;
+ }
+ },
+
+ // Function to update the clock with the current time left, and submit the quiz if necessary.
+ update: function() {
+ var Y = M.mod_quiz.timer.Y;
+ var secondsleft = Math.floor((M.mod_quiz.timer.endtime - new Date().getTime())/1000);
+
+ // If time has expired, Set the hidden form field that says time has expired.
+ if (secondsleft < 0) {
+ M.mod_quiz.timer.stop(null);
+ Y.one('#quiz-time-left').setContent(M.str.quiz.timesup);
+ var input = Y.one('input[name=timeup]');
+ input.set('value', 1);
+ input.ancestor('form').submit();
+ return;
+ }
+
+ // If time has nearly expired, change the colour.
+ if (secondsleft < 100) {
+ Y.one('#quiz-timer').removeClass('timeleft' + (secondsleft + 2))
+ .removeClass('timeleft' + (secondsleft + 1))
+ .addClass('timeleft' + secondsleft);
+ }
+
+ // Update the time display.
+ var hours = Math.floor(secondsleft/3600);
+ secondsleft -= hours*3600;
+ var minutes = Math.floor(secondsleft/60);
+ secondsleft -= minutes*60;
+ var seconds = secondsleft;
+ Y.one('#quiz-time-left').setContent('' + hours + ':' +
+ M.mod_quiz.timer.two_digit(minutes) + ':' +
+ M.mod_quiz.timer.two_digit(seconds));
+
+ // Arrange for this method to be called again soon.
+ M.mod_quiz.timer.timeoutid = setTimeout(M.mod_quiz.timer.update, 100);
+ }
+};
+
+M.mod_quiz.nav = M.mod_quiz.nav || {};
+
+M.mod_quiz.nav.update_flag_state = function(attemptid, questionid, newstate) {
+ var Y = M.mod_quiz.nav.Y;
+ var navlink = Y.one('#quiznavbutton' + questionid);
+ navlink.removeClass('flagged');
+ if (newstate == 1) {
+ navlink.addClass('flagged');
+ }
+};
+
+M.mod_quiz.nav.init = function(Y) {
+ M.mod_quiz.nav.Y = Y;
+
+ Y.all('#quiznojswarning').remove();
+
+ Y.delegate('click', function(e) {
+ if (this.hasClass('thispage')) {
+ return;
+ }
+
+ e.preventDefault(e);
+
+ var pageidmatch = this.get('href').match(/page=(\d+)/);
+ var pageno;
+ if (pageidmatch) {
+ pageno = pageidmatch[1];
+ } else {
+ pageno = 0;
+ }
+ Y.one('#nextpagehiddeninput').set('value', pageno);
+
+ var form = Y.one('#responseform');
+
+ var questionidmatch = this.get('href').match(/#q(\d+)/);
+ if (questionidmatch) {
+ form.set(action, form.get(action) + '#q' + questionidmatch[1]);
+ }
+
+ form.submit();
+ }, document.body, '.qnbutton');
+
+ if (Y.one('a.endtestlink')) {
+ Y.on('click', function(e) {
+ e.preventDefault();
+ Y.one('#nextpagehiddeninput').set('value', -1);
+ Y.one('#responseform').submit();
+ }, 'a.endtestlink');
+ }
+
+ if (M.core_question_flags) {
+ M.core_question_flags.add_listener(M.mod_quiz.nav.update_flag_state);
+ }
+};
+
+M.mod_quiz.secure_window = {
+ init: function(Y) {
+ if (window.location.href.substring(0,4) == 'file') {
+ window.location = 'about:blank';
+ }
+ Y.delegate('contextmenu', M.mod_quiz.secure_window.prevent, document.body, '*');
+ Y.delegate('mousedown', M.mod_quiz.secure_window.prevent_mouse, document.body, '*');
+ Y.delegate('mouseup', M.mod_quiz.secure_window.prevent_mouse, document.body, '*');
+ Y.delegate('dragstart', M.mod_quiz.secure_window.prevent, document.body, '*');
+ Y.delegate('selectstart', M.mod_quiz.secure_window.prevent, document.body, '*');
+ M.mod_quiz.secure_window.clear_status;
+ Y.on('beforeprint', function() {
+ Y.one(document.body).setStyle('display', 'none');
+ }, window);
+ Y.on('afterprint', function() {
+ Y.one(document.body).setStyle('display', 'block');
+ }, window);
+ Y.on('key', M.mod_quiz.secure_window.prevent, '*', 'press:67,86,88+ctrl');
+ Y.on('key', M.mod_quiz.secure_window.prevent, '*', 'up:67,86,88+ctrl');
+ Y.on('key', M.mod_quiz.secure_window.prevent, '*', 'down:67,86,88+ctrl');
+ Y.on('key', M.mod_quiz.secure_window.prevent, '*', 'press:67,86,88+meta');
+ Y.on('key', M.mod_quiz.secure_window.prevent, '*', 'up:67,86,88+meta');
+ Y.on('key', M.mod_quiz.secure_window.prevent, '*', 'down:67,86,88+meta');
+ },
+
+ clear_status: function() {
+ window.status = '';
+ setTimeout(M.mod_quiz.secure_window.clear_status, 10);
+ },
+
+ prevent: function(e) {
+ alert(M.str.quiz.functiondisabledbysecuremode);
+ e.halt();
+ },
+
+ prevent_mouse: function(e) {
+ if (e.button != 1 || !/^(INPUT|TEXTAREA|BUTTON|SELECT)$/i.test(e.target.get('tagName'))) {
+ alert(M.str.quiz.functiondisabledbysecuremode);
+ }
+ e.halt();
+ },
+
+ close: function(url, delay) {
+ setTimeout(function() {
+ if (window.opener) {
+ window.opener.document.location.reload();
+ window.close();
+ } else {
+ window.location.href = url;
+ }
+ }, delay*1000);
+ }
+};
diff --git a/mod/quiz/quiz.js b/mod/quiz/quiz.js
deleted file mode 100644
index 3473abb68cc..00000000000
--- a/mod/quiz/quiz.js
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * JavaScript library for the quiz module.
- *
- * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
- */
-
-function init_quiz_form() {
- var responseform = document.getElementById('responseform');
- responseform.setAttribute('autocomplete', 'off');
- YAHOO.util.Event.addListener(responseform, 'keypress', check_enter);
- YAHOO.util.Event.addListener(responseform, 'submit', quiz_timer.stop);
-}
-
-/* Use this in an onkeypress handler, to stop enter submitting the forum unless you
-are actually on the submit button. Don't stop the user typing things in text areas. */
-function check_enter(e) {
- var target = e.target ? e.target : e.srcElement;
- var keyCode = e.keyCode ? e.keyCode : e.which;
- if (keyCode==13 && target.nodeName.toLowerCase()!='a' &&
- (!target.type || !(target.type=='submit' || target.type=='textarea'))) {
- YAHOO.util.Event.preventDefault(e);
- }
-}
-
-// Code for updating the countdown timer that is used on timed quizzes.
-quiz_timer = {
- // The outer div, so we can get at it to move it when the page scrolls.
- timerouter: null,
-
- // The element that the time should be displayed in.
- timerdisplay: null,
-
- // The main quiz for, which we will need to submit when the time expires.
- quizform: null,
-
- // String that is displayed after the time has run out.
- strtimeup: '',
-
- // How long is left, in seconds.
- endtime: 0,
-
- // How often we update the clock display. Delay in milliseconds.
- updatedelay: 500,
-
- // This records the id of the timeout that updates the clock periodically, so we can cancel it
- // Once time has run out.
- timeoutid: null,
-
- // Colours used to change the timer bacground colour when time had nearly run out.
- // This array is indexed by number of seconds left.
- finalcolours: [
- '#ff0000',
- '#ff1111',
- '#ff2222',
- '#ff3333',
- '#ff4444',
- '#ff5555',
- '#ff6666',
- '#ff7777',
- '#ff8888',
- '#ff9999',
- '#ffaaaa',
- '#ffbbbb',
- '#ffcccc',
- '#ffdddd',
- '#ffeeee',
- '#ffffff',
- ],
-
- // Initialise method.
- initialise: function(strtimeup, timeleft) {
- // Set some fields.
- quiz_timer.strtimeup = strtimeup;
- quiz_timer.endtime = new Date().getTime() + timeleft*1000;
-
- // Get references to some bits of the DOM we need.
- quiz_timer.timerouter = document.getElementById('quiz-timer');
- quiz_timer.timerdisplay = document.getElementById('quiz-time-left');
- quiz_timer.quizform = document.getElementById('responseform');
-
- // Make the timer visible.
- quiz_timer.timerouter.style.display = 'block';
-
- // Get things started.
- quiz_timer.update_time();
- },
-
- // Stop method. Stops the timer if it is running.
- stop: function() {
- if (quiz_timer.timeoutid) {
- clearTimeout(quiz_timer.timeoutid);
- }
- },
-
- // Function that updates the text displayed in element timer_display.
- set_displayed_time: function(str) {
- var display = quiz_timer.timerdisplay
- if (!display.firstChild) {
- display.appendChild(document.createTextNode(str))
- } else if (display.firstChild.nodeType == 3) {
- display.firstChild.replaceData(0, display.firstChild.length, str);
- } else {
- display.replaceChild(document.createTextNode(str), display.firstChild);
- }
- },
-
- // Function to convert a number between 0 and 99 to a two-digit string.
- two_digit: function(num) {
- if (num < 10) {
- return '0' + num;
- } else {
- return num;
- }
- },
-
- // Function to update the clock with the current time left, and submit the quiz if necessary.
- update_time: function() {
- var secondsleft = Math.floor((quiz_timer.endtime - new Date().getTime())/1000);
-
- // If time has expired, Set the hidden form field that says time has expired.
- if (secondsleft < 0) {
- quiz_timer.stop();
- quiz_timer.set_displayed_time(quiz_timer.strtimeup);
- quiz_timer.quizform.elements.timeup.value = 1;
- if (quiz_timer.quizform.onsubmit) {
- quiz_timer.quizform.onsubmit();
- }
- quiz_timer.quizform.submit();
- return;
- }
-
- // If time has nearly expired, change the colour.
- if (secondsleft < quiz_timer.finalcolours.length) {
- quiz_timer.timerouter.style.backgroundColor = quiz_timer.finalcolours[secondsleft];
- }
-
- // Update the time display.
- var hours = Math.floor(secondsleft/3600);
- secondsleft -= hours*3600;
- var minutes = Math.floor(secondsleft/60);
- secondsleft -= minutes*60;
- var seconds = secondsleft;
- quiz_timer.set_displayed_time('' + hours + ':' + quiz_timer.two_digit(minutes) + ':' +
- quiz_timer.two_digit(seconds));
-
- // Arrange for this method to be called again soon.
- quiz_timer.timeoutid = setTimeout(quiz_timer.update_time, quiz_timer.updatedelay);
- }
-};
-
-// Set up synchronisation between question flags and the corresponding button in the nav panel.
-function quiz_init_nav_flags() {
- var navblock = document.getElementById('quiznavigation');
- var buttons = YAHOO.util.Dom.getElementsByClassName('qnbutton', 'a', navblock);
- for (var i = 0; i < buttons.length; i++) {
- var button = buttons[i];
- var questionid = button.id.match(/\d+/)[0];
- button.stateupdater = new quiz_nav_updater(button, questionid);
- }
-}
-
-// Make the links in the attempt nav panel submit the form.
-function quiz_init_attempt_nav() {
- var warning = document.getElementById('quiznojswarning');
- warning.parentNode.removeChild(warning);
- var navblock = document.getElementById('quiznavigation');
- var buttons = YAHOO.util.Dom.getElementsByClassName('qnbutton', 'a', navblock);
- for (var i = 0; i < buttons.length; i++) {
- var button = buttons[i];
- if (YAHOO.util.Dom.hasClass(button, 'thispage')) {
- continue;
- }
- var pageidmatch = button.href.match(/page=(\d+)/);
- var page;
- if (pageidmatch) {
- page = pageidmatch[1];
- } else {
- page = 0;
- }
- var nav = {pageid: page};
- var questionidmatch = button.href.match(/#q(\d+)/);
- if (questionidmatch) {
- nav.questionid = questionidmatch[1];
- }
- YAHOO.util.Event.addListener(button, 'click', quiz_nav_button_click, nav);
- }
- var endlink = YAHOO.util.Dom.getElementsByClassName('endtestlink', 'a', navblock)[0];
- YAHOO.util.Event.addListener(endlink, 'click', quiz_end_test_click);
-}
-
-function quiz_nav_button_click(e, nav) {
- YAHOO.util.Event.preventDefault(e);
- document.getElementById('nextpagehiddeninput').value = nav.pageid;
- var form = document.getElementById('responseform');
- if (nav.questionid) {
- form.action += '#q' + nav.questionid;
- }
- form.submit();
-}
-
-function quiz_end_test_click(e) {
- YAHOO.util.Event.preventDefault(e);
- document.getElementById('nextpagehiddeninput').value = -1;
- document.getElementById('responseform').submit();
-}
-
-function quiz_nav_updater(element, questionid) {
- this.element = element;
- question_flag_changer.add_flag_state_listener(questionid, this);
-};
-
-quiz_nav_updater.prototype.flag_state_changed = function(newstate) {
- this.element.className = this.element.className.replace(/\s*\bflagged\b\s*/, ' ');
- if (newstate) {
- this.element.className += ' flagged';
- }
-};
-
-quiz_secure_window = {
- // The message displayed when the secure window interferes with the user.
- protection_message: null,
-
- // Used by close. The URL to redirect to, if we find we are not acutally in a pop-up window.
- close_next_url: '',
-
- // Code for secure window. This used to be in protect_js.php. I don't understand it,
- // I have just moved it for clenliness reasons.
- initialise: function(strmessage) {
- quiz_secure_window.protection_message = strmessage;
- if (document.layers) {
- document.captureEvents(Event.MOUSEDOWN);
- }
- document.onmousedown = quiz_secure_window.intercept_click;
- document.oncontextmenu = function() {alert(quiz_secure_window.protection_message); return false;};
- },
-
- // Code for secure window. This used to be in protect_js.php. I don't understand it,
- // I have just moved it for clenliness reasons.
- intercept_click: function(e) {
- if (document.all) {
- if (event.button==1) {
- return false;
- }
- if (event.button==2) {
- alert(quiz_securewindow_message);
- return false;
- }
- }
- if (document.layers) {
- if (e.which > 1) {
- alert(quiz_securewindow_message);
- return false;
- }
- }
- },
-
- close: function(url, delay) {
- if (url != '') {
- quiz_secure_window.close_next_url = url;
- }
- if (delay > 0) {
- setTimeout(function() {quiz_secure_window.close('', 0);}, delay*1000);
- } else {
- if (window.opener) {
- window.opener.document.location.reload();
- window.close();
- } else if (quiz_secure_window.close_next_url != '') {
- window.location.href = quiz_secure_window.close_next_url;
- }
- }
- }
-};
-
-function reveal_start_button() {
- document.getElementById('quizstartbutton').style.cssText = '';
-}
\ No newline at end of file
diff --git a/mod/quiz/review.php b/mod/quiz/review.php
index 15901714519..55c572bb416 100644
--- a/mod/quiz/review.php
+++ b/mod/quiz/review.php
@@ -85,7 +85,10 @@
}
}
-/// Arrange for the navigation to be displayed.
+ // Initialise the JavaScript.
+ $headtags = $attemptobj->get_html_head_contributions($page);
+
+ // Arrange for the navigation to be displayed.
$navbc = $attemptobj->get_navigation_panel('quiz_review_nav_panel', $page, $showall);
$firstregion = reset($PAGE->blocks->get_regions());
$PAGE->blocks->add_pretend_block($navbc, $firstregion);
@@ -213,7 +216,7 @@
/// Form for saving flags if necessary.
if ($options->flags == QUESTION_FLAGSEDITABLE) {
echo '