(function($) { var multipleSortableClass = 'ui-multisortable-multiple'; var dragStarted = false; var multiSort = {}; multiSort.isBelow = function(elm, compare) { var elmOriginalPosition = elm.data('dragmultiple:originalPosition'); var compareOriginalPosition = compare.data('dragmultiple:originalPosition'); return elmOriginalPosition.top > compareOriginalPosition.top; }; multiSort.reset = function(elm) { $(elm) .removeClass("ui-sortable-helper") .removeAttr('style') .data('dragMultipleActive', false); }; multiSort.sort = function(current, positions) { positions.after.reverse(); $.each(positions.after, function () { multiSort.reset(this); current.after(this); }); $.each(positions.before, function () { multiSort.reset(this); current.before(this); }); }; multiSort.sortPositions = function(elm, current) { //saved if the elements are after or before the current var insertAfter = []; var insertBefore = []; $(elm).find('.' + multipleSortableClass).each(function () { var elm = $(this); if (elm[0] === current[0] || !current.hasClass(multipleSortableClass)) return; if (multiSort.isBelow(elm, current)) { insertAfter.push(elm); } else { insertBefore.push(elm); } }); return {'after': insertAfter, 'before': insertBefore}; }; $.widget( "ui.sortable", $.ui.sortable, { _mouseStart: function() { dragStarted = false; this._superApply( arguments ); }, _createHelper: function () { var helper = this._superApply( arguments ); if ($(helper).hasClass(multipleSortableClass)) { $(this.element).find('.' + multipleSortableClass).each(function () { $(this) .data('dragmultiple:originalPosition', $(this).position()) .data('dragMultipleActive', true); }); } return helper; }, _mouseStop: function (event, ui) { var current = this.helper; var elms = []; if (current.hasClass(multipleSortableClass)) { elms = $(this.element).find('.' + multipleSortableClass); } if (!elms.length) { elms = [current]; } //save the order of the elements relative to the main var positions = multiSort.sortPositions(this.element, current); this._superApply( arguments ); if (this.element !== this.currentContainer.element) { // change to another sortable list multiSort.sort(current, positions); $(this.currentContainer.element).trigger('multiplesortreceive', { 'item': current, 'items': elms, 'source': this.element }); } else if (current.hasClass(multipleSortableClass)) { // sort in the same list multiSort.sort(current, positions); } $(this.element).trigger('multiplesortstop', { 'item': current, 'items': elms }); }, _mouseDrag: function(key, value) { this._super(key, value); var current = this.helper; if (!current.hasClass(multipleSortableClass)) return; // following the drag element var currentLeft = current.position().left; var currentTop = current.position().top; var currentZIndex = current.css('z-index'); var currentPosition = current.css('position'); var positions = multiSort.sortPositions(this.element, current); positions.before.reverse(); [{'positions': positions.after, type: 'after'}, {'positions': positions.before, type: 'before'}] .forEach(function (item) { $.each(item.positions, function (index, elm) { var top; if (item.type === 'after') { top = currentTop + ((index + 1) * current.outerHeight()); } else { top = currentTop - ((index + 1) * current.outerHeight()); } elm .addClass("ui-sortable-helper") .css({ width: elm.outerWidth(), height: elm.outerHeight(), position: currentPosition, zIndex: currentZIndex, top: top, left: currentLeft }); }); }); // it only refresh position the first time because // jquery-ui has saved the old positions of the draggable elements // and with this will remove all elements with dragMultipleActive if (!dragStarted) { dragStarted = true; this.refreshPositions(); } } }); }(jQuery))