Commit | Line | Data |
---|---|---|
e1a2d0d9 CC |
1 | YUI.add('moodle-mod_quiz-toolboxes', function (Y, NAME) { |
2 | ||
3 | /** | |
4 | * Resource and activity toolbox class. | |
5 | * | |
6 | * This class is responsible for managing AJAX interactions with activities and resources | |
7 | * when viewing a course in editing mode. | |
8 | * | |
9 | * @module moodle-course-toolboxes | |
10 | * @namespace M.course.toolboxes | |
11 | */ | |
12 | ||
13 | // The CSS classes we use. | |
14 | var CSS = { | |
15 | ACTIVITYINSTANCE : 'activityinstance', | |
16 | AVAILABILITYINFODIV : 'div.availabilityinfo', | |
17 | CONTENTWITHOUTLINK : 'contentwithoutlink', | |
18 | CONDITIONALHIDDEN : 'conditionalhidden', | |
19 | DIMCLASS : 'dimmed', | |
20 | DIMMEDTEXT : 'dimmed_text', | |
21 | EDITINSTRUCTIONS : 'editinstructions', | |
22 | EDITINGMAXMARK: 'editor_displayed', | |
23 | HIDE : 'hide', | |
24 | JOIN: 'page_join', | |
25 | MODINDENTCOUNT : 'mod-indent-', | |
26 | MODINDENTHUGE : 'mod-indent-huge', | |
27 | MODULEIDPREFIX : 'slot-', | |
28 | PAGE: 'page', | |
29 | SECTIONHIDDENCLASS : 'hidden', | |
30 | SECTIONIDPREFIX : 'section-', | |
31 | SLOT : 'slot', | |
32 | SHOW : 'editing_show', | |
33 | TITLEEDITOR : 'titleeditor' | |
34 | }, | |
35 | // The CSS selectors we use. | |
36 | SELECTOR = { | |
37 | ACTIONAREA: '.actions', | |
38 | ACTIONLINKTEXT : '.actionlinktext', | |
39 | ACTIVITYACTION : 'a.cm-edit-action[data-action], a.editing_maxmark', | |
40 | ACTIVITYFORM : 'span.instancemaxmarkcontainer form', | |
41 | ACTIVITYICON : 'img.activityicon', | |
42 | ACTIVITYINSTANCE : '.' + CSS.ACTIVITYINSTANCE, | |
43 | ACTIVITYLINK: '.' + CSS.ACTIVITYINSTANCE + ' > a', | |
44 | ACTIVITYLI : 'li.activity', | |
45 | ACTIVITYMAXMARK : 'input[name=maxmark]', | |
46 | COMMANDSPAN : '.commands', | |
47 | CONTENTAFTERLINK : 'div.contentafterlink', | |
48 | CONTENTWITHOUTLINK : 'div.contentwithoutlink', | |
49 | EDITMAXMARK: 'a.editing_maxmark', | |
50 | HIDE : 'a.editing_hide', | |
51 | HIGHLIGHT : 'a.editing_highlight', | |
52 | INSTANCENAME : 'span.instancename', | |
53 | INSTANCEMAXMARK : 'span.instancemaxmark', | |
54 | MODINDENTDIV : '.mod-indent', | |
55 | MODINDENTOUTER : '.mod-indent-outer', | |
6375e98c | 56 | NUMQUESTIONS : '.numberofquestions', |
e1a2d0d9 CC |
57 | PAGECONTENT : 'div#page-content', |
58 | PAGELI : 'li.page', | |
59 | SECTIONUL : 'ul.section', | |
60 | SHOW : 'a.' + CSS.SHOW, | |
61 | SHOWHIDE : 'a.editing_showhide', | |
62 | SLOTLI : 'li.slot', | |
63 | SUMMARKS : '.mod_quiz_summarks' | |
64 | }, | |
65 | BODY = Y.one(document.body); | |
66 | ||
67 | // Setup the basic namespace. | |
68 | M.mod_quiz = M.mod_quiz || {}; | |
69 | ||
70 | /** | |
71 | * The toolbox class is a generic class which should never be directly | |
72 | * instantiated. Please extend it instead. | |
73 | * | |
74 | * @class toolbox | |
75 | * @constructor | |
76 | * @protected | |
77 | * @extends Base | |
78 | */ | |
79 | var TOOLBOX = function() { | |
80 | TOOLBOX.superclass.constructor.apply(this, arguments); | |
81 | }; | |
82 | ||
83 | Y.extend(TOOLBOX, Y.Base, { | |
84 | /** | |
85 | * Send a request using the REST API | |
86 | * | |
87 | * @method send_request | |
88 | * @param {Object} data The data to submit with the AJAX request | |
89 | * @param {Node} [statusspinner] A statusspinner which may contain a section loader | |
90 | * @param {Function} success_callback The callback to use on success | |
91 | * @param {Object} [optionalconfig] Any additional configuration to submit | |
92 | * @chainable | |
93 | */ | |
94 | send_request: function(data, statusspinner, success_callback, optionalconfig) { | |
95 | // Default data structure | |
96 | if (!data) { | |
97 | data = {}; | |
98 | } | |
99 | // Handle any variables which we must pass back through to | |
100 | var pageparams = this.get('config').pageparams, | |
101 | varname; | |
102 | for (varname in pageparams) { | |
103 | data[varname] = pageparams[varname]; | |
104 | } | |
105 | ||
106 | data.sesskey = M.cfg.sesskey; | |
107 | data.courseid = this.get('courseid'); | |
108 | data.quizid = this.get('quizid'); | |
109 | ||
110 | var uri = M.cfg.wwwroot + this.get('ajaxurl'); | |
111 | ||
112 | // Define the configuration to send with the request | |
113 | var responsetext = []; | |
114 | var config = { | |
115 | method: 'POST', | |
116 | data: data, | |
117 | on: { | |
118 | success: function(tid, response) { | |
119 | try { | |
120 | responsetext = Y.JSON.parse(response.responseText); | |
121 | if (responsetext.error) { | |
122 | new M.core.ajaxException(responsetext); | |
123 | } | |
124 | } catch (e) {} | |
125 | ||
126 | // Run the callback if we have one. | |
1a14f8a6 | 127 | if (responsetext.hasOwnProperty('newsummarks')) { |
e1a2d0d9 CC |
128 | Y.one(SELECTOR.SUMMARKS).setHTML(responsetext.newsummarks); |
129 | } | |
1a14f8a6 | 130 | if (responsetext.hasOwnProperty('newnumquestions')) { |
6375e98c CC |
131 | Y.one(SELECTOR.NUMQUESTIONS).setHTML(M.util.get_string('numquestionsx', 'quiz', responsetext.newnumquestions)); |
132 | } | |
e1a2d0d9 CC |
133 | if (success_callback) { |
134 | Y.bind(success_callback, this, responsetext)(); | |
135 | } | |
136 | ||
137 | if (statusspinner) { | |
138 | window.setTimeout(function() { | |
139 | statusspinner.hide(); | |
140 | }, 400); | |
141 | } | |
142 | }, | |
143 | failure: function(tid, response) { | |
144 | if (statusspinner) { | |
145 | statusspinner.hide(); | |
146 | } | |
147 | new M.core.ajaxException(response); | |
148 | } | |
149 | }, | |
150 | context: this | |
151 | }; | |
152 | ||
153 | // Apply optional config | |
154 | if (optionalconfig) { | |
155 | for (varname in optionalconfig) { | |
156 | config[varname] = optionalconfig[varname]; | |
157 | } | |
158 | } | |
159 | ||
160 | if (statusspinner) { | |
161 | statusspinner.show(); | |
162 | } | |
163 | ||
164 | // Send the request | |
165 | Y.io(uri, config); | |
166 | return this; | |
167 | } | |
168 | }, | |
169 | { | |
170 | NAME: 'mod_quiz-toolbox', | |
171 | ATTRS: { | |
172 | /** | |
173 | * The ID of the Moodle Course being edited. | |
174 | * | |
175 | * @attribute courseid | |
176 | * @default 0 | |
177 | * @type Number | |
178 | */ | |
179 | courseid: { | |
180 | 'value': 0 | |
181 | }, | |
182 | ||
183 | /** | |
184 | * The Moodle course format. | |
185 | * | |
186 | * @attribute format | |
187 | * @default 'topics' | |
188 | * @type String | |
189 | */ | |
190 | quizid: { | |
191 | 'value': 0 | |
192 | }, | |
193 | /** | |
194 | * The URL to use when submitting requests. | |
195 | * @attribute ajaxurl | |
196 | * @default null | |
197 | * @type String | |
198 | */ | |
199 | ajaxurl: { | |
200 | 'value': null | |
201 | }, | |
202 | /** | |
203 | * Any additional configuration passed when creating the instance. | |
204 | * | |
205 | * @attribute config | |
206 | * @default {} | |
207 | * @type Object | |
208 | */ | |
209 | config: { | |
210 | 'value': {} | |
211 | } | |
212 | } | |
213 | } | |
214 | ); | |
215 | /** | |
216 | * Resource and activity toolbox class. | |
217 | * | |
218 | * This class is responsible for managing AJAX interactions with activities and resources | |
219 | * when viewing a quiz in editing mode. | |
220 | * | |
221 | * @module mod_quiz-resource-toolbox | |
222 | * @namespace M.mod_quiz.resource_toolbox | |
223 | */ | |
224 | ||
225 | /** | |
226 | * Resource and activity toolbox class. | |
227 | * | |
228 | * This is a class extending TOOLBOX containing code specific to resources | |
229 | * | |
230 | * This class is responsible for managing AJAX interactions with activities and resources | |
231 | * when viewing a quiz in editing mode. | |
232 | * | |
233 | * @class resources | |
234 | * @constructor | |
235 | * @extends M.course.toolboxes.toolbox | |
236 | */ | |
237 | var RESOURCETOOLBOX = function() { | |
238 | RESOURCETOOLBOX.superclass.constructor.apply(this, arguments); | |
239 | }; | |
240 | ||
241 | Y.extend(RESOURCETOOLBOX, TOOLBOX, { | |
242 | /** | |
243 | * An Array of events added when editing a max mark field. | |
244 | * These should all be detached when editing is complete. | |
245 | * | |
246 | * @property editmaxmarkevents | |
247 | * @protected | |
248 | * @type Array | |
249 | * @protected | |
250 | */ | |
251 | editmaxmarkevents: [], | |
252 | ||
253 | /** | |
254 | * | |
255 | */ | |
256 | NODE_PAGE: 1, | |
257 | NODE_SLOT: 2, | |
258 | NODE_JOIN: 3, | |
259 | ||
260 | /** | |
261 | * Initialize the resource toolbox | |
262 | * | |
263 | * For each activity the commands are updated and a reference to the activity is attached. | |
264 | * This way it doesn't matter where the commands are going to called from they have a reference to the | |
265 | * activity that they relate to. | |
266 | * This is essential as some of the actions are displayed in an actionmenu which removes them from the | |
267 | * page flow. | |
268 | * | |
269 | * This function also creates a single event delegate to manage all AJAX actions for all activities on | |
270 | * the page. | |
271 | * | |
272 | * @method initializer | |
273 | * @protected | |
274 | */ | |
275 | initializer: function() { | |
276 | M.mod_quiz.quizbase.register_module(this); | |
277 | BODY.delegate('key', this.handle_data_action, 'down:enter', SELECTOR.ACTIVITYACTION, this); | |
278 | Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this); | |
279 | }, | |
280 | ||
281 | /** | |
282 | * Handles the delegation event. When this is fired someone has triggered an action. | |
283 | * | |
284 | * Note not all actions will result in an AJAX enhancement. | |
285 | * | |
286 | * @protected | |
287 | * @method handle_data_action | |
288 | * @param {EventFacade} ev The event that was triggered. | |
289 | * @returns {boolean} | |
290 | */ | |
291 | handle_data_action: function(ev) { | |
292 | // We need to get the anchor element that triggered this event. | |
293 | var node = ev.target; | |
294 | if (!node.test('a')) { | |
295 | node = node.ancestor(SELECTOR.ACTIVITYACTION); | |
296 | } | |
297 | ||
298 | // From the anchor we can get both the activity (added during initialisation) and the action being | |
299 | // performed (added by the UI as a data attribute). | |
300 | var action = node.getData('action'), | |
301 | activity = node.ancestor(SELECTOR.ACTIVITYLI); | |
302 | ||
303 | if (!node.test('a') || !action || !activity) { | |
304 | // It wasn't a valid action node. | |
305 | return; | |
306 | } | |
307 | ||
308 | // Switch based upon the action and do the desired thing. | |
309 | switch (action) { | |
310 | case 'editmaxmark': | |
311 | // The user wishes to edit the maxmark of the resource. | |
312 | this.edit_maxmark(ev, node, activity, action); | |
313 | break; | |
314 | case 'delete': | |
315 | // The user is deleting the activity. | |
316 | this.delete_with_confirmation(ev, node, activity, action); | |
317 | break; | |
a69f81f0 CC |
318 | case 'addpagebreak': |
319 | case 'removepagebreak': | |
320 | // The user is adding or removing a page break. | |
321 | this.update_page_break(ev, node, activity, action); | |
e1a2d0d9 CC |
322 | break; |
323 | default: | |
324 | // Nothing to do here! | |
325 | break; | |
326 | } | |
327 | }, | |
328 | ||
329 | /** | |
330 | * Add a loading icon to the specified activity. | |
331 | * | |
332 | * The icon is added within the action area. | |
333 | * | |
334 | * @method add_spinner | |
335 | * @param {Node} activity The activity to add a loading icon to | |
336 | * @return {Node|null} The newly created icon, or null if the action area was not found. | |
337 | */ | |
338 | add_spinner: function(activity) { | |
339 | var actionarea = activity.one(SELECTOR.ACTIONAREA); | |
340 | if (actionarea) { | |
341 | return M.util.add_spinner(Y, actionarea); | |
342 | } | |
343 | return null; | |
344 | }, | |
345 | ||
346 | /** | |
347 | * Deletes the given activity or resource after confirmation. | |
348 | * | |
349 | * @protected | |
350 | * @method delete_with_confirmation | |
351 | * @param {EventFacade} ev The event that was fired. | |
352 | * @param {Node} button The button that triggered this action. | |
353 | * @param {Node} activity The activity node that this action will be performed on. | |
354 | * @chainable | |
355 | */ | |
356 | delete_with_confirmation: function(ev, button, activity) { | |
a69f81f0 | 357 | // Prevent the default button action. |
e1a2d0d9 CC |
358 | ev.preventDefault(); |
359 | ||
a69f81f0 | 360 | // Get the element we're working on. |
e1a2d0d9 CC |
361 | var element = activity, |
362 | // Create confirm string (different if element has or does not have name) | |
363 | confirmstring = '', | |
364 | qtypename = M.util.get_string('pluginname', | |
365 | 'qtype_' + element.getAttribute('class').match(/qtype_([^\s]*)/)[1]); | |
366 | confirmstring = M.util.get_string('confirmremovequestion', 'quiz', qtypename); | |
367 | ||
368 | // Create the confirmation dialogue. | |
369 | var confirm = new M.core.confirm({ | |
370 | question: confirmstring, | |
371 | modal: true | |
372 | }); | |
373 | ||
374 | // If it is confirmed. | |
375 | confirm.on('complete-yes', function() { | |
376 | ||
a69f81f0 | 377 | var spinner = this.add_spinner(element); |
e1a2d0d9 CC |
378 | var data = { |
379 | 'class': 'resource', | |
380 | 'action': 'DELETE', | |
381 | 'id': Y.Moodle.mod_quiz.util.slot.getId(element) | |
382 | }; | |
a69f81f0 CC |
383 | this.send_request(data, spinner, function(response) { |
384 | if (response.deleted) { | |
385 | // Actually remove the element. | |
386 | Y.Moodle.mod_quiz.util.slot.remove(element); | |
387 | this.reorganise_edit_page(); | |
388 | if (M.core.actionmenu && M.core.actionmenu.instance) { | |
389 | M.core.actionmenu.instance.hideMenu(); | |
390 | } | |
391 | } else { | |
392 | window.location.reload(true); | |
393 | } | |
394 | }); | |
e1a2d0d9 CC |
395 | |
396 | }, this); | |
397 | ||
398 | return this; | |
399 | }, | |
400 | ||
401 | ||
402 | /** | |
403 | * Edit the maxmark for the resource | |
404 | * | |
405 | * @protected | |
406 | * @method edit_maxmark | |
407 | * @param {EventFacade} ev The event that was fired. | |
408 | * @param {Node} button The button that triggered this action. | |
409 | * @param {Node} activity The activity node that this action will be performed on. | |
410 | * @param {String} action The action that has been requested. | |
411 | * @return Boolean | |
412 | */ | |
413 | edit_maxmark : function(ev, button, activity) { | |
414 | // Get the element we're working on | |
415 | var activityid = Y.Moodle.mod_quiz.util.slot.getId(activity), | |
416 | instancemaxmark = activity.one(SELECTOR.INSTANCEMAXMARK), | |
417 | instance = activity.one(SELECTOR.ACTIVITYINSTANCE), | |
418 | currentmaxmark = instancemaxmark.get('firstChild'), | |
419 | oldmaxmark = currentmaxmark.get('data'), | |
420 | maxmarktext = oldmaxmark, | |
421 | thisevent, | |
422 | anchor = instancemaxmark,// Grab the anchor so that we can swap it with the edit form. | |
423 | data = { | |
424 | 'class' : 'resource', | |
425 | 'field' : 'getmaxmark', | |
426 | 'id' : activityid | |
427 | }; | |
428 | ||
429 | // Prevent the default actions. | |
430 | ev.preventDefault(); | |
431 | ||
432 | this.send_request(data, null, function(response) { | |
433 | if (M.core.actionmenu && M.core.actionmenu.instance) { | |
434 | M.core.actionmenu.instance.hideMenu(); | |
435 | } | |
436 | ||
a69f81f0 | 437 | // Try to retrieve the existing string from the server. |
e1a2d0d9 CC |
438 | if (response.instancemaxmark) { |
439 | maxmarktext = response.instancemaxmark; | |
440 | } | |
441 | ||
a69f81f0 | 442 | // Create the editor and submit button. |
e1a2d0d9 CC |
443 | var editform = Y.Node.create('<form action="#" />'); |
444 | var editinstructions = Y.Node.create('<span class="' + CSS.EDITINSTRUCTIONS + '" id="id_editinstructions" />') | |
445 | .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle')); | |
446 | var editor = Y.Node.create('<input name="maxmark" type="text" class="' + CSS.TITLEEDITOR + '" />').setAttrs({ | |
447 | 'value' : maxmarktext, | |
448 | 'autocomplete' : 'off', | |
449 | 'aria-describedby' : 'id_editinstructions', | |
450 | 'maxLength' : '12', | |
451 | 'size' : parseInt(this.get('config').questiondecimalpoints, 10) + 2 | |
452 | }); | |
453 | ||
a69f81f0 | 454 | // Clear the existing content and put the editor in. |
e1a2d0d9 CC |
455 | editform.appendChild(editor); |
456 | editform.setData('anchor', anchor); | |
457 | instance.insert(editinstructions, 'before'); | |
458 | anchor.replace(editform); | |
459 | ||
460 | // Force the editing instruction to match the mod-indent position. | |
461 | var padside = 'left'; | |
462 | if (right_to_left()) { | |
463 | padside = 'right'; | |
464 | } | |
465 | ||
466 | // We hide various components whilst editing: | |
467 | activity.addClass(CSS.EDITINGMAXMARK); | |
468 | ||
a69f81f0 | 469 | // Focus and select the editor text. |
e1a2d0d9 CC |
470 | editor.focus().select(); |
471 | ||
472 | // Cancel the edit if we lose focus or the escape key is pressed. | |
473 | thisevent = editor.on('blur', this.edit_maxmark_cancel, this, activity, false); | |
474 | this.editmaxmarkevents.push(thisevent); | |
475 | thisevent = editor.on('key', this.edit_maxmark_cancel, 'esc', this, activity, true); | |
476 | this.editmaxmarkevents.push(thisevent); | |
477 | ||
478 | // Handle form submission. | |
479 | thisevent = editform.on('submit', this.edit_maxmark_submit, this, activity, oldmaxmark); | |
480 | this.editmaxmarkevents.push(thisevent); | |
481 | }); | |
482 | }, | |
483 | ||
484 | /** | |
485 | * Handles the submit event when editing the activity or resources maxmark. | |
486 | * | |
487 | * @protected | |
488 | * @method edit_maxmark_submit | |
489 | * @param {EventFacade} ev The event that triggered this. | |
490 | * @param {Node} activity The activity whose maxmark we are altering. | |
491 | * @param {String} originalmaxmark The original maxmark the activity or resource had. | |
492 | */ | |
493 | edit_maxmark_submit : function(ev, activity, originalmaxmark) { | |
a69f81f0 | 494 | // We don't actually want to submit anything. |
e1a2d0d9 CC |
495 | ev.preventDefault(); |
496 | var newmaxmark = Y.Lang.trim(activity.one(SELECTOR.ACTIVITYFORM + ' ' + SELECTOR.ACTIVITYMAXMARK).get('value')); | |
497 | var spinner = this.add_spinner(activity); | |
498 | this.edit_maxmark_clear(activity); | |
499 | activity.one(SELECTOR.INSTANCEMAXMARK).setContent(newmaxmark); | |
500 | if (newmaxmark !== null && newmaxmark !== "" && newmaxmark !== originalmaxmark) { | |
501 | var data = { | |
502 | 'class' : 'resource', | |
503 | 'field' : 'updatemaxmark', | |
504 | 'maxmark' : newmaxmark, | |
505 | 'id' : Y.Moodle.mod_quiz.util.slot.getId(activity) | |
506 | }; | |
507 | this.send_request(data, spinner, function(response) { | |
508 | if (response.instancemaxmark) { | |
509 | activity.one(SELECTOR.INSTANCEMAXMARK).setContent(response.instancemaxmark); | |
510 | } | |
511 | }); | |
512 | } | |
513 | }, | |
514 | ||
515 | /** | |
516 | * Handles the cancel event when editing the activity or resources maxmark. | |
517 | * | |
518 | * @protected | |
519 | * @method edit_maxmark_cancel | |
520 | * @param {EventFacade} ev The event that triggered this. | |
521 | * @param {Node} activity The activity whose maxmark we are altering. | |
522 | * @param {Boolean} preventdefault If true we should prevent the default action from occuring. | |
523 | */ | |
524 | edit_maxmark_cancel : function(ev, activity, preventdefault) { | |
525 | if (preventdefault) { | |
526 | ev.preventDefault(); | |
527 | } | |
528 | this.edit_maxmark_clear(activity); | |
529 | }, | |
530 | ||
531 | /** | |
532 | * Handles clearing the editing UI and returning things to the original state they were in. | |
533 | * | |
534 | * @protected | |
535 | * @method edit_maxmark_clear | |
536 | * @param {Node} activity The activity whose maxmark we were altering. | |
537 | */ | |
538 | edit_maxmark_clear : function(activity) { | |
539 | // Detach all listen events to prevent duplicate triggers | |
540 | new Y.EventHandle(this.editmaxmarkevents).detach(); | |
541 | ||
542 | var editform = activity.one(SELECTOR.ACTIVITYFORM), | |
543 | instructions = activity.one('#id_editinstructions'); | |
544 | if (editform) { | |
545 | editform.replace(editform.getData('anchor')); | |
546 | } | |
547 | if (instructions) { | |
548 | instructions.remove(); | |
549 | } | |
550 | ||
551 | // Remove the editing class again to revert the display. | |
552 | activity.removeClass(CSS.EDITINGMAXMARK); | |
553 | ||
554 | // Refocus the link which was clicked originally so the user can continue using keyboard nav. | |
555 | Y.later(100, this, function() { | |
556 | activity.one(SELECTOR.EDITMAXMARK).focus(); | |
557 | }); | |
558 | ||
559 | // This hack is to keep Behat happy until they release a version of | |
560 | // MinkSelenium2Driver that fixes | |
561 | // https://github.com/Behat/MinkSelenium2Driver/issues/80. | |
562 | if (!Y.one('input[name=maxmark')) { | |
563 | Y.one('body').append('<input type="text" name="maxmark" style="display: none">'); | |
564 | } | |
565 | }, | |
566 | ||
567 | /** | |
568 | * Joins or separates the given slot with the page of the previous slot. Reorders the pages of | |
569 | * the other slots | |
570 | * | |
571 | * @protected | |
a69f81f0 | 572 | * @method update_page_break |
e1a2d0d9 CC |
573 | * @param {EventFacade} ev The event that was fired. |
574 | * @param {Node} button The button that triggered this action. | |
575 | * @param {Node} activity The activity node that this action will be performed on. | |
576 | * @chainable | |
577 | */ | |
a69f81f0 | 578 | update_page_break: function(ev, button, activity, action) { |
e1a2d0d9 CC |
579 | // Prevent the default button action |
580 | ev.preventDefault(); | |
581 | ||
a69f81f0 CC |
582 | nextactivity = activity.next('li.activity.slot'); |
583 | var spinner = this.add_spinner(nextactivity), | |
e1a2d0d9 | 584 | slotid = 0; |
a69f81f0 | 585 | var value = action === 'removepagebreak' ? 1 : 2; |
e1a2d0d9 CC |
586 | |
587 | var data = { | |
588 | 'class': 'resource', | |
a69f81f0 | 589 | 'field': 'updatepagebreak', |
e1a2d0d9 CC |
590 | 'id': slotid, |
591 | 'value': value | |
592 | }; | |
593 | ||
a69f81f0 | 594 | slotid = Y.Moodle.mod_quiz.util.slot.getId(nextactivity); |
e1a2d0d9 CC |
595 | if (slotid) { |
596 | data.id = Number(slotid); | |
597 | } | |
598 | this.send_request(data, spinner, function(response) { | |
a69f81f0 CC |
599 | if (response.slots) { |
600 | if (action === 'addpagebreak') { | |
601 | Y.Moodle.mod_quiz.util.page.add(activity); | |
602 | } else { | |
603 | var page = activity.next(Y.Moodle.mod_quiz.util.page.SELECTORS.PAGE); | |
604 | Y.Moodle.mod_quiz.util.page.remove(page, true); | |
605 | } | |
606 | this.reorganise_edit_page(); | |
607 | } else { | |
608 | window.location.reload(true); | |
609 | } | |
e1a2d0d9 CC |
610 | }); |
611 | ||
612 | return this; | |
613 | }, | |
e1a2d0d9 | 614 | |
a69f81f0 CC |
615 | /** |
616 | * Reorganise the UI after every edit action. | |
617 | * | |
618 | * @protected | |
619 | * @method reorganise_edit_page | |
620 | */ | |
621 | reorganise_edit_page: function() { | |
622 | Y.Moodle.mod_quiz.util.slot.reorderSlots(); | |
623 | Y.Moodle.mod_quiz.util.slot.reorderPageBreaks(); | |
624 | Y.Moodle.mod_quiz.util.page.reorderPages(); | |
e1a2d0d9 CC |
625 | }, |
626 | ||
627 | NAME : 'mod_quiz-resource-toolbox', | |
628 | ATTRS : { | |
629 | courseid : { | |
630 | 'value' : 0 | |
631 | }, | |
632 | quizid : { | |
633 | 'value' : 0 | |
634 | } | |
635 | } | |
636 | }); | |
637 | ||
638 | M.mod_quiz.resource_toolbox = null; | |
639 | M.mod_quiz.init_resource_toolbox = function(config) { | |
640 | M.mod_quiz.resource_toolbox = new RESOURCETOOLBOX(config); | |
641 | return M.mod_quiz.resource_toolbox; | |
642 | }; | |
643 | /** | |
644 | * Resource and activity toolbox class. | |
645 | * | |
646 | * This class is responsible for managing AJAX interactions with activities and resources | |
647 | * when viewing a course in editing mode. | |
648 | * | |
649 | * @module moodle-mod_quiz-toolboxes | |
650 | * @namespace M.mod_quiz.toolboxes | |
651 | */ | |
652 | ||
653 | /** | |
654 | * Section toolbox class. | |
655 | * | |
656 | * This class is responsible for managing AJAX interactions with sections | |
657 | * when viewing a course in editing mode. | |
658 | * | |
659 | * @class section | |
660 | * @constructor | |
661 | * @extends M.mod_quiz.toolboxes.toolbox | |
662 | */ | |
663 | var SECTIONTOOLBOX = function() { | |
664 | SECTIONTOOLBOX.superclass.constructor.apply(this, arguments); | |
665 | }; | |
666 | ||
667 | Y.extend(SECTIONTOOLBOX, TOOLBOX, { | |
668 | /** | |
669 | * Initialize the section toolboxes module. | |
670 | * | |
671 | * Updates all span.commands with relevant handlers and other required changes. | |
672 | * | |
673 | * @method initializer | |
674 | * @protected | |
675 | */ | |
676 | initializer : function() { | |
677 | M.mod_quiz.quizbase.register_module(this); | |
678 | ||
679 | // Section Highlighting. | |
680 | Y.delegate('click', this.toggle_highlight, SELECTOR.PAGECONTENT, SELECTOR.SECTIONLI + ' ' + SELECTOR.HIGHLIGHT, this); | |
681 | ||
682 | // Section Visibility. | |
683 | Y.delegate('click', this.toggle_hide_section, SELECTOR.PAGECONTENT, SELECTOR.SECTIONLI + ' ' + SELECTOR.SHOWHIDE, this); | |
684 | }, | |
685 | ||
686 | toggle_hide_section : function(e) { | |
687 | // Prevent the default button action. | |
688 | e.preventDefault(); | |
689 | ||
690 | // Get the section we're working on. | |
691 | var section = e.target.ancestor(M.mod_quiz.format.get_section_selector(Y)), | |
692 | button = e.target.ancestor('a', true), | |
693 | hideicon = button.one('img'), | |
694 | ||
695 | // The value to submit | |
696 | value, | |
697 | ||
698 | // The text for strings and images. Also determines the icon to display. | |
699 | action, | |
700 | nextaction; | |
701 | ||
702 | if (!section.hasClass(CSS.SECTIONHIDDENCLASS)) { | |
703 | section.addClass(CSS.SECTIONHIDDENCLASS); | |
704 | value = 0; | |
705 | action = 'hide'; | |
706 | nextaction = 'show'; | |
707 | } else { | |
708 | section.removeClass(CSS.SECTIONHIDDENCLASS); | |
709 | value = 1; | |
710 | action = 'show'; | |
711 | nextaction = 'hide'; | |
712 | } | |
713 | ||
714 | var newstring = M.util.get_string(nextaction + 'fromothers', 'format_' + this.get('format')); | |
715 | hideicon.setAttrs({ | |
716 | 'alt' : newstring, | |
717 | 'src' : M.util.image_url('i/' + nextaction) | |
718 | }); | |
719 | button.set('title', newstring); | |
720 | ||
721 | // Change the highlight status | |
722 | var data = { | |
723 | 'class' : 'section', | |
724 | 'field' : 'visible', | |
725 | 'id' : Y.Moodle.core_course.util.section.getId(section.ancestor(M.mod_quiz.edit.get_section_wrapper(Y), true)), | |
726 | 'value' : value | |
727 | }; | |
728 | ||
729 | var lightbox = M.util.add_lightbox(Y, section); | |
730 | lightbox.show(); | |
731 | ||
732 | this.send_request(data, lightbox, function(response) { | |
733 | var activities = section.all(SELECTOR.ACTIVITYLI); | |
734 | activities.each(function(node) { | |
735 | var button; | |
736 | if (node.one(SELECTOR.SHOW)) { | |
737 | button = node.one(SELECTOR.SHOW); | |
738 | } else { | |
739 | button = node.one(SELECTOR.HIDE); | |
740 | } | |
741 | var activityid = Y.Moodle.mod_quiz.util.slot.getId(node); | |
742 | ||
743 | // NOTE: resourcestotoggle is returned as a string instead | |
744 | // of a Number so we must cast our activityid to a String. | |
745 | if (Y.Array.indexOf(response.resourcestotoggle, "" + activityid) !== -1) { | |
746 | M.mod_quiz.resource_toolbox.handle_resource_dim(button, node, action); | |
747 | } | |
748 | }, this); | |
749 | }); | |
750 | }, | |
751 | ||
752 | /** | |
753 | * Toggle highlighting the current section. | |
754 | * | |
755 | * @method toggle_highlight | |
756 | * @param {EventFacade} e | |
757 | */ | |
758 | toggle_highlight : function(e) { | |
759 | // Prevent the default button action. | |
760 | e.preventDefault(); | |
761 | ||
762 | // Get the section we're working on. | |
763 | var section = e.target.ancestor(M.mod_quiz.edit.get_section_selector(Y)); | |
764 | var button = e.target.ancestor('a', true); | |
765 | var buttonicon = button.one('img'); | |
766 | ||
767 | // Determine whether the marker is currently set. | |
768 | var togglestatus = section.hasClass('current'); | |
769 | var value = 0; | |
770 | ||
771 | // Set the current highlighted item text. | |
772 | var old_string = M.util.get_string('markthistopic', 'moodle'); | |
773 | Y.one(SELECTOR.PAGECONTENT) | |
774 | .all(M.mod_quiz.edit.get_section_selector(Y) + '.current ' + SELECTOR.HIGHLIGHT) | |
775 | .set('title', old_string); | |
776 | Y.one(SELECTOR.PAGECONTENT) | |
777 | .all(M.mod_quiz.edit.get_section_selector(Y) + '.current ' + SELECTOR.HIGHLIGHT + ' img') | |
778 | .set('alt', old_string) | |
779 | .set('src', M.util.image_url('i/marker')); | |
780 | ||
781 | // Remove the highlighting from all sections. | |
782 | Y.one(SELECTOR.PAGECONTENT).all(M.mod_quiz.edit.get_section_selector(Y)) | |
783 | .removeClass('current'); | |
784 | ||
785 | // Then add it if required to the selected section. | |
786 | if (!togglestatus) { | |
787 | section.addClass('current'); | |
788 | value = Y.Moodle.core_course.util.section.getId(section.ancestor(M.mod_quiz.edit.get_section_wrapper(Y), true)); | |
789 | var new_string = M.util.get_string('markedthistopic', 'moodle'); | |
790 | button | |
791 | .set('title', new_string); | |
792 | buttonicon | |
793 | .set('alt', new_string) | |
794 | .set('src', M.util.image_url('i/marked')); | |
795 | } | |
796 | ||
797 | // Change the highlight status. | |
798 | var data = { | |
799 | 'class' : 'course', | |
800 | 'field' : 'marker', | |
801 | 'value' : value | |
802 | }; | |
803 | var lightbox = M.util.add_lightbox(Y, section); | |
804 | lightbox.show(); | |
805 | this.send_request(data, lightbox); | |
806 | } | |
807 | }, { | |
808 | NAME : 'mod_quiz-section-toolbox', | |
809 | ATTRS : { | |
810 | courseid : { | |
811 | 'value' : 0 | |
812 | }, | |
813 | quizid : { | |
814 | 'value' : 0 | |
815 | }, | |
816 | format : { | |
817 | 'value' : 'topics' | |
818 | } | |
819 | } | |
820 | }); | |
821 | ||
822 | M.mod_quiz.init_section_toolbox = function(config) { | |
823 | return new SECTIONTOOLBOX(config); | |
824 | }; | |
825 | ||
826 | ||
827 | }, '@VERSION@', { | |
828 | "requires": [ | |
829 | "base", | |
830 | "node", | |
831 | "event", | |
832 | "event-key", | |
833 | "io", | |
834 | "moodle-mod_quiz-quizbase", | |
835 | "moodle-mod_quiz-util-slot", | |
836 | "moodle-core-notification-ajaxexception" | |
837 | ] | |
838 | }); |