Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

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