Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
/**
2
 * Section toolbox class.
3
 *
4
 * This class is responsible for managing AJAX interactions with sections
5
 * when adding, editing, removing section headings.
6
 *
7
 * @module moodle-mod_quiz-toolboxes
8
 * @namespace M.mod_quiz.toolboxes
9
 */
10
 
11
/**
12
 * Section toolbox class.
13
 *
14
 * This class is responsible for managing AJAX interactions with sections
15
 * when adding, editing, removing section headings when editing a quiz.
16
 *
17
 * @class section
18
 * @constructor
19
 * @extends M.mod_quiz.toolboxes.toolbox
20
 */
21
var SECTIONTOOLBOX = function() {
22
    SECTIONTOOLBOX.superclass.constructor.apply(this, arguments);
23
};
24
 
25
Y.extend(SECTIONTOOLBOX, TOOLBOX, {
26
    /**
27
     * An Array of events added when editing a max mark field.
28
     * These should all be detached when editing is complete.
29
     *
30
     * @property editsectionevents
31
     * @protected
32
     * @type Array
33
     * @protected
34
     */
35
    editsectionevents: [],
36
 
37
    /**
38
     * Initialize the section toolboxes module.
39
     *
40
     * Updates all span.commands with relevant handlers and other required changes.
41
     *
42
     * @method initializer
43
     * @protected
44
     */
45
    initializer: function() {
46
        M.mod_quiz.quizbase.register_module(this);
47
 
48
        BODY.delegate('key', this.handle_data_action, 'down:enter', SELECTOR.ACTIVITYACTION, this);
49
        Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this);
50
        Y.delegate('change', this.handle_data_action, BODY, SELECTOR.EDITSHUFFLEQUESTIONSACTION, this);
51
    },
52
 
53
    /**
54
     * Handles the delegation event. When this is fired someone has triggered an action.
55
     *
56
     * Note not all actions will result in an AJAX enhancement.
57
     *
58
     * @protected
59
     * @method handle_data_action
60
     * @param {EventFacade} ev The event that was triggered.
61
     * @returns {boolean}
62
     */
63
    handle_data_action: function(ev) {
64
        // We need to get the anchor element that triggered this event.
65
        var node = ev.target;
66
        if (!node.test('a') && !node.test('input[data-action]')) {
67
            node = node.ancestor(SELECTOR.ACTIVITYACTION);
68
        }
69
 
70
        // From the anchor we can get both the activity (added during initialisation) and the action being
71
        // performed (added by the UI as a data attribute).
72
        var action = node.getData('action'),
73
            activity = node.ancestor(SELECTOR.ACTIVITYLI);
74
 
75
        if ((!node.test('a') && !node.test('input[data-action]')) || !action || !activity) {
76
            // It wasn't a valid action node.
77
            return;
78
        }
79
 
80
        // Switch based upon the action and do the desired thing.
81
        switch (action) {
82
            case 'edit_section_title':
83
                // The user wishes to edit the section headings.
84
                this.edit_section_title(ev, node, activity, action);
85
                break;
86
            case 'shuffle_questions':
87
                // The user wishes to edit the shuffle questions of the section (resource).
88
                this.edit_shuffle_questions(ev, node, activity, action);
89
                break;
90
            case 'deletesection':
91
                // The user is deleting the activity.
92
                this.delete_section_with_confirmation(ev, node, activity, action);
93
                break;
94
            default:
95
                // Nothing to do here!
96
                break;
97
        }
98
    },
99
 
100
    /**
101
     * Deletes the given section heading after confirmation.
102
     *
103
     * @protected
104
     * @method delete_section_with_confirmation
105
     * @param {EventFacade} ev The event that was fired.
106
     * @param {Node} button The button that triggered this action.
107
     * @param {Node} activity The activity node that this action will be performed on.
108
     * @chainable
109
     */
110
    delete_section_with_confirmation: function(ev, button, activity) {
111
        ev.preventDefault();
112
        require(['core/notification'], function(Notification) {
113
            Notification.saveCancelPromise(
114
                M.util.get_string('confirm', 'moodle'),
115
                M.util.get_string('confirmremovesectionheading', 'quiz', activity.getData('sectionname')),
116
                M.util.get_string('yes', 'moodle')
117
            ).then(function() {
118
                var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.ACTIONAREA));
119
                var data = {
120
                    'class': 'section',
121
                    'action': 'DELETE',
122
                    'id': activity.get('id').replace('section-', '')
123
                };
124
                this.send_request(data, spinner, function(response) {
125
                    if (response.deleted) {
126
                        window.location.reload(true);
127
                    }
128
                });
129
 
130
                return;
131
            }.bind(this)).catch(function() {
132
                // User cancelled.
133
            });
134
        }.bind(this));
