mirror of
https://github.com/moodle/moodle.git
synced 2025-08-04 08:26:37 +02:00
mod quiz + questions MDL-22370 convert JavaScript to YUI3 and modules.
Note, quiz editing JS has not been done yet.
This commit is contained in:
parent
24f17d7588
commit
ff065f96bc
15 changed files with 418 additions and 478 deletions
|
@ -2083,16 +2083,31 @@ function question_format_grade($cmoptions, $grade) {
|
|||
*/
|
||||
function question_init_qengine_js() {
|
||||
global $CFG, $PAGE, $OUTPUT;
|
||||
$config = array(
|
||||
'actionurl' => $CFG->wwwroot . '/question/toggleflag.php',
|
||||
'flagicon' => '' . $OUTPUT->pix_url('i/flagged'),
|
||||
'unflagicon' => '' . $OUTPUT->pix_url('i/unflagged'),
|
||||
'flagtooltip' => get_string('clicktoflag', 'question'),
|
||||
'unflagtooltip' => get_string('clicktounflag', 'question'),
|
||||
'flaggedalt' => get_string('flagged', 'question'),
|
||||
'unflaggedalt' => get_string('notflagged', 'question'),
|
||||
static $done = false;
|
||||
if ($done) {
|
||||
return;
|
||||
}
|
||||
$module = array(
|
||||
'name' => 'core_question_flags',
|
||||
'fullpath' => '/question/flags.js',
|
||||
'requires' => array('base', 'dom', 'event-delegate', 'io-base'),
|
||||
);
|
||||
$PAGE->requires->data_for_js('qengine_config', $config);
|
||||
$actionurl = $CFG->wwwroot . '/question/toggleflag.php';
|
||||
$flagattributes = array(
|
||||
0 => array(
|
||||
'src' => $OUTPUT->pix_url('i/unflagged') . '',
|
||||
'title' => get_string('clicktoflag', 'question'),
|
||||
'alt' => get_string('notflagged', 'question'),
|
||||
),
|
||||
1 => array(
|
||||
'src' => $OUTPUT->pix_url('i/flagged') . '',
|
||||
'title' => get_string('clicktounflag', 'question'),
|
||||
'alt' => get_string('flagged', 'question'),
|
||||
),
|
||||
);
|
||||
$PAGE->requires->js_init_call('M.core_question_flags.init',
|
||||
array($actionurl, $flagattributes), false, $module);
|
||||
$done = true;
|
||||
}
|
||||
|
||||
/// FUNCTIONS THAT SIMPLY WRAP QUESTIONTYPE METHODS //////////////////////////////////
|
||||
|
@ -2109,12 +2124,10 @@ function question_init_qengine_js() {
|
|||
* @param array $states an array of question state objects, whose keys are question ids.
|
||||
* Must contain the state of all the questions in $questionlist
|
||||
*/
|
||||
function get_html_head_contributions($questionlist, &$questions, &$states) {
|
||||
function question_get_html_head_contributions($questionlist, &$questions, &$states) {
|
||||
global $CFG, $PAGE, $QTYPES;
|
||||
|
||||
// The question engine's own JavaScript.
|
||||
$PAGE->requires->yui2_lib('connection');
|
||||
$PAGE->requires->js('/question/qengine.js');
|
||||
question_init_qengine_js();
|
||||
|
||||
// Anything that questions on this page need.
|
||||
|
@ -2131,7 +2144,7 @@ function get_html_head_contributions($questionlist, &$questions, &$states) {
|
|||
* @param $question A question object. Only $question->qtype is used.
|
||||
* @return string Deprecated. Some HTML code that can go inside the head tag.
|
||||
*/
|
||||
function get_editing_head_contributions($question) {
|
||||
function question_get_editing_head_contributions($question) {
|
||||
global $QTYPES;
|
||||
$QTYPES[$question->qtype]->get_editing_head_contributions();
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -88,6 +88,10 @@
|
|||
|
||||
/// Print the quiz page ////////////////////////////////////////////////////////
|
||||
|
||||
// Initialise the JavaScript.
|
||||
$headtags = $attemptobj->get_html_head_contributions($page);
|
||||
$PAGE->requires->js_init_call('M.mod_quiz.init_attempt_form', null, false, quiz_get_js_module());
|
||||
|
||||
// Arrange for the navigation to be displayed.
|
||||
$navbc = $attemptobj->get_navigation_panel('quiz_attempt_nav_panel', $page);
|
||||
$firstregion = reset($PAGE->blocks->get_regions());
|
||||
|
@ -95,7 +99,6 @@
|
|||
|
||||
// Print the page header
|
||||
$title = get_string('attempt', 'quiz', $attemptobj->get_attempt_number());
|
||||
$headtags = $attemptobj->get_html_head_contributions($page);
|
||||
$PAGE->set_heading($attemptobj->get_course()->fullname);
|
||||
if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) {
|
||||
$accessmanager->setup_secure_page($attemptobj->get_course()->shortname . ': ' .
|
||||
|
@ -129,11 +132,6 @@
|
|||
// Start the form
|
||||
echo '<form id="responseform" method="post" action="', s($attemptobj->processattempt_url()),
|
||||
'" enctype="multipart/form-data" accept-charset="utf-8">', "\n";
|
||||
|
||||
// A quiz page with a lot of questions can take a long time to load, and we
|
||||
// want the protection afforded by init_quiz_form immediately, so include the
|
||||
// JS now.
|
||||
echo html_writer::script(js_writer::function_call('init_quiz_form'));
|
||||
echo '<div>';
|
||||
|
||||
/// Print all the questions
|
||||
|
@ -154,7 +152,7 @@
|
|||
// Some hidden fields to trach what is going on.
|
||||
echo '<input type="hidden" name="attempt" value="' . $attemptobj->get_attemptid() . '" />';
|
||||
echo '<input type="hidden" name="nextpage" id="nextpagehiddeninput" value="' . $nextpage . '" />';
|
||||
echo '<input type="hidden" name="timeup" id="timeup" value="0" />';
|
||||
echo '<input type="hidden" name="timeup" value="0" />';
|
||||
echo '<input type="hidden" name="sesskey" value="' . sesskey() . '" />';
|
||||
|
||||
// Add a hidden field with questionids. Do this at the end of the form, so
|
||||
|
|
|
@ -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 .= '<a href="' . s($this->attemptobj->summary_url()) . '" class="endtestlink">' . get_string('endtest', 'quiz') . '</a>';
|
||||
$output .= $this->attemptobj->get_timer_html();
|
||||
$output .= html_writer::script(js_writer::function_call('quiz_init_attempt_nav'));
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -357,6 +357,7 @@ $string['formulaerror'] = 'Formula errors!';
|
|||
$string['fractionsaddwrong'] = 'The positive grades you have chosen do not add up to 100%<br />Instead, they add up to {$a}%<br />Do you want to go back and fix this question?';
|
||||
$string['fractionsnomax'] = 'One of the answers should be 100%, so that it is<br />possible to get a full grade for this question.<br />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';
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
);
|
||||
}
|
220
mod/quiz/module.js
Normal file
220
mod/quiz/module.js
Normal file
|
@ -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);
|
||||
}
|
||||
};
|
276
mod/quiz/quiz.js
276
mod/quiz/quiz.js
|
@ -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 = '';
|
||||
}
|
|
@ -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 '<form action="' . s($attemptobj->review_url(0, $page, $showall)) .
|
||||
'" method="post"><div>';
|
||||
'" method="post" class="questionflagsaveform"><div>';
|
||||
echo '<input type="hidden" name="sesskey" value="' . sesskey() . '" />';
|
||||
}
|
||||
|
||||
|
@ -232,11 +235,11 @@
|
|||
/// Close form if we opened it.
|
||||
if ($options->flags == QUESTION_FLAGSEDITABLE) {
|
||||
echo '<div class="submitbtns">' . "\n" .
|
||||
'<input type="submit" id="savingflagssubmit" name="savingflags" value="' .
|
||||
'<input type="submit" class="questionflagsavebutton" name="savingflags" value="' .
|
||||
get_string('saveflags', 'question') . '" />' .
|
||||
"</div>\n" .
|
||||
"\n</div></form>\n";
|
||||
$PAGE->requires->js_function_call('question_flag_changer.init_flag_save_form', array('savingflagssubmit'));
|
||||
$PAGE->requires->js_init_call('M.mod_quiz.init_review_form', null, false, quiz_get_js_module());
|
||||
}
|
||||
|
||||
/// Print a link to the next page.
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#page-mod-quiz-summary #page .controls,
|
||||
#page-mod-quiz-review #page .controls {text-align: center;margin: 8px auto;}
|
||||
|
||||
body.jsenabled .questionflagcheckbox {display: none;}
|
||||
|
||||
#page-mod-quiz-edit div.question div.content .questiontext,
|
||||
#categoryquestions .questiontext {-o-text-overflow:ellipsis;text-overflow:ellipsis;position:relative;zoom:1;padding-left:0.3em;max-width:40%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}
|
||||
|
||||
|
@ -53,11 +55,37 @@ table#categoryquestions td,
|
|||
#page-mod-quiz-view .generalbox#feedback h2 {margin: 0;}
|
||||
#page-mod-quiz-view .generalbox#feedback h3 {text-align: left;}
|
||||
#page-mod-quiz-view .generalbox#feedback .overriddennotice {text-align: center;font-size: 0.7em;}
|
||||
.quizstartbuttondiv.quizsecuremoderequired input { display: none; }
|
||||
.jsenabled .quizstartbuttondiv.quizsecuremoderequired input { display: inline; }
|
||||
|
||||
/** Mod quiz summary **/
|
||||
|
||||
#page-mod-quiz-summary #content {text-align: center;}
|
||||
#page-mod-quiz-summary .questionflag {width: 16px;height: 16px;vertical-align: middle;}
|
||||
#page-mod-quiz-summary #quiz-timer {margin-top: 1em;display: none; /* Revealed by JavaScript if applicable */}
|
||||
#page-mod-quiz-summary #quiz-timer {text-align: center;}
|
||||
@media print {
|
||||
.quiz-secure-window * { display: none !important; }
|
||||
}
|
||||
|
||||
/** Countdown timer. */
|
||||
#quiz-timer {display: none; margin-top: 1em;}
|
||||
#quiz-time-left {font-weight: bold;}
|
||||
#quiz-timer.timeleft15 {background: #ffffff;}
|
||||
#quiz-timer.timeleft14 {background: #ffeeee;}
|
||||
#quiz-timer.timeleft13 {background: #ffdddd;}
|
||||
#quiz-timer.timeleft12 {background: #ffcccc;}
|
||||
#quiz-timer.timeleft11 {background: #ffbbbb;}
|
||||
#quiz-timer.timeleft10 {background: #ffaaaa;}
|
||||
#quiz-timer.timeleft9 {background: #ff9999;}
|
||||
#quiz-timer.timeleft8 {background: #ff8888;}
|
||||
#quiz-timer.timeleft7 {background: #ff7777;}
|
||||
#quiz-timer.timeleft6 {background: #ff6666;}
|
||||
#quiz-timer.timeleft5 {background: #ff5555;}
|
||||
#quiz-timer.timeleft4 {background: #ff4444;}
|
||||
#quiz-timer.timeleft3 {background: #ff3333;}
|
||||
#quiz-timer.timeleft2 {background: #ff2222;}
|
||||
#quiz-timer.timeleft1 {background: #ff1111;}
|
||||
#quiz-timer.timeleft0 {background: #ff0000;}
|
||||
|
||||
/** Mod quiz review **/
|
||||
#page-mod-quiz-review .pagingbar {margin: 1.5em auto;}
|
||||
|
@ -99,10 +127,6 @@ table#categoryquestions td,
|
|||
#page-mod-quiz-grading table#grading td {border-left-width: 1px;border-right-width: 1px;border-left-style: solid;border-right-style: solid;vertical-align: bottom;}
|
||||
|
||||
/** Mod quiz attempt **/
|
||||
#page-mod-quiz-attempt #quiz-timer {display: none; /* Revealed by JavaScript if applicable */}
|
||||
#page-mod-quiz-attempt #quiz-time-left {font-weight: bold;}
|
||||
#page-mod-quiz-attempt #quiz-timer-outer {border-color: #dddddd;background: white;}
|
||||
|
||||
#categoryquestions .r1 {background: #e4e4e4;}
|
||||
#categoryquestions .header {text-align: center;padding: 0 2px;border: 0 none;}
|
||||
#categoryquestions th.modifiername .sorters,
|
||||
|
|
|
@ -51,7 +51,6 @@ if (empty($attemptobj->get_quiz()->showblocks)) {
|
|||
}
|
||||
|
||||
/// Print the page header
|
||||
$PAGE->requires->js('/mod/quiz/quiz.js');
|
||||
$title = get_string('summaryofattempt', 'quiz');
|
||||
if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) {
|
||||
$accessmanager->setup_secure_page($attemptobj->get_course()->shortname . ': ' .
|
||||
|
|
65
question/flags.js
Normal file
65
question/flags.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
// This script, and the YUI libraries that it needs, are inluded by
|
||||
// the $PAGE->requires->js calls in question_get_html_head_contributions in lib/questionlib.php.
|
||||
|
||||
M.core_question_flags = {
|
||||
flagattributes: null,
|
||||
actionurl: null,
|
||||
listeners: [],
|
||||
|
||||
init: function(Y, actionurl, flagattributes) {
|
||||
M.core_question_flags.flagattributes = flagattributes;
|
||||
M.core_question_flags.actionurl = actionurl;
|
||||
|
||||
Y.all('div.questionflag').each(function(flagdiv, i) {
|
||||
var checkbox = flagdiv.one('input.questionflagcheckbox');
|
||||
if (!checkbox) {
|
||||
return;
|
||||
}
|
||||
|
||||
var input = Y.Node.create('<input type="hidden" />');
|
||||
input.set('id', checkbox.get('id'));
|
||||
input.set('name', checkbox.get('name'));
|
||||
input.set('value', checkbox.get('checked') ? 1 : 0);
|
||||
|
||||
// Create an image input to replace the img tag.
|
||||
var image = Y.Node.create('<input type="image" class="questionflagimage" />');
|
||||
M.core_question_flags.update_flag(input, image);
|
||||
|
||||
checkbox.remove();
|
||||
flagdiv.one('label').remove();
|
||||
flagdiv.append(input);
|
||||
flagdiv.append(image);
|
||||
});
|
||||
|
||||
Y.delegate('click', function(e) {
|
||||
var input = this.previous('input');
|
||||
input.set('value', 1 - input.get('value'));
|
||||
M.core_question_flags.update_flag(input, this);
|
||||
var postdata = this.previous('input.questionflagpostdata').get('value') +
|
||||
input.get('value')
|
||||
|
||||
e.halt();
|
||||
Y.io(M.core_question_flags.actionurl , {method: 'POST', 'data': postdata});
|
||||
M.core_question_flags.fire_listeners(postdata);
|
||||
}, document.body, 'input.questionflagimage');
|
||||
|
||||
},
|
||||
|
||||
update_flag: function(input, image) {
|
||||
image.setAttrs(M.core_question_flags.flagattributes[input.get('value')]);
|
||||
},
|
||||
|
||||
add_listener: function(listener) {
|
||||
M.core_question_flags.listeners.push(listener);
|
||||
},
|
||||
|
||||
fire_listeners: function(postdata) {
|
||||
for (var i = 0; i < M.core_question_flags.listeners.length; i++) {
|
||||
M.core_question_flags.listeners[i](
|
||||
postdata.match(/\baid=(\d+)\b/)[1],
|
||||
postdata.match(/\bqid=(\d+)\b/)[1],
|
||||
postdata.match(/\bnewstate=(\d+)\b/)[1]
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -207,7 +207,7 @@
|
|||
|
||||
$strpreview = get_string('preview', 'quiz').' '.format_string($questions[$id]->name);
|
||||
$questionlist = array($id);
|
||||
get_html_head_contributions($questionlist, $questions, $states[$historylength]);
|
||||
question_get_html_head_contributions($questionlist, $questions, $states[$historylength]);
|
||||
$PAGE->set_title($strpreview);
|
||||
$PAGE->set_heading($COURSE->fullname);
|
||||
echo $OUTPUT->header();
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
// This script, and the YUI libraries that it needs, are inluded by
|
||||
// the $PAGE->requires->js calls in get_html_head_contributions in lib/questionlib.php.
|
||||
|
||||
question_flag_changer = {
|
||||
flag_state_listeners: new Object(),
|
||||
|
||||
init_flag: function(checkboxid, postdata) {
|
||||
// Create a hidden input - you can't just repurpose the old checkbox, IE
|
||||
// does not cope - and put it in place of the checkbox.
|
||||
var checkbox = document.getElementById(checkboxid);
|
||||
var input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
checkbox.parentNode.appendChild(input);
|
||||
checkbox.parentNode.removeChild(checkbox);
|
||||
input.id = checkbox.id;
|
||||
input.name = checkbox.name;
|
||||
input.value = checkbox.checked ? 1 : 0;
|
||||
input.ajaxpostdata = postdata;
|
||||
|
||||
// Create an image input to replace the img tag.
|
||||
var image = document.createElement('input');
|
||||
image.type = 'image';
|
||||
image.statestore = input;
|
||||
question_flag_changer.update_image(image);
|
||||
input.parentNode.appendChild(image);
|
||||
|
||||
// Remove the label.
|
||||
var label = document.getElementById(checkboxid + 'label');
|
||||
label.parentNode.removeChild(label);
|
||||
|
||||
// Add the event handler.
|
||||
YAHOO.util.Event.addListener(image, 'click', this.flag_state_change);
|
||||
},
|
||||
|
||||
init_flag_save_form: function(submitbuttonid) {
|
||||
// With JS on, we just want to remove all visible traces of the form.
|
||||
var button = document.getElementById(submitbuttonid);
|
||||
button.parentNode.removeChild(button);
|
||||
},
|
||||
|
||||
flag_state_change: function(e) {
|
||||
var image = e.target ? e.target : e.srcElement;
|
||||
var input = image.statestore;
|
||||
input.value = 1 - input.value;
|
||||
question_flag_changer.update_image(image);
|
||||
var postdata = input.ajaxpostdata
|
||||
if (input.value == 1) {
|
||||
postdata += '&newstate=1'
|
||||
} else {
|
||||
postdata += '&newstate=0'
|
||||
}
|
||||
YAHOO.util.Event.preventDefault(e);
|
||||
question_flag_changer.fire_state_changed(input);
|
||||
YAHOO.util.Connect.asyncRequest('POST', qengine_config.actionurl, null, postdata);
|
||||
},
|
||||
|
||||
update_image: function(image) {
|
||||
if (image.statestore.value == 1) {
|
||||
image.src = qengine_config.flagicon;
|
||||
image.alt = qengine_config.flaggedalt;
|
||||
image.title = qengine_config.unflagtooltip;
|
||||
} else {
|
||||
image.src = qengine_config.unflagicon;
|
||||
image.alt = qengine_config.unflaggedalt;
|
||||
image.title = qengine_config.flagtooltip;
|
||||
}
|
||||
},
|
||||
|
||||
add_flag_state_listener: function(questionid, listener) {
|
||||
var key = 'q' + questionid;
|
||||
if (!question_flag_changer.flag_state_listeners.hasOwnProperty(key)) {
|
||||
question_flag_changer.flag_state_listeners[key] = [];
|
||||
}
|
||||
question_flag_changer.flag_state_listeners[key].push(listener);
|
||||
},
|
||||
|
||||
questionid_from_inputid: function(inputid) {
|
||||
return inputid.replace(/resp(\d+)__flagged/, '$1');
|
||||
},
|
||||
|
||||
fire_state_changed: function(input) {
|
||||
var questionid = question_flag_changer.questionid_from_inputid(input.id);
|
||||
var key = 'q' + questionid;
|
||||
if (!question_flag_changer.flag_state_listeners.hasOwnProperty(key)) {
|
||||
return;
|
||||
}
|
||||
var newstate = input.value == 1;
|
||||
for (var i = 0; i < question_flag_changer.flag_state_listeners[key].length; i++) {
|
||||
question_flag_changer.flag_state_listeners[key][i].flag_state_changed(newstate);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -967,12 +967,15 @@ class default_questiontype {
|
|||
$aid = $state->attempt;
|
||||
$qid = $state->question;
|
||||
$checksum = question_get_toggleflag_checksum($aid, $qid, $qsid);
|
||||
$postdata = "qsid=$qsid&aid=$aid&qid=$qid&checksum=$checksum&sesskey=" . sesskey();
|
||||
$postdata = "qsid=$qsid&aid=$aid&qid=$qid&checksum=$checksum&sesskey=" .
|
||||
sesskey() . '&newstate=';
|
||||
$flagcontent = '<input type="checkbox" id="' . $id . '" name="' . $id .
|
||||
'" value="1" ' . $checked . ' />' .
|
||||
'<label id="' . $id . 'label" for="' . $id . '">' . $this->get_question_flag_tag(
|
||||
'" class="questionflagcheckbox" value="1" ' . $checked . ' />' .
|
||||
'<input type="hidden" value="' . s($postdata) . '" class="questionflagpostdata" />' .
|
||||
'<label id="' . $id . 'label" for="' . $id .
|
||||
'" class="questionflaglabel">' . $this->get_question_flag_tag(
|
||||
$state->flagged, $id . 'img') . '</label>' . "\n";
|
||||
$PAGE->requires->js_function_call('question_flag_changer.init_flag', array($id, $postdata));
|
||||
question_init_qengine_js();
|
||||
break;
|
||||
default:
|
||||
$flagcontent = '';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue