Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('moodle-mod_quiz-dragdrop', function (Y, NAME) {
2
 
3
/* eslint-disable no-unused-vars */
4
/**
5
 * Drag and Drop for Quiz sections and slots.
6
 *
7
 * @module moodle-mod-quiz-dragdrop
8
 */
9
 
10
var CSS = {
11
    ACTIONAREA: '.actions',
12
    ACTIVITY: 'activity',
13
    ACTIVITYINSTANCE: 'activityinstance',
14
    CONTENT: 'content',
15
    COURSECONTENT: 'mod-quiz-edit-content',
16
    EDITINGMOVE: 'editing_move',
17
    ICONCLASS: 'iconsmall',
18
    JUMPMENU: 'jumpmenu',
19
    LEFT: 'left',
20
    LIGHTBOX: 'lightbox',
21
    MOVEDOWN: 'movedown',
22
    MOVEUP: 'moveup',
23
    PAGE: 'page',
24
    PAGECONTENT: 'page-content',
25
    RIGHT: 'right',
26
    SECTION: 'slots',
27
    SECTIONADDMENUS: 'section_add_menus',
28
    SECTIONHANDLE: 'section-handle',
29
    SLOTS: 'slots',
30
    SUMMARY: 'summary',
31
    SECTIONDRAGGABLE: 'sectiondraggable'
32
},
33
// The CSS selectors we use.
34
SELECTOR = {
35
    PAGE: 'li.page',
36
    SLOT: 'li.slot'
37
};
38
/**
39
 * Section drag and drop.
40
 *
41
 * @class M.mod_quiz.dragdrop.section
42
 * @constructor
43
 * @extends M.core.dragdrop
44
 */
45
var DRAGSECTION = function() {
46
    DRAGSECTION.superclass.constructor.apply(this, arguments);
47
};
48
Y.extend(DRAGSECTION, M.core.dragdrop, {
49
    sectionlistselector: null,
50
 
51
    initializer: function() {
52
        // Set group for parent class
53
        this.groups = [CSS.SECTIONDRAGGABLE];
54
        this.samenodeclass = 'section';
55
        this.parentnodeclass = 'slots';
56
 
57
        // Check if we are in single section mode
58
        if (Y.Node.one('.' + CSS.JUMPMENU)) {
59
            return false;
60
        }
61
        // Initialise sections dragging
62
        this.sectionlistselector = 'li.section';
63
        if (this.sectionlistselector) {
64
            this.sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + this.sectionlistselector;
65
 
66
            this.setup_for_section(this.sectionlistselector);
67
 
68
            // Make each li element in the lists of sections draggable
69
            var del = new Y.DD.Delegate({
70
                container: '.' + CSS.COURSECONTENT,
71
                nodes: '.' + CSS.SECTIONDRAGGABLE,
72
                target: true,
73
                handles: ['.' + CSS.LEFT],
74
                dragConfig: {groups: this.groups}
75
            });
76
            del.dd.plug(Y.Plugin.DDProxy, {
77
                // Don't move the node at the end of the drag
78
                moveOnEnd: false
79
            });
80
            del.dd.plug(Y.Plugin.DDConstrained, {
81
                // Keep it inside the .mod-quiz-edit-content
82
                constrain: '#' + CSS.PAGECONTENT,
83
                stickY: true
84
            });
85
            del.dd.plug(Y.Plugin.DDWinScroll);
86
        }
87
    },
88
 
89
    /**
90
     * Apply dragdrop features to the specified selector or node that refers to section(s)
91
     *
92
     * @method setup_for_section
93
     * @param {String} baseselector The CSS selector or node to limit scope to
94
     */
95
    setup_for_section: function(baseselector) {
96
        Y.Node.all(baseselector).each(function(sectionnode) {
97
            // Determine the section ID
98
            var sectionid = Y.Moodle.core_course.util.section.getId(sectionnode);
99
 
100
            // We skip the top section as it is not draggable
101
            if (sectionid > 0) {
102
                // Remove move icons
103
                var movedown = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEDOWN);
104
                var moveup = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEUP);
105
 
106
                // Add dragger icon
107
                var title = M.util.get_string('movesection', 'moodle', sectionid);
108
                var cssleft = sectionnode.one('.' + CSS.LEFT);
109
 
110
                if ((movedown || moveup) && cssleft) {
111
                    cssleft.setStyle('cursor', 'move');
112
                    cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true));
113
 
114
                    if (moveup) {
115
                        moveup.remove();
116
                    }
117
                    if (movedown) {
118
                        movedown.remove();
119
                    }
120
 
121
                    // This section can be moved - add the class to indicate this to Y.DD.
122
                    sectionnode.addClass(CSS.SECTIONDRAGGABLE);
123
                }
124
            }
125
        }, this);
126
    },
127
 
128
    /*
129
     * Drag-dropping related functions
130
     */
131
    drag_start: function(e) {
132
        // Get our drag object
133
        var drag = e.target;
134
        // Creat a dummy structure of the outer elemnents for clean styles application
135
        var containernode = Y.Node.create('<ul class="slots"></ul>');
136
        var sectionnode = Y.Node.create('<ul class="section"></ul>');
137
        sectionnode.setStyle('margin', 0);
138
        sectionnode.setContent(drag.get('node').get('innerHTML'));
139
        containernode.appendChild(sectionnode);
140
        drag.get('dragNode').setContent(containernode);
141
        drag.get('dragNode').addClass(CSS.COURSECONTENT);
142
    },
143
 
144
    drag_dropmiss: function(e) {
145
        // Missed the target, but we assume the user intended to drop it
146
        // on the last last ghost node location, e.drag and e.drop should be
147
        // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
148
        this.drop_hit(e);
149
    },
150
 
151
    get_section_index: function(node) {
152
        var sectionlistselector = '.' + CSS.COURSECONTENT + ' li.section',
153
            sectionList = Y.all(sectionlistselector),
154
            nodeIndex = sectionList.indexOf(node),
155
            zeroIndex = sectionList.indexOf(Y.one('#section-0'));
156
 
157
        return (nodeIndex - zeroIndex);
158
    },
159
 
160
    drop_hit: function(e) {
161
        var drag = e.drag;
162
 
163
        // Get references to our nodes and their IDs.
164
        var dragnode = drag.get('node'),
165
            dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode),
166
            loopstart = dragnodeid,
167
 
168
            dropnodeindex = this.get_section_index(dragnode),
169
            loopend = dropnodeindex;
170
 
171
        if (dragnodeid === dropnodeindex) {
172
            return;
173
        }
174
 
175
 
176
        if (loopstart > loopend) {
177
            // If we're going up, we need to swap the loop order
178
            // because loops can't go backwards.
179
            loopstart = dropnodeindex;
180
            loopend = dragnodeid;
181
        }
182
 
183
        // Get the list of nodes.
184
        drag.get('dragNode').removeClass(CSS.COURSECONTENT);
185
        var sectionlist = Y.Node.all(this.sectionlistselector);
186
 
187
        // Add a lightbox if it's not there.
188
        var lightbox = M.util.add_lightbox(Y, dragnode);
189
 
190
        // Handle any variables which we must pass via AJAX.
191
        var params = {},
192
            pageparams = this.get('config').pageparams,
193
            varname;
194
 
195
        for (varname in pageparams) {
196
            if (!pageparams.hasOwnProperty(varname)) {
197
                continue;
198
            }
199
            params[varname] = pageparams[varname];
200
        }
201
 
202
        // Prepare request parameters
203
        params.sesskey = M.cfg.sesskey;
204
        params.courseid = this.get('courseid');
205
        params.quizid = this.get('quizid');
206
        params['class'] = 'section';
207
        params.field = 'move';
208
        params.id = dragnodeid;
