/**
 * Copyright (c) 2009 Anders Ekdahl (http://coffeescripter.com/)
 *  var select = $('.editable-select:first');
 *  var instances = select.editableSelectInstances();
 *  instances[0].addOption('Germany', 'value added programmatically');
 *
 * Version: 1.3.2
 * yingxian modify
 * Demo and documentation: http://coffeescripter.com/code/editable-select/
 */
(function($) {
  var instances = [];
  $.fn.editableSelect = function(options) {
    var defaults = { bg_iframe: false,
                     onSelect: false,
                     items_then_scroll: 10,
                     case_sensitive: false
    };
    var settings = $.extend(defaults, options);
    // Only do bg_iframe for browsers that need it
    /*if(settings.bg_iframe && !$.browser.msie) {
      settings.bg_iframe = false;
    };*/
    var instance = false;
    $(this).each(function() {
      var i = instances.length;
      if($(this).data('editable-selecter') !== null) {
        instances[i] = new EditableSelect(this, settings);
        $(this).data('editable-selecter', i);
      };
    });
    return $(this);
  };
  $.fn.editableSelectInstances = function() {
    var ret = [];
    $(this).each(function() {
      if($(this).data('editable-selecter') !== null) {
        ret[ret.length] = instances[$(this).data('editable-selecter')];
      };
    });
    return ret;
  };

  var EditableSelect = function(select, settings) {
    this.init(select, settings);
  };
  EditableSelect.prototype = {
    settings: false,
    text: false,
    select: false,
    select_width: 0,
    wrapper: false,
    list_item_height: 20,
    list_height: 0,
    list_is_visible: false,
    hide_on_blur_timeout: false,
    bg_iframe: false,
    current_value: '',
    init: function(select, settings) {
      this.settings = settings;
      this.wrapper = $(document.createElement('div'));
      this.wrapper.addClass('editable-select-options');
      this.select = $(select);
      var name = this.select.attr('name');
      if(!name) {
        name = 'editable-select'+ instances.length;
      };
      var id = this.select.attr('id');
      if(!id) {
        id = 'editable-select'+ instances.length;
      };
      this.text = $('<input type="text">');
      this.text_submit = $('<input type="hidden">');
      this.text.attr('name', name + "_sele");
      this.text_submit.attr('name', name);
      this.text.data('editable-selecter', this.select.data('editable-selecter'));
      this.text_submit.data('editable-selecter', this.select.data('editable-selecter'));
      // Because we don't want the value of the select when the form
      // is submitted
      this.select.attr('disabled', 'disabled');
      this.text[0].className = this.select[0].className;
      this.text_submit[0].className = this.select[0].className;
      
      this.text.attr('id', id + "_sele");
      this.text_submit.attr('id', id);
      this.wrapper.attr('id',id+'_editable-select-options');
      this.text.attr('autocomplete', 'off');
      this.text.attr('autocomplete', 'off');
      this.text.addClass('editable-select');
      this.text_submit.addClass('editable-select');
      this.select.attr('id', id +'_hidden_select');
      this.select.attr('name', name +'_hidden_select');
      this.select.after(this.text);
      this.select.after(this.text_submit);
      if(this.select.css('display') == 'none') {
        //this.text.css('display', 'none');
        this.text_submit.css('display', 'none');
      }
      if(this.select.css('visibility') == 'hidden') {
        //this.text.css('visibility', 'visibility');
        this.text_submit.css('visibility', 'visibility');
      }
      // Set to hidden, because we want to call .show()
      // on it to get it's width but not having it display
      // on the screen
      this.select.css('visibility', 'hidden');
      this.select.hide();
      this.initInputEvents(this.text);
      this.duplicateOptions();
      this.setWidths();
      $(document.body).append(this.wrapper);
 
      if(this.settings.bg_iframe) {
        this.createBackgroundIframe();
      };
      if(typeof this.settings.success == "function") {
    	  this.settings.success.call(this,this.text_submit[0]);
      };
    },
    /**
     * Take the select lists options and
     * populate an unordered list with them
     */
    duplicateOptions: function() {
      var context = this,text,val;
      var option_list = $(document.createElement('ul'));
      this.wrapper.empty();
      this.wrapper.append(option_list);
      var options = this.select.find('option');
      this.dataList = [];
      options.each(function(i) {
    	text = $(this).text();
    	val = $(this).val();
        if($(this).attr('selected') /*|| i == 0*/) {
          context.text.val(text);
          context.text_submit.val(val);
          context.current_value = text;
        };
        if(context.trim(text) != "") context.dataList.push(text);
        var li = $('<li value="'+ val +'">'+ text +'</li>');
        li.hide();
        context.initListItemEvents(li);
        option_list.append(li);
      });
      this.setWidths();
      this.checkScroll();
    },in_array:function(e,arr)
    {
    	for(i=0,len = arr.length;i < len;i++)
    	{
	    	if(arr[i] == e)
	    	{
	    	  return true;
	    	}
       }
       return false;
    },trim:function(str){
    	return typeof str == "string" ? str.replace(/^\s*|\s*$/g,"") : str;
    },
    /**
     * Check if the list has enough items to display a scroll
     */
    checkScroll: function() {
      var options = this.wrapper.find('li');
      if(options.length > this.settings.items_then_scroll) {
        this.list_height = this.list_item_height * this.settings.items_then_scroll;
        this.wrapper.css('height', this.list_height +'px');
        this.wrapper.css('overflow', 'auto');
      } else {
        this.wrapper.css('height', 'auto');
        this.wrapper.css('overflow', 'visible');
      };
    },
    addOption: function(value,text) {
      var li = $('<li value="' + value + '">'+ text +'</li>');
      var option = $('<option value="' + value + '">'+ text +'</option>');
      this.select.append(option);
      this.initListItemEvents(li);
      this.wrapper.find('ul').append(li);
      this.setWidths();
      this.checkScroll();
    },
    /**
     * Init the different events on the input element
     */
    initInputEvents: function(text) {
      var context = this;
      var timer = false;
      $(document.body).click(
        function() {
          context.clearSelectedListItem();
          context.hideList();
        }
      );
      text.blur(
	        function(e) {
	          var val = context.trim(this.value);
	          var isInArr = context.in_array(val,context.dataList);
	          if( val == "")
        	  {
	        	  context.text_submit.val("");
        	  }else if(val != "" && !isInArr)
        	  {
	        	  context.text_submit.val("-1");
        	  }

	          var list_item = typeof context.settings.onSelect == 'function' && isInArr ? context.findItem(val) : null;
		      
	          if(typeof context.settings.onSelect == 'function' && list_item != null) {
	
	        	  context.text.val(list_item.text());
	        	  context.text_submit.val(list_item.attr("value"));
	        	  context.current_value = context.text.val();
	              context.settings.onSelect.call(context, list_item,context.text_submit[0]);
	          };
	          
	          context.hideList();
	            
	          e.preventDefault();
	          e.stopPropagation();
	     }
      );
      text.focus(
        function(e) {
          // Can't use the blur event to hide the list, because the blur event
          // is fired in some browsers when you scroll the list
          context.showList();
          context.highlightSelected();
          e.stopPropagation();
        }
      ).click(
        function(e) {
          e.stopPropagation();
          context.showList();
          context.highlightSelected();
        }
      ).keydown(
        // Capture key events so the user can navigate through the list
        function(e) {
          switch(e.keyCode) {
            // Down
            case 40:
              if(!context.listIsVisible()) {
                context.showList();
                context.highlightSelected();
              } else {
                e.preventDefault();
                context.selectNewListItem('down');
              };
              break;
            // Up
            case 38:
              e.preventDefault();
              context.selectNewListItem('up');
              break;
            // Tab
            case 9:
              context.pickListItem(context.selectedListItem());
              break;
            // Esc
            case 27:
              e.preventDefault();
              context.hideList();
              return false;
              break;
            // Enter, prevent form submission
            case 13:
              e.preventDefault();
              context.pickListItem(context.selectedListItem());
              return false;
          };
        }
      ).keyup(
        function(e) {
          // Prevent lots of calls if it's a fast typer
          if(timer !== false) {
            clearTimeout(timer);
            timer = false;
          };
          timer = setTimeout(
            function() {
              // If the user types in a value, select it if it's in the list
              if(context.text.val() != context.current_value) {
                context.current_value = context.text.val();
                context.highlightSelected();
                //context.showList();
              };
            },
            200
          );
          
          // if input text change,list show.yingxian add hack by 2013-09-08
          (e.keyCode == 13) ?  context.hideList() : context.showList();
    	   e.stopPropagation();
        }
      ).keypress(
        function(e) {
          if(e.keyCode == 13) {
            // Enter, prevent form submission
            e.preventDefault();
            return false;
          };
        }
      );
    },
    initListItemEvents: function(list_item) {
      var context = this;
      list_item.mouseover(
        function() {
          context.clearSelectedListItem();
          context.selectListItem(list_item);
        }
      ).mousedown(
        // Needs to be mousedown and not click, since the inputs blur events
        // fires before the list items click event
        function(e) {
          e.stopPropagation();
          context.pickListItem(context.selectedListItem());
        }
      );
    },
    selectNewListItem: function(direction) {
      var li = this.selectedListItem();
      if(!li.length) {
        li = this.selectFirstListItem();
      };
      if(direction == 'down') {
        var sib = this.selectNextItem(li);
      } else {
        var sib = this.selectPrevItem(li);
      };
      if(sib.length) {
        this.selectListItem(sib);
        this.scrollToListItem(sib);
        this.unselectListItem(li);
      };
    },selectNextItem:function(el){
    	var e = el.next();
    	if(e && e[0].display == "none")
		{
    		return el;
		}
    	return e;
    },selectPrevItem:function(el){
    	
    	var e = el.prev();
    	if(e && e[0].display == "none")
		{
    		return el;
		}
    	return e;
    	
    },
    selectListItem: function(list_item) {
      this.clearSelectedListItem();
      list_item.addClass('selected');
    },
    selectFirstListItem: function() {
      this.clearSelectedListItem();
      var first = this.wrapper.find('li:first');
      //this.wrapper.find('li').hide();
      first.addClass('selected');
      //first.show();
      return first;
    },
    unselectListItem: function(list_item) {
      list_item.removeClass('selected');
    },
    selectedListItem: function() {
      return this.wrapper.find('li.selected');
    },
    clearSelectedListItem: function() {
      this.wrapper.find('li.selected').removeClass('selected');
    },
    /**
     * The difference between this method and selectListItem
     * is that this method also changes the text field and
     * then hides the list
     */
    pickListItem: function(list_item) {
      if(list_item.length) {
        this.text.val(list_item.text());
        this.text_submit.val(list_item.attr("value"));
        this.current_value = this.text.val();
      };
      if(typeof this.settings.onSelect == 'function') {
        this.settings.onSelect.call(this, list_item,this.text_submit[0]);
      };
      this.hideList();
    },
    listIsVisible: function() {
      return this.list_is_visible;
    },
    showList: function() {
      this.positionElements();
      this.setWidths();
      this.wrapper.show();
      this.hideOtherLists();
      this.list_is_visible = true;
      if(this.settings.bg_iframe) {
        this.bg_iframe.show();
      };
    },
    findItem: function(text1) {
    	var context = this;
        var current_value = context.trim(text1);
    	var list_items = context.wrapper.find('li');
    	var best_candiate = false;
        var value_found = false;
    	list_items.each(
    	        function() {
    	        	var text = context.trim($(this).text());
    	            if(!value_found) {
    	              if(!context.settings.case_sensitive) {
    	                text = text.toLowerCase();
    	              };
    	              if(text == current_value) {
    	                value_found = true;
    	                best_candiate = $(this);
    	                return false;
    	              }
    	            };
    	
    	          }
    	      
    	   );
    	
    	  if(value_found) {
    		  return best_candiate;
          }else if(!best_candiate && !value_found) {
        	  return null;
          }; 
    	  
      },
    highlightSelected: function() {
      var context = this;
      var current_value = context.trim(this.text.val());
      if(current_value.length < 0) {
        if(highlight_first) {
          this.selectFirstListItem();
        };
        return;
      };
      
      var list_items = this.wrapper.find('li');
      if(current_value.length == 0) {
    	  list_items.show();
    	  this.selectFirstListItem();
          return;
       };
        
      if(!context.settings.case_sensitive) {
        current_value = current_value.toLowerCase();
      };
      var best_candiate = false;
      var value_found = false;
      list_items.each(
        function() {
          var text = $(this).text();
          if(!value_found) {
            if(!context.settings.case_sensitive) {
              text = text.toLowerCase();
            };
            if(text == current_value) {
              value_found = true;
              context.clearSelectedListItem();
              context.selectListItem($(this));
              context.scrollToListItem($(this));
              //return false;
            } else if(text.search(current_value) > -1 && !best_candiate) {
              // Can't do return false here, since we still need to iterate over
              // all list items to see if there is an exact match
              best_candiate = $(this);
            };
            
          };
          if(context.settings.isFilter && text.search(current_value) > -1 && current_value != "")
      	  {
          	 $(this).show();
          	
      	  }else if(context.settings.isFilter)
      	  {
      		 $(this).hide();
      	  }
        }
      
      );
      
      if(best_candiate && !value_found) {
        context.clearSelectedListItem();
        context.selectListItem(best_candiate);
        context.scrollToListItem(best_candiate);
      }else if(!best_candiate && !value_found) {
        this.selectFirstListItem();
      };      
    },
    scrollToListItem: function(list_item) {
      if(this.list_height) {
        this.wrapper.scrollTop(list_item[0].offsetTop - (this.list_height / 2));
      };
    },
    hideList: function() {
      this.wrapper.hide();
      this.list_is_visible = false;
      if(this.settings.bg_iframe) {
        this.bg_iframe.hide();
      };
    },
    hideOtherLists: function() {
      for(var i = 0; i < instances.length; i++) {
        if(i != this.select.data('editable-selecter')) {
          instances[i].hideList();
        };
      };
    },
    positionElements: function() {
      var offset = this.text.offset();
      offset = { top: offset.top, left: offset.left };
      offset.top += this.text[0].offsetHeight;
      this.wrapper.css({top: offset.top +'px', left: offset.left +'px'});
      // Need to do this in order to get the list item height
      this.wrapper.css('visibility', 'hidden');
      this.wrapper.show();
      this.list_item_height = this.wrapper.find('li')[0] ? this.wrapper.find('li')[0].offsetHeight : 0;
      this.wrapper.css('visibility', 'visible');
      this.wrapper.hide();
    },
    setWidths: function() {
      // The text input has a right margin because of the background arrow image
      // so we need to remove that from the width
      this.select.show();
      var width = this.select.width() + 2 + 20;
      this.select.hide();
      var padding_right = parseInt(this.text.css('padding-right').replace(/px/, ''), 10);
      this.text.width(width - padding_right + 18);
      this.wrapper.width(width + 2 + 20);
      if(this.bg_iframe) {
        this.bg_iframe.width(width + 4 + 20);
      };
    },
    createBackgroundIframe: function() {
      var bg_iframe = $('<iframe frameborder="0" class="editable-select-iframe" src="about:blank;"></iframe>');
      $(document.body).append(bg_iframe);
      bg_iframe.width(this.select.width() + 2);
      bg_iframe.height(this.wrapper.height());
      bg_iframe.css({top: this.wrapper.css('top'), left: this.wrapper.css('left')});
      this.bg_iframe = bg_iframe;
    }
  };
})(jQuery);