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:
Tim Hunt 2010-08-04 15:22:04 +00:00
parent 24f17d7588
commit ff065f96bc
15 changed files with 418 additions and 478 deletions

View file

@ -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

View file

@ -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

View file

@ -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;
}
}

View file

@ -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';

View file

@ -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
View 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);
}
};

View file

@ -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 = '';
}

View file

@ -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.

View file

@ -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,

View file

@ -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
View 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]
);
}
}
};

View file

@ -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();

View file

@ -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);
}
}
};

View file

@ -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 = '';