/** * jquery.multisortable.js - v0.2 * https://github.com/shvetsgroup/jquery.multisortable * * Author: Ethan Atlakson, Jay Hayes, Gabriel Such, Alexander Shvets * Last Revision 3/16/2012 * multi-selectable, multi-sortable jQuery plugin */ !function($) { $.fn.multiselectable = function(options) { if (!options) { options = {} } options = $.extend({}, $.fn.multiselectable.defaults, options); function mouseDown(e) { var item = $(this), parent = item.parent(), myIndex = item.index(); var prev = parent.find('.multiselectable-previous'); // If no previous selection found, start selecting from first selected item. prev = prev.length ? prev : $(parent.find('.' + options.selectedClass)[0]).addClass('multiselectable-previous'); var prevIndex = prev.index(); if (e.ctrlKey || e.metaKey) { if (item.hasClass(options.selectedClass)) { item.removeClass(options.selectedClass).removeClass('multiselectable-previous') if (item.not('.child').length) { item.nextUntil(':not(.child)').removeClass(options.selectedClass); } } else { parent.find('.multiselectable-previous').removeClass('multiselectable-previous'); item.addClass(options.selectedClass).addClass('multiselectable-previous') if (item.not('.child').length) { item.nextUntil(':not(.child)').addClass(options.selectedClass); } } } if (e.shiftKey) { var last_shift_range = parent.find('.multiselectable-shift'); last_shift_range.removeClass(options.selectedClass).removeClass('multiselectable-shift'); var shift_range; if (prevIndex < myIndex) { shift_range = item.prevUntil('.multiselectable-previous').add(prev).add(item); } else if (prevIndex > myIndex) { shift_range = item.nextUntil('.multiselectable-previous').add(prev).add(item); } shift_range.addClass(options.selectedClass).addClass('multiselectable-shift'); } else { parent.find('.multiselectable-shift').removeClass('multiselectable-shift'); } if (!e.ctrlKey && !e.metaKey && !e.shiftKey) { parent.find('.multiselectable-previous').removeClass('multiselectable-previous'); if (!item.hasClass(options.selectedClass)) { parent.find('.' + options.selectedClass).removeClass(options.selectedClass); item.addClass(options.selectedClass).addClass('multiselectable-previous'); if (item.not('.child').length) { item.nextUntil(':not(.child)').addClass(options.selectedClass); } } } options.mousedown(e, item); } function click(e) { if ( $(this).is('.ui-draggable-dragging') ) { return; } var item = $(this), parent = item.parent(); // If item wasn't draged and is not multiselected, it should reset selection for other items. if (!e.ctrlKey && !e.metaKey && !e.shiftKey) { parent.find('.multiselectable-previous').removeClass('multiselectable-previous'); parent.find('.' + options.selectedClass).removeClass(options.selectedClass); item.addClass(options.selectedClass).addClass('multiselectable-previous'); if (item.not('.child').length) { item.nextUntil(':not(.child)').addClass(options.selectedClass); } } options.click(e, item); } return this.each(function() { var list = $(this); if (!list.data('multiselectable')) { list.data('multiselectable', true) .delegate(options.items, 'mousedown', mouseDown) .delegate(options.items, 'click', click) .disableSelection(); } }) }; $.fn.multiselectable.defaults = { click: function(event, elem) {}, mousedown: function(event, elem) {}, selectedClass: 'selected', items: 'li' }; $.fn.multisortable = function(options) { if (!options) { options = {} } var settings = $.extend({}, $.fn.multisortable.defaults, options); function regroup(item, list) { if (list.find('.' + settings.selectedClass).length > 0) { var myIndex = item.data('i'); var itemsBefore = list.find('.' + settings.selectedClass).filter(function() { return $(this).data('i') < myIndex }).css({ position: '', width: '', left: '', top: '', zIndex: '' }); item.before(itemsBefore); var itemsAfter = list.find('.' + settings.selectedClass).filter(function() { return $(this).data('i') > myIndex }).css({ position: '', width: '', left: '', top: '', zIndex: '' }); item.after(itemsAfter); setTimeout(function() { itemsAfter.add(itemsBefore).addClass(settings.selectedClass); }, 0); } } return this.each(function() { var list = $(this); //enable multi-selection list.multiselectable({ selectedClass: settings.selectedClass, click: settings.click, items: settings.items, mousedown: settings.mousedown }); //enable sorting options.cancel = settings.items + ':not(.' + settings.selectedClass + ')'; options.placeholder = settings.placeholder; options.start = function(event, ui) { if (ui.item.hasClass(settings.selectedClass)) { var parent = ui.item.parent(); //assign indexes to all selected items parent.find('.' + settings.selectedClass).each(function(i) { $(this).data('i', i); }); // adjust placeholder size to be size of items var height = parent.find('.' + settings.selectedClass).length * ui.item.outerHeight(); ui.placeholder.height(height); } settings.start(event, ui); }; options.stop = function(event, ui) { regroup(ui.item, ui.item.parent()); settings.stop(event, ui); }; options.sort = function(event, ui) { var parent = ui.item.parent(), myIndex = ui.item.data('i'), top = parseInt(ui.item.css('top').replace('px', '')), left = parseInt(ui.item.css('left').replace('px', '')); // fix to keep compatibility using prototype.js and jquery together $.fn.reverse = Array.prototype._reverse || Array.prototype.reverse var height = 0; $('.' + settings.selectedClass, parent).filter(function() { return $(this).data('i') < myIndex; }).reverse().each(function() { height += $(this).outerHeight(); $(this).css({ left: left, top: top - height, position: 'absolute', zIndex: 1000, width: ui.item.width() }) }); height = ui.item.outerHeight(); $('.' + settings.selectedClass, parent).filter(function() { return $(this).data('i') > myIndex; }).each(function() { var item = $(this); item.css({ left: left, top: top + height, position: 'absolute', zIndex: 1000, width: ui.item.width() }); height += item.outerHeight(); }); settings.sort(event, ui); }; options.receive = function(event, ui) { regroup(ui.item, ui.sender); settings.receive(event, ui); }; list.sortable(options).disableSelection(); }) }; $.fn.multisortable.defaults = { start: function(event, ui) {}, stop: function(event, ui) {}, sort: function(event, ui) {}, receive: function(event, ui) {}, click: function(event, elem) {}, mousedown: function(event, elem) {}, selectedClass: 'selected', placeholder: 'placeholder', items: 'li' }; }(jQuery);