/*
 * jQuery fancySelect - v0.1 5-19-2011
 * Replaces a dropdown select box with an html list that can be styled to your liking
 * http://www.ryanbagwell.com
 *
 * Copyright (c) 2011 Ryan Bagwell
 * Dual licensed under the MIT and GPL licenses.
 *
 * Usage:
 *
 * $(select).fancySelect(options);
 * 
 * Default Options: 
 * 
 *  options = {
 *   initialItems: 7,  //the number of items to shown when the dropdown is displayed
 *   scrollSpeed: 80, 
 *   onSelect: function(){}, //callback for when an item is selected and the drop down is closed
 *   height: 26, //the height of the select box
 *   width: 200, //the width of the select box
 *   pointerText: "&#9660;" //what to display in the pointer box (default is a down arrow)
 *   noScroll: false //disable scrolling altogether and show all of the list items 
 *  }
 * 
 * 
*/
(function($){
 
  var fancyClass = function(options) {
    var wrapper,
        selectedItem,
        listElem,
        listItems,
        pointerElem,
        advanceIt,
        inputElem;
    
    var parent = this;
    
    this.listen = function() {
      this.setToggle();
      this.setSelectHandler();
      this.setClickOutHandler();
    };
    
    
    //sets the click action
    this.setToggle = function() {
      $(this.listElem).toggle(function(e) {
 			  parent.showList();
      },function(e) {
        parent.hideList();
       });
    }
      
    this.hideList = function() {
  		$(this.listElem).removeClass('open');
      $(this.listItems).not('.selected').hide();
    }
      
    this.showList = function() {
      
      $(this.listElem).addClass('open');
      
      //if there are fewer list items than what we initially specify, show all of the items and stop here 
			if ($(this.listItems).length < options.initialItems - 1 || options.noScroll) {
				$(this.listItems).show();
				return;
			}
  			
  		var start = $(this.selectedItem).index();
  		var end = $(this.selectedItem).index() + options.initialItems;

      $(this.listItems).slice(start,end).show();
    	
    	$(this.listElem).addClass('open');
    
      this.setScrollListener();
  
		}
    
    this.setScrollListener = function() {
      $(this.listItems).mouseenter(function() {
         
        if (!$(parent.listElem).hasClass('open'))
          return;
                  
        var e = this;  
        e.t = setTimeout(function() {
          parent.scroll($(e).index())
          },options.scrollSpeed);
  		});		
		}    
      
    this.scroll = function(i) {
				
			//if there are fewer list items than what we initially specify, don't advance
			if ($(this.listItems).length < options.initialItems - 1) {
			  return;
			}
            
			var elem = $(this.listItems).eq(i); 

			//don't scroll if it's the first or last item
			if (i == 0 || i == this.listItems.length - 1) {
				return;
      }

			//up scrolling
			if ($(elem).prev().css('display') == 'none') {
				$(elem).prev().show();
				var toHide = i + (options.initialItems - 1);
				$(elem).parent().children().eq(toHide).hide();						
      }
			
			//down scrolling
      if ($(elem).next().css('display') == 'none') {
       $(elem).next().show();
       var toHide = i - (options.initialItems - 1);
       $(this.listItems).eq(toHide).hide();            
      }
		
		}
			
    
		this.removeAdvanceHover = function(elem) {
			$(this.listItems).unbind('mouseenter mouseleave');
		}
  		
    this.setSelectHandler = function() {
			$(this.listItems).click(function() {
        if (!$(this).parents('ul').hasClass('open')) {
          parent.showList();
          return;
        }
        parent.selectedItem = this;
        parent.setInputValue();
      });
    }
  		
    this.setInputValue = function() {

      if (!$(this.selectedItem).parent().hasClass('open'))
        return;
           
      var value = $(this.selectedItem).find('span').text();
      
      $(this.inputElem).attr('value',value);
      
      $(this.listItems).removeClass('selected');
      
      $(this.selectedItem).addClass('selected');
     
      options.onSelect(this.selectedItem);  
		
    }
 
  		
    this.getItem = function(opts) {
      var span = $('<span></span>').css('display','none').addClass('value').text(opts.value);
      return $('<li></li>').addClass(opts.selected).addClass(opts.hidden).text(opts.displayText).append(span);
    }
 
 
    this.getItemList = function(selectElem,i) {

      var ul = $("<ul></ul>").css({
        position:'absolute',
        width:'100%',
        'list-style-type':'none'
      }).attr('id','fancySelect-'+ i);
       
      $(selectElem).find('option').each(function(i) {

        $(ul).append(parent.getItem({
          displayText:$(this).html(),
          value: $(this).attr('value'),
          selected: $(this).attr('selected')?$(this).attr('selected'):'',
          hidden: (i >= options.initialItems)?'hidden':''
        }));
          
      });
      
      if ($(ul).find('.selected').length == 0)
         $(ul).find('li').first().addClass('selected');
      
      return ul;
    } 
 

    this.getPointer = function() {
      
      var pointer = $("<div class='dropdown-pointer'>"+options.pointerText+"</div>");
      
      $(pointer).css({
        cursor:'pointer',
        position:'absolute',
        right:'0px',
        width: options.height+'px',
        height: options.height+'px',
        'z-index':1
      }); 
      
      return pointer;
  
    }
    
    this.getInput = function(name) {
      var inputElem = document.createElement('input');
      inputElem.setAttribute('type','hidden');
      inputElem.setAttribute('name',name);
      return inputElem;
    }
    
    //replaces the select element with new markup
    this.replaceHTML = function(selectElem,i) {

      var fancySelect = $("<div></div>")
        .css('position','relative')
        .addClass('dropdown-wrapper fancy-'+i+' fancyselect')
        .height(options.height);

      $(fancySelect).append(
        this.getItemList(selectElem,i),
        this.getPointer(),
        this.getInput($(selectElem).attr('name'))
      );
              
      $(fancySelect).find('li').not('.selected').css('display','none');
      
      $(selectElem).replaceWith(fancySelect);
      
      //set the initial input value
      var value = $(this.wrapper).find('li.selected span.value').text();
      $(this.wrapper).find('input').attr('value',value);
      
      this.setValues(i);
  
    }
    
    
    //sets a bunch of properties that many functions can access
    this.setValues = function(i) {
      var wrapper = $('.fancy-'+i);
      this.wrapper = wrapper;
      this.selectedItem = $(wrapper).find('li.selected');
      this.listElem = $(wrapper).find('ul');
  		this.listItems = $(wrapper).find('li');
      this.pointerElem = $(wrapper).find('.dropdown-pointer');
      this.inputElem = $(wrapper).find('input');
      $(this.listItems).css('cursor','pointer');
    }
    
    //adds a click event to any item other than the dropdown to close
    this.setClickOutHandler = function() { 
      $('html').not(this.wrapper).click(function(e) {
        e.stopPropagation();
        if ($(parent.listElem).hasClass('open'))
          $(parent.listElem).trigger('click');
      });
    }
  
  } //end class object

  $.fn.fancySelect = function(options) {
   
    var wrapset = this;
   
    var defaults = {
      initialItems: 7,
      scrollSpeed: 80,
      borderColor: '#DEDEDE',
      onSelect: function(){},
      height: 26,
      width: 200,
      pointerText: "&#9660;",
      noScroll: false
    }
     
    options = $.extend(defaults,options);
    
    var selectObjects = new Array();
        
    $(wrapset).each(function(i) {
      selectObjects['fancy'+i] = new fancyClass(options);
      selectObjects['fancy'+i].replaceHTML(this,i);
      selectObjects['fancy'+i].listen();
    });

  }      

})( jQuery );
