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