JAVASCRIPT   115

dragdrop.js

Guest on 29th August 2021 02:55:49 PM

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

Raw Paste


Login or Register to edit or fork this paste. It's free.