MDL-47959 mod_quiz: fixed 0 questions bug
[moodle.git] / mod / quiz / yui / build / moodle-mod_quiz-toolboxes / moodle-mod_quiz-toolboxes.js
1 YUI.add('moodle-mod_quiz-toolboxes', function (Y, NAME) {
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  */
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',
56         NUMQUESTIONS : '.numberofquestions',
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);
67 // Setup the basic namespace.
68 M.mod_quiz = M.mod_quiz || {};
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 };
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         }
106         data.sesskey = M.cfg.sesskey;
107         data.courseid = this.get('courseid');
108         data.quizid = this.get('quizid');
110         var uri = M.cfg.wwwroot + this.get('ajaxurl');
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) {}
126                     // Run the callback if we have one.
127                     if (responsetext.hasOwnProperty('newsummarks')) {
128                         Y.one(SELECTOR.SUMMARKS).setHTML(responsetext.newsummarks);
129                     }
130                     if (responsetext.hasOwnProperty('newnumquestions')) {
131                         Y.one(SELECTOR.NUMQUESTIONS).setHTML(M.util.get_string('numquestionsx', 'quiz', responsetext.newnumquestions));
132                     }
133                     if (success_callback) {
134                         Y.bind(success_callback, this, responsetext)();
135                     }
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         };
153         // Apply optional config
154         if (optionalconfig) {
155             for (varname in optionalconfig) {
156                 config[varname] = optionalconfig[varname];
157             }
158         }
160         if (statusspinner) {
161             statusspinner.show();
162         }
164         // Send the request
165         Y.io(uri, config);
166         return this;
167     }
168 },
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         },
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     }
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  */
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 };
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: [],
253     /**
254      *
255      */
256     NODE_PAGE: 1,
257     NODE_SLOT: 2,
258     NODE_JOIN: 3,
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     },
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         }
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);
303         if (!node.test('a') || !action || !activity) {
304             // It wasn't a valid action node.
305             return;
306         }
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;
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);
322                 break;
323             default:
324                 // Nothing to do here!
325                 break;
326         }
327     },
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     },
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) {
357         // Prevent the default button action.
358         ev.preventDefault();
360         // Get the element we're working on.
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);
368         // Create the confirmation dialogue.
369         var confirm = new M.core.confirm({
370             question: confirmstring,
371             modal: true
372         });
374         // If it is confirmed.
375         confirm.on('complete-yes', function() {
377             var spinner = this.add_spinner(element);
378             var data = {
379                 'class': 'resource',
380                 'action': 'DELETE',
381                 'id': Y.Moodle.mod_quiz.util.slot.getId(element)
382             };
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             });
396         }, this);
398         return this;
399     },
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             };
429         // Prevent the default actions.
430         ev.preventDefault();
432         this.send_request(data, null, function(response) {
433             if (M.core.actionmenu && M.core.actionmenu.instance) {
434                 M.core.actionmenu.instance.hideMenu();
435             }
437             // Try to retrieve the existing string from the server.
438             if (response.instancemaxmark) {
439                 maxmarktext = response.instancemaxmark;
440             }
442             // Create the editor and submit button.
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             });
454             // Clear the existing content and put the editor in.
455             editform.appendChild(editor);
456             editform.setData('anchor', anchor);
457             instance.insert(editinstructions, 'before');
458             anchor.replace(editform);
460             // Force the editing instruction to match the mod-indent position.
461             var padside = 'left';
462             if (right_to_left()) {
463                 padside = 'right';
464             }
466             // We hide various components whilst editing:
467             activity.addClass(CSS.EDITINGMAXMARK);
469             // Focus and select the editor text.
470             editor.focus().select();
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);
478             // Handle form submission.
479             thisevent = editform.on('submit', this.edit_maxmark_submit, this, activity, oldmaxmark);
480             this.editmaxmarkevents.push(thisevent);
481         });
482     },
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) {
494         // We don't actually want to submit anything.
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     },
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     },
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();
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         }
551         // Remove the editing class again to revert the display.
552         activity.removeClass(CSS.EDITINGMAXMARK);
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         });
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     },
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
572      * @method update_page_break
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      */
578     update_page_break: function(ev, button, activity, action) {
579         // Prevent the default button action
580         ev.preventDefault();
582         nextactivity = activity.next('li.activity.slot');
583         var spinner = this.add_spinner(nextactivity),
584             slotid = 0;
585         var value = action === 'removepagebreak' ? 1 : 2;
587         var data = {
588             'class': 'resource',
589             'field': 'updatepagebreak',
590             'id':    slotid,
591             'value': value
592         };
594         slotid = Y.Moodle.mod_quiz.util.slot.getId(nextactivity);
595         if (slotid) {
596             data.id = Number(slotid);
597         }
598         this.send_request(data, spinner, function(response) {
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             }
610         });
612         return this;
613     },
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();
625     },
627     NAME : 'mod_quiz-resource-toolbox',
628     ATTRS : {
629         courseid : {
630             'value' : 0
631         },
632         quizid : {
633             'value' : 0
634         }
635     }
636 });
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  */
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 };
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);
679         // Section Highlighting.
680         Y.delegate('click', this.toggle_highlight, SELECTOR.PAGECONTENT, SELECTOR.SECTIONLI + ' ' + SELECTOR.HIGHLIGHT, this);
682         // Section Visibility.
683         Y.delegate('click', this.toggle_hide_section, SELECTOR.PAGECONTENT, SELECTOR.SECTIONLI + ' ' + SELECTOR.SHOWHIDE, this);
684     },
686     toggle_hide_section : function(e) {
687         // Prevent the default button action.
688         e.preventDefault();
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'),
695         // The value to submit
696             value,
698         // The text for strings and images. Also determines the icon to display.
699             action,
700             nextaction;
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         }
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);
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         };
729         var lightbox = M.util.add_lightbox(Y, section);
730         lightbox.show();
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);
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     },
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();
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');
767         // Determine whether the marker is currently set.
768         var togglestatus = section.hasClass('current');
769         var value = 0;
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'));
781         // Remove the highlighting from all sections.
782         Y.one(SELECTOR.PAGECONTENT).all(M.mod_quiz.edit.get_section_selector(Y))
783             .removeClass('current');
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         }
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 });
822 M.mod_quiz.init_section_toolbox = function(config) {
823     return new SECTIONTOOLBOX(config);
824 };
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 });