Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('moodle-core-dragdrop', function (Y, NAME) {
2
 
3
/* eslint-disable no-empty-function */
4
/**
5
 * The core drag and drop module for Moodle which extends the YUI drag and
6
 * drop functionality with additional features.
7
 *
8
 * @module moodle-core-dragdrop
9
 */
10
var MOVEICON = {
11
    pix: "i/move_2d",
12
    largepix: "i/dragdrop",
13
    component: 'moodle',
14
    cssclass: 'moodle-core-dragdrop-draghandle'
15
};
16
 
17
/**
18
 * General DRAGDROP class, this should not be used directly,
19
 * it is supposed to be extended by your class
20
 *
21
 * @class M.core.dragdrop
22
 * @constructor
23
 * @extends Base
24
 */
25
var DRAGDROP = function() {
26
    DRAGDROP.superclass.constructor.apply(this, arguments);
27
};
28
 
29
Y.extend(DRAGDROP, Y.Base, {
30
    /**
31
     * Whether the item is being moved upwards compared with the last
32
     * location.
33
     *
34
     * @property goingup
35
     * @type Boolean
36
     * @default null
37
     */
38
    goingup: null,
39
 
40
    /**
41
     * Whether the item is being moved upwards compared with the start
42
     * point.
43
     *
44
     * @property absgoingup
45
     * @type Boolean
46
     * @default null
47
     */
48
    absgoingup: null,
49
 
50
    /**
51
     * The class for the object.
52
     *
53
     * @property samenodeclass
54
     * @type String
55
     * @default null
56
     */
57
    samenodeclass: null,
58
 
59
    /**
60
     * The class on the parent of the item being moved.
61
     *
62
     * @property parentnodeclass
63
     * @type String
64
     * @default
65
     */
66
    parentnodeclass: null,
67
 
68
    /**
69
     * The label to use with keyboard drag/drop to describe items of the same Node.
70
     *
71
     * @property samenodelabel
72
     * @type Object
73
     * @default null
74
     */
75
    samenodelabel: null,
76
 
77
    /**
78
     * The label to use with keyboard drag/drop to describe items of the parent Node.
79
     *
80
     * @property samenodelabel
81
     * @type Object
82
     * @default null
83
     */
84
    parentnodelabel: null,
85
 
86
    /**
87
     * The groups for this instance.
88
     *
89
     * @property groups
90
     * @type Array
91
     * @default []
92
     */
93
    groups: [],
94
 
95
    /**
96
     * The previous drop location.
97
     *
98
     * @property lastdroptarget
99
     * @type Node
100
     * @default null
101
     */
102
    lastdroptarget: null,
103
 
104
    /**
105
     * Should the direction of a keyboard drag and drop item be detected.
106
     *
107
     * @property detectkeyboarddirection
108
     * @type Boolean
109
     * @default false
110
     */
111
    detectkeyboarddirection: false,
112
 
113
    /**
114
     * Listeners.
115
     *
116
     * @property listeners
117
     * @type Array
118
     * @default null
119
     */
120
    listeners: null,
121
 
122
    /**
123
     * The initializer which sets up the move action.
124
     *
125
     * @method initializer
126
     * @protected
127
     */
128
    initializer: function() {
129
        this.listeners = [];
130
 
131
        // Listen for all drag:start events.
132
        this.listeners.push(Y.DD.DDM.on('drag:start', this.global_drag_start, this));
133
 
134
        // Listen for all drag:over events.
135
        this.listeners.push(Y.DD.DDM.on('drag:over', this.globalDragOver, this));
136
 
137
        // Listen for all drag:end events.
138
        this.listeners.push(Y.DD.DDM.on('drag:end', this.global_drag_end, this));
139
 
140
        // Listen for all drag:drag events.
141
        this.listeners.push(Y.DD.DDM.on('drag:drag', this.global_drag_drag, this));
142
 
143
        // Listen for all drop:over events.
144
        this.listeners.push(Y.DD.DDM.on('drop:over', this.global_drop_over, this));
145
 
146
        // Listen for all drop:hit events.
147
        this.listeners.push(Y.DD.DDM.on('drop:hit', this.global_drop_hit, this));
148
 
149
        // Listen for all drop:miss events.
150
        this.listeners.push(Y.DD.DDM.on('drag:dropmiss', this.global_drag_dropmiss, this));
151
 
152
        // Add keybaord listeners for accessible drag/drop
153
        this.listeners.push(Y.one(Y.config.doc.body).delegate('key', this.global_keydown,
154
                'down:32, enter, esc', '.' + MOVEICON.cssclass, this));
155
 
156
        // Make the accessible drag/drop respond to a single click.
157
        this.listeners.push(Y.one(Y.config.doc.body).delegate('click', this.global_keydown,
158
                '.' + MOVEICON.cssclass, this));
159
    },
160
 
161
    /**
162
     * The destructor to shut down the instance of the dragdrop system.
163
     *
164
     * @method destructor
165
     * @protected
166
     */
167
    destructor: function() {
168
        new Y.EventHandle(this.listeners).detach();
169
    },
170
 
171
    /**
172
     * Build a new drag handle Node.
173
     *
174
     * @method get_drag_handle
175
     * @param {String} title The title on the drag handle
176
     * @param {String} classname The name of the class to add to the node
177
     * wrapping the drag icon
178
     * @param {String} iconclass Additional class to add to the icon.
179
     * @return Node The built drag handle.
180
     */
181
    get_drag_handle: function(title, classname, iconclass) {
182
 
183
        var dragelement = Y.Node.create('<span></span>')
184
            .addClass(classname)
185
            .setAttribute('title', title)
186
            .setAttribute('tabIndex', 0)
187
            .setAttribute('data-draggroups', this.groups)
188
            .setAttribute('role', 'button');
189
        dragelement.addClass(MOVEICON.cssclass);
190
 
191
        window.require(['core/templates'], function(Templates) {
192
            Templates.renderPix('i/move_2d', 'core').then(function(html) {
193
                var dragicon = Y.Node.create(html);
194
                dragicon.setStyle('cursor', 'move');
195
                if (typeof iconclass != 'undefined') {
196
                    dragicon.addClass(iconclass);
197
                }
198
                dragelement.appendChild(dragicon);
199
            });
200
        });
201
 
202
        return dragelement;
203
    },
204
 
205
    lock_drag_handle: function(drag, classname) {
206
        drag.removeHandle('.' + classname);
207
    },
208
 
209
    unlock_drag_handle: function(drag, classname) {
210
        drag.addHandle('.' + classname);
211
        drag.get('activeHandle').focus();
212
    },
213
 
214
    ajax_failure: function(response) {
215
        var e = {
216
            name: response.status + ' ' + response.statusText,
217
            message: response.responseText
218
        };
219
        return new M.core.exception(e);
220
    },
221
 
222
    in_group: function(target) {
223
        var ret = false;
224
        Y.each(this.groups, function(v) {
225
            if (target._groups[v]) {
226
                ret = true;
227
            }
228
        }, this);
229
        return ret;
230
    },
231
    /*
232
     * Drag-dropping related functions
233
     */
234
    global_drag_start: function(e) {
235
        // Get our drag object
236
        var drag = e.target;
237
        // Check that drag object belongs to correct group
238
        if (!this.in_group(drag)) {
239
            return;
240
        }
241
        // Store the nodes current style, so we can restore it later.
242
        this.originalstyle = drag.get('node').getAttribute('style');
243
        // Set some general styles here
244
        drag.get('node').setStyle('opacity', '.25');
245
        drag.get('dragNode').setStyles({
246
            opacity: '.75',
247
            borderColor: drag.get('node').getStyle('borderColor'),
248
            backgroundColor: drag.get('node').getStyle('backgroundColor')
249
        });
250
        drag.get('dragNode').empty();
251
        this.drag_start(e);
252
    },
253
 
254
    /**
255
     * Drag-dropping related functions
256
     *
257
     * @param {EventFacade} e
258
     */
259
    globalDragOver: function(e) {
260
        this.dragOver(e);
261
    },
262
 
263
    global_drag_end: function(e) {
264
        var drag = e.target;
265
        // Check that drag object belongs to correct group
266
        if (!this.in_group(drag)) {
267
            return;
268
        }
269
        // Put our general styles back
270
        drag.get('node').setAttribute('style', this.originalstyle);
271
        this.drag_end(e);
272
    },
273
 
274
    global_drag_drag: function(e) {
275
        var drag = e.target,
276
            info = e.info;
277
 
278
        // Check that drag object belongs to correct group
279
        if (!this.in_group(drag)) {
280
            return;
281
        }
282
 
283
        // Note, we test both < and > situations here. We don't want to
284
        // effect a change in direction if the user is only moving side
285
        // to side with no Y position change.
286
 
287
        // Detect changes in the position relative to the start point.
288
        if (info.start[1] < info.xy[1]) {
289
            // We are going up if our final position is higher than our start position.
290
            this.absgoingup = true;
291
 
292
        } else if (info.start[1] > info.xy[1]) {
293
            // Otherwise we're going down.
294
            this.absgoingup = false;
295
        }
296
 
297
        // Detect changes in the position relative to the last movement.
298
        if (info.delta[1] < 0) {
299
            // We are going up if our final position is higher than our start position.
300
            this.goingup = true;
301
 
302
        } else if (info.delta[1] > 0) {
303
            // Otherwise we're going down.
304
            this.goingup = false;
305
        }
306
 
307
        this.drag_drag(e);
308
    },
309
 
310
    global_drop_over: function(e) {
311
        // Check that drop object belong to correct group.
312
        if (!e.drop || !e.drop.inGroup(this.groups)) {
313
            return;
314
        }
315
 
316
        // Get a reference to our drag and drop nodes.
317
        var drag = e.drag.get('node'),
318
            drop = e.drop.get('node');
319
 
320
        // Save last drop target for the case of missed target processing.
321
        this.lastdroptarget = e.drop;
322
 
323
        // Are we dropping within the same parent node?
324
        if (drop.hasClass(this.samenodeclass)) {
325
            var where;
326
 
327
            if (this.goingup) {
328
                where = "before";
329
            } else {
330
                where = "after";
331
            }
332
 
333
            // Add the node contents so that it's moved, otherwise only the drag handle is moved.
334
            drop.insert(drag, where);
335
        } else if ((drop.hasClass(this.parentnodeclass) || drop.test('[data-droptarget="1"]')) && !drop.contains(drag)) {
336
            // We are dropping on parent node and it is empty
337
            if (this.goingup) {
338
                drop.append(drag);
339
            } else {
340
                drop.prepend(drag);
341
            }
342
        }
343
        this.drop_over(e);
344
    },
345
 
346
    global_drag_dropmiss: function(e) {
347
        // drag:dropmiss does not have e.drag and e.drop properties
348
        // we substitute them for the ease of use. For e.drop we use,
349
        // this.lastdroptarget (ghost node we use for indicating where to drop)
350
        e.drag = e.target;
351
        e.drop = this.lastdroptarget;
352
        // Check that drag object belongs to correct group
353
        if (!this.in_group(e.drag)) {
354
            return;
355
        }
356
        // Check that drop object belong to correct group
357
        if (!e.drop || !e.drop.inGroup(this.groups)) {
358
            return;
359
        }
360
        this.drag_dropmiss(e);
361
    },
362
 
363
    global_drop_hit: function(e) {
364
        // Check that drop object belong to correct group
365
        if (!e.drop || !e.drop.inGroup(this.groups)) {
366
            return;
367
        }
368
        this.drop_hit(e);
369
    },
370
 
371
    /**
372
     * This is used to build the text for the heading of the keyboard
373
     * drag drop menu and the text for the nodes in the list.
374
     * @method find_element_text
375
     * @param {Node} n The node to start searching for a valid text node.
376
     * @return {string} The text of the first text-like child node of n.
377
     */
378
    find_element_text: function(n) {
379
        var text = '';
380
 
381
        // Try to resolve using aria-label first.
382
        text = n.get('aria-label') || '';
383
        if (text.length > 0) {
384
            return text;
385
        }
386
 
387
        // Now try to resolve using aria-labelledby.
388
        var labelledByNode = n.get('aria-labelledby');
389
        if (labelledByNode) {
390
            var labelNode = Y.one('#' + labelledByNode);
391
            if (labelNode && labelNode.get('text').length > 0) {
392
                return labelNode.get('text');
393
            }
394
        }
395
 
396
        // The valid node types to get text from.
397
        var nodes = n.all('h2, h3, h4, h5, span:not(.actions):not(.menu-action-text), p, div.no-overflow, div.dimmed_text');
398
 
399
        nodes.each(function() {
400
            if (text === '') {
401
                if (Y.Lang.trim(this.get('text')) !== '') {
402
                    text = this.get('text');
403
                }
404
            }
405
        });
406
 
407
        if (text !== '') {
408
            return text;
409
        }
410
        return M.util.get_string('emptydragdropregion', 'moodle');
411
    },
412
 
413
    /**
414
     * This is used to initiate a keyboard version of a drag and drop.
415
     * A dialog will open listing all the valid drop targets that can be selected
416
     * using tab, tab, tab, enter.
417
     * @method global_start_keyboard_drag
418
     * @param {Event} e The keydown / click event on the grab handle.
419
     * @param {Node} dragcontainer The resolved draggable node (an ancestor of the drag handle).
420
     * @param {Node} draghandle The node that triggered this action.
421
     */
422
    global_start_keyboard_drag: function(e, draghandle, dragcontainer) {
423
        M.core.dragdrop.keydragcontainer = dragcontainer;
424
        M.core.dragdrop.keydraghandle = draghandle;
425
 
426
        // Get the name of the thing to move.
427
        var nodetitle = this.find_element_text(dragcontainer);
428
        var dialogtitle = M.util.get_string('movecontent', 'moodle', nodetitle);
429
 
430
        // Build the list of drop targets.
431
        var droplist = Y.Node.create('<ul></ul>');
432
        droplist.addClass('dragdrop-keyboard-drag');
433
        var listitem, listlink, listitemtext;
434
 
435
        // Search for possible drop targets.
436
        var droptargets = Y.all('.' + this.samenodeclass + ', .' + this.parentnodeclass);
437
 
438
        droptargets.each(function(node) {
439
            var validdrop = false;
440
            var labelroot = node;
441
            var className = node.getAttribute("class").split(' ').join(', .');
442
 
443
            if (node.drop && node.drop.inGroup(this.groups) && node.drop.get('node') !== dragcontainer &&
444
                    !(node.next(className) === dragcontainer && !this.detectkeyboarddirection)) {
445
                // This is a drag and drop target with the same class as the grabbed node.
446
                validdrop = true;
447
            } else {
448
                var elementgroups = node.getAttribute('data-draggroups').split(' ');
449
                var i, j;
450
                for (i = 0; i < elementgroups.length; i++) {
451
                    for (j = 0; j < this.groups.length; j++) {
452
                        if (elementgroups[i] === this.groups[j] && !node.ancestor('.yui3-dd-proxy') && !(node == dragcontainer ||
453
                            node.next(className) === dragcontainer || node.get('children').item(0) == dragcontainer)) {
454
                                // This is a parent node of the grabbed node (used for dropping in empty sections).
455
                                validdrop = true;
456
                                // This node will have no text - so we get the first valid text from the parent.
457
                                labelroot = node.get('parentNode');
458
                                break;
459
                        }
460
                    }
461
                    if (validdrop) {
462
                        break;
463
                    }
464
                }
465
            }
466
 
467
            if (validdrop) {
468
                // It is a valid drop target - create a list item for it.
469
                listitem = Y.Node.create('<li></li>');
470
                listlink = Y.Node.create('<a></a>');
471
                nodetitle = this.find_element_text(labelroot);
472
 
473
                if (this.samenodelabel && node.hasClass(this.samenodeclass)) {
474
                    listitemtext = M.util.get_string(this.samenodelabel.identifier, this.samenodelabel.component, nodetitle);
475
                } else if (this.parentnodelabel && node.hasClass(this.parentnodeclass)) {
476
                    listitemtext = M.util.get_string(this.parentnodelabel.identifier, this.parentnodelabel.component, nodetitle);
477
                } else {
478
                    listitemtext = M.util.get_string('tocontent', 'moodle', nodetitle);
479
                }
480
                listlink.setContent(listitemtext);
481
 
482
                // Add a data attribute so we can get the real drop target.
483
                listlink.setAttribute('data-drop-target', node.get('id'));
484
                // Allow tabbing to the link.
485
                listlink.setAttribute('tabindex', '0');
486
                listlink.setAttribute('role', 'button');
487
 
488
                // Set the event listeners for enter, space or click.
489
                listlink.on('click', this.global_keyboard_drop, this);
490
                listlink.on('key', this.global_keyboard_drop, 'down:enter,32', this);
491
 
492
                // Add to the list or drop targets.
493
                listitem.append(listlink);
494
                droplist.append(listitem);
495
            }
496
        }, this);
497
 
498
        // Create the dialog for the interaction.
499
        M.core.dragdrop.dropui = new M.core.dialogue({
500
            headerContent: dialogtitle,
501
            bodyContent: droplist,
502
            draggable: true,
503
            visible: true,
504
            center: true,
505
            modal: true
506
        });
507
 
508
        M.core.dragdrop.dropui.after('visibleChange', function(e) {
509
            // After the dialogue has been closed, we call the cancel function. This will
510
            // ensure that tidying up happens (e.g. focusing on the start Node).
511
            if (e.prevVal && !e.newVal) {
512
                this.global_cancel_keyboard_drag();
513
            }
514
        }, this);
515
 
516
        // Focus the first drop target.
517
        if (droplist.one('a')) {
518
            droplist.one('a').focus();
519
        }
520
    },
521
 
522
    /**
523
     * This is used as a simulated drag/drop event in order to prevent any
524
     * subtle bugs from creating a real instance of a drag drop event. This means
525
     * there are no state changes in the Y.DD.DDM and any undefined functions
526
     * will trigger an obvious and fatal error.
527
     * The end result is that we call all our drag/drop handlers but do not bubble the
528
     * event to anyone else.
529
     *
530
     * The functions/properties implemented in the wrapper are:
531
     * e.target
532
     * e.drag
533
     * e.drop
534
     * e.drag.get('node')
535
     * e.drop.get('node')
536
     * e.drag.addHandle()
537
     * e.drag.removeHandle()
538
     *
539
     * @method simulated_drag_drop_event
540
     * @param {Node} dragnode The drag container node
541
     * @param {Node} dropnode The node to initiate the drop on
542
     */
543
    simulated_drag_drop_event: function(dragnode, dropnode) {
544
 
545
        // Subclass for wrapping both drag and drop.
546
        var DragDropWrapper = function(node) {
547
            this.node = node;
548
        };
549
 
550
        // Method e.drag.get() - get the node.
551
        DragDropWrapper.prototype.get = function(param) {
552
            if (param === 'node' || param === 'dragNode' || param === 'dropNode') {
553
                return this.node;
554
            }
555
            if (param === 'activeHandle') {
556
                return this.node.one('.editing_move');
557
            }
558
            return null;
559
        };
560
 
561
        // Method e.drag.inGroup() - we have already run the group checks before triggering the event.
562
        DragDropWrapper.prototype.inGroup = function() {
563
            return true;
564
        };
565
 
566
        // Method e.drag.addHandle() - we don't want to run this.
567
        DragDropWrapper.prototype.addHandle = function() {};
568
        // Method e.drag.removeHandle() - we don't want to run this.
569
        DragDropWrapper.prototype.removeHandle = function() {};
570
 
571
        // Create instances of the DragDropWrapper.
572
        this.drop = new DragDropWrapper(dropnode);
573
        this.drag = new DragDropWrapper(dragnode);
574
        this.target = this.drop;
575
    },
576
 
577
    /**
578
     * This is used to complete a keyboard version of a drag and drop.
579
     * A drop event will be simulated based on the drag and drop nodes.
580
     * @method global_keyboard_drop
581
     * @param {Event} e The keydown / click event on the proxy drop node.
582
     */
583
    global_keyboard_drop: function(e) {
584
        // The drag node was saved.
585
        var dragcontainer = M.core.dragdrop.keydragcontainer;
586
        // The real drop node is stored in an attribute of the proxy.
587
        var droptarget = Y.one('#' + e.target.getAttribute('data-drop-target'));
588
 
589
        // Close the dialog.
590
        M.core.dragdrop.dropui.hide();
591
        // Cancel the event.
592
        e.preventDefault();
593
        // Detect the direction of travel.
594
        if (this.detectkeyboarddirection && dragcontainer.getY() > droptarget.getY()) {
595
            // We can detect the keyboard direction and it is going up.
596
            this.absgoingup = true;
597
            this.goingup = true;
598
        } else {
599
            // The default behaviour is to treat everything as moving down.
600
            this.absgoingup = false;
601
            this.goingup = false;
602
        }
603
        // Convert to drag drop events.
604
        var dragevent = new this.simulated_drag_drop_event(dragcontainer, dragcontainer);
605
        var dropevent = new this.simulated_drag_drop_event(dragcontainer, droptarget);
606
        // Simulate the full sequence.
607
        this.drag_start(dragevent);
608
        this.global_drop_over(dropevent);
609
 
610
        if (droptarget.hasClass(this.parentnodeclass) && droptarget.contains(dragcontainer)) {
611
            // Handle the case where an item is dropped into a container (for example an activity into a new section).
612
            droptarget.prepend(dragcontainer);
613
        }
614
 
615
        this.global_drop_hit(dropevent);
616
    },
617
 
618
    /**
619
     * This is used to cancel a keyboard version of a drag and drop.
620
     *
621
     * @method global_cancel_keyboard_drag
622
     */
623
    global_cancel_keyboard_drag: function() {
624
        if (M.core.dragdrop.keydragcontainer) {
625
            // Focus on the node which was being dragged.
626
            M.core.dragdrop.keydraghandle.focus();
627
            M.core.dragdrop.keydragcontainer = null;
628
        }
629
        if (M.core.dragdrop.dropui) {
630
            M.core.dragdrop.dropui.destroy();
631
        }
632
    },
633
 
634
    /**
635
     * Process key events on the drag handles.
636
     *
637
     * @method global_keydown
638
     * @param {EventFacade} e The keydown / click event on the drag handle.
639
     */
640
    global_keydown: function(e) {
641
        var draghandle = e.target.ancestor('.' + MOVEICON.cssclass, true),
642
            dragcontainer,
643
            draggroups;
644
 
645
        if (draghandle === null) {
646
            // The element clicked did not have a a draghandle in it's lineage.
647
            return;
648
        }
649
 
650
        if (e.keyCode === 27) {
651
            // Escape to cancel from anywhere.
652
            this.global_cancel_keyboard_drag();
653
            e.preventDefault();
654
            return;
655
        }
656
 
657
        // Only process events on a drag handle.
658
        if (!draghandle.hasClass(MOVEICON.cssclass)) {
659
            return;
660
        }
661
 
662
        // Do nothing if not space or enter.
663
        if (e.keyCode !== 13 && e.keyCode !== 32 && e.type !== 'click') {
664
            return;
665
        }
666
 
667
        // Check the drag groups to see if we are the handler for this node.
668
        draggroups = draghandle.getAttribute('data-draggroups').split(' ');
669
        var i, j;
670
        var validgroup = false;
671
 
672
        for (i = 0; i < draggroups.length; i++) {
673
            for (j = 0; j < this.groups.length; j++) {
674
                if (draggroups[i] === this.groups[j]) {
675
                    validgroup = true;
676
                    break;
677
                }
678
            }
679
            if (validgroup) {
680
                break;
681
            }
682
        }
683
        if (!validgroup) {
684
            return;
685
        }
686
 
687
        // Valid event - start the keyboard drag.
688
        dragcontainer = draghandle.ancestor('.yui3-dd-drop');
689
        this.global_start_keyboard_drag(e, draghandle, dragcontainer);
690
 
691
        e.preventDefault();
692
    },
693
 
694
 
695
    // Abstract functions definitions.
696
 
697
    /**
698
     * Callback to use when dragging starts.
699
     *
700
     * @method drag_start
701
     * @param {EventFacade} e
702
     */
703
    drag_start: function() {},
704
 
705
    /**
706
     * Callback to use for the drag:over event.
707
     *
708
     * @method dragOver
709
     * @param {EventFacade} e
710
     */
711
    dragOver: function() {},
712
 
713
    /**
714
     * Callback to use when dragging ends.
715
     *
716
     * @method drag_end
717
     * @param {EventFacade} e
718
     */
719
    drag_end: function() {},
720
 
721
    /**
722
     * Callback to use during dragging.
723
     *
724
     * @method drag_drag
725
     * @param {EventFacade} e
726
     */
727
    drag_drag: function() {},
728
 
729
    /**
730
     * Callback to use when dragging ends and is not over a drop target.
731
     *
732
     * @method drag_dropmiss
733
     * @param {EventFacade} e
734
     */
735
    drag_dropmiss: function() {},
736
 
737
    /**
738
     * Callback to use when a drop over event occurs.
739
     *
740
     * @method drop_over
741
     * @param {EventFacade} e
742
     */
743
    drop_over: function() {},
744
 
745
    /**
746
     * Callback to use on drop:hit.
747
     *
748
     * @method drop_hit
749
     * @param {EventFacade} e
750
     */
751
    drop_hit: function() {}
752
}, {
753
    NAME: 'dragdrop',
754
    ATTRS: {}
755
});
756
 
757
M.core = M.core || {};
758
M.core.dragdrop = DRAGDROP;
759
 
760
 
761
}, '@VERSION@', {"requires": ["base", "node", "io", "dom", "dd", "event-key", "event-focus", "moodle-core-notification"]});