diff --git a/lib/amd/build/sortable_list.min.js b/lib/amd/build/sortable_list.min.js
index 058c5e29467..3e89c7eb549 100644
--- a/lib/amd/build/sortable_list.min.js
+++ b/lib/amd/build/sortable_list.min.js
@@ -1 +1 @@
-define(["jquery","core/log","core/autoscroll","core/str","core/modal_factory","core/modal_events","core/notification"],function(a,b,c,d,e,f,g){var h,i,j,k={targetListSelector:null,moveHandlerSelector:null,isHorizontal:!1,autoScroll:!0,elementNameCallback:function(a){return a.text()},destinationNameCallback:function(a,b){return b.length?D(b).then(function(a){return d.get_string("movecontentafter","moodle",a)}):d.get_string("movecontenttothetop","moodle")},moveDialogueTitleCallback:function(a){return D(a).then(function(a){return d.get_string("movecontent","moodle",a)})}},l={keyboardDragClass:"dragdrop-keyboard-drag",isDraggedClass:"sortable-list-is-dragged",currentPositionClass:"sortable-list-current-position",sourceListClass:"sortable-list-source",targetListClass:"sortable-list-target",overElementClass:"sortable-list-over-element"},m={},n=null,o=0,p=function(){var b=[m.isDraggedClass,m.currentPositionClass,m.overElementClass,m.targetListClass,m.sourceListClass];for(var c in b)a("."+b[c]).removeClass(b[c]);h&&(h.remove(),h=a())},q=function(b){if(b.originalEvent&&b.originalEvent.touches&&void 0!==b.originalEvent.touches[0]){var c=b.originalEvent.touches[0];b.pageX=c.pageX,b.pageY=c.pageY}void 0===b.pageX?(b.pageX=j.pageX,b.pageY=j.pageY):j=b,void 0===b.clientX&&(b.clientX=Math.round(b.pageX-a(window).scrollLeft()),b.clientY=Math.round(b.pageY-a(window).scrollTop()))},r=function(b){if(m=b.data.config,null!==n){if("click"===n.type)return;y(n.sourceList,n.sourceNextElement),z()}if("mousedown"!==b.type||1===b.which){q(b);var d=a(b.target).closest(a(b.currentTarget).children());if(d.length&&(null===m.moveHandlerSelector||a(b.target).closest(m.moveHandlerSelector,d).length)){b.stopPropagation(),b.preventDefault(),o++,n={element:d,sourceNextElement:d.next(),sourceList:d.parent(),targetNextElement:d.next(),targetList:d.parent(),type:b.type,dropped:!1,startX:b.pageX,startY:b.pageY,startTime:(new Date).getTime()},a(m.targetListSelector).addClass(m.targetListClass);var e=d.offset();d.addClass(m.currentPositionClass),i={x:e.left-b.pageX,y:e.top-b.pageY},h=a();var f=o;setTimeout(function(){null!==n&&"click"!==n.type&&"keypress"!==n.type&&o===f&&s()},500),a("body").on("mousemove touchmove mouseup touchend",x),a("body").on("keypress",B),m.autoScroll&&c.start(function(){a("body").trigger("mousemove")}),A("dragstart")}}},s=function(){h=n.element.clone(),n.sourceList.append(h),h.removeAttr("id").removeClass(m.currentPositionClass).addClass(m.isDraggedClass).css({position:"fixed"}),h.offset({top:i.y+j.pageY,left:i.x+j.pageX})},t=function(b){if(!("keypress"===b.type&&13!==b.originalEvent.keyCode&&32!==b.originalEvent.keyCode||null!==n&&"click"===n.type)){b.preventDefault(),b.stopPropagation(),m=b.data.config;var c=a(b.currentTarget),d=c.closest(m.listSelector),e=c.closest(d.children());e.length&&(o++,n={element:e,sourceNextElement:e.next(),sourceList:d,targetNextElement:e.next(),targetList:d,dropped:!1,type:b.type,startTime:(new Date).getTime()},A("dragstart"),H(c))}},u=function(a,b,c){if(!c.length)return null;var d=c[0],e=0,f=d.getBoundingClientRect(),g=b-(f.top+window.scrollY),h=a-(f.left+window.scrollX);return h>=-e&&h<=f.width+e&&g>=-e&&g<=f.height+e?{x:h,y:g,xRatio:f.width?h/f.width:0,yRatio:f.height?g/f.height:0}:null},v=function(){return!h||!h.length||this!==h[0]},w=function(a){var b=m.isHorizontal;return b===!0||b===!1?b:b(a)},x=function(b){q(b),h.offset({top:-1e3,left:-1e3});var c=a(document.elementFromPoint(b.clientX,b.clientY)),d=c.closest("."+m.targetListClass+" > :not(."+m.isDraggedClass+")"),e=c.closest("."+m.targetListClass);if(a("."+m.overElementClass).removeClass(m.overElementClass),d.addClass(m.overElementClass),h.offset({top:i.y+b.pageY,left:i.x+b.pageX}),e.length&&!e.children().filter(v).length)y(e,a());else if(1===d.length&&!n.element.find(d[0]).length){var f=u(b.pageX,b.pageY,d);if(f){var g=d.parent(),j=w(g)?f.xRatio:f.yRatio,k=d.find("."+m.targetListClass),l=function(){return this!==n.element[0]},o=!k.children().filter(v).filter(l).length;k.length&&o&&j>.2&&j<.8?y(k,a()):j>.5?y(g,d.next().filter(v)):y(g,d)}}"mouseup"!==b.type&&"touchend"!==b.type||(n.endX=b.pageX,n.endY=b.pageY,n.endTime=(new Date).getTime(),n.dropped=!0,A("drop"),z())},y=function(a,b){var c=n.element;b.length&&b[0]===c[0]||a[0]===n.targetList[0]&&b.length===n.targetNextElement.length&&b[0]===n.targetNextElement[0]||(b.length?a[0].insertBefore(c[0],b[0]):h&&h.parent().length&&h.parent()[0]===a[0]?a[0].insertBefore(c[0],h[0]):a[0].appendChild(c[0]),n.targetList=a,n.targetNextElement=b,A("drag"))},z=function(){p(),m.autoScroll&&c.stop(),a("body").off("mousemove touchmove mouseup touchend",x),a("body").off("keypress",B),A("dragend"),n=null},A=function(a){n.element.trigger("sortablelist-"+a,n)},B=function(a){"keypress"===a.type&&27===a.originalEvent.keyCode&&(y(n.sourceList,n.sourceNextElement),z())},C=function(b){var c=b;return"object"==typeof b&&b.hasOwnProperty("then")||(c=a.Deferred(),c.resolve(b)),c},D=function(a){return C(m.elementNameCallback(a))},E=function(a,b){return C(m.destinationNameCallback(a,b))},F=function(a){return C(m.moveDialogueTitleCallback(a))},G=function(){var b=[],c=a(m.targetListSelector),d=a("
").addClass(m.keyboardDragClass),e=a.when().then(function(){return d}),f=function(b,c,f){c.is(n.element)||f.is(n.element)||(e=e.then(function(){return E(b,f)}).then(function(e){var f=a("").appendTo(d),g=a('').attr("data-sortable-quickmove",1).appendTo(f);return g.data("parent-element",b).data("before-element",c).text(e),d}))},g=function(){if(a.inArray(this,b)===-1){b.push(this);var d=a(this),e=d.children();e.each(function(){var b=a(this);f(d,b,b.prev()),b.find(c).each(g)}),f(d,a(),e.last())}};return c.each(g),e},H=function(b){e.create({type:e.types.CANCEL,title:F(n.element),body:G()}).then(function(c){var d=function(d){d.preventDefault(),d.stopPropagation(),y(a(d.currentTarget).data("parent-element"),a(d.currentTarget).data("before-element")),n.endTime=(new Date).getTime(),n.dropped=!0,b.focus(),A("drop"),c.hide()};return c.getRoot().on("click","[data-sortable-quickmove]",d),c.getRoot().on(f.hidden,function(){c.getRoot().off("click","[data-sortable-quickmove]",d),c.destroy(),z()}),c.setLarge(),c.show(),c})["catch"](g.exception)};return{init:function(b,c){c=a.extend({},k,l,c||{}),c.listSelector=b,c.targetListSelector||(c.targetListSelector=b),"object"==typeof c.listSelector?a(c.listSelector).on("mousedown touchstart",{config:c},r):a("body").on("mousedown touchstart",c.listSelector,{config:c},r),null!==c.moveHandlerSelector&&a("body").on("click keypress",c.moveHandlerSelector,{config:c},t)}}});
\ No newline at end of file
+define(["jquery","core/log","core/autoscroll","core/str","core/modal_factory","core/modal_events","core/notification"],function(a,b,c,d,e,f,g){var h={targetListSelector:null,moveHandlerSelector:null,isHorizontal:!1,autoScroll:!0},i={keyboardDragClass:"dragdrop-keyboard-drag",isDraggedClass:"sortable-list-is-dragged",currentPositionClass:"sortable-list-current-position",sourceListClass:"sortable-list-source",targetListClass:"sortable-list-target",overElementClass:"sortable-list-over-element"},j=function(b,c){this.info=null,this.proxy=null,this.proxyDelta=null,this.dragCounter=0,this.lastEvent=null,this.config=a.extend({},h,c||{}),this.config.listSelector=b,this.config.targetListSelector||(this.config.targetListSelector=b),"object"==typeof this.config.listSelector?a(this.config.listSelector).on("mousedown touchstart",a.proxy(this.dragStartHandler,this)):a("body").on("mousedown touchstart",this.config.listSelector,a.proxy(this.dragStartHandler,this)),null!==this.config.moveHandlerSelector&&a("body").on("click keypress",this.config.moveHandlerSelector,a.proxy(this.clickHandler,this))};return j.EVENTS={DRAGSTART:"sortablelist-dragstart",DRAG:"sortablelist-drag",DROP:"sortablelist-drop",DRAGEND:"sortablelist-dragend"},j.prototype.resetDraggedClasses=function(){var b=[i.isDraggedClass,i.currentPositionClass,i.overElementClass,i.targetListClass];for(var c in b)a("."+b[c]).removeClass(b[c]);this.proxy&&(this.proxy.remove(),this.proxy=a())},j.prototype.calculatePositionOnPage=function(b){if(b.originalEvent&&b.originalEvent.touches&&void 0!==b.originalEvent.touches[0]){var c=b.originalEvent.touches[0];b.pageX=c.pageX,b.pageY=c.pageY}void 0===b.pageX?(b.pageX=this.lastEvent.pageX,b.pageY=this.lastEvent.pageY):this.lastEvent=b,void 0===b.clientX&&(b.clientX=Math.round(b.pageX-a(window).scrollLeft()),b.clientY=Math.round(b.pageY-a(window).scrollTop()))},j.prototype.dragStartHandler=function(b){if(null!==this.info){if("click"===this.info.type)return;this.moveElement(this.info.sourceList,this.info.sourceNextElement),this.finishDragging()}if("mousedown"!==b.type||1===b.which){this.calculatePositionOnPage(b);var d=a(b.target).closest(a(b.currentTarget).children());if(d.length&&(null===this.config.moveHandlerSelector||a(b.target).closest(this.config.moveHandlerSelector,d).length)){b.stopPropagation(),b.preventDefault(),this.dragCounter++,this.info={element:d,sourceNextElement:d.next(),sourceList:d.parent(),targetNextElement:d.next(),targetList:d.parent(),type:b.type,dropped:!1,startX:b.pageX,startY:b.pageY,startTime:(new Date).getTime()},a(this.config.targetListSelector).addClass(i.targetListClass);var e=d.offset();d.addClass(i.currentPositionClass),this.proxyDelta={x:e.left-b.pageX,y:e.top-b.pageY},this.proxy=a();var f=this.dragCounter;setTimeout(a.proxy(function(){null!==this.info&&"click"!==this.info.type&&"keypress"!==this.info.type&&this.dragCounter===f&&this.createProxy()},this),500),a(window).on("mousemove touchmove mouseup touchend",a.proxy(this.dragHandler,this)),a(window).on("keypress",a.proxy(this.dragcancelHandler,this)),this.config.autoScroll&&c.start(function(){a(window).trigger("mousemove")}),this.executeCallback(j.EVENTS.DRAGSTART)}}},j.prototype.createProxy=function(){this.proxy=this.info.element.clone(),this.info.sourceList.append(this.proxy),this.proxy.removeAttr("id").removeClass(i.currentPositionClass).addClass(i.isDraggedClass).css({position:"fixed"}),this.proxy.offset({top:this.proxyDelta.y+this.lastEvent.pageY,left:this.proxyDelta.x+this.lastEvent.pageX})},j.prototype.clickHandler=function(b){if(!("keypress"===b.type&&13!==b.originalEvent.keyCode&&32!==b.originalEvent.keyCode||null!==this.info&&"click"===this.info.type)){b.preventDefault(),b.stopPropagation();var c=a(b.currentTarget),d=c.closest(this.config.listSelector),e=c.closest(d.children());e.length&&(this.dragCounter++,this.info={element:e,sourceNextElement:e.next(),sourceList:d,targetNextElement:e.next(),targetList:d,dropped:!1,type:b.type,startTime:(new Date).getTime()},this.executeCallback(j.EVENTS.DRAGSTART),this.displayMoveDialogue(c))}},j.prototype.getPositionInNode=function(a,b,c){if(!c.length)return null;var d=c[0],e=0,f=d.getBoundingClientRect(),g=b-(f.top+window.scrollY),h=a-(f.left+window.scrollX);return h>=-e&&h<=f.width+e&&g>=-e&&g<=f.height+e?{x:h,y:g,xRatio:f.width?h/f.width:0,yRatio:f.height?g/f.height:0}:null},j.prototype.isListHorizontal=function(a){var b=this.config.isHorizontal;return b===!0||b===!1?b:b(a)},j.prototype.dragHandler=function(b){this.calculatePositionOnPage(b),this.proxy.offset({top:-1e3,left:-1e3});var c=a(document.elementFromPoint(b.clientX,b.clientY)),d=c.closest("."+i.targetListClass+" > :not(."+i.isDraggedClass+")"),e=c.closest("."+i.targetListClass),f=this.proxy,g=function(){return!f||!f.length||this!==f[0]};if(a("."+i.overElementClass).removeClass(i.overElementClass),d.addClass(i.overElementClass),this.proxy.offset({top:this.proxyDelta.y+b.pageY,left:this.proxyDelta.x+b.pageX}),e.length&&!e.children().filter(g).length)this.moveElement(e,a());else if(1===d.length&&!this.info.element.find(d[0]).length){var h=this.getPositionInNode(b.pageX,b.pageY,d);if(h){var k=d.parent(),l=this.isListHorizontal(k)?h.xRatio:h.yRatio,m=d.find("."+i.targetListClass),n=this.info.element[0],o=function(){return this!==n},p=!m.children().filter(g).filter(o).length;m.length&&p&&l>.2&&l<.8?this.moveElement(m,a()):l>.5?this.moveElement(k,d.next().filter(g)):this.moveElement(k,d)}}"mouseup"!==b.type&&"touchend"!==b.type||(this.info.endX=b.pageX,this.info.endY=b.pageY,this.info.endTime=(new Date).getTime(),this.info.dropped=!0,this.executeCallback(j.EVENTS.DROP),this.finishDragging())},j.prototype.moveElement=function(a,b){var c=this.info.element;b.length&&b[0]===c[0]||a[0]===this.info.targetList[0]&&b.length===this.info.targetNextElement.length&&b[0]===this.info.targetNextElement[0]||(b.length?a[0].insertBefore(c[0],b[0]):this.proxy&&this.proxy.parent().length&&this.proxy.parent()[0]===a[0]?a[0].insertBefore(c[0],this.proxy[0]):a[0].appendChild(c[0]),this.info.targetList=a,this.info.targetNextElement=b,this.executeCallback(j.EVENTS.DRAG))},j.prototype.finishDragging=function(){this.resetDraggedClasses(),this.config.autoScroll&&c.stop(),a(window).off("mousemove touchmove mouseup touchend",a.proxy(this.dragHandler,this)),a(window).off("keypress",a.proxy(this.dragcancelHandler,this)),this.executeCallback(j.EVENTS.DRAGEND),this.info=null},j.prototype.executeCallback=function(a){this.info.element.trigger(a,this.info)},j.prototype.dragcancelHandler=function(a){"keypress"===a.type&&27===a.originalEvent.keyCode&&(this.moveElement(this.info.sourceList,this.info.sourceNextElement),this.finishDragging())},j.prototype.getElementName=function(b){return a.Deferred().resolve(b.text())},j.prototype.getDestinationName=function(a,b){return b.length?this.getElementName(b).then(function(a){return d.get_string("movecontentafter","moodle",a)}):d.get_string("movecontenttothetop","moodle")},j.prototype.getMoveDialogueTitle=function(a){return this.getElementName(a).then(function(a){return d.get_string("movecontent","moodle",a)})},j.prototype.getDestinationsList=function(){var b=[],c=a(this.config.targetListSelector),d=a("").addClass(i.keyboardDragClass),e=a.when().then(function(){return d}),f=a.proxy(function(b,c,f){c.is(this.info.element)||f.is(this.info.element)||a.contains(this.info.element[0],b[0])||(e=e.then(a.proxy(function(){return this.getDestinationName(b,f)},this)).then(function(e){var f=a("").appendTo(d),g=a('').attr("data-core_sortable_list-quickmove",1).appendTo(f);return g.data("parent-element",b).data("before-element",c).text(e),d}))},this),g=function(){if(a.inArray(this,b)===-1){b.push(this);var d=a(this),e=d.children();e.each(function(){var b=a(this);f(d,b,b.prev()),b.find(c).each(g)}),f(d,a(),e.last())}};return c.each(g),e},j.prototype.displayMoveDialogue=function(b){e.create({type:e.types.CANCEL,title:this.getMoveDialogueTitle(this.info.element),body:this.getDestinationsList()}).then(a.proxy(function(c){var d=a.proxy(function(d){d.preventDefault(),d.stopPropagation(),this.moveElement(a(d.currentTarget).data("parent-element"),a(d.currentTarget).data("before-element")),this.info.endTime=(new Date).getTime(),this.info.dropped=!0,b.focus(),this.executeCallback(j.EVENTS.DROP),c.hide()},this);return c.getRoot().on("click","[data-core_sortable_list-quickmove]",d),c.getRoot().on(f.hidden,a.proxy(function(){c.getRoot().off("click","[data-core_sortable_list-quickmove]",d),c.destroy(),this.finishDragging()},this)),c.setLarge(),c.show(),c},this))["catch"](g.exception)},j});
\ No newline at end of file
diff --git a/lib/amd/src/sortable_list.js b/lib/amd/src/sortable_list.js
index d381912b84c..d0c8ef7c3f8 100644
--- a/lib/amd/src/sortable_list.js
+++ b/lib/amd/src/sortable_list.js
@@ -18,15 +18,20 @@
*
* Example of usage:
*
- * define(['jquery', 'core/sortable_list'], function($, sortableList) {
- * sortableList.init({
- * listSelector: 'ul.my-awesome-list', // mandatory, CSS selector for the list (usually or )
- * moveHandlerSelector: '.draghandle' // CSS selector of the crossarrow handle. Make sure that this
- * element can handle keypress and mouse click events for displaying accessible move popup.
- * });
- * $('ul.my-awesome-list > *').on('sortablelist-drop', function(evt, info) {
+ * define(['jquery', 'core/sortable_list'], function($, SortableList) {
+ * var list = new SortableList('ul.my-awesome-list', // source list (usually or ) - selector or element
+ * {
+ * moveHandlerSelector: '.draghandle' // CSS selector of the crossarrow handle. Make sure that this
+ * // element can handle keypress and mouse click events for displaying accessible move popup.
+ * });
+ * $('ul.my-awesome-list > *').on(SortableList.EVENTS.DROP, function(evt, info) {
* console.log(info);
* });
+ *
+ * // Advanced usage. Overwrite methods getElementName, getDestinationName, moveDialogueTitle, for example:
+ * list.getElementName = function(element) {
+ * return $.Deferred().resolve(element.attr('data-name'));
+ * }
* }
*
* More details: https://docs.moodle.org/dev/Sortable_list
@@ -34,10 +39,10 @@
* For the full list of possible parameters see var defaultParameters below.
*
* The following jQuery events are fired:
- * - sortablelist-dragstart : when user started dragging a list element
- * - sortablelist-drag : when user dragged a list element to a new position
- * - sortablelist-drop : when user dropped a list element
- * - sortablelist-dragend : when user finished dragging - either fired right after dropping or
+ * - SortableList.EVENTS.DRAGSTART : when user started dragging a list element
+ * - SortableList.EVENTS.DRAG : when user dragged a list element to a new position
+ * - SortableList.EVENTS.DROP : when user dropped a list element
+ * - SortableList.EVENTS.DROPEND : when user finished dragging - either fired right after dropping or
* if "Esc" was pressed during dragging
*
* @module core/sortable_list
@@ -59,25 +64,7 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
targetListSelector: null,
moveHandlerSelector: null,
isHorizontal: false,
- autoScroll: true,
- elementNameCallback: function(element) {
- return element.text();
- },
- destinationNameCallback: function(parentElement, afterElement) {
- if (!afterElement.length) {
- return str.get_string('movecontenttothetop', 'moodle');
- } else {
- return getElementName(afterElement)
- .then(function(name) {
- return str.get_string('movecontentafter', 'moodle', name);
- });
- }
- },
- moveDialogueTitleCallback: function(element) {
- return getElementName(element).then(function(name) {
- return str.get_string('movecontent', 'moodle', name);
- });
- }
+ autoScroll: true
};
/**
@@ -96,72 +83,78 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
};
/**
- * Stores parameters of the currently dragged item
+ * Initialise sortable list.
*
- * @private
+ * @param {(String|jQuery|Element)} root JQuery/DOM element representing sortable list (i.e. , ) or CSS selector
+ * @param {Object} config Parameters for the list. See defaultParameters above for examples.
+ * @property {(String|jQuery|Element)} config.targetListSelector target lists, by default same as root
+ * @property {String} config.moveHandlerSelector CSS selector for a drag handle. By default the whole item is a handle.
+ * Without drag handle sorting is not accessible!
+ * @property {String} config.targetListSelector CSS selector for target lists. By default the same as root
+ * @property {(Boolean|Function)} config.isHorizontal Set to true if the list is horizontal
+ * (can also be a callback with list as an argument)
+ * @property {Boolean} config.autoScroll Engages autoscroll module for automatic vertical scrolling of the
+ * whole page, by default true
+ */
+ var SortableList = function(root, config) {
+
+ this.info = null;
+ this.proxy = null;
+ this.proxyDelta = null;
+ this.dragCounter = 0;
+ this.lastEvent = null;
+
+ this.config = $.extend({}, defaultParameters, config || {});
+ this.config.listSelector = root;
+ if (!this.config.targetListSelector) {
+ this.config.targetListSelector = root;
+ }
+ if (typeof this.config.listSelector === 'object') {
+ // The root is an element on the page. Register a listener for this element.
+ $(this.config.listSelector).on('mousedown touchstart', $.proxy(this.dragStartHandler, this));
+ } else {
+ // The root is a CSS selector. Register a listener that picks up the element dynamically.
+ $('body').on('mousedown touchstart', this.config.listSelector, $.proxy(this.dragStartHandler, this));
+ }
+ if (this.config.moveHandlerSelector !== null) {
+ $('body').on('click keypress', this.config.moveHandlerSelector, $.proxy(this.clickHandler, this));
+ }
+
+ };
+
+ /**
+ * Events fired by this entity
+ *
+ * @public
* @type {Object}
*/
- var config = {};
-
- /**
- * Stores information about currently dragged item
- *
- * @private
- * @type {Object}
- */
- var info = null;
-
- /**
- * Stores the proxy object
- *
- * @private
- * @type {jQuery}
- */
- var proxy;
-
- /**
- * Stores initial position of the proxy
- *
- * @private
- * @type {Object}
- */
- var proxyDelta;
-
- /**
- * Counter of drag events
- *
- * @private
- * @type {Number}
- */
- var dragCounter = 0;
+ SortableList.EVENTS = {
+ DRAGSTART: 'sortablelist-dragstart',
+ DRAG: 'sortablelist-drag',
+ DROP: 'sortablelist-drop',
+ DRAGEND: 'sortablelist-dragend'
+ };
/**
* Resets the temporary classes assigned during dragging
* @private
*/
- var resetDraggedClasses = function() {
+ SortableList.prototype.resetDraggedClasses = function() {
var classes = [
- config.isDraggedClass,
- config.currentPositionClass,
- config.overElementClass,
- config.targetListClass,
- config.sourceListClass
+ CSS.isDraggedClass,
+ CSS.currentPositionClass,
+ CSS.overElementClass,
+ CSS.targetListClass,
];
for (var i in classes) {
$('.' + classes[i]).removeClass(classes[i]);
}
- if (proxy) {
- proxy.remove();
- proxy = $();
+ if (this.proxy) {
+ this.proxy.remove();
+ this.proxy = $();
}
};
- /**
- * {Event} stores the last event that had pageX and pageY defined
- * @private
- */
- var lastEvent;
-
/**
* Calculates evt.pageX, evt.pageY, evt.clientX and evt.clientY
*
@@ -171,7 +164,7 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
* @private
* @param {Event} evt
*/
- var calculatePositionOnPage = function(evt) {
+ SortableList.prototype.calculatePositionOnPage = function(evt) {
if (evt.originalEvent && evt.originalEvent.touches && evt.originalEvent.touches[0] !== undefined) {
// This is a touchmove or touchstart event, get position from the first touch position.
@@ -183,10 +176,10 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
if (evt.pageX === undefined) {
// Information is not present in case of touchend or when event was emulated by autoScroll.
// Take the absolute mouse position from the last event.
- evt.pageX = lastEvent.pageX;
- evt.pageY = lastEvent.pageY;
+ evt.pageX = this.lastEvent.pageX;
+ evt.pageY = this.lastEvent.pageY;
} else {
- lastEvent = evt;
+ this.lastEvent = evt;
}
if (evt.clientX === undefined) {
@@ -202,16 +195,15 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
* @private
* @param {Event} evt
*/
- var dragStartHandler = function(evt) {
- config = evt.data.config;
- if (info !== null) {
- if (info.type === 'click') {
+ SortableList.prototype.dragStartHandler = function(evt) {
+ if (this.info !== null) {
+ if (this.info.type === 'click') {
// Ignore double click.
return;
}
// Mouse down or touch while already dragging, cancel previous dragging.
- moveElement(info.sourceList, info.sourceNextElement);
- finishDragging();
+ this.moveElement(this.info.sourceList, this.info.sourceNextElement);
+ this.finishDragging();
}
if (evt.type === 'mousedown' && evt.which !== 1) {
@@ -219,7 +211,7 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
return;
}
- calculatePositionOnPage(evt);
+ this.calculatePositionOnPage(evt);
var movedElement = $(evt.target).closest($(evt.currentTarget).children());
if (!movedElement.length) {
// Can't find the element user wants to drag. They clicked on the list but outside of any element of the list.
@@ -227,8 +219,8 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
}
// Check that we grabbed the element by the handle.
- if (config.moveHandlerSelector !== null) {
- if (!$(evt.target).closest(config.moveHandlerSelector, movedElement).length) {
+ if (this.config.moveHandlerSelector !== null) {
+ if (!$(evt.target).closest(this.config.moveHandlerSelector, movedElement).length) {
return;
}
}
@@ -238,8 +230,8 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
// Information about moved element with original location.
// This object is passed to event observers.
- dragCounter++;
- info = {
+ this.dragCounter++;
+ this.info = {
element: movedElement,
sourceNextElement: movedElement.next(),
sourceList: movedElement.parent(),
@@ -252,50 +244,51 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
startTime: new Date().getTime()
};
- $(config.targetListSelector).addClass(config.targetListClass);
+ $(this.config.targetListSelector).addClass(CSS.targetListClass);
var offset = movedElement.offset();
- movedElement.addClass(config.currentPositionClass);
- proxyDelta = {x: offset.left - evt.pageX, y: offset.top - evt.pageY};
- proxy = $();
- var thisDragCounter = dragCounter;
- setTimeout(function() {
+ movedElement.addClass(CSS.currentPositionClass);
+ this.proxyDelta = {x: offset.left - evt.pageX, y: offset.top - evt.pageY};
+ this.proxy = $();
+ var thisDragCounter = this.dragCounter;
+ setTimeout($.proxy(function() {
// This mousedown event may in fact be a beginning of a 'click' event. Use timeout before showing the
// dragged object so we can catch click event. When timeout finishes make sure that click event
// has not happened during this half a second.
// Verify dragcounter to make sure the user did not manage to do two very fast drag actions one after another.
- if (info === null || info.type === 'click' || info.type === 'keypress' || dragCounter !== thisDragCounter) {
+ if (this.info === null || this.info.type === 'click' || this.info.type === 'keypress'
+ || this.dragCounter !== thisDragCounter) {
return;
}
// Create a proxy - the copy of the dragged element that moves together with a mouse.
- createProxy();
- }, 500);
+ this.createProxy();
+ }, this), 500);
// Start drag.
- $('body').on('mousemove touchmove mouseup touchend', dragHandler);
- $('body').on('keypress', dragcancelHandler);
+ $(window).on('mousemove touchmove mouseup touchend', $.proxy(this.dragHandler, this));
+ $(window).on('keypress', $.proxy(this.dragcancelHandler, this));
// Start autoscrolling. Every time the page is scrolled emulate the mousemove event.
- if (config.autoScroll) {
+ if (this.config.autoScroll) {
autoScroll.start(function() {
- $('body').trigger('mousemove');
+ $(window).trigger('mousemove');
});
}
- executeCallback('dragstart');
+ this.executeCallback(SortableList.EVENTS.DRAGSTART);
};
/**
* Creates a "proxy" object - a copy of the element that is being moved that always follows the mouse
* @private
*/
- var createProxy = function() {
- proxy = info.element.clone();
- info.sourceList.append(proxy);
- proxy.removeAttr('id').removeClass(config.currentPositionClass)
- .addClass(config.isDraggedClass).css({position: 'fixed'});
- proxy.offset({top: proxyDelta.y + lastEvent.pageY, left: proxyDelta.x + lastEvent.pageX});
+ SortableList.prototype.createProxy = function() {
+ this.proxy = this.info.element.clone();
+ this.info.sourceList.append(this.proxy);
+ this.proxy.removeAttr('id').removeClass(CSS.currentPositionClass)
+ .addClass(CSS.isDraggedClass).css({position: 'fixed'});
+ this.proxy.offset({top: this.proxyDelta.y + this.lastEvent.pageY, left: this.proxyDelta.x + this.lastEvent.pageX});
};
/**
@@ -304,29 +297,28 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
* @private
* @param {Event} evt
*/
- var clickHandler = function(evt) {
+ SortableList.prototype.clickHandler = function(evt) {
if (evt.type === 'keypress' && evt.originalEvent.keyCode !== 13 && evt.originalEvent.keyCode !== 32) {
return;
}
- if (info !== null && info.type === 'click') {
+ if (this.info !== null && this.info.type === 'click') {
// Ignore double click.
return;
}
evt.preventDefault();
evt.stopPropagation();
- config = evt.data.config;
// Find the element that this draghandle belongs to.
var clickedElement = $(evt.currentTarget),
- sourceList = clickedElement.closest(config.listSelector),
+ sourceList = clickedElement.closest(this.config.listSelector),
movedElement = clickedElement.closest(sourceList.children());
if (!movedElement.length) {
return;
}
// Store information about moved element with original location.
- dragCounter++;
- info = {
+ this.dragCounter++;
+ this.info = {
element: movedElement,
sourceNextElement: movedElement.next(),
sourceList: sourceList,
@@ -337,8 +329,8 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
startTime: new Date().getTime()
};
- executeCallback('dragstart');
- displayMoveDialogue(clickedElement);
+ this.executeCallback(SortableList.EVENTS.DRAGSTART);
+ this.displayMoveDialogue(clickedElement);
};
/**
@@ -352,7 +344,7 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
* @param {jQuery} element
* @returns {(Object|null)}
*/
- var getPositionInNode = function(pageX, pageY, element) {
+ SortableList.prototype.getPositionInNode = function(pageX, pageY, element) {
if (!element.length) {
return null;
}
@@ -372,24 +364,14 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
return null;
};
- /**
- * Callback for filter that checks that current element is not proxy
- *
- * @private
- * @return {boolean}
- */
- var isNotProxy = function() {
- return !proxy || !proxy.length || this !== proxy[0];
- };
-
/**
* Check if list is horizontal
*
* @param {jQuery} element
* @return {Boolean}
*/
- var isListHorizontal = function(element) {
- var isHorizontal = config.isHorizontal;
+ SortableList.prototype.isListHorizontal = function(element) {
+ var isHorizontal = this.config.isHorizontal;
if (isHorizontal === true || isHorizontal === false) {
return isHorizontal;
}
@@ -402,64 +384,69 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
* @private
* @param {Event} evt
*/
- var dragHandler = function(evt) {
+ SortableList.prototype.dragHandler = function(evt) {
- calculatePositionOnPage(evt);
+ this.calculatePositionOnPage(evt);
// We can not use evt.target here because it will most likely be our proxy.
// Move the proxy out of the way so we can find the element at the current mouse position.
- proxy.offset({top: -1000, left: -1000});
+ this.proxy.offset({top: -1000, left: -1000});
// Find the element at the current mouse position.
var element = $(document.elementFromPoint(evt.clientX, evt.clientY));
// Find the list element and the list over the mouse position.
- var current = element.closest('.' + config.targetListClass + ' > :not(.' + config.isDraggedClass + ')'),
- currentList = element.closest('.' + config.targetListClass);
+ var current = element.closest('.' + CSS.targetListClass + ' > :not(.' + CSS.isDraggedClass + ')'),
+ currentList = element.closest('.' + CSS.targetListClass),
+ proxy = this.proxy,
+ isNotProxy = function() {
+ return !proxy || !proxy.length || this !== proxy[0];
+ };
// Add the specified class to the list element we are hovering.
- $('.' + config.overElementClass).removeClass(config.overElementClass);
- current.addClass(config.overElementClass);
+ $('.' + CSS.overElementClass).removeClass(CSS.overElementClass);
+ current.addClass(CSS.overElementClass);
// Move proxy to the current position.
- proxy.offset({top: proxyDelta.y + evt.pageY, left: proxyDelta.x + evt.pageX});
+ this.proxy.offset({top: this.proxyDelta.y + evt.pageY, left: this.proxyDelta.x + evt.pageX});
if (currentList.length && !currentList.children().filter(isNotProxy).length) {
// Mouse is over an empty list.
- moveElement(currentList, $());
- } else if (current.length === 1 && !info.element.find(current[0]).length) {
+ this.moveElement(currentList, $());
+ } else if (current.length === 1 && !this.info.element.find(current[0]).length) {
// Mouse is over an element in a list - find whether we should move the current position
// above or below this element.
- var coordinates = getPositionInNode(evt.pageX, evt.pageY, current);
+ var coordinates = this.getPositionInNode(evt.pageX, evt.pageY, current);
if (coordinates) {
var parent = current.parent(),
- ratio = isListHorizontal(parent) ? coordinates.xRatio : coordinates.yRatio,
- subList = current.find('.' + config.targetListClass),
+ ratio = this.isListHorizontal(parent) ? coordinates.xRatio : coordinates.yRatio,
+ subList = current.find('.' + CSS.targetListClass),
+ currentElement = this.info.element[0],
isNotCurrent = function() {
- return this !== info.element[0];
+ return this !== currentElement;
},
subListEmpty = !subList.children().filter(isNotProxy).filter(isNotCurrent).length;
if (subList.length && subListEmpty && ratio > 0.2 && ratio < 0.8) {
// This is an element that is a parent of an empty list and we are around the middle of this element.
// Treat it as if we are over this empty list.
- moveElement(subList, $());
+ this.moveElement(subList, $());
} else if (ratio > 0.5) {
// Insert after this element.
- moveElement(parent, current.next().filter(isNotProxy));
+ this.moveElement(parent, current.next().filter(isNotProxy));
} else {
// Insert before this element.
- moveElement(parent, current);
+ this.moveElement(parent, current);
}
}
}
if (evt.type === 'mouseup' || evt.type === 'touchend') {
// Drop the moved element.
- info.endX = evt.pageX;
- info.endY = evt.pageY;
- info.endTime = new Date().getTime();
- info.dropped = true;
- executeCallback('drop');
- finishDragging();
+ this.info.endX = evt.pageX;
+ this.info.endY = evt.pageY;
+ this.info.endTime = new Date().getTime();
+ this.info.dropped = true;
+ this.executeCallback(SortableList.EVENTS.DROP);
+ this.finishDragging();
}
};
@@ -470,15 +457,15 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
* @param {jQuery} parentElement
* @param {jQuery} beforeElement
*/
- var moveElement = function(parentElement, beforeElement) {
- var dragEl = info.element;
+ SortableList.prototype.moveElement = function(parentElement, beforeElement) {
+ var dragEl = this.info.element;
if (beforeElement.length && beforeElement[0] === dragEl[0]) {
// Insert before the current position of the dragged element - nothing to do.
return;
}
- if (parentElement[0] === info.targetList[0] &&
- beforeElement.length === info.targetNextElement.length &&
- beforeElement[0] === info.targetNextElement[0]) {
+ if (parentElement[0] === this.info.targetList[0] &&
+ beforeElement.length === this.info.targetNextElement.length &&
+ beforeElement[0] === this.info.targetNextElement[0]) {
// Insert in the same location as the current position - nothing to do.
return;
}
@@ -486,34 +473,34 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
if (beforeElement.length) {
// Move the dragged element before the specified element.
parentElement[0].insertBefore(dragEl[0], beforeElement[0]);
- } else if (proxy && proxy.parent().length && proxy.parent()[0] === parentElement[0]) {
+ } else if (this.proxy && this.proxy.parent().length && this.proxy.parent()[0] === parentElement[0]) {
// We need to move to the end of the list but the last element in this list is a proxy.
// Always leave the proxy in the end of the list.
- parentElement[0].insertBefore(dragEl[0], proxy[0]);
+ parentElement[0].insertBefore(dragEl[0], this.proxy[0]);
} else {
// Insert in the end of a list (when proxy is in another list).
parentElement[0].appendChild(dragEl[0]);
}
// Save the current position of the dragged element in the list.
- info.targetList = parentElement;
- info.targetNextElement = beforeElement;
- executeCallback('drag');
+ this.info.targetList = parentElement;
+ this.info.targetNextElement = beforeElement;
+ this.executeCallback(SortableList.EVENTS.DRAG);
};
/**
* Finish dragging (when dropped or cancelled).
* @private
*/
- var finishDragging = function() {
- resetDraggedClasses();
- if (config.autoScroll) {
+ SortableList.prototype.finishDragging = function() {
+ this.resetDraggedClasses();
+ if (this.config.autoScroll) {
autoScroll.stop();
}
- $('body').off('mousemove touchmove mouseup touchend', dragHandler);
- $('body').off('keypress', dragcancelHandler);
- executeCallback('dragend');
- info = null;
+ $(window).off('mousemove touchmove mouseup touchend', $.proxy(this.dragHandler, this));
+ $(window).off('keypress', $.proxy(this.dragcancelHandler, this));
+ this.executeCallback(SortableList.EVENTS.DRAGEND);
+ this.info = null;
};
/**
@@ -522,8 +509,8 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
* @private
* @param {String} eventName
*/
- var executeCallback = function(eventName) {
- info.element.trigger('sortablelist-' + eventName, info);
+ SortableList.prototype.executeCallback = function(eventName) {
+ this.info.element.trigger(eventName, this.info);
};
/**
@@ -532,41 +519,25 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
* @private
* @param {Event} evt
*/
- var dragcancelHandler = function(evt) {
+ SortableList.prototype.dragcancelHandler = function(evt) {
if (evt.type !== 'keypress' || evt.originalEvent.keyCode !== 27) {
// Only cancel dragging when Esc was pressed.
return;
}
// Dragging was cancelled. Return item to the original position.
- moveElement(info.sourceList, info.sourceNextElement);
- finishDragging();
- };
-
- /**
- * Helper method to convert a string to a promise
- *
- * @private
- * @param {(String|Promise)} value
- * @return {Promise}
- */
- var convertToPromise = function(value) {
- var p = value;
- if (typeof value !== 'object' || !value.hasOwnProperty('then')) {
- p = $.Deferred();
- p.resolve(value);
- }
- return p;
+ this.moveElement(this.info.sourceList, this.info.sourceNextElement);
+ this.finishDragging();
};
/**
* Returns the name of the current element to be used in the move dialogue
*
- * @private
+ * @public
* @param {jQuery} element
* @return {Promise}
*/
- var getElementName = function(element) {
- return convertToPromise(config.elementNameCallback(element));
+ SortableList.prototype.getElementName = function(element) {
+ return $.Deferred().resolve(element.text());
};
/**
@@ -574,24 +545,33 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
*
* Note that we use "after" in the label for better UX
*
- * @private
+ * @public
* @param {jQuery} parentElement
* @param {jQuery} afterElement
* @return {Promise}
*/
- var getDestinationName = function(parentElement, afterElement) {
- return convertToPromise(config.destinationNameCallback(parentElement, afterElement));
+ SortableList.prototype.getDestinationName = function(parentElement, afterElement) {
+ if (!afterElement.length) {
+ return str.get_string('movecontenttothetop', 'moodle');
+ } else {
+ return this.getElementName(afterElement)
+ .then(function(name) {
+ return str.get_string('movecontentafter', 'moodle', name);
+ });
+ }
};
/**
* Returns the title for the move dialogue ("Move elementY")
*
- * @private
+ * @public
* @param {jQuery} element
* @return {Promise}
*/
- var getMoveDialogueTitle = function(element) {
- return convertToPromise(config.moveDialogueTitleCallback(element));
+ SortableList.prototype.getMoveDialogueTitle = function(element) {
+ return this.getElementName(element).then(function(name) {
+ return str.get_string('movecontent', 'moodle', name);
+ });
};
/**
@@ -600,28 +580,33 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
* @private
* @return {Promise}
*/
- var getDestinationsList = function() {
+ SortableList.prototype.getDestinationsList = function() {
var addedLists = [],
- targets = $(config.targetListSelector),
- destinations = $('').addClass(config.keyboardDragClass),
+ targets = $(this.config.targetListSelector),
+ destinations = $('').addClass(CSS.keyboardDragClass),
result = $.when().then(function() {
return destinations;
}),
- createLink = function(parentElement, beforeElement, afterElement) {
- if (beforeElement.is(info.element) || afterElement.is(info.element)) {
+ createLink = $.proxy(function(parentElement, beforeElement, afterElement) {
+ if (beforeElement.is(this.info.element) || afterElement.is(this.info.element)) {
+ // Can not move before or after itself.
+ return;
+ }
+ if ($.contains(this.info.element[0], parentElement[0])) {
+ // Can not move to its own child.
return;
}
result = result
- .then(function() {
- return getDestinationName(parentElement, afterElement);
- })
+ .then($.proxy(function() {
+ return this.getDestinationName(parentElement, afterElement);
+ }, this))
.then(function(txt) {
var li = $('').appendTo(destinations);
- var a = $('').attr('data-sortable-quickmove', 1).appendTo(li);
+ var a = $('').attr('data-core_sortable_list-quickmove', 1).appendTo(li);
a.data('parent-element', parentElement).data('before-element', beforeElement).text(txt);
return destinations;
});
- },
+ }, this),
addList = function() {
// Destination lists may be nested. We want to add all move destinations in the same
// order they appear on the screen for the user.
@@ -648,82 +633,35 @@ function($, log, autoScroll, str, ModalFactory, ModalEvents, Notification) {
* @param {jQuery} clickedElement element to return focus to after the modal is closed
* @private
*/
- var displayMoveDialogue = function(clickedElement) {
+ SortableList.prototype.displayMoveDialogue = function(clickedElement) {
ModalFactory.create({
type: ModalFactory.types.CANCEL,
- title: getMoveDialogueTitle(info.element),
- body: getDestinationsList()
- }).then(function(modal) {
- var quickMoveHandler = function(e) {
+ title: this.getMoveDialogueTitle(this.info.element),
+ body: this.getDestinationsList()
+ }).then($.proxy(function(modal) {
+ var quickMoveHandler = $.proxy(function(e) {
e.preventDefault();
e.stopPropagation();
- moveElement($(e.currentTarget).data('parent-element'), $(e.currentTarget).data('before-element'));
- info.endTime = new Date().getTime();
- info.dropped = true;
+ this.moveElement($(e.currentTarget).data('parent-element'), $(e.currentTarget).data('before-element'));
+ this.info.endTime = new Date().getTime();
+ this.info.dropped = true;
clickedElement.focus();
- executeCallback('drop');
+ this.executeCallback(SortableList.EVENTS.DROP);
modal.hide();
- };
- modal.getRoot().on('click', '[data-sortable-quickmove]', quickMoveHandler);
- modal.getRoot().on(ModalEvents.hidden, function() {
+ }, this);
+ modal.getRoot().on('click', '[data-core_sortable_list-quickmove]', quickMoveHandler);
+ modal.getRoot().on(ModalEvents.hidden, $.proxy(function() {
// Always destroy when hidden, it is generated dynamically each time.
- modal.getRoot().off('click', '[data-sortable-quickmove]', quickMoveHandler);
+ modal.getRoot().off('click', '[data-core_sortable_list-quickmove]', quickMoveHandler);
modal.destroy();
- finishDragging();
- });
+ this.finishDragging();
+ }, this));
modal.setLarge();
modal.show();
return modal;
- }).catch(Notification.exception);
+ }, this)).catch(Notification.exception);
};
- return {
- /**
- * Initialise sortable list.
- *
- * @param {(String|jQuery|Element)} root JQuery/DOM element representing sortable list (i.e. , ) or CSS selector
- * @param {Object} config Parameters for the list. See defaultParameters above for examples.
- * @property {(String|jQuery|Element)} config.targetListSelector target lists, by default same as root
- * @property {String} config.moveHandlerSelector CSS selector for a drag handle. By default the whole item is a handle.
- * Without drag handle sorting is not accessible!
- * @property {(Boolean|Function)} config.isHorizontal Set to true if the list is horizontal
- * (can also be a callback with list as an argument)
- * @property {Boolean} config.autoScroll Engages autoscroll module for automatic vertical scrolling of the
- * whole page, by default true
- * @property {Function} config.elementNameCallback Should return a string or Promise. Used for move dialogue title and
- * destination name
- * @property {Function} config.destinationNameCallback Callback that returns a string or Promise with the label
- * for the move destination
- * @property {Function} config.moveDialogueTitleCallback Should return a string or Promise. Used to form move dialogue title
- * @property {String} config.keyboardDragClass Class of the list of destinations in the popup
- * (default 'dragdrop-keyboard-drag')
- * @property {String} config.isDraggedClass Class added to the element that is dragged
- * (default 'sortable-list-is-dragged')
- * @property {String} config.currentPositionClass Class added to the current position of a dragged element
- * (default 'sortable-list-current-position')
- * @property {String} config.sourceListClass Class added to the list where dragging was started from
- * (default 'sortable-list-source')
- * @property {String} config.targetListClass Class added to all lists where item can be dropped
- * (default 'sortable-list-target')
- * @property {String} config.overElementClass Class added to the list element when the dragged element is above it
- * (default 'sortable-list-over-element')
- */
- init: function(root, config) {
- config = $.extend({}, defaultParameters, CSS, config || {});
- config.listSelector = root;
- if (!config.targetListSelector) {
- config.targetListSelector = root;
- }
- if (typeof config.listSelector === 'object') {
- // The root is an element on the page. Register a listener for this element.
- $(config.listSelector).on('mousedown touchstart', {config: config}, dragStartHandler);
- } else {
- // The root is a CSS selector. Register a listener that picks up the element dynamically.
- $('body').on('mousedown touchstart', config.listSelector, {config: config}, dragStartHandler);
- }
- if (config.moveHandlerSelector !== null) {
- $('body').on('click keypress', config.moveHandlerSelector, {config: config}, clickHandler);
- }
- }
- };
+ return SortableList;
+
});