Merge branch 'MDL-33073-master-1' of git://git.luns.net.uk/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         RIGHTDIV : 'div.right',
32         SECTIONHIDDENCLASS : 'hidden',
33         SECTIONIDPREFIX : 'section-',
34         SECTIONLI : 'li.section',
35         SHOW : 'a.editing_show',
36         SHOWHIDE : 'a.editing_showhide'
37     };
39     /**
40      * The toolbox classes
41      *
42      * TOOLBOX is a generic class which should never be directly instantiated
43      * RESOURCETOOLBOX is a class extending TOOLBOX containing code specific to resources
44      * SECTIONTOOLBOX is a class extending TOOLBOX containing code specific to sections
45      */
46     var TOOLBOX = function() {
47         TOOLBOX.superclass.constructor.apply(this, arguments);
48     }
50     Y.extend(TOOLBOX, Y.Base, {
51         /**
52          * Replace the button click at the selector with the specified
53          * callback
54          *
55          * @param toolboxtarget The selector of the working area
56          * @param selector The 'button' to replace
57          * @param callback The callback to apply
58          * @param cursor An optional cursor style to apply
59          */
60         replace_button : function(toolboxtarget, selector, callback, cursor) {
61             if (!cursor) {
62                 // Set the default cursor type to pointer to match the
63                 // anchor
64                 cursor = 'pointer';
65             }
66             var button = Y.one(toolboxtarget).all(selector)
67                 .setStyle('cursor', cursor);
69             // on isn't chainable and will return an event
70             button.on('click', callback, this);
72             return button;
73         },
74           /**
75            * Toggle the visibility and availability for the specified
76            * resource show/hide button
77            */
78         toggle_hide_resource_ui : function(button) {
79             var element = button.ancestor(CSS.ACTIVITYLI);
80             var hideicon = button.one('img');
82             var dimarea;
83             var toggle_class;
84             if (this.is_label(element)) {
85                 toggle_class = CSS.DIMMEDTEXT;
86                 dimarea = element.one(CSS.MODINDENTDIV + ' div');
87             } else {
88                 toggle_class = CSS.DIMCLASS;
89                 dimarea = element.one('a');
90             }
92             var status = '';
93             var value;
94             if (dimarea.hasClass(toggle_class)) {
95                 status = 'hide';
96                 value = 1;
97             } else {
98                 status = 'show';
99                 value = 0;
100             }
102             // Change the UI
103             dimarea.toggleClass(toggle_class);
104             // We need to toggle dimming on the description too
105             element.all(CSS.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);
106             var newstring = M.util.get_string(status, 'moodle');
107             hideicon.setAttrs({
108                 'alt' : newstring,
109                 'title' : newstring,
110                 'src'   : M.util.image_url('t/' + status)
111             });
112             button.set('title', newstring);
113             button.set('className', 'editing_'+status);
115             return value;
116         },
117         /**
118          * Send a request using the REST API
119          *
120          * @param data The data to submit
121          * @param statusspinner (optional) A statusspinner which may contain a section loader
122          * @param optionalconfig (optional) Any additional configuration to submit
123          * @return response responseText field from responce
124          */
125         send_request : function(data, statusspinner, optionalconfig) {
126             // Default data structure
127             if (!data) {
128                 data = {};
129             }
130             // Handle any variables which we must pass back through to
131             var pageparams = this.get('config').pageparams;
132             for (varname in pageparams) {
133                 data[varname] = pageparams[varname];
134             }
136             data.sesskey = M.cfg.sesskey;
137             data.courseId = this.get('courseid');
139             var uri = M.cfg.wwwroot + this.get('ajaxurl');
141             // Define the configuration to send with the request
142             var responsetext = [];
143             var config = {
144                 method: 'POST',
145                 data: data,
146                 on: {
147                     success: function(tid, response) {
148                         try {
149                             responsetext = Y.JSON.parse(response.responseText);
150                             if (responsetext.error) {
151                                 new M.core.ajaxException(responsetext);
152                             }
153                         } catch (e) {}
154                         if (statusspinner) {
155                             window.setTimeout(function(e) {
156                                 statusspinner.hide();
157                             }, 400);
158                         }
159                     },
160                     failure : function(tid, response) {
161                         if (statusspinner) {
162                             statusspinner.hide();
163                         }
164                         new M.core.ajaxException(response);
165                     }
166                 },
167                 context: this,
168                 sync: true
169             }
171             // Apply optional config
172             if (optionalconfig) {
173                 for (varname in optionalconfig) {
174                     config[varname] = optionalconfig[varname];
175                 }
176             }
178             if (statusspinner) {
179                 statusspinner.show();
180             }
182             // Send the request
183             Y.io(uri, config);
184             return responsetext;
185         },
186         is_label : function(target) {
187             return target.hasClass(CSS.HASLABEL);
188         },
189         /**
190          * Return the module ID for the specified element
191          *
192          * @param element The <li> element to determine a module-id number for
193          * @return string The module ID
194          */
195         get_element_id : function(element) {
196             return element.get('id').replace(CSS.MODULEIDPREFIX, '');
197         },
198         /**
199          * Return the module ID for the specified element
200          *
201          * @param element The <li> element to determine a module-id number for
202          * @return string The module ID
203          */
204         get_section_id : function(section) {
205             return section.get('id').replace(CSS.SECTIONIDPREFIX, '');
206         }
207     },
208     {
209         NAME : 'course-toolbox',
210         ATTRS : {
211             // The ID of the current course
212             courseid : {
213                 'value' : 0
214             },
215             ajaxurl : {
216                 'value' : 0
217             },
218             config : {
219                 'value' : 0
220             }
221         }
222     }
223     );
226     var RESOURCETOOLBOX = function() {
227         RESOURCETOOLBOX.superclass.constructor.apply(this, arguments);
228     }
230     Y.extend(RESOURCETOOLBOX, TOOLBOX, {
231         // Variables
232         GROUPS_NONE     : 0,
233         GROUPS_SEPARATE : 1,
234         GROUPS_VISIBLE  : 2,
236         /**
237          * Initialize the resource toolbox
238          *
239          * Updates all span.commands with relevant handlers and other required changes
240          */
241         initializer : function(config) {
242             this.setup_for_resource();
243             M.course.coursebase.register_module(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             // Edit Title
262             this.replace_button(toolboxtarget, CSS.COMMANDSPAN + ' ' + CSS.EDITTITLE, this.edit_resource_title);
264             // Move left and right
265             this.replace_button(toolboxtarget, CSS.COMMANDSPAN + ' ' + CSS.MOVELEFT, this.move_left);
266             this.replace_button(toolboxtarget, CSS.COMMANDSPAN + ' ' + CSS.MOVERIGHT, this.move_right);
268             // Delete
269             this.replace_button(toolboxtarget, CSS.COMMANDSPAN + ' ' + CSS.DELETE, this.delete_resource);
271             // Show/Hide
272             var showhide = this.replace_button(toolboxtarget, CSS.COMMANDSPAN + ' ' + CSS.HIDE, this.toggle_hide_resource);
273             var shown = this.replace_button(toolboxtarget, CSS.COMMANDSPAN + ' ' + CSS.SHOW, this.toggle_hide_resource);
275             showhide = showhide.concat(shown);
276             showhide.each(function(node) {
277                 var section = node.ancestor(CSS.SECTIONLI);
278                 if (section && section.hasClass(CSS.SECTIONHIDDENCLASS)) {
279                     node.setStyle('cursor', 'auto');
280                 }
281             });
283             // Change Group Mode
284             var groups;
285             groups = this.replace_button(toolboxtarget, CSS.COMMANDSPAN + ' ' + CSS.GROUPSNONE, this.toggle_groupmode);
286             groups.setAttribute('groupmode', this.GROUPS_NONE);
288             groups = this.replace_button(toolboxtarget, CSS.COMMANDSPAN + ' ' + CSS.GROUPSSEPARATE, this.toggle_groupmode);
289             groups.setAttribute('groupmode', this.GROUPS_SEPARATE);
291             groups = this.replace_button(toolboxtarget, CSS.COMMANDSPAN + ' ' + CSS.GROUPSVISIBLE, this.toggle_groupmode);
292             groups.setAttribute('groupmode', this.GROUPS_VISIBLE);
293         },
294         move_left : function(e) {
295             this.move_leftright(e, -1);
296         },
297         move_right : function(e) {
298             this.move_leftright(e, 1);
299         },
300         move_leftright : function(e, direction) {
301             // Prevent the default button action
302             e.preventDefault();
304             // Get the element we're working on
305             var element = e.target.ancestor(CSS.ACTIVITYLI);
307             // And we need to determine the current and new indent level
308             var indentdiv = element.one(CSS.MODINDENTDIV);
309             var indent = indentdiv.getAttribute('class').match(/mod-indent-(\d{1,})/);
311             if (indent) {
312                 var oldindent = parseInt(indent[1]);
313                 var newindent = Math.max(0, (oldindent + parseInt(direction)));
314                 indentdiv.removeClass(indent[0]);
315             } else {
316                 var oldindent = 0;
317                 var newindent = 1;
318             }
320             // Perform the move
321             indentdiv.addClass(CSS.MODINDENTCOUNT + newindent);
322             var data = {
323                 'class' : 'resource',
324                 'field' : 'indent',
325                 'value' : newindent,
326                 'id'    : this.get_element_id(element)
327             };
328             var spinner = M.util.add_spinner(Y, element.one(CSS.SPINNERCOMMANDSPAN));
329             this.send_request(data, spinner);
331             // Handle removal/addition of the moveleft button
332             if (newindent == 0) {
333                 element.one(CSS.MOVELEFT).remove();
334             } else if (newindent == 1 && oldindent == 0) {
335                 this.add_moveleft(element);
336             }
338             // Handle massive indentation to match non-ajax display
339             var hashugeclass = indentdiv.hasClass(CSS.MODINDENTHUGE);
340             if (newindent > 15 && !hashugeclass) {
341                 indentdiv.addClass(CSS.MODINDENTHUGE);
342             } else if (newindent <= 15 && hashugeclass) {
343                 indentdiv.removeClass(CSS.MODINDENTHUGE);
344             }
345         },
346         delete_resource : function(e) {
347             // Prevent the default button action
348             e.preventDefault();
350             // Get the element we're working on
351             var element   = e.target.ancestor(CSS.ACTIVITYLI);
353             var confirmstring = '';
354             if (this.is_label(element)) {
355                 // Labels are slightly different to other activities
356                 var plugindata = {
357                     type : M.util.get_string('pluginname', 'label')
358                 }
359                 confirmstring = M.util.get_string('deletechecktype', 'moodle', plugindata)
360             } else {
361                 var plugindata = {
362                     type : M.util.get_string('pluginname', element.getAttribute('class').match(/modtype_([^\s]*)/)[1]),
363                     name : element.one(CSS.INSTANCENAME).get('firstChild').get('data')
364                 }
365                 confirmstring = M.util.get_string('deletechecktypename', 'moodle', plugindata);
366             }
368             // Confirm element removal
369             if (!confirm(confirmstring)) {
370                 return false;
371             }
373             // Actually remove the element
374             element.remove();
375             var data = {
376                 'class' : 'resource',
377                 'action' : 'DELETE',
378                 'id'    : this.get_element_id(element)
379             };
380             this.send_request(data);
381         },
382         toggle_hide_resource : function(e) {
383             // Prevent the default button action
384             e.preventDefault();
386             // Return early if the current section is hidden
387             var section = e.target.ancestor(CSS.SECTIONLI);
388             if (section && section.hasClass(CSS.SECTIONHIDDENCLASS)) {
389                 return;
390             }
392             // Get the element we're working on
393             var element = e.target.ancestor(CSS.ACTIVITYLI);
395             var button = e.target.ancestor('a', true);
397             var value = this.toggle_hide_resource_ui(button);
399             // Send the request
400             var data = {
401                 'class' : 'resource',
402                 'field' : 'visible',
403                 'value' : value,
404                 'id'    : this.get_element_id(element)
405             };
406             var spinner = M.util.add_spinner(Y, element.one(CSS.SPINNERCOMMANDSPAN));
407             this.send_request(data, spinner);
408         },
409         toggle_groupmode : function(e) {
410             // Prevent the default button action
411             e.preventDefault();
413             // Get the element we're working on
414             var element = e.target.ancestor(CSS.ACTIVITYLI);
416             var button = e.target.ancestor('a', true);
417             var icon = button.one('img');
419             // Current Mode
420             var groupmode = button.getAttribute('groupmode');
421             groupmode++;
422             if (groupmode > 2) {
423                 groupmode = 0;
424             }
425             button.setAttribute('groupmode', groupmode);
427             var newtitle = '';
428             var iconsrc = '';
429             switch (groupmode) {
430                 case this.GROUPS_NONE:
431                     newtitle = 'groupsnone';
432                     iconsrc = M.util.image_url('t/groupn');
433                     break;
434                 case this.GROUPS_SEPARATE:
435                     newtitle = 'groupsseparate';
436                     iconsrc = M.util.image_url('t/groups');
437                     break;
438                 case this.GROUPS_VISIBLE:
439                     newtitle = 'groupsvisible';
440                     iconsrc = M.util.image_url('t/groupv');
441                     break;
442             }
443             newtitle = M.util.get_string('clicktochangeinbrackets', 'moodle',
444                     M.util.get_string(newtitle, 'moodle'));
446             // Change the UI
447             icon.setAttrs({
448                 'alt' : newtitle,
449                 'title' : newtitle,
450                 'src' : iconsrc
451             });
452             button.setAttribute('title', newtitle);
454             // And send the request
455             var data = {
456                 'class' : 'resource',
457                 'field' : 'groupmode',
458                 'value' : groupmode,
459                 'id'    : this.get_element_id(element)
460             };
461             var spinner = M.util.add_spinner(Y, element.one(CSS.SPINNERCOMMANDSPAN));
462             this.send_request(data, spinner);
463         },
464         /**
465          * Add the moveleft button
466          * This is required after moving left from an initial position of 0
467          *
468          * @param target The encapsulating <li> element
469          */
470         add_moveleft : function(target) {
471             var left_string = M.util.get_string('moveleft', 'moodle');
472             var newicon = Y.Node.create('<img />')
473                 .addClass(CSS.GENERICICONCLASS)
474                 .setAttrs({
475                     'src'   : M.util.image_url('t/left', 'moodle'),
476                     'title' : left_string,
477                     'alt'   : left_string
478                 });
479             var moveright = target.one(CSS.MOVERIGHT);
480             var newlink = moveright.getAttribute('href').replace('indent=1', 'indent=-1');
481             var anchor = new Y.Node.create('<a />')
482                 .setStyle('cursor', 'pointer')
483                 .addClass(CSS.MOVELEFTCLASS)
484                 .setAttribute('href', newlink)
485                 .setAttribute('title', left_string);
486             anchor.appendChild(newicon);
487             anchor.on('click', this.move_left, this);
488             moveright.insert(anchor, 'before');
489         },
490         /**
491          * Edit the title for the resource
492          */
493         edit_resource_title : function(e) {
494             // Get the element we're working on
495             var element = e.target.ancestor(CSS.ACTIVITYLI);
496             var instancename  = element.one(CSS.INSTANCENAME);
497             var currenttitle = instancename.get('firstChild');
498             var oldtitle = currenttitle.get('data');
499             var titletext = oldtitle;
500             var editbutton = element.one('a.' + CSS.EDITTITLECLASS + ' img');
502             // Disable the current href to prevent redirections when editing
503             var anchor = instancename.ancestor('a');
504             anchor.setAttribute('oldhref', anchor.getAttribute('href'));
505             anchor.removeAttribute('href');
507             var data = {
508                 'class'   : 'resource',
509                 'field'   : 'gettitle',
510                 'id'      : this.get_element_id(element)
511             };
513             // Try to retrieve the existing string from the server
514             var response = this.send_request(data, editbutton);
515             if (response.instancename) {
516                 titletext = response.instancename;
517             }
519             // Create the editor and submit button
520             var editor = Y.Node.create('<input />')
521                 .setAttrs({
522                     'name'  : 'title',
523                     'value' : titletext,
524                     'autocomplete' : 'off'
525                 })
526                 .addClass('titleeditor');
527             var editform = Y.Node.create('<form />')
528                 .setStyle('padding', '0')
529                 .setStyle('display', 'inline')
530                 .setAttribute('action', '#');
532             var editinstructions = Y.Node.create('<span />')
533                 .addClass('editinstructions')
534                 .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle'));
536             // Clear the existing content and put the editor in
537             currenttitle.set('data', '');
538             editform.appendChild(editor);
539             instancename.appendChild(editform);
540             element.appendChild(editinstructions);
541             e.preventDefault();
543             // Focus and select the editor text
544             editor.focus().select();
546             // Handle cancellation of the editor
547             editor.on('blur', function(e) {
548                 // Detach the blur event before removing as some actions trigger multiple blurs in
549                 // some browser
550                 editor.detach('blur');
551                 editform.remove();
552                 editinstructions.remove();
554                 // Set the title and anchor back to their previous settings
555                 currenttitle.set('data', oldtitle);
556                 anchor.setAttribute('href', anchor.getAttribute('oldhref'));
557                 anchor.removeAttribute('oldhref');
558             });
560             // Handle form submission
561             editform.on('submit', function(e) {
562                 // We don't actually want to submit anything
563                 e.preventDefault();
565                 // Detach the handlers to prevent multiple submissions
566                 editform.detach('submit');
567                 editor.detach('blur');
569                 // We only accept strings which have valid content
570                 var newtitle = Y.Lang.trim(editor.get('value'));
571                 if (newtitle != null && newtitle != "" && newtitle != titletext) {
572                     var data = {
573                         'class'   : 'resource',
574                         'field'   : 'updatetitle',
575                         'title'   : newtitle,
576                         'id'      : this.get_element_id(element)
577                     };
578                     var response = this.send_request(data, editbutton);
579                     if (response.instancename) {
580                         currenttitle.set('data', response.instancename);
581                     }
582                 } else {
583                     // Invalid content. Set the title back to it's original contents
584                     currenttitle.set('data', oldtitle);
585                 }
587                 editform.remove();
588                 editinstructions.remove();
590                 // We need a timeout here otherwise hitting return to save in some browsers triggers
591                 // the anchor
592                 setTimeout(function(e) {
593                     anchor.setAttribute('href', anchor.getAttribute('oldhref'));
594                     anchor.removeAttribute('oldhref');
595                 }, 500);
596             }, this);
597         }
598     }, {
599         NAME : 'course-resource-toolbox',
600         ATTRS : {
601             courseid : {
602                 'value' : 0
603             },
604             format : {
605                 'value' : 'topics'
606             }
607         }
608     });
610     var SECTIONTOOLBOX = function() {
611         SECTIONTOOLBOX.superclass.constructor.apply(this, arguments);
612     }
614     Y.extend(SECTIONTOOLBOX, TOOLBOX, {
615         /**
616          * Initialize the toolboxes module
617          *
618          * Updates all span.commands with relevant handlers and other required changes
619          */
620         initializer : function(config) {
621             this.setup_for_section();
622             M.course.coursebase.register_module(this);
623         },
624         /**
625          * Update any section areas within the scope of the specified
626          * selector with AJAX equivelants
627          *
628          * @param baseselector The selector to limit scope to
629          * @return void
630          */
631         setup_for_section : function(baseselector) {
632             if (!baseselector) {
633                 var baseselector = CSS.PAGECONTENT;
634             }
636             Y.all(baseselector).each(this._setup_for_section, this);
637         },
638         _setup_for_section : function(toolboxtarget) {
639             // Section Highlighting
640             this.replace_button(toolboxtarget, CSS.RIGHTDIV + ' ' + CSS.HIGHLIGHT, this.toggle_highlight);
642             // Section Visibility
643             this.replace_button(toolboxtarget, CSS.RIGHTDIV + ' ' + CSS.SHOWHIDE, this.toggle_hide_section);
644         },
645         toggle_hide_section : function(e) {
646             // Prevent the default button action
647             e.preventDefault();
649             // Get the section we're working on
650             var section = e.target.ancestor(CSS.SECTIONLI);
651             var button = e.target.ancestor('a', true);
652             var hideicon = button.one('img');
654             // The value to submit
655             var value;
656             // The status text for strings and images
657             var status;
659             if (!section.hasClass(CSS.SECTIONHIDDENCLASS)) {
660                 section.addClass(CSS.SECTIONHIDDENCLASS);
661                 value = 0;
662                 status = 'show';
664             } else {
665                 section.removeClass(CSS.SECTIONHIDDENCLASS);
666                 value = 1;
667                 status = 'hide';
668             }
670             var newstring = M.util.get_string(status + 'fromothers', 'format_' + this.get('format'));
671             hideicon.setAttrs({
672                 'alt' : newstring,
673                 'title' : newstring,
674                 'src'   : M.util.image_url('i/' + status)
675             });
676             button.set('title', newstring);
678             // Change the highlight status
679             var data = {
680                 'class' : 'section',
681                 'field' : 'visible',
682                 'id'    : this.get_section_id(section),
683                 'value' : value
684             };
686             var lightbox = M.util.add_lightbox(Y, section);
687             lightbox.show();
689             var response = this.send_request(data, lightbox);
691             var activities = section.all(CSS.ACTIVITYLI);
692             activities.each(function(node) {
693                 if (node.one(CSS.SHOW)) {
694                     var button = node.one(CSS.SHOW);
695                 } else {
696                     var button = node.one(CSS.HIDE);
697                 }
698                 var activityid = this.get_element_id(node);
700                 if (Y.Array.indexOf(response.resourcestotoggle, activityid) != -1) {
701                     this.toggle_hide_resource_ui(button);
702                 }
704                 if (value == 0) {
705                     button.setStyle('cursor', 'auto');
706                 } else {
707                     button.setStyle('cursor', 'pointer');
708                 }
709             }, this);
710         },
711         toggle_highlight : function(e) {
712             // Prevent the default button action
713             e.preventDefault();
715             // Get the section we're working on
716             var section = e.target.ancestor(CSS.SECTIONLI);
717             var button = e.target.ancestor('a', true);
718             var buttonicon = button.one('img');
720             // Determine whether the marker is currently set
721             var togglestatus = section.hasClass('current');
722             var value = 0;
724             // Set the current highlighted item text
725             var old_string = M.util.get_string('markthistopic', 'moodle');
726             Y.one(CSS.PAGECONTENT)
727                 .all(CSS.SECTIONLI + '.current ' + CSS.HIGHLIGHT)
728                 .set('title', old_string);
729             Y.one(CSS.PAGECONTENT)
730                 .all(CSS.SECTIONLI + '.current ' + CSS.HIGHLIGHT + ' img')
731                 .set('title', old_string)
732                 .set('alt', old_string)
733                 .set('src', M.util.image_url('i/marker'));
735             // Remove the highlighting from all sections
736             var allsections = Y.one(CSS.PAGECONTENT).all(CSS.SECTIONLI)
737                 .removeClass('current');
739             // Then add it if required to the selected section
740             if (!togglestatus) {
741                 section.addClass('current');
742                 value = this.get_section_id(section);
743                 var new_string = M.util.get_string('markedthistopic', 'moodle');
744                 button
745                     .set('title', new_string);
746                 buttonicon
747                     .set('title', new_string)
748                     .set('alt', new_string)
749                     .set('src', M.util.image_url('i/marked'));
750             }
752             // Change the highlight status
753             var data = {
754                 'class' : 'course',
755                 'field' : 'marker',
756                 'value' : value
757             };
758             var lightbox = M.util.add_lightbox(Y, section);
759             lightbox.show();
760             this.send_request(data, lightbox);
761         }
762     }, {
763         NAME : 'course-section-toolbox',
764         ATTRS : {
765             courseid : {
766                 'value' : 0
767             },
768             format : {
769                 'value' : 'topics'
770             }
771         }
772     });
774     M.course = M.course || {};
776     M.course.init_resource_toolbox = function(config) {
777         return new RESOURCETOOLBOX(config);
778     };
780     M.course.init_section_toolbox = function(config) {
781         return new SECTIONTOOLBOX(config);
782     };
784 },
785 '@VERSION@', {
786     requires : ['base', 'node', 'io', 'moodle-course-coursebase']
788 );