/**
 * 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);
            }
        })
    };

    $.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);
        })
    };

    $.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);