// jQuery comboselect plugin
// version 1.0.0
// (c)2008 Jason Huck
// http://devblog.jasonhuck.com/
//
// Transforms a single select element into a pair of multi-selects
// with controls to move items left to right and vice versa. Keeps
// items sorted alphabetically in both lists (if desired). Selected
// items are submitted by the original form element. Double-clicking 
// moves an item from one side to the other.
//
// Written against jQuery 1.2.3, but older versions may work.
//
// Usage: $('#myselect').comboselect({
// 		sort: [string,'none'|'left'|'right'|default:'both'],	// which sides to sort
// 		addbtn: [string,default:' &gt; '], 						// label for the "add" button
// 		rembtn: [string,default:' &lt; ']						// label for the "remove" button
// });


(function($) {
  jQuery.fn.comboselect = function(settings) {
    settings = jQuery.extend({
      sort: 'both',    // which sides to sort: none, left, right, or both
      addbtn: ' &gt; ',  // text of the "add" button
      rembtn: ' &lt; ',  // text of the "remove" button
      addAllbtn: 'All &gt;',
      remAllbtn: '&lt; All'
    }, settings);

    this.each(function() {
      // the id of the original element
      var selectID = this.id;

      // ids for the left and right sides
      // of the combo box we're creating
      var leftID = selectID + '_left';
      var rightID = selectID + '_right';

      // the form which contains the original element
      var theForm = $(this).parents('form');

      // place to store markup for the combo box
      var combo = '';

      // copy of the options from the original element
      var opts = $(this).children().clone();

      // add an ID to each option for the sorting plugin
      opts.each(function() {
        $(this).attr('id', $(this).attr('value'));
      });

      // build the combo box
      combo += '<fieldset class="comboselect">';
      combo += '<select id="' + leftID + '" name="' + leftID + '" class="csleft" multiple="multiple">';
      combo += '</select>';
      combo += '<fieldset class="selectbuttons">';
      combo += '<input type="button" class="csadd" value="' + settings.addbtn + '" />';
      combo += '<input type="button" class="csaddall" value="' + settings.addAllbtn + '" />';
      combo += '<input type="button" class="csremove" value="' + settings.rembtn + '" />';
      combo += '<input type="button" class="csremoveall" value="' + settings.remAllbtn + '" />';
      combo += '</fieldset>';
      combo += '<select id="' + rightID + '" name="' + rightID + '" class="csright" multiple="multiple">';
      combo += '</select>';
      combo += '</fieldset>';

      // hide the original element and
      // add the combo box after it
      $(this).hide().after(combo);

      // find the combo box in the DOM and append
      // a copy of the options from the original
      // element to the left side
      theForm.find('#' + leftID).append(opts);

      // bind a submit event to the enclosing form...
      theForm.submit(function() {
        // ...which looks at each option element
        // from the right side...
        $('#' + rightID).find('option').each(function() {
          // ...and selects the corresponding option
          // from the original element
          var v = $(this).attr('value');
          $('#' + selectID).find('option[value="' + v + '"]').attr('selected', 'selected');
        });

        return true;
      });
    });

    // add/remove buttons
    $('input.csadd').click(function() {
      var left = $(this).parent().parent().find('select.csleft');
      var leftOpts = $(this).parent().parent().find('select.csleft option:selected');
      var right = $(this).parent().parent().find('select.csright');
      right.append(leftOpts);
      right.sortOptions();
      clearHighlights(left.attr('id'), right.attr('id'));
    });

    $('input.csaddall').click(function() {
      var left = $(this).parent().parent().find('select.csleft');
      var allLeftOpts = $(this).parent().parent().find('select.csleft option');
      var right = $(this).parent().parent().find('select.csright');
      right.append(allLeftOpts);
      right.sortOptions();
      clearHighlights(left.attr('id'), right.attr('id'));
    });

    $('input.csremove').click(function() {
      var right = $(this).parent().parent().find('select.csright');
      var rightOpts = $(this).parent().parent().find('select.csright option:selected');
      var left = $(this).parent().parent().find('select.csleft');
      left.append(rightOpts);
      left.sortOptions();
      clearHighlights(left.attr('id'), right.attr('id'));
    });

    $('input.csremoveall').click(function() {
      var right = $(this).parent().parent().find('select.csright');
      var allRightOpts = $(this).parent().parent().find('select.csright option');
      var left = $(this).parent().parent().find('select.csleft');
      left.append(allRightOpts);
      left.sortOptions();
      clearHighlights(left.attr('id'), right.attr('id'));
    });

    function clearHighlights(leftID, rightID) {
      var selectFirst = function() {
        // clear highlights
        $('#' + leftID + ', #' + rightID).find('option:selected').removeAttr('selected');
      };
      //hack to prevent ie6 from throwing an error
      jQuery.browser.msie ? setTimeout(selectFirst, 1) : selectFirst();
    }

    return this;
  };
})(jQuery);

(function($) {

  /**
   * Sort options (ascending or descending) in a select box (or series of select boxes)
   *
   * @name     sortOptions
   * @author   Sam Collett (http://www.texotela.co.uk)
   * @type     jQuery
   * @param    Boolean ascending   (optional) Sort ascending (true/undefined), or descending (false)
   * @example  // ascending
   * $("#myselect").sortOptions(); // or $("#myselect").sortOptions(true);
   * @example  // descending
   * $("#myselect").sortOptions(false);
   *
   */
  $.fn.sortOptions = function(ascending)
  {
    var a = typeof(ascending) == "undefined" ? true : !!ascending;
    this.each(
        function()
        {
          if (this.nodeName.toLowerCase() != "select") return;
          // get options
          var o = this.options;
          // get number of options
          var oL = o.length;
          // create an array for sorting
          var sA = [];
          // loop through options, adding to sort array
          for (var i = 0; i < oL; i++)
          {
            sA[i] = {
              v: o[i].value,
              t: o[i].text
            }
          }
          // sort items in array
          sA.sort(
              function(o1, o2)
              {
                // option text is made lowercase for case insensitive sorting
                o1t = o1.t.toLowerCase(),o2t = o2.t.toLowerCase();
                // if options are the same, no sorting is needed
                if (o1t == o2t) return 0;
                if (a)
                {
                  return o1t < o2t ? -1 : 1;
                }
                else
                {
                  return o1t > o2t ? -1 : 1;
                }
              }
              );
          // change the options to match the sort array
          for (var i = 0; i < oL; i++)
          {
            o[i].text = sA[i].t;
            o[i].value = sA[i].v;
          }
        }
        );
    return this;
  };

})(jQuery);