135
    },
136
 
137
    /**
138
     * Edit the edit section title for the section
139
     *
140
     * @protected
141
     * @method edit_section_title
142
     * @param {EventFacade} ev The event that was fired.
143
     * @param {Node} button The button that triggered this action.
144
     * @param {Node} activity The activity node that this action will be performed on.
145
     * @param {String} action The action that has been requested.
146
     * @return Boolean
147
     */
148
    edit_section_title: function(ev, button, activity) {
149
        // Get the element we're working on
150
        var activityid = activity.get('id').replace('section-', ''),
151
            instancesection = activity.one(SELECTOR.INSTANCESECTION),
152
            thisevent,
153
            anchor = instancesection, // Grab the anchor so that we can swap it with the edit form.
154
            data = {
155
                'class': 'section',
156
                'field': 'getsectiontitle',
157
                'id':    activityid
158
            };
159
 
160
        // Prevent the default actions.
161
        ev.preventDefault();
162
 
163
        this.send_request(data, null, function(response) {
164
            // Try to retrieve the existing string from the server.
165
            var oldtext = response.instancesection;
166
 
167
            // Create the editor and submit button.
168
            var editform = Y.Node.create('<form action="#" />');
169
            var editinstructions = Y.Node.create('<span class="' + CSS.EDITINSTRUCTIONS + '" id="id_editinstructions" />')
170
                .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle'));
171
            var editor = Y.Node.create('<input name="section" type="text" />').setAttrs({
172
                'value': oldtext,
173
                'autocomplete': 'off',
174
                'aria-describedby': 'id_editinstructions',
175
                'maxLength': '255' // This is the maxlength in DB.
176
            });
177
 
178
            // Clear the existing content and put the editor in.
179
            editform.appendChild(editor);
180
            editform.setData('anchor', anchor);
181
            instancesection.insert(editinstructions, 'before');
182
            anchor.replace(editform);
183
 
184
            // Focus and select the editor text.
185
            editor.focus().select();
186
            // Cancel the edit if we lose focus or the escape key is pressed.
187
            thisevent = editor.on('blur', this.edit_section_title_cancel, this, activity, false);
188
            this.editsectionevents.push(thisevent);
189
            thisevent = editor.on('key', this.edit_section_title_cancel, 'esc', this, activity, true);
190
            this.editsectionevents.push(thisevent);
191
            // Handle form submission.
192
            thisevent = editform.on('submit', this.edit_section_title_submit, this, activity, oldtext);
193
            this.editsectionevents.push(thisevent);
194
        });
195
    },
196
 
197
    /**
198
     * Handles the submit event when editing section heading.
199
     *
200
     * @protected
201
     * @method edit_section_title_submiy
202
     * @param {EventFacade} ev The event that triggered this.
203
     * @param {Node} activity The activity whose maxmark we are altering.
204
     * @param {String} oldtext The original maxmark the activity or resource had.
205
     */
206
    edit_section_title_submit: function(ev, activity, oldtext) {
207
         // We don't actually want to submit anything.
208
        ev.preventDefault();
209
        var newtext = Y.Lang.trim(activity.one(SELECTOR.SECTIONFORM + ' ' + SELECTOR.SECTIONINPUT).get('value'));
210
        var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.INSTANCESECTIONAREA));
211
        this.edit_section_title_clear(activity);
212
        if (newtext !== null && newtext !== oldtext) {
213
            var instancesection = activity.one(SELECTOR.INSTANCESECTION);
214
            var instancesectiontext = newtext;
215
            if (newtext.trim() === '') {
216
                // Add a sr-only default section heading text to make sure we don't end up with an empty section heading.
217
                instancesectiontext = M.util.get_string('sectionnoname', 'quiz');
218
                instancesection.addClass('sr-only');
219
            } else {
220
                // Show the section heading when a non-empty value is set.
221
                instancesection.removeClass('sr-only');
222
            }
223
            instancesection.setContent(instancesectiontext);
224
 
225
            var data = {
226
                'class':      'section',
227
                'field':      'updatesectiontitle',
228
                'newheading': newtext,
229
                'id':         activity.get('id').replace('section-', '')
230
            };
231
            this.send_request(data, spinner, function(response) {
232
                if (response) {
233
                    // Set the content of the section heading if for some reason the response is different from the new text.
234
                    // e.g. filters were applied, the update failed, etc.
235
                    if (newtext !== response.instancesection) {
236
                        if (response.instancesection.trim() === '') {
237
                            // Add a sr-only default section heading text.
238
                            instancesectiontext = M.util.get_string('sectionnoname', 'quiz');
239
                            instancesection.addClass('sr-only');
240
                        } else {
241
                            instancesectiontext = response.instancesection;
242
                            // Show the section heading when a non-empty value is set.
243
                            instancesection.removeClass('sr-only');
244
                        }
245
                        instancesection.setContent(instancesectiontext);
246
                    }
247
 
248
                    activity.one(SELECTOR.EDITSECTIONICON).set('title',
249
                            M.util.get_string('sectionheadingedit', 'quiz', response.instancesection));
250
                    activity.one(SELECTOR.EDITSECTIONICON).set('alt',
251
                            M.util.get_string('sectionheadingedit', 'quiz', response.instancesection));
252
                    var deleteicon = activity.one(SELECTOR.DELETESECTIONICON);
253
                    if (deleteicon) {
254
                        deleteicon.set('title', M.util.get_string('sectionheadingremove', 'quiz', response.instancesection));
255
                        deleteicon.set('alt', M.util.get_string('sectionheadingremove', 'quiz', response.instancesection));
256
                    }
257
                }
258
            });
259
        }