209
        params.value = dropnodeindex;
210
 
211
        // Perform the AJAX request.
212
        var uri = M.cfg.wwwroot + this.get('ajaxurl');
213
        Y.io(uri, {
214
            method: 'POST',
215
            data: params,
216
            on: {
217
                start: function() {
218
                    lightbox.show();
219
                },
220
                success: function(tid, response) {
221
                    // Update section titles, we can't simply swap them as
222
                    // they might have custom title
223
                    try {
224
                        var responsetext = Y.JSON.parse(response.responseText);
225
                        if (responsetext.error) {
226
                            new M.core.ajaxException(responsetext);
227
                        }
228
                        M.mod_quiz.edit.process_sections(Y, sectionlist, responsetext, loopstart, loopend);
229
                    } catch (e) {
230
                        // Ignore.
231
                    }
232
 
233
                    // Update all of the section IDs - first unset them, then set them
234
                    // to avoid duplicates in the DOM.
235
                    var index;
236
 
237
                    // Classic bubble sort algorithm is applied to the section
238
                    // nodes between original drag node location and the new one.
239
                    var swapped = false;
240
                    do {
241
                        swapped = false;
242
                        for (index = loopstart; index <= loopend; index++) {
243
                            if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) >
244
                                        Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) {
245
                                // Swap section id.
246
                                var sectionid = sectionlist.item(index - 1).get('id');
247
                                sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id'));
248
                                sectionlist.item(index).set('id', sectionid);
249
 
250
                                // See what format needs to swap.
251
                                M.mod_quiz.edit.swap_sections(Y, index - 1, index);
252
 
253
                                // Update flag.
254
                                swapped = true;
255
                            }
256
                        }
257
                        loopend = loopend - 1;
258
                    } while (swapped);
259
 
260
                    window.setTimeout(function() {
261
                        lightbox.hide();
262
                    }, 250);
263
                },
264
 
265
                failure: function(tid, response) {
266
                    this.ajax_failure(response);
267
                    lightbox.hide();
268
                }
269
            },
270
            context: this
271
        });
272
    }
273
 
274
}, {
275
    NAME: 'mod_quiz-dragdrop-section',
276
    ATTRS: {
277
        courseid: {
278
            value: null
279
        },
280
        quizid: {
281
            value: null
282
        },
283
        ajaxurl: {
284
            value: 0
285
        },
286
        config: {
287
            value: 0
288
        }
289
    }
290
});
291
 
292
M.mod_quiz = M.mod_quiz || {};
293
M.mod_quiz.init_section_dragdrop = function(params) {
294
    new DRAGSECTION(params);
295
};
296
/**
297
 * Resource drag and drop.
298
 *
299
 * @class M.course.dragdrop.resource
300
 * @constructor
301
 * @extends M.core.dragdrop
302
 */
