/*
 * jQuery UI Multiselect
 *
 * Authors:
 *  Michael Aufreiter (quasipartikel.at)
 *  Yanick Rochon (yanick.rochon[at]gmail[dot]com)
 * 
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 * 
 * http://www.quasipartikel.at/multiselect/
 *
 * 
 * Depends:
 *	ui.core.js
 *	ui.sortable.js
 *
 * Optional:
 * localization (http://plugins.jquery.com/project/localisation)
 * scrollTo (http://plugins.jquery.com/project/ScrollTo)
 * 
 * Todo:
 *  Make batch actions faster
 *  Implement dynamic insertion through remote calls
 */


(function($) {

    $.widget("ui.multiselect", {
        options: {
            sortable: true,
            searchable: true,
            animated: 'fast',
            show: 'slideDown',
            hide: 'slideUp',
            dividerLocation: 0.6,
            nodeComparator: function (node1, node2) {
                var text1 = node1.text(),
			    text2 = node2.text();
                return text1 == text2 ? 0 : (text1 < text2 ? -1 : 1);
            }
        },
        _create: function() {
            this.element.hide();
            this.id = this.element.attr("id");
            this.container = $('<div id="MultiselectContainer" class="ui-multiselect ui-helper-clearfix ui-widget"></div>').insertAfter(this.element);
            this.count = 0; // number of currently selected options
            this.selectedContainer = $('<div id="' + this.id + 'SelectedDiv" class="selected"></div>').appendTo(this.container);
            this.availableContainer = $('<div id="' + this.id + 'AvailableDiv" class="available"></div>').appendTo(this.container);
            this.selectedActions = $('<div class="actions ui-widget-header ui-helper-clearfix"><span class="count">0 ' + $.ui.multiselect.locale.itemsCount + '</span><a href="#" id="' + this.id + 'Remove" class="remove-all">' + $.ui.multiselect.locale.removeAll + '</a></div>').appendTo(this.selectedContainer);
            this.availableActions = $('<div class="actions ui-widget-header ui-helper-clearfix"><input id="' + this.id + 'InputA" type="text" class="search empty ui-widget-content ui-corner-all"/><a href="#" id="' + this.id + 'Add" class="add-all">' + $.ui.multiselect.locale.addAll + '</a></div>').appendTo(this.availableContainer);
            this.selectedList = $('<ul id="' + this.id + 'SelectedList" class="selected connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function() { return false; }).appendTo(this.selectedContainer);
            this.availableList = $('<ul class="available connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function() { return false; }).appendTo(this.availableContainer);

            var that = this;

            // set dimensions
            //alert(this.element.width() +" " + this.element.css("width"));
            if (this.element.css("width") != "auto") {
                /*this.container.css("width", this.element.css("width"));
                this.selectedContainer.css("width", Math.floor(this.element.css("width") * this.options.dividerLocation));
                this.availableContainer.css("width", Math.floor(this.element.css("width") * (1 - this.options.dividerLocation)));
                */
                this.container.width(this.element.width() + 1);
                this.selectedContainer.width(Math.floor(this.element.width() * this.options.dividerLocation));
                this.availableContainer.width(Math.floor(this.element.width() * (1 - this.options.dividerLocation)));


            } else {
                //TODO ?
            }

          


            if (this.element.css("height") != "auto") {
                // fix list height to match <option> depending on their individual header's heights
               /* this.selectedList.css("height", Math.max(this.element.css("height") - this.selectedActions.css("height"), 1));
                this.availableList.css("height", Math.max(this.element.css("height") - this.availableActions.css("height"), 1));*/

                this.selectedList.height(Math.max(this.element.height() - this.selectedActions.height(), 1));
                this.availableList.height(Math.max(this.element.height() - this.availableActions.height(), 1));

            } else {
                //TODO ?
            }

            if (!this.options.animated) {
                this.options.show = 'show';
                this.options.hide = 'hide';
            }

            // init lists
            this._populateLists(this.element.find('option'));

            // make selection sortable
            if (this.options.sortable) {
                $("ul.selected").sortable({
                    placeholder: 'ui-state-highlight',
                    axis: 'y',
                    update: function(event, ui) {
                        // apply the new sort order to the original selectbox
                        that.selectedList.find('li').each(function() {
                            if ($(this).data('optionLink'))
                                $(this).data('optionLink').remove().appendTo(that.element);
                        });
                    },
                    receive: function(event, ui) {
                        ui.item.data('optionLink').attr('selected', true);
                        // increment count
                        that.count += 1;
                        that._updateCount();
                        // workaround, because there's no way to reference 
                        // the new element, see http://dev.jqueryui.com/ticket/4303
                        //                        that.selectedList.children('.ui-draggable').each(function() {
                        //                            $(this).removeClass('ui-draggable');
                        //                            $(this).data('optionLink', ui.item.data('optionLink'));
                        //                            $(this).data('idx', ui.item.data('idx'));
                        //                            that._applyItemState($(this), true);
                        //                        });

                        // workaround according to http://dev.jqueryui.com/ticket/4088
                        setTimeout(function() { ui.item.remove(); }, 1);
                    }
                });
            }

            // set up livesearch
            if (this.options.searchable) {
                this._registerSearchEvents(this.availableContainer.find('input.search'));
            } else {
                $('.search').hide();
            }

            // batch actions
            $("#" + this.id + "Remove").click(function() {
                that._populateLists(that.element.find('option').removeAttr('selected'));
                return false;
            });
            $("#" + this.id + "Add").click(function() {
                that._populateLists(that.element.find('option').attr('selected', 'selected'));
                return false;
            });

            $("#" + this.id + "SelectedDiv .connected-list").css("max-height", this.options.max_height);
            trace(this.id + "SelectedDiv");
            $("#" + this.id + "AvailableDiv .connected-list").css("max-height", this.options.max_height);
        },
        destroy: function() {
            this.element.show();
            this.container.remove();

            //$.widget.prototype.destroy.apply(this, arguments);
            if ($.widget.prototype.destroy !== undefined)
                $.widget.prototype.destroy.apply(this, arguments);
        },
        _populateLists: function(options) {
            this.selectedList.children('.ui-element').remove();
            this.availableList.children('.ui-element').remove();
            this.count = 0;

            var that = this;
            var items = $(options.map(function(i) {
                var item = that._getOptionNode(this).appendTo(this.selected ? that.selectedList : that.availableList).show();
                if (this.selected) {
                    that.count += 1;
                }
                that._applyItemState(item, this.selected);
                item.data('idx', i);
                return item[0];
            }));

            // update count
            this._updateCount();
        },
        _updateCount: function() {
            this.selectedContainer.find('span.count').text(this.count + " " + $.ui.multiselect.locale.itemsCount);
        },
        _getOptionNode: function(option) {
            var valueOption = $(option).val();
            var defaultAttr = $(option).attr('default');
            option = $(option);
            var node;
            if (defaultAttr == "true") {
                node = $('<li id="' + this.id + valueOption + '" onclick="SetDefault(\'' + this.id + valueOption + '\')" class="ui-state-default ui-element" title="' + option.text() + '"><input id="' + this.id + valueOption + 'Input" type="hidden" value="' + valueOption + '"/><span class="ui-icon"/>' + option.text() + '<a href="#" class="action"><span class="ui-corner-all ui-icon"/></a></li>').hide();
            }
            else {
                node = $('<li class="ui-state-default ui-element" title="' + option.text() + '"><span class="ui-icon"/>' + option.text() + '<a href="#" class="action"><span class="ui-corner-all ui-icon"/></a></li>').hide();
            }
            node.data('optionLink', option);
            return node;
        },
        // clones an item with associated data
        // didn't find a smarter away around this
        _cloneWithData: function(clonee) {
            var clone = clonee.clone();
            clone.data('optionLink', clonee.data('optionLink'));
            clone.data('idx', clonee.data('idx'));
            return clone;
        },
        _setSelected: function(item, selected) {
            item.data('optionLink').attr('selected', selected);
            if (selected) {
                var selectedItem = this._cloneWithData(item);
                item[this.options.hide](this.options.animated, function() { $(this).remove(); });
                selectedItem.appendTo(this.selectedList).hide()[this.options.show](this.options.animated);
                this._applyItemState(selectedItem, true);
                return selectedItem;
            } else {

                // look for successor based on initial option index
                var items = this.availableList.find('li'), comparator = this.options.nodeComparator;
                var succ = null, i = item.data('idx'), direction = comparator(item, $(items[i]));

                // TODO: test needed for dynamic list populating
                if (direction) {
                    while (i >= 0 && i < items.length) {
                        direction > 0 ? i++ : i--;
                        if (direction != comparator(item, $(items[i]))) {
                            // going up, go back one item down, otherwise leave as is
                            succ = items[direction > 0 ? i : i + 1];
                            break;
                        }
                    }
                } else {
                    succ = items[i];
                }

                var availableItem = this._cloneWithData(item);
                succ ? availableItem.insertBefore($(succ)) : availableItem.appendTo(this.availableList);
                item[this.options.hide](this.options.animated, function() { $(this).remove(); });
                availableItem.hide()[this.options.show](this.options.animated);

                this._applyItemState(availableItem, false);
                return availableItem;
            }
        },
        _applyItemState: function(item, selected) {
            if (selected) {
                if (this.options.sortable)
                    item.children('span').addClass('ui-icon-arrowthick-2-n-s').removeClass('ui-helper-hidden').addClass('ui-icon');
                else
                    item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon');
                item.find('a.action span').addClass('ui-icon-minus').removeClass('ui-icon-plus');
                this._registerRemoveEvents(item.find('a.action'));

            } else {
                item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon');
                item.find('a.action span').addClass('ui-icon-plus').removeClass('ui-icon-minus');
                var isDefault = item.hasClass("defaultSelected");
                if (isDefault) {
                    item.removeClass('ui-state-highlight');
                    item.removeClass('defaultSelected');
                }
                this._registerAddEvents(item.find('a.action'));
            }

            this._registerHoverEvents(item);
        },
        // taken from John Resig's liveUpdate script
        _filter: function(list) {
            var input = $(this);
            var rows = list.children('li'),
			cache = rows.map(function() {

			    return $(this).text().toLowerCase();
			});

            var term = $.trim(input.val().toLowerCase()), scores = [];

            if (!term) {
                rows.show();
            } else {
                rows.hide();

                cache.each(function(i) {
                    if (this.indexOf(term) > -1) { scores.push(i); }
                });

                $.each(scores, function() {
                    $(rows[this]).show();
                });
            }
        },
        _registerHoverEvents: function(elements) {
            elements.removeClass('ui-state-hover');
            elements.mouseover(function() {
                $(this).addClass('ui-state-hover');
            });
            elements.mouseout(function() {
                $(this).removeClass('ui-state-hover');
            });
        },
        _registerAddEvents: function(elements) {
            var that = this;
            elements.click(function() {
                var item = that._setSelected($(this).parent(), true);
                that.count += 1;
                that._updateCount();
                return false;
            })
            // make draggable
            //		.each(function() {
            //		    $(this).parent().draggable({
            //		        connectToSortable: 'ul.selected',
            //		        helper: function() {
            //		            var selectedItem = that._cloneWithData($(this)).css("width", $(this).css("width") - 50);
            //		            selectedItem.css("width", $(this).css("width"));
            //		            return selectedItem;
            //		        },
            //		        appendTo: '.ui-multiselect',
            //		        containment: '.ui-multiselect',
            //		        revert: 'invalid'
            //		    });
            //		});
        },
        _registerRemoveEvents: function(elements) {
            var that = this;
            elements.click(function() {
                that._setSelected($(this).parent(), false);
                that.count -= 1;
                that._updateCount();
                return false;
            });
        },
        _registerSearchEvents: function(input) {
            var that = this;

            input.focus(function() {
                $(this).addClass('ui-state-active');
            })
		.blur(function() {
		    $(this).removeClass('ui-state-active');
		})
		.keypress(function(e) {
		    if (e.keyCode == 13)
		        return false;
		})
		.keyup(function() {
		    that._filter.apply(this, [that.availableList]);
		});
        }
    });

    $.extend($.ui.multiselect, {
        defaults: {
            sortable: false,
            searchable: true,
            dragable: false,
            animated: 'fast',
            show: 'slideDown',
            hide: 'slideUp',
            dividerLocation: 0.6,
            nodeComparator: function(node1, node2) {
                var text1 = node1.text(),
			    text2 = node2.text();
                return text1 == text2 ? 0 : (text1 < text2 ? -1 : 1);
            },
            max_height: 130
        },
        locale: {
            addAll: 'Add all',
            removeAll: 'Remove all',
            itemsCount: 'items selected'
        }
    });

})(jQuery);