260
    },
261
 
262
    /**
263
     * Handles the cancel event when editing the activity or resources maxmark.
264
     *
265
     * @protected
266
     * @method edit_maxmark_cancel
267
     * @param {EventFacade} ev The event that triggered this.
268
     * @param {Node} activity The activity whose maxmark we are altering.
269
     * @param {Boolean} preventdefault If true we should prevent the default action from occuring.
270
     */
271
    edit_section_title_cancel: function(ev, activity, preventdefault) {
272
        if (preventdefault) {
273
            ev.preventDefault();
274
        }
275
        this.edit_section_title_clear(activity);
276
    },
277
 
278
    /**
279
     * Handles clearing the editing UI and returning things to the original state they were in.
280
     *
281
     * @protected
282
     * @method edit_maxmark_clear
283
     * @param {Node} activity  The activity whose maxmark we were altering.
284
     */
285
    edit_section_title_clear: function(activity) {
286
        // Detach all listen events to prevent duplicate triggers
287
        new Y.EventHandle(this.editsectionevents).detach();
288
 
289
        var editform = activity.one(SELECTOR.SECTIONFORM),
290
            instructions = activity.one('#id_editinstructions');
291
        if (editform) {
292
            editform.replace(editform.getData('anchor'));
293
        }
294
        if (instructions) {
295
            instructions.remove();
296
        }
297
 
298
        // Refocus the link which was clicked originally so the user can continue using keyboard nav.
299
        Y.later(100, this, function() {
300
            activity.one(SELECTOR.EDITSECTION).focus();
301
        });
302
 
303
        // This hack is to keep Behat happy until they release a version of
304
        // MinkSelenium2Driver that fixes
305
        // https://github.com/Behat/MinkSelenium2Driver/issues/80.
306
        if (!Y.one('input[name=section]')) {
307
            Y.one('body').append('<input type="text" name="section" style="display: none">');
308
        }
309
    },
310
 
311
    /**
312
     * Edit the edit shuffle questions for the section
313
     *
314
     * @protected
315
     * @method edit_shuffle_questions
316
     * @param {EventFacade} ev The event that was fired.
317
     * @param {Node} button The button that triggered this action.
318
     * @param {Node} activity The activity node that this action will be performed on.
319
     * @return Boolean
320
     */
321
    edit_shuffle_questions: function(ev, button, activity) {
322
        var newvalue;
323
        if (activity.one(SELECTOR.EDITSHUFFLEQUESTIONSACTION).get('checked')) {
324
            newvalue = 1;
325
            activity.addClass('shuffled');
326
        } else {
327
            newvalue = 0;
328
            activity.removeClass('shuffled');
329
        }
330
 
331
        // Prevent the default actions.
332
        ev.preventDefault();
333
 
334
        // Get the element we're working on
335
        var data = {
336
            'class': 'section',
337
            'field': 'updateshufflequestions',
338
            'id': activity.get('id').replace('section-', ''),
339
            'newshuffle': newvalue
340
        };
341
 
342
        // Send request.
343
        var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.EDITSHUFFLEAREA));
344
        this.send_request(data, spinner);
345
    }
346
 
347
}, {
348
    NAME: 'mod_quiz-section-toolbox',
349
    ATTRS: {
350
        courseid: {
351
            'value': 0
352
        },
353
        quizid: {
354
            'value': 0
355
        }
356
    }
357
});
358
 
359
M.mod_quiz.init_section_toolbox = function(config) {
360
    return new SECTIONTOOLBOX(config);
361
};