Merge branch 'wip-mdl-36736-m23' of git://github.com/rajeshtaneja/moodle into MOODLE_...
[moodle.git] / course / yui / toolboxes / toolboxes.js
1 YUI.add('moodle-course-toolboxes', function(Y) {
2     WAITICON = {'pix':"i/loading_small",'component':'moodle'};
3     // The CSS selectors we use
4     var CSS = {
5         ACTIVITYLI : 'li.activity',
6         COMMANDSPAN : 'span.commands',
7         SPINNERCOMMANDSPAN : 'span.commands',
8         CONTENTAFTERLINK : 'div.contentafterlink',
9         DELETE : 'a.editing_delete',
10         DIMCLASS : 'dimmed',
11         DIMMEDTEXT : 'dimmed_text',
12         EDITTITLE : 'a.editing_title',
13         EDITTITLECLASS : 'edittitle',
14         GENERICICONCLASS : 'iconsmall',
15         GROUPSNONE : 'a.editing_groupsnone',
16         GROUPSSEPARATE : 'a.editing_groupsseparate',
17         GROUPSVISIBLE : 'a.editing_groupsvisible',
18         HASLABEL : 'label',
19         HIDE : 'a.editing_hide',
20         HIGHLIGHT : 'a.editing_highlight',
21         INSTANCENAME : 'span.instancename',
22         LIGHTBOX : 'lightbox',
23         MODINDENTCOUNT : 'mod-indent-',
24         MODINDENTDIV : 'div.mod-indent',
25         MODINDENTHUGE : 'mod-indent-huge',
26         MODULEIDPREFIX : 'module-',
27         MOVELEFT : 'a.editing_moveleft',
28         MOVELEFTCLASS : 'editing_moveleft',
29         MOVERIGHT : 'a.editing_moveright',
30         PAGECONTENT : 'div#page-content',
31         RIGHTSIDE : '.right',
32         SECTIONHIDDENCLASS : 'hidden',
33         SECTIONIDPREFIX : 'section-',
34         SECTIONLI : 'li.section',
35         SHOW : 'a.editing_show',
36         SHOWHIDE : 'a.editing_showhide',
37         CONDITIONALHIDDEN : 'conditionalhidden',
38         AVAILABILITYINFODIV : 'div.availabilityinfo',
39         SHOWCLASS : 'editing_show',
40         HIDECLASS : 'hide'
41     };
43     /**
44      * The toolbox classes
45      *
46      * TOOLBOX is a generic class which should never be directly instantiated
47      * RESOURCETOOLBOX is a class extending TOOLBOX containing code specific to resources
48      * SECTIONTOOLBOX is a class extending TOOLBOX containing code specific to sections
49      */
50     var TOOLBOX = function() {
51         TOOLBOX.superclass.constructor.apply(this, arguments);
52     }
54     Y.extend(TOOLBOX, Y.Base, {
55         /**
56          * Toggle the visibility and availability for the specified
57          * resource show/hide button
58          */
59         toggle_hide_resource_ui : function(button) {
60             var element = button.ancestor(CSS.ACTIVITYLI);
61             var hideicon = button.one('img');
63             var dimarea;
64             var toggle_class;
65             if (this.is_label(element)) {
66                 toggle_class = CSS.DIMMEDTEXT;
67                 dimarea = element.one(CSS.MODINDENTDIV + ' div');
68             } else {
69                 toggle_class = CSS.DIMCLASS;
70                 dimarea = element.one('a');
71             }
73             var status = '';
74             var value;
75             if (button.hasClass(CSS.SHOWCLASS)) {
76                 status = 'hide';
77                 value = 1;
78             } else {
79                 status = 'show';
80                 value = 0;
81             }
82             // Update button info.
83             var newstring = M.util.get_string(status, 'moodle');
84             hideicon.setAttrs({
85                 'alt' : newstring,
86                 'src'   : M.util.image_url('t/' + status)
87             });
88             button.set('title', newstring);
89             button.set('className', 'editing_'+status);
91             // If activity is conditionally hidden, then don't toggle.
92             if (!dimarea.hasClass(CSS.CONDITIONALHIDDEN)) {
93                 // Change the UI.
94                 dimarea.toggleClass(toggle_class);
95                 // We need to toggle dimming on the description too.
96                 element.all(CSS.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);
97             }
98             // Toggle availablity info for conditional activities.
99             var availabilityinfo = element.one(CSS.AVAILABILITYINFODIV);
101             if (availabilityinfo) {
102                 availabilityinfo.toggleClass(CSS.HIDECLASS);
103             }
104             return value;
105         },
106         /**
107          * Send a request using the REST API
108          *
109          * @param data The data to submit
110          * @param statusspinner (optional) A statusspinner which may contain a section loader
111          * @param optionalconfig (optional) Any additional configuration to submit
112          * @return response responseText field from responce
113          */
114         send_request : function(data, statusspinner, optionalconfig) {
115             // Default data structure
116             if (!data) {
117                 data = {};
118             }
119             // Handle any variables which we must pass back through to
120             var pageparams = this.get('config').pageparams;
121             for (varname in pageparams) {
122                 data[varname] = pageparams[varname];
123             }
125             data.sesskey = M.cfg.sesskey;
126             data.courseId = this.get('courseid');
128             var uri = M.cfg.wwwroot + this.get('ajaxurl');
130             // Define the configuration to send with the request
131             var responsetext = [];
132             var config = {
133                 method: 'POST',
134                 data: data,
135                 on: {
136                     success: function(tid, response) {
137                         try {
138                             responsetext = Y.JSON.parse(response.responseText);
139                             if (responsetext.error) {
140                                 new M.core.ajaxException(responsetext);
141                             }
142                         } catch (e) {}
143                         if (statusspinner) {
144                             window.setTimeout(function(e) {
145                                 statusspinner.hide();
146                             }, 400);
147                         }
148                     },
149                     failure : function(tid, response) {
150                         if (statusspinner) {
151                             statusspinner.hide();
152                         }
153                         new M.core.ajaxException(response);
154                     }
155                 },
156                 context: this,
157                 sync: true
158             }
160             // Apply optional config
161             if (optionalconfig) {
162                 for (varname in optionalconfig) {
163                     config[varname] = optionalconfig[varname];
164                 }
165             }
167             if (statusspinner) {
168                 statusspinner.show();
169             }
171             // Send the request
172             Y.io(uri, config);
173             return responsetext;
174         },
175         is_label : function(target) {
176             return target.hasClass(CSS.HASLABEL);
177         },
178         /**
179          * Return the module ID for the specified element
180          *
181          * @param element The <li> element to determine a module-id number for
182          * @return string The module ID
183          */
184         get_element_id : function(element) {
185             return element.get('id').replace(CSS.MODULEIDPREFIX, '');
186         },
187         /**
188          * Return the module ID for the specified element
189          *
190          * @param element The <li> element to determine a module-id number for
191          * @return string The module ID
192          */
193         get_section_id : function(section) {
194             return section.get('id').replace(CSS.SECTIONIDPREFIX, '');
195         }
196     },
197     {
198         NAME : 'course-toolbox',
199         ATTRS : {
200             // The ID of the current course
201             courseid : {
202                 'value' : 0
203             },
204             ajaxurl : {
205                 'value' : 0
206             },
207             config : {
208                 'value' : 0
209             }
210         }
211     }
212     );
215     var RESOURCETOOLBOX = function() {
216         RESOURCETOOLBOX.superclass.constructor.apply(this, arguments);
217     }
219     Y.extend(RESOURCETOOLBOX, TOOLBOX, {
220         // Variables
221         GROUPS_NONE     : 0,
222         GROUPS_SEPARATE : 1,
223         GROUPS_VISIBLE  : 2,
225         /**
226          * Initialize the resource toolbox
227          *
228          * Updates all span.commands with relevant handlers and other required changes
229          */
230         initializer : function(config) {
231             this.setup_for_resource();
232             M.course.coursebase.register_module(this);
234             var prefix = CSS.ACTIVITYLI + ' ' + CSS.COMMANDSPAN + ' ';
235             Y.delegate('click', this.edit_resource_title, CSS.PAGECONTENT, prefix + CSS.EDITTITLE, this);
236             Y.delegate('click', this.move_left, CSS.PAGECONTENT, prefix + CSS.MOVELEFT, this);
237             Y.delegate('click', this.move_right, CSS.PAGECONTENT, prefix + CSS.MOVERIGHT, this);
238             Y.delegate('click', this.delete_resource, CSS.PAGECONTENT, prefix + CSS.DELETE, this);
239             Y.delegate('click', this.toggle_hide_resource, CSS.PAGECONTENT, prefix + CSS.HIDE, this);
240             Y.delegate('click', this.toggle_hide_resource, CSS.PAGECONTENT, prefix + CSS.SHOW, this);
241             Y.delegate('click', this.toggle_groupmode, CSS.PAGECONTENT, prefix + CSS.GROUPSNONE, this);
242             Y.delegate('click', this.toggle_groupmode, CSS.PAGECONTENT, prefix + CSS.GROUPSSEPARATE, this);
243             Y.delegate('click', this.toggle_groupmode, CSS.PAGECONTENT, prefix + CSS.GROUPSVISIBLE, this);
244         },
246         /**
247          * Update any span.commands within the scope of the specified
248          * selector with AJAX equivelants
249          *
250          * @param baseselector The selector to limit scope to
251          * @return void
252          */
253         setup_for_resource : function(baseselector) {
254             if (!baseselector) {
255                 var baseselector = CSS.PAGECONTENT + ' ' + CSS.ACTIVITYLI;
256             }
258             Y.all(baseselector).each(this._setup_for_resource, this);
259         },
260         _setup_for_resource : function(toolboxtarget) {
261             toolboxtarget = Y.one(toolboxtarget);
262             // "Disable" show/hide icons (change cursor to not look clickable) if section is hidden
263             var showhide = toolboxtarget.all(CSS.COMMANDSPAN + ' ' + CSS.HIDE);
264             showhide.concat(toolboxtarget.all(CSS.COMMANDSPAN + ' ' + CSS.SHOW));
265             showhide.each(function(node) {
266                 var section = node.ancestor(CSS.SECTIONLI);
267                 if (section && section.hasClass(CSS.SECTIONHIDDENCLASS)) {
268                     node.setStyle('cursor', 'auto');
269                 }
270             });
272             // Set groupmode attribute for use by this.toggle_groupmode()
273             var groups;
274             groups = toolboxtarget.all(CSS.COMMANDSPAN + ' ' + CSS.GROUPSNONE);
275             groups.setAttribute('groupmode', this.GROUPS_NONE);
277             groups = toolboxtarget.all(CSS.COMMANDSPAN + ' ' + CSS.GROUPSSEPARATE);
278             groups.setAttribute('groupmode', this.GROUPS_SEPARATE);
280             groups = toolboxtarget.all(CSS.COMMANDSPAN + ' ' + CSS.GROUPSVISIBLE);
281             groups.setAttribute('groupmode', this.GROUPS_VISIBLE);
282         },
283         move_left : function(e) {
284             this.move_leftright(e, -1);
285         },
286         move_right : function(e) {
287             this.move_leftright(e, 1);
288         },
289         move_leftright : function(e, direction) {
290             // Prevent the default button action
291             e.preventDefault();
293             // Get the element we're working on
294             var element = e.target.ancestor(CSS.ACTIVITYLI);
296             // And we need to determine the current and new indent level
297             var indentdiv = element.one(CSS.MODINDENTDIV);
298             var indent = indentdiv.getAttribute('class').match(/mod-indent-(\d{1,})/);
300             if (indent) {
301                 var oldindent = parseInt(indent[1]);
302                 var newindent = Math.max(0, (oldindent + parseInt(direction)));
303                 indentdiv.removeClass(indent[0]);
304             } else {
305                 var oldindent = 0;
306                 var newindent = 1;
307             }
309             // Perform the move
310             indentdiv.addClass(CSS.MODINDENTCOUNT + newindent);
311             var data = {
312                 'class' : 'resource',
313                 'field' : 'indent',
314                 'value' : newindent,
315                 'id'    : this.get_element_id(element)
316             };
317             var spinner = M.util.add_spinner(Y, element.one(CSS.SPINNERCOMMANDSPAN));
318             this.send_request(data, spinner);
320             // Handle removal/addition of the moveleft button
321             if (newindent == 0) {
322                 element.one(CSS.MOVELEFT).remove();
323             } else if (newindent == 1 && oldindent == 0) {
324                 this.add_moveleft(element);
325             }
327             // Handle massive indentation to match non-ajax display
328             var hashugeclass = indentdiv.hasClass(CSS.MODINDENTHUGE);
329             if (newindent > 15 && !hashugeclass) {
330                 indentdiv.addClass(CSS.MODINDENTHUGE);
331             } else if (newindent <= 15 && hashugeclass) {
332                 indentdiv.removeClass(CSS.MODINDENTHUGE);
333             }
334         },
335         delete_resource : function(e) {
336             // Prevent the default button action
337             e.preventDefault();
339             // Get the element we're working on
340             var element   = e.target.ancestor(CSS.ACTIVITYLI);
342             var confirmstring = '';
343             if (this.is_label(element)) {
344                 // Labels are slightly different to other activities
345                 var plugindata = {
346                     type : M.util.get_string('pluginname', 'label')
347                 }
348                 confirmstring = M.util.get_string('deletechecktype', 'moodle', plugindata)
349             } else {
350                 var plugindata = {
351                     type : M.util.get_string('pluginname', element.getAttribute('class').match(/modtype_([^\s]*)/)[1]),
352                     name : element.one(CSS.INSTANCENAME).get('firstChild').get('data')
353                 }
354                 confirmstring = M.util.get_string('deletechecktypename', 'moodle', plugindata);
355             }
357             // Confirm element removal
358             if (!confirm(confirmstring)) {
359                 return false;
360             }
362             // Actually remove the element
363             element.remove();
364             var data = {
365                 'class' : 'resource',
366                 'action' : 'DELETE',
367                 'id'    : this.get_element_id(element)
368             };
369             this.send_request(data);
370         },
371         toggle_hide_resource : function(e) {
372             // Prevent the default button action
373             e.preventDefault();
375             // Return early if the current section is hidden
376             var section = e.target.ancestor(M.course.format.get_section_selector(Y));
377             if (section && section.hasClass(CSS.SECTIONHIDDENCLASS)) {
378                 return;
379             }
381             // Get the element we're working on
382             var element = e.target.ancestor(CSS.ACTIVITYLI);
384             var button = e.target.ancestor('a', true);
386             var value = this.toggle_hide_resource_ui(button);
388             // Send the request
389             var data = {
390                 'class' : 'resource',
391                 'field' : 'visible',
392                 'value' : value,
393                 'id'    : this.get_element_id(element)
394             };
395             var spinner = M.util.add_spinner(Y, element.one(CSS.SPINNERCOMMANDSPAN));
396             this.send_request(data, spinner);
397             return false; // Need to return false to stop the delegate for the new state firing
398         },
399         toggle_groupmode : function(e) {
400             // Prevent the default button action
401             e.preventDefault();
403             // Get the element we're working on
404             var element = e.target.ancestor(CSS.ACTIVITYLI);
406             var button = e.target.ancestor('a', true);
407             var icon = button.one('img');
409             // Current Mode
410             var groupmode = button.getAttribute('groupmode');
411             groupmode++;
412             if (groupmode > 2) {
413                 groupmode = 0;
414             }
415             button.setAttribute('groupmode', groupmode);
417             var newtitle = '';
418             var iconsrc = '';
419             switch (groupmode) {
420                 case this.GROUPS_NONE:
421                     newtitle = 'groupsnone';
422                     iconsrc = M.util.image_url('t/groupn');
423                     break;
424                 case this.GROUPS_SEPARATE:
425                     newtitle = 'groupsseparate';
426                     iconsrc = M.util.image_url('t/groups');
427                     break;
428                 case this.GROUPS_VISIBLE:
429                     newtitle = 'groupsvisible';
430                     iconsrc = M.util.image_url('t/groupv');
431                     break;
432             }
433             newtitle = M.util.get_string('clicktochangeinbrackets', 'moodle',
434                     M.util.get_string(newtitle, 'moodle'));
436             // Change the UI
437             icon.setAttrs({
438                 'alt' : newtitle,
439                 'src' : iconsrc
440             });
441             button.setAttribute('title', newtitle);
443             // And send the request
444             var data = {
445                 'class' : 'resource',
446                 'field' : 'groupmode',
447                 'value' : groupmode,
448                 'id'    : this.get_element_id(element)
449             };
450             var spinner = M.util.add_spinner(Y, element.one(CSS.SPINNERCOMMANDSPAN));
451             this.send_request(data, spinner);
452             return false; // Need to return false to stop the delegate for the new state firing
453         },
454         /**
455          * Add the moveleft button
456          * This is required after moving left from an initial position of 0
457          *
458          * @param target The encapsulating <li> element
459          */
460         add_moveleft : function(target) {
461             var left_string = M.util.get_string('moveleft', 'moodle');
462             var moveimage = 't/left'; // ltr mode
463             if ( Y.one(document.body).hasClass('dir-rtl') ) {
464                 moveimage = 't/right';
465             } else {
466                 moveimage = 't/left';
467             }
468             var newicon = Y.Node.create('<img />')
469                 .addClass(CSS.GENERICICONCLASS)
470                 .setAttrs({
471                     'src'   : M.util.image_url(moveimage, 'moodle'),
472                     'alt'   : left_string
473                 });
474             var moveright = target.one(CSS.MOVERIGHT);
475             var newlink = moveright.getAttribute('href').replace('indent=1', 'indent=-1');
476             var anchor = new Y.Node.create('<a />')
477                 .setStyle('cursor', 'pointer')
478                 .addClass(CSS.MOVELEFTCLASS)
479                 .setAttribute('href', newlink)
480                 .setAttribute('title', left_string);
481             anchor.appendChild(newicon);
482             moveright.insert(anchor, 'before');
483         },
484         /**
485          * Edit the title for the resource
486          */
487         edit_resource_title : function(e) {
488             // Get the element we're working on
489             var element = e.target.ancestor(CSS.ACTIVITYLI);
490             var instancename  = element.one(CSS.INSTANCENAME);
491             var currenttitle = instancename.get('firstChild');
492             var oldtitle = currenttitle.get('data');
493             var titletext = oldtitle;
494             var editbutton = element.one('a.' + CSS.EDITTITLECLASS + ' img');
496             // Handle events for edit_resource_title
497             var listenevents = [];
498             var thisevent;
500             // Grab the anchor so that we can swap it with the edit form
501             var anchor = instancename.ancestor('a');
503             var data = {
504                 'class'   : 'resource',
505                 'field'   : 'gettitle',
506                 'id'      : this.get_element_id(element)
507             };
509             // Try to retrieve the existing string from the server
510             var response = this.send_request(data, editbutton);
511             if (response.instancename) {
512                 titletext = response.instancename;
513             }
515             // Create the editor and submit button
516             var editor = Y.Node.create('<input />')
517                 .setAttrs({
518                     'name'  : 'title',
519                     'value' : titletext,
520                     'autocomplete' : 'off'
521                 })
522                 .addClass('titleeditor');
523             var editform = Y.Node.create('<form />')
524                 .setStyle('padding', '0')
525                 .setStyle('display', 'inline')
526                 .setAttribute('action', '#');
528             var editinstructions = Y.Node.create('<span />')
529                 .addClass('editinstructions')
530                 .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle'));
532             // Clear the existing content and put the editor in
533             currenttitle.set('data', '');
534             editform.appendChild(editor);
535             anchor.replace(editform);
536             element.appendChild(editinstructions);
537             e.preventDefault();
539             // Focus and select the editor text
540             editor.focus().select();
542             // Handle removal of the editor
543             var clear_edittitle = function() {
544                 // Detach all listen events to prevent duplicate triggers
545                 var thisevent;
546                 while (thisevent = listenevents.shift()) {
547                     thisevent.detach();
548                 }
550                 if (editinstructions) {
551                     // Convert back to anchor and remove instructions
552                     editform.replace(anchor);
553                     editinstructions.remove();
554                     editinstructions = null;
555                 }
556             }
558             // Handle cancellation of the editor
559             var cancel_edittitle = function(e) {
560                 clear_edittitle();
562                 // Set the title and anchor back to their previous settings
563                 currenttitle.set('data', oldtitle);
564             };
566             // Cancel the edit if we lose focus or the escape key is pressed
567             thisevent = editor.on('blur', cancel_edittitle);
568             listenevents.push(thisevent);
569             thisevent = Y.one('document').on('keyup', function(e) {
570                 if (e.keyCode == 27) {
571                     cancel_edittitle(e);
572                 }
573             });
574             listenevents.push(thisevent);
576             // Handle form submission
577             thisevent = editform.on('submit', function(e) {
578                 // We don't actually want to submit anything
579                 e.preventDefault();
581                 // Clear the edit title boxes
582                 clear_edittitle();
584                 // We only accept strings which have valid content
585                 var newtitle = Y.Lang.trim(editor.get('value'));
586                 if (newtitle != null && newtitle != "" && newtitle != titletext) {
587                     var data = {
588                         'class'   : 'resource',
589                         'field'   : 'updatetitle',
590                         'title'   : newtitle,
591                         'id'      : this.get_element_id(element)
592                     };
593                     var response = this.send_request(data, editbutton);
594                     if (response.instancename) {
595                         currenttitle.set('data', response.instancename);
596                     }
597                 } else {
598                     // Invalid content. Set the title back to it's original contents
599                     currenttitle.set('data', oldtitle);
600                 }
601             }, this);
602             listenevents.push(thisevent);
603         }
604     }, {
605         NAME : 'course-resource-toolbox',
606         ATTRS : {
607             courseid : {
608                 'value' : 0
609             },
610             format : {
611                 'value' : 'topics'
612             }
613         }
614     });
616     var SECTIONTOOLBOX = function() {
617         SECTIONTOOLBOX.superclass.constructor.apply(this, arguments);
618     }
620     Y.extend(SECTIONTOOLBOX, TOOLBOX, {
621         /**
622          * Initialize the toolboxes module
623          *
624          * Updates all span.commands with relevant handlers and other required changes
625          */
626         initializer : function(config) {
627             this.setup_for_section();
628             M.course.coursebase.register_module(this);
630             // Section Highlighting
631             Y.delegate('click', this.toggle_highlight, CSS.PAGECONTENT, CSS.SECTIONLI + ' ' + CSS.HIGHLIGHT, this);
632             // Section Visibility
633             Y.delegate('click', this.toggle_hide_section, CSS.PAGECONTENT, CSS.SECTIONLI + ' ' + CSS.SHOWHIDE, this);
634         },
635         /**
636          * Update any section areas within the scope of the specified
637          * selector with AJAX equivelants
638          *
639          * @param baseselector The selector to limit scope to
640          * @return void
641          */
642         setup_for_section : function(baseselector) {
643             // Left here for potential future use - not currently needed due to YUI delegation in initializer()
644             /*if (!baseselector) {
645                 var baseselector = CSS.PAGECONTENT;
646             }
648             Y.all(baseselector).each(this._setup_for_section, this);*/
649         },
650         _setup_for_section : function(toolboxtarget) {
651             // Left here for potential future use - not currently needed due to YUI delegation in initializer()
652         },
653         toggle_hide_section : function(e) {
654             // Prevent the default button action
655             e.preventDefault();
657             // Get the section we're working on
658             var section = e.target.ancestor(M.course.format.get_section_selector(Y));
659             var button = e.target.ancestor('a', true);
660             var hideicon = button.one('img');
662             // The value to submit
663             var value;
664             // The status text for strings and images
665             var status;
667             if (!section.hasClass(CSS.SECTIONHIDDENCLASS)) {
668                 section.addClass(CSS.SECTIONHIDDENCLASS);
669                 value = 0;
670                 status = 'show';
672             } else {
673                 section.removeClass(CSS.SECTIONHIDDENCLASS);
674                 value = 1;
675                 status = 'hide';
676             }
678             var newstring = M.util.get_string(status + 'fromothers', 'format_' + this.get('format'));
679             hideicon.setAttrs({
680                 'alt' : newstring,
681                 'src'   : M.util.image_url('i/' + status)
682             });
683             button.set('title', newstring);
685             // Change the highlight status
686             var data = {
687                 'class' : 'section',
688                 'field' : 'visible',
689                 'id'    : this.get_section_id(section.ancestor(M.course.format.get_section_wrapper(Y), true)),
690                 'value' : value
691             };
693             var lightbox = M.util.add_lightbox(Y, section);
694             lightbox.show();
696             var response = this.send_request(data, lightbox);
698             var activities = section.all(CSS.ACTIVITYLI);
699             activities.each(function(node) {
700                 if (node.one(CSS.SHOW)) {
701                     var button = node.one(CSS.SHOW);
702                 } else {
703                     var button = node.one(CSS.HIDE);
704                 }
705                 var activityid = this.get_element_id(node);
707                 if (Y.Array.indexOf(response.resourcestotoggle, activityid) != -1) {
708                     this.toggle_hide_resource_ui(button);
709                 }
711                 if (value == 0) {
712                     button.setStyle('cursor', 'auto');
713                 } else {
714                     button.setStyle('cursor', 'pointer');
715                 }
716             }, this);
717         },
718         toggle_highlight : function(e) {
719             // Prevent the default button action
720             e.preventDefault();
722             // Get the section we're working on
723             var section = e.target.ancestor(M.course.format.get_section_selector(Y));
724             var button = e.target.ancestor('a', true);
725             var buttonicon = button.one('img');
727             // Determine whether the marker is currently set
728             var togglestatus = section.hasClass('current');
729             var value = 0;
731             // Set the current highlighted item text
732             var old_string = M.util.get_string('markthistopic', 'moodle');
733             Y.one(CSS.PAGECONTENT)
734                 .all(M.course.format.get_section_selector(Y) + '.current ' + CSS.HIGHLIGHT)
735                 .set('title', old_string);
736             Y.one(CSS.PAGECONTENT)
737                 .all(M.course.format.get_section_selector(Y) + '.current ' + CSS.HIGHLIGHT + ' img')
738                 .set('alt', old_string)
739                 .set('src', M.util.image_url('i/marker'));
741             // Remove the highlighting from all sections
742             var allsections = Y.one(CSS.PAGECONTENT).all(M.course.format.get_section_selector(Y))
743                 .removeClass('current');
745             // Then add it if required to the selected section
746             if (!togglestatus) {
747                 section.addClass('current');
748                 value = this.get_section_id(section.ancestor(M.course.format.get_section_wrapper(Y), true));
749                 var new_string = M.util.get_string('markedthistopic', 'moodle');
750                 button
751                     .set('title', new_string);
752                 buttonicon
753                     .set('alt', new_string)
754                     .set('src', M.util.image_url('i/marked'));
755             }
757             // Change the highlight status
758             var data = {
759                 'class' : 'course',
760                 'field' : 'marker',
761                 'value' : value
762             };
763             var lightbox = M.util.add_lightbox(Y, section);
764             lightbox.show();
765             this.send_request(data, lightbox);
766         }
767     }, {
768         NAME : 'course-section-toolbox',
769         ATTRS : {
770             courseid : {
771                 'value' : 0
772             },
773             format : {
774                 'value' : 'topics'
775             }
776         }
777     });
779     M.course = M.course || {};
781     M.course.init_resource_toolbox = function(config) {
782         return new RESOURCETOOLBOX(config);
783     };
785     M.course.init_section_toolbox = function(config) {
786         return new SECTIONTOOLBOX(config);
787     };
789 },
790 '@VERSION@', {
791     requires : ['base', 'node', 'io', 'moodle-course-coursebase']
793 );