/*
 * multiselect2side jQuery plugin
 *
 * Copyright (c) 2010 Giovanni Casassa (senamion.com - senamion.it)
 *
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://www.senamion.com
 *
 */

(function($)
{
  // SORT INTERNAL
  function internalSort(a, b) {
    var compA = $(a).text().toUpperCase();
    var compB = $(b).text().toUpperCase();
    return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
  };

  var methods = {
    init : function(options) {
      var o = {
        selectedPosition: 'right',
        moveOptions: true,
        labelTop: 'Top',
        labelBottom: 'Bottom',
        labelUp: 'Up',
        labelDown: 'Down',
        labelSort: 'Sort',
        labelsx: 'Available',
        labeldx: 'Selected',
        maxSelected: -1,
        autoSort: false,
        autoSortAvailable: false,
        search: false,
        caseSensitive: false,
        delay: 200,
        optGroupSearch: false,
        minSize: 6
      };

      return this.each(function () {
        var  el = $(this);
        var data = el.data('multiselect2side');

        if (options)
          $.extend(o, options);

        if (!data)
          el.data('multiselect2side', o);

        var  originalName = $(this).attr("name");
        if (originalName.indexOf('[') != -1)
          originalName = originalName.substring(0, originalName.indexOf('['));

        var  nameDx = originalName + "ms2side__dx";
        var  nameSx = originalName + "ms2side__sx";
        var size = $(this).attr("size");
        // SIZE MIN
        if (size < o.minSize) {
          $(this).attr("size", "" + o.minSize);
          size = o.minSize;
        }

        // UP AND DOWN
        var divUpDown =
            "<div class='ms2side__updown'>" +
              "<p class='SelSort' title='Sort'>" + o.labelSort + "</p>" +
              "<p class='MoveTop' title='Move on top selected option'>" + o.labelTop + "</p>" +
              "<p class='MoveUp' title='Move up selected option'>" + o.labelUp + "</p>" +
              "<p class='MoveDown' title='Move down selected option'>" + o.labelDown + "</p>" +
              "<p class='MoveBottom' title='Move on bottom selected option'>" + o.labelBottom + "</p>" +
            "</div>";

        // INPUT TEXT FOR SEARCH OPTION
        var  leftSearch = false, rightSearch = false;
        // BOTH SEARCH AND OPTGROUP SEARCH
        if (o.search != false && o.optGroupSearch != false) {
          var ss = 
            o.optGroupSearch + "<select class='small' ><option value=__null__> </option></select> " +
            o.search + "<input class='small' type='text' /><a href='#'> </a>";

          if (o.selectedPosition == 'right')
            leftSearch = ss;
          else
            rightSearch = ss;
        }
        else if (o.search != false) {
          var  ss = o.search + "<input type='text' /><a href='#'> </a>";

          if (o.selectedPosition == 'right')
            leftSearch = ss;
          else
            rightSearch = ss;
        }
        else if (o.optGroupSearch != false) {
          var  ss = o.optGroupSearch + "<select><option value=__null__> </option></select>";

          if (o.selectedPosition == 'right')
            leftSearch = ss;
          else
            rightSearch = ss;
        }

        // CREATE NEW ELEMENT (AND HIDE IT) AFTER THE HIDDED ORGINAL SELECT
        var htmlToAdd = 
          "<div class='ms2side__div'>" +
              ((o.selectedPosition != 'right' && o.moveOptions) ? divUpDown : "") +
            "<div class='ms2side__select'>" +
              ((o.labelsx || leftSearch != false) ? ("<div class='ms2side__header'>" + (leftSearch != false ? leftSearch : o.labelsx) + "</div>") : "") +
              "<select title='" + o.labelsx + "' name='" + nameSx + "' id='" + nameSx + "' size='" + size + "' multiple='multiple' ></select>" +
            "</div>" +
            "<div class='ms2side__options'>" +
              ((o.selectedPosition == 'right')
              ?
              ("<p class='AddOne' title='Add Selected'>&rsaquo;</p>" +
              "<p class='AddAll' title='Add All'>&raquo;</p>" +
              "<p class='RemoveOne' title='Remove Selected'>&lsaquo;</p>" +
              "<p class='RemoveAll' title='Remove All'>&laquo;</p>")
              :
              ("<p class='AddOne' title='Add Selected'>&lsaquo;</p>" +
              "<p class='AddAll' title='Add All'>&laquo;</p>" +
              "<p class='RemoveOne' title='Remove Selected'>&rsaquo;</p>" +
              "<p class='RemoveAll' title='Remove All'>&raquo;</p>")
              ) +
            "</div>" +
            "<div class='ms2side__select'>" +
              ((o.labeldx || rightSearch != false) ? ("<div class='ms2side__header'>" + (rightSearch != false ? rightSearch : o.labeldx) + "</div>") : "") +
              "<select title='" + o.labeldx + "' name='" + nameDx + "' id='" + nameDx + "' size='" + size + "' multiple='multiple' ></select>" +
            "</div>" +
            ((o.selectedPosition == 'right' && o.moveOptions) ? divUpDown : "") +
          "</div>";
        el.after(htmlToAdd).hide();

        // ELEMENTS
        var allSel = el.next().children(".ms2side__select").children("select");
        var  leftSel = (o.selectedPosition == 'right') ? allSel.eq(0) : allSel.eq(1);
        var  rightSel = (o.selectedPosition == 'right') ? allSel.eq(1) : allSel.eq(0);
        // HEIGHT DIV
        var  heightDiv = $(".ms2side__select").eq(0).height();

        // SELECT optgroup
        var searchSelect = $();

        // SEARCH INPUT
        var searchInput = $(this).next().find("input:text");
        var  removeFilter = searchInput.next().hide();
        var  toid = false;
        var searchV = false;


        // SELECT optgroup - ADD ALL OPTGROUP AS OPTION
        if (o.optGroupSearch != false) {
          var  lastOptGroupSearch = false;

          searchSelect = $(this).next().find("select").eq(0);

          el.children("optgroup").each(function() {
            if (searchSelect.find("[value='" + $(this).attr("label") + "']").size() == 0)
              searchSelect.append("<option value='" + $(this).attr("label") + "'>" + $(this).attr("label") + "</option>");
          });
          searchSelect.change(function() {
            var  sEl = $(this);

            if (sEl.val() != lastOptGroupSearch) {

              // IF EXIST SET SEARCH TEXT TO VOID
              if (searchInput.val() != "") {
                clearTimeout(toid);
                removeFilter.hide();
                searchInput.val("");//.trigger('keyup');
                searchV = "";
                // fto();
              }

              setTimeout(function() {
                if (sEl.val() == "__null__") {
                  els = el.find("option:not(:selected)");
                }
                else
                  els = el.find("optgroup[label='" + sEl.val() + "']").children("option:not(:selected)");

                // REMOVE ORIGINAL ELEMENTS AND ADD OPTION OF OPTGROUP SELECTED
                leftSel.find("option").remove();
                els.each(function() {
                  leftSel.append($(this).clone());
                });
                lastOptGroupSearch = sEl.val();
                leftSel.trigger('change');
              }, 100);
            }
          });
        }


        // SEARCH FUNCTION
        var  fto = function() {
          var  els = leftSel.children();
          var  toSearch = el.find("option:not(:selected)");

          // RESET OptGroupSearch
          lastOptGroupSearch = "__null__";
          searchSelect.val("__null__");

          if (searchV == searchInput.val())
            return;

          searchInput
            .addClass("wait")
            .removeAttr("style");

          searchV = searchInput.val();

          // A LITTLE TIMEOUT TO VIEW WAIT CLASS ON INPUT ON IE
          setTimeout(function() {
            leftSel.children().remove();
            if (searchV == "") {
              toSearch.clone().appendTo(leftSel).removeAttr("selected");
              removeFilter.hide();
            }
            else {
              toSearch.each(function() {
                var  myText = $(this).text();

                if (o.caseSensitive)
                  find = myText.indexOf(searchV);
                else
                  find = myText.toUpperCase().indexOf(searchV.toUpperCase());

                if (find != -1)
                  $(this).clone().appendTo(leftSel).removeAttr("selected");
              });

              if (leftSel.children().length == 0)
                searchInput.css({'border': '1px red solid'});

              removeFilter.show();
              leftSel.trigger('change');
            }
            leftSel.trigger('change');
            searchInput.removeClass("wait");
          }, 5);
        };


        // REMOVE FILTER ON SEARCH FUNCTION
        removeFilter.click(function() {
          clearTimeout(toid);
          searchInput.val("");
          fto();
          return false;
        });

        // ON CHANGE TEXT INPUT
        searchInput.keyup(function() {
          clearTimeout(toid);
          toid = setTimeout(fto, o.delay);
        });


        // CENTER MOVE OPTIONS AND UPDOWN OPTIONS
        $(this).next().find('.ms2side__options, .ms2side__updown').each(function(){
          var  top = ((heightDiv/2) - ($(this).height()/2));
          if (top > 0)
            $(this).css('padding-top',  top + 'px' );
        })

        // MOVE SELECTED OPTION TO RIGHT, NOT SELECTED TO LEFT
        $(this).find("option:selected").clone().appendTo(rightSel); // .removeAttr("selected");
        $(this).find("option:not(:selected)").clone().appendTo(leftSel);

        // SELECT FIRST LEFT ITEM AND DESELECT IN RIGHT (NOT IN IE6)
//        if (!($.browser.msie && $.browser.version == '6.0')) {
//          leftSel.find("option").eq(0).attr("selected", true);
//          rightSel.children().removeAttr("selected");
//        }

        // ON CHANGE SORT SELECTED OPTIONS
        var  nLastAutosort = 0;
        if (o.autoSort)
          allSel.change(function() {
            var  selectDx = rightSel.find("option");

            if (selectDx.length != nLastAutosort) {
              // SORT SELECTED ELEMENT
              selectDx.sort(internalSort);
              // FIRST REMOVE FROM ORIGINAL SELECT
              el.find("option:selected").remove();
              // AFTER ADD ON ORIGINAL AND RIGHT SELECT
              selectDx.each(function() {
                rightSel.append($(this).clone());
                $(this).appendTo(el).attr("selected", true);
                //el.append($(this).attr("selected", true));    HACK IE6
              });
              nLastAutosort = selectDx.length;
            }
          });

        // ON CHANGE SORT AVAILABLE OPTIONS (NOT NECESSARY IN ORIGINAL SELECT)
        var  nLastAutosortAvailable = 0;
        if (o.autoSortAvailable)
          allSel.change(function() {
            var  selectSx = leftSel.find("option");

            if (selectSx.length != nLastAutosortAvailable) {
              // SORT SELECTED ELEMENT
              selectSx.sort(internalSort);
              // REMOVE ORIGINAL ELEMENTS AND ADD SORTED
              leftSel.find("option").remove();
              selectSx.each(function() {
                leftSel.append($(this).clone());
              });
              nLastAutosortAvailable = selectSx.length;
            }
          });

        // ON CHANGE REFRESH ALL BUTTON STATUS
        allSel.change(function() {
          // HACK FOR IE6 (SHOW AND HIDE ORIGINAL SELECT)
//          if ($.browser.msie && $.browser.version == '6.0')
//            el.show().hide();
          var  div = $(this).parent().parent();
          var  selectSx = leftSel.children();
          var  selectDx = rightSel.children();
          var  selectedSx = leftSel.find("option:selected");
          var  selectedDx = rightSel.find("option:selected");

          if (selectedSx.size() == 0 ||
              (o.maxSelected >= 0 && (selectedSx.size() + selectDx.size()) > o.maxSelected))
            div.find(".AddOne").addClass('ms2side__hide');
          else
            div.find(".AddOne").removeClass('ms2side__hide');

          // FIRST HIDE ALL
          div.find(".RemoveOne, .MoveUp, .MoveDown, .MoveTop, .MoveBottom, .SelSort").addClass('ms2side__hide');
          if (selectDx.size() > 1)
            div.find(".SelSort").removeClass('ms2side__hide');
          if (selectedDx.size() > 0) {
            div.find(".RemoveOne").removeClass('ms2side__hide');
            // ALL SELECTED - NO MOVE
            if (selectedDx.size() < selectDx.size()) {  // FOR NOW (JOE) && selectedDx.size() == 1
              if (selectedDx.val() != selectDx.val())  // FIRST OPTION, NO UP AND TOP BUTTON
                div.find(".MoveUp, .MoveTop").removeClass('ms2side__hide');
              if (selectedDx.last().val() != selectDx.last().val())  // LAST OPTION, NO DOWN AND BOTTOM BUTTON
                div.find(".MoveDown, .MoveBottom").removeClass('ms2side__hide');
            }
          }

          if (selectSx.size() == 0 ||
              (o.maxSelected >= 0 && selectSx.size() >= o.maxSelected))
            div.find(".AddAll").addClass('ms2side__hide');
          else
            div.find(".AddAll").removeClass('ms2side__hide');

          if (selectDx.size() == 0)
            div.find(".RemoveAll").addClass('ms2side__hide');
          else
            div.find(".RemoveAll").removeClass('ms2side__hide');
        });

        // DOUBLE CLICK ON LEFT SELECT OPTION
        leftSel.dblclick(function () {
          $(this).find("option:selected").each(function(i, selected){

            if (o.maxSelected < 0 || rightSel.children().size() < o.maxSelected) {
              $(this).remove().appendTo(rightSel);
              el.find("[value='" + $(selected).val() + "']").remove().appendTo(el).attr("selected", true);
            }
          });
          $(this).trigger('change');
        });

        // DOUBLE CLICK ON RIGHT SELECT OPTION
        rightSel.dblclick(function () {
          $(this).find("option:selected").each(function(i, selected){
            $(this).remove().appendTo(leftSel);
            el.find("[value='" + $(selected).val() + "']").removeAttr("selected").remove().appendTo(el);
          });
          $(this).trigger('change');

          // TRIGGER CHANGE AND VALUE NULL FORM OPTGROUP SEARCH (IF EXIST)
          searchSelect.val("__null__").trigger("change");
          // TRIGGER CLICK ON REMOVE FILTER (IF EXIST)
          removeFilter.click();
        });

        // CLICK ON OPTION
        $(this).next().find('.ms2side__options').children().click(function () {
          if (!$(this).hasClass("ms2side__hide")) {
            if ($(this).hasClass("AddOne")) {
              leftSel.find("option:selected").each(function(i, selected){
                $(this).remove().appendTo(rightSel);
                el.find("[value='" + $(selected).val() + "']").remove().appendTo(el).attr("selected", true);
              });
            }
            else if ($(this).hasClass("AddAll")) {  // ALL SELECTED
              // TEST IF HAVE A FILTER OR A SELECT OPTGROUP
              if (removeFilter.is(":visible") || (searchSelect.length > 0 && searchSelect.val() != "__null__"))
                leftSel.children().each(function(i, selected){
                  $(this).remove().appendTo(rightSel);
                  el.find("[value='" + $(selected).val() + "']").remove().appendTo(el).attr("selected", true);
                });
              else {
                leftSel.children().remove().appendTo(rightSel);
                el.find('option').attr("selected", true);
                // el.children().attr("selected", true); -- PROBLEM WITH OPTGROUP
              }
            }
            else if ($(this).hasClass("RemoveOne")) {
              rightSel.find("option:selected").each(function(i, selected){
                $(this).remove().appendTo(leftSel);
                el.find("[value='" + $(selected).val() + "']").remove().appendTo(el).removeAttr("selected");
              });
              // TRIGGER CLICK ON REMOVE FILTER (IF EXIST)
              removeFilter.click();
              // TRIGGER CHANGE AND VALUE NULL FORM OPTGROUP SEARCH (IF EXIST)
              searchSelect.val("__null__").trigger("change");
            }
            else if ($(this).hasClass("RemoveAll")) {  // ALL REMOVED
              rightSel.children().appendTo(leftSel);
              rightSel.children().remove();
              el.find('option').removeAttr("selected");
              //el.children().removeAttr("selected"); -- PROBLEM WITH OPTGROUP
              // TRIGGER CLICK ON REMOVE FILTER (IF EXIST)
              removeFilter.click();
              // TRIGGER CHANGE AND VALUE NULL FORM OPTGROUP SEARCH (IF EXIST)
              searchSelect.val("__null__").trigger("change");
            }
          }

          leftSel.trigger('change');
        });

        // CLICK ON UP - DOWN
        $(this).next().find('.ms2side__updown').children().click(function () {
          var  selectedDx = rightSel.find("option:selected");
          var  selectDx = rightSel.find("option");

          if (!$(this).hasClass("ms2side__hide")) {
            if ($(this).hasClass("SelSort")) {
              // SORT SELECTED ELEMENT
              selectDx.sort(internalSort);
              // FIRST REMOVE FROM ORIGINAL SELECT
              el.find("option:selected").remove();
              // AFTER ADD ON ORIGINAL AND RIGHT SELECT
              selectDx.each(function() {
                rightSel.append($(this).clone().attr("selected", true));
                el.append($(this).attr("selected", true));
              });
            }
            else if ($(this).hasClass("MoveUp")) {
              var  prev = selectedDx.first().prev();
              var  hPrev = el.find("[value='" + prev.val() + "']");

              selectedDx.each(function() {
                $(this).insertBefore(prev);
                el.find("[value='" + $(this).val() + "']").insertBefore(hPrev);  // HIDDEN SELECT
              });
            }
            else if ($(this).hasClass("MoveDown")) {
              var  next = selectedDx.last().next();
              var  hNext = el.find("[value='" + next.val() + "']");

              selectedDx.each(function() {
                $(this).insertAfter(next);
                el.find("[value='" + $(this).val() + "']").insertAfter(hNext);  // HIDDEN SELECT
              });
            }
            else if ($(this).hasClass("MoveTop")) {
              var  first = selectDx.first();
              var  hFirst = el.find("[value='" + first.val() + "']");

              selectedDx.each(function() {
                $(this).insertBefore(first);
                el.find("[value='" + $(this).val() + "']").insertBefore(hFirst);  // HIDDEN SELECT
              });
            }
            else if ($(this).hasClass("MoveBottom")) {
              var  last = selectDx.last();
              var  hLast = el.find("[value='" + last.val() + "']");

              selectedDx.each(function() {
                last = $(this).insertAfter(last);  // WITH last = SAME POSITION OF SELECTED OPTION AFTER MOVE
                hLast = el.find("[value='" + $(this).val() + "']").insertAfter(hLast);  // HIDDEN SELECT
              });
            }
          }

          leftSel.trigger('change');
        });

        // HOVER ON OPTION
        $(this).next().find('.ms2side__options, .ms2side__updown').children().hover(
          function () {
            $(this).addClass('ms2side_hover');
          },
          function () {
            $(this).removeClass('ms2side_hover');
          }
        );

        // UPDATE BUTTON ON START
        leftSel.trigger('change');
        // SHOW WHEN ALL READY
        $(this).next().show();
      });
    },
    destroy : function( ) {
      return this.each(function () {
        var  el = $(this);
        var data = el.data('multiselect2side');

        if (!data)
          return;

        el.show().next().remove();
      });
    },
    addOption : function(options) {
      var oAddOption = {
        name: false,
        value: false,
        selected: false
      };

      return this.each(function () {
        var  el = $(this);
        var data = el.data('multiselect2side');

        if (!data)
          return;

        if (options)
          $.extend(oAddOption, options);

        var  strEl = "<option value='" + oAddOption.value + "' " + (oAddOption.selected ? "selected" : "") + " >" + oAddOption.name + "</option>";

        el.append(strEl);

        // ELEMENTS
        var allSel = el.next().children(".ms2side__select").children("select");
        var  leftSel = (data.selectedPosition == 'right') ? allSel.eq(0) : allSel.eq(1);
        var  rightSel = (data.selectedPosition == 'right') ? allSel.eq(1) : allSel.eq(0);

        if (oAddOption.selected)
          rightSel.append(strEl).trigger('change');
        else
          leftSel.append(strEl).trigger('change');
      });
    }
  };

  $.fn.multiselect2side = function( method ) {
    if ( methods[method] ) {
      return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.multiselect2side' );
    }    
  };

})(jQuery);