MDL-29254 Fixed whitespace
[moodle.git] / mod / quiz / module.js
blob63c44d65d8d50513a3c13c48a30ec52daacdcadc
1 // This file is part of Moodle - http://moodle.org/
2 //
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16 /**
17  * JavaScript library for the quiz module.
18  *
19  * @package    mod
20  * @subpackage quiz
21  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 M.mod_quiz = M.mod_quiz || {};
27 M.mod_quiz.init_attempt_form = function(Y) {
28     M.core_question_engine.init_form(Y, '#responseform');
29     Y.on('submit', M.mod_quiz.timer.stop, '#responseform');
32 M.mod_quiz.init_review_form = function(Y) {
33     M.core_question_engine.init_form(Y, '.questionflagsaveform');
34     Y.on('submit', function(e) { e.halt(); }, '.questionflagsaveform');
37 M.mod_quiz.init_comment_popup = function(Y) {
38     // Add a close button to the window.
39     var closebutton = Y.Node.create('<input type="button" />');
40     closebutton.set('value', M.util.get_string('cancel', 'moodle'));
41     Y.one('#id_submitbutton').ancestor().append(closebutton);
42     Y.on('click', function() { window.close() }, closebutton);
45 // Code for updating the countdown timer that is used on timed quizzes.
46 M.mod_quiz.timer = {
47     // YUI object.
48     Y: null,
50     // Timestamp at which time runs out, according to the student's computer's clock.
51     endtime: 0,
53     // This records the id of the timeout that updates the clock periodically,
54     // so we can cancel.
55     timeoutid: null,
57     /**
58      * @param Y the YUI object
59      * @param timeleft, the time remaining, in seconds.
60      */
61     init: function(Y, timeleft) {
62         M.mod_quiz.timer.Y = Y;
63         M.mod_quiz.timer.endtime = new Date().getTime() + timeleft*1000;
64         M.mod_quiz.timer.update();
65         Y.one('#quiz-timer').setStyle('display', 'block');
66     },
68     /**
69      * Stop the timer, if it is running.
70      */
71     stop: function(e) {
72         if (M.mod_quiz.timer.timeoutid) {
73             clearTimeout(M.mod_quiz.timer.timeoutid);
74         }
75     },
77     /**
78      * Function to convert a number between 0 and 99 to a two-digit string.
79      */
80     two_digit: function(num) {
81         if (num < 10) {
82             return '0' + num;
83         } else {
84             return num;
85         }
86     },
88     // Function to update the clock with the current time left, and submit the quiz if necessary.
89     update: function() {
90         var Y = M.mod_quiz.timer.Y;
91         var secondsleft = Math.floor((M.mod_quiz.timer.endtime - new Date().getTime())/1000);
93         // If time has expired, Set the hidden form field that says time has expired.
94         if (secondsleft < 0) {
95             M.mod_quiz.timer.stop(null);
96             Y.one('#quiz-time-left').setContent(M.str.quiz.timesup);
97             var input = Y.one('input[name=timeup]');
98             input.set('value', 1);
99             input.ancestor('form').submit();
100             return;
101         }
103         // If time has nearly expired, change the colour.
104         if (secondsleft < 100) {
105             Y.one('#quiz-timer').removeClass('timeleft' + (secondsleft + 2))
106                     .removeClass('timeleft' + (secondsleft + 1))
107                     .addClass('timeleft' + secondsleft);
108         }
110         // Update the time display.
111         var hours = Math.floor(secondsleft/3600);
112         secondsleft -= hours*3600;
113         var minutes = Math.floor(secondsleft/60);
114         secondsleft -= minutes*60;
115         var seconds = secondsleft;
116         Y.one('#quiz-time-left').setContent(hours + ':' +
117                 M.mod_quiz.timer.two_digit(minutes) + ':' +
118                 M.mod_quiz.timer.two_digit(seconds));
120         // Arrange for this method to be called again soon.
121         M.mod_quiz.timer.timeoutid = setTimeout(M.mod_quiz.timer.update, 100);
122     }
125 M.mod_quiz.nav = M.mod_quiz.nav || {};
127 M.mod_quiz.nav.update_flag_state = function(attemptid, questionid, newstate) {
128     var Y = M.mod_quiz.nav.Y;
129     var navlink = Y.one('#quiznavbutton' + questionid);
130     navlink.removeClass('flagged');
131     if (newstate == 1) {
132         navlink.addClass('flagged');
133         navlink.one('.accesshide .flagstate').setContent(M.str.question.flagged);
134     } else {
135         navlink.one('.accesshide .flagstate').setContent('');
136     }
139 M.mod_quiz.nav.init = function(Y) {
140     M.mod_quiz.nav.Y = Y;
142     Y.all('#quiznojswarning').remove();
144     var form = Y.one('#responseform');
145     if (form) {
146         Y.delegate('click', function(e) {
147             if (this.hasClass('thispage')) {
148                 return;
149             }
151             e.preventDefault();
153             var pageidmatch = this.get('href').match(/page=(\d+)/);
154             var pageno;
155             if (pageidmatch) {
156                 pageno = pageidmatch[1];
157             } else {
158                 pageno = 0;
159             }
160             Y.one('#followingpage').set('value', pageno);
162             var questionidmatch = this.get('href').match(/#q(\d+)/);
163             if (questionidmatch) {
164                 form.set('action', form.get('action') + '#q' + questionidmatch[1]);
165             }
167             form.submit();
168         }, document.body, '.qnbutton');
169     }
171     if (Y.one('a.endtestlink')) {
172         Y.on('click', function(e) {
173             e.preventDefault();
174             Y.one('#followingpage').set('value', -1);
175             Y.one('#responseform').submit();
176         }, 'a.endtestlink');
177     }
179     if (M.core_question_flags) {
180         M.core_question_flags.add_listener(M.mod_quiz.nav.update_flag_state);
181     }
184 M.mod_quiz.secure_window = {
185     init: function(Y) {
186         if (window.location.href.substring(0, 4) == 'file') {
187             window.location = 'about:blank';
188         }
189         Y.delegate('contextmenu', M.mod_quiz.secure_window.prevent, document, '*');
190         Y.delegate('mousedown', M.mod_quiz.secure_window.prevent_mouse, document, '*');
191         Y.delegate('mouseup', M.mod_quiz.secure_window.prevent_mouse, document, '*');
192         Y.delegate('dragstart', M.mod_quiz.secure_window.prevent, document, '*');
193         Y.delegate('selectstart', M.mod_quiz.secure_window.prevent, document, '*');
194         M.mod_quiz.secure_window.clear_status;
195         Y.on('beforeprint', function() {
196             Y.one(document.body).setStyle('display', 'none');
197         }, window);
198         Y.on('afterprint', function() {
199             Y.one(document.body).setStyle('display', 'block');
200         }, window);
201         Y.on('key', M.mod_quiz.secure_window.prevent, '*', 'press:67,86,88+ctrl');
202         Y.on('key', M.mod_quiz.secure_window.prevent, '*', 'up:67,86,88+ctrl');
203         Y.on('key', M.mod_quiz.secure_window.prevent, '*', 'down:67,86,88+ctrl');
204         Y.on('key', M.mod_quiz.secure_window.prevent, '*', 'press:67,86,88+meta');
205         Y.on('key', M.mod_quiz.secure_window.prevent, '*', 'up:67,86,88+meta');
206         Y.on('key', M.mod_quiz.secure_window.prevent, '*', 'down:67,86,88+meta');
207     },
209     clear_status: function() {
210         window.status = '';
211         setTimeout(M.mod_quiz.secure_window.clear_status, 10);
212     },
214     prevent: function(e) {
215         alert(M.str.quiz.functiondisabledbysecuremode);
216         e.halt();
217     },
219     prevent_mouse: function(e) {
220         if (e.button == 1 && /^(INPUT|TEXTAREA|BUTTON|SELECT|LABEL|A)$/i.test(e.target.get('tagName'))) {
221             // Left click on a button or similar. No worries.
222             return;
223         }
224         e.halt();
225     },
227     /**
228      * Event handler for the quiz start attempt button.
229      */
230     start_attempt_action: function(e, args) {
231         if (args.startattemptwarning == '') {
232             openpopup(e, args);
233         } else {
234             M.util.show_confirm_dialog(e, {
235                 message: args.startattemptwarning,
236                 callback: function() {
237                     openpopup(e, args);
238                 },
239                 continuelabel: M.util.get_string('startattempt', 'quiz')
240             });
241         }
242     },
244     init_close_button: function(Y, url) {
245         Y.on('click', function(e) {
246             M.mod_quiz.secure_window.close(url, 0)
247         }, '#secureclosebutton');
248     },
250     close: function(url, delay) {
251         setTimeout(function() {
252             if (window.opener) {
253                 window.opener.document.location.reload();
254                 window.close();
255             } else {
256                 window.location.href = url;
257             }
258         }, delay*1000);
259     }