303
var DRAGRESOURCE = function() {
304
    DRAGRESOURCE.superclass.constructor.apply(this, arguments);
305
};
306
Y.extend(DRAGRESOURCE, M.core.dragdrop, {
307
    initializer: function() {
308
        // Set group for parent class
309
        this.groups = ['resource'];
310
        this.samenodeclass = CSS.ACTIVITY;
311
        this.parentnodeclass = CSS.SECTION;
312
 
313
        this.samenodelabel = {
314
            identifier: 'dragtoafter',
315
            component: 'quiz'
316
        };
317
        this.parentnodelabel = {
318
            identifier: 'dragtostart',
319
            component: 'quiz'
320
        };
321
 
322
        // Go through all sections
323
        this.setup_for_section();
324
 
325
        // Initialise drag & drop for all resources/activities
326
        var nodeselector = 'li.' + CSS.ACTIVITY;
327
        var del = new Y.DD.Delegate({
328
            container: '.' + CSS.COURSECONTENT,
329
            nodes: nodeselector,
330
            target: true,
331
            handles: ['.' + CSS.EDITINGMOVE],
332
            dragConfig: {groups: this.groups}
333
        });
334
        del.dd.plug(Y.Plugin.DDProxy, {
335
            // Don't move the node at the end of the drag
336
            moveOnEnd: false,
337
            cloneNode: true
338
        });
339
        del.dd.plug(Y.Plugin.DDConstrained, {
340
            // Keep it inside the .mod-quiz-edit-content
341
            constrain: '#' + CSS.SLOTS
342
        });
343
        del.dd.plug(Y.Plugin.DDWinScroll);
344
 
345
        M.mod_quiz.quizbase.register_module(this);
346
        M.mod_quiz.dragres = this;
347
    },
348
 
349
    /**
350
     * Apply dragdrop features to the specified selector or node that refers to section(s)
351
     *
352
     * @method setup_for_section
353
     * @param {String} baseselector The CSS selector or node to limit scope to
354
     */
355
    setup_for_section: function() {
356
        Y.Node.all('.mod-quiz-edit-content ul.slots ul.section').each(function(resources) {
357
            resources.setAttribute('data-draggroups', this.groups.join(' '));
358
            // Define empty ul as droptarget, so that item could be moved to empty list
359
            new Y.DD.Drop({
360
                node: resources,
361
                groups: this.groups,
362
                padding: '20 0 20 0'
363
            });
364
 
365
            // Initialise each resource/activity in this section
366
            this.setup_for_resource('li.activity');
367
        }, this);
368
    },
369
 
370
    /**
371
     * Apply dragdrop features to the specified selector or node that refers to resource(s)
372
     *
373
     * @method setup_for_resource
374
     * @param {String} baseselector The CSS selector or node to limit scope to
375
     */
376
    setup_for_resource: function(baseselector) {
377
        Y.Node.all(baseselector).each(function(resourcesnode) {
378
            // Replace move icons
379
            var move = resourcesnode.one('a.' + CSS.EDITINGMOVE);
380
            if (move) {
381
                var resourcedraghandle = this.get_drag_handle(M.util.get_string('move', 'moodle'),
382
                                                              CSS.EDITINGMOVE, CSS.ICONCLASS, true);
383
                move.replace(resourcedraghandle);
384
            }
385
        }, this);
386
    },
387
 
388
    drag_start: function(e) {
389
        // Get our drag object
390
        var drag = e.target;
391
        drag.get('dragNode').setContent(drag.get('node').get('innerHTML'));
392
        drag.get('dragNode').all('.icon').setStyle('vertical-align', 'baseline');
393
    },
394
 
395
    drag_dropmiss: function(e) {
396
        // Missed the target, but we assume the user intended to drop it
397
        // on the last ghost node location, e.drag and e.drop should be
398
        // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
399
        this.drop_hit(e);
400
    },
401
 
402
    drop_hit: function(e) {
403
        var drag = e.drag;
404
        // Get a reference to our drag node
405
        var dragnode = drag.get('node');
406
 
407
        // Add spinner if it not there
408
        var actionarea = dragnode.one(CSS.ACTIONAREA);
409
        var spinner = M.util.add_spinner(Y, actionarea);
410
 
411
        var params = {};
412
 
413
        // Handle any variables which we must pass back through to
414
        var pageparams = this.get('config').pageparams;
415
        var varname;
416
        for (varname in pageparams) {
417
            params[varname] = pageparams[varname];
418
        }
419
 
420
        // Prepare request parameters
421
        params.sesskey = M.cfg.sesskey;
422
        params.courseid = this.get('courseid');
423
        params.quizid = this.get('quizid');
424
        params['class'] = 'resource';
425
        params.field = 'move';
426
        params.id = Number(Y.Moodle.mod_quiz.util.slot.getId(dragnode));
427
        params.sectionId = Y.Moodle.core_course.util.section.getId(dragnode.ancestor('li.section', true));
428
 
429
        var previousslot = dragnode.previous(SELECTOR.SLOT);
430
        if (previousslot) {
431
            params.previousid = Number(Y.Moodle.mod_quiz.util.slot.getId(previousslot));
432
        }
433
 
434
        var previouspage = dragnode.previous(SELECTOR.PAGE);
435
        if (previouspage) {
436
            params.page = Number(Y.Moodle.mod_quiz.util.page.getId(previouspage));
437
        }
438
 
439
        // Do AJAX request
440
        var uri = M.cfg.wwwroot + this.get('ajaxurl');
441
 
442
        Y.io(uri, {
443
            method: 'POST',
444
            data: params,
445
            on: {
446
                start: function() {
447
                    this.lock_drag_handle(drag, CSS.EDITINGMOVE);
448
                    spinner.show();
449
                },
450
                success: function(tid, response) {
451
                    var responsetext = Y.JSON.parse(response.responseText);
452
                    var params = {element: dragnode, visible: responsetext.visible};
453
                    M.mod_quiz.quizbase.invoke_function('set_visibility_resource_ui', params);
454
                    this.unlock_drag_handle(drag, CSS.EDITINGMOVE);
455
                    window.setTimeout(function() {
456
                        spinner.hide();
457
                    }, 250);
458
                    M.mod_quiz.resource_toolbox.reorganise_edit_page();
459
                },
460
                failure: function(tid, response) {
461
                    this.ajax_failure(response);
462
                    this.unlock_drag_handle(drag, CSS.SECTIONHANDLE);
463
                    spinner.hide();
464
                    window.location.reload(true);
465
                }
466
            },
467
            context: this
468
        });
469
    },
470
 
471
    global_drop_over: function(e) {
472
        // Overriding parent method so we can stop the slots being dragged before the first page node.
473
 
474
        // Check that drop object belong to correct group.
475
        if (!e.drop || !e.drop.inGroup(this.groups)) {
476
            return;
477
        }
478
 
479
        // Get a reference to our drag and drop nodes.
480
        var drag = e.drag.get('node'),
481
            drop = e.drop.get('node');
482
 
483
        // Save last drop target for the case of missed target processing.
484
        this.lastdroptarget = e.drop;
485
 
486
        // Are we dropping within the same parent node?
487
        if (drop.hasClass(this.samenodeclass)) {
488
            var where;
489
 
490
            if (this.goingup) {
491
                where = "before";
492
            } else {
493
                where = "after";
494
            }
495
 
496
            drop.insert(drag, where);
497
        } else if ((drop.hasClass(this.parentnodeclass) || drop.test('[data-droptarget="1"]')) && !drop.contains(drag)) {
498
            // We are dropping on parent node and it is empty
499
            if (this.goingup) {
500
                drop.append(drag);
501
            } else {
502
                drop.prepend(drag);
503
            }
504
        }
505
        this.drop_over(e);
506
    }
507
}, {
508
    NAME: 'mod_quiz-dragdrop-resource',
509
    ATTRS: {
510
        courseid: {
511
            value: null
512
        },
513
        quizid: {
514
            value: null
515
        },
516
        ajaxurl: {
517
            value: 0
518
        },
519
        config: {
520
            value: 0
521
        }
522
    }
523
});
524
 
525
M.mod_quiz = M.mod_quiz || {};
526
M.mod_quiz.init_resource_dragdrop = function(params) {
527
    new DRAGRESOURCE(params);
528
};
529
 
530
 
531
}, '@VERSION@', {
532
    "requires": [
533
        "base",
534
        "node",
535
        "io",
536
        "dom",
537
        "dd",
538
        "dd-scroll",
539
        "moodle-core-dragdrop",
540
        "moodle-core-notification",
541
        "moodle-mod_quiz-quizbase",
542
        "moodle-mod_quiz-util-base",
543
        "moodle-mod_quiz-util-page",
544
        "moodle-mod_quiz-util-slot",
545
        "moodle-course-util"
546
    ]
547
});