/**
 * Copyright (c) 2009 Anders Ekdahl (http://coffeescripter.com/)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Version: 1.3.1
 *
 * Demo and documentation: http://coffeescripter.com/code/editable-select/
 *
 * ATTENTION: This plugin was heavily adjusted.
 */
(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(typeof $(this).data('editable-selecter') == 'undefined') {
        instances[i] = new EditableSelect(this, settings);
        $(this).data('editable-selecter', i);
      };
    });
    return $(this);
  };
  $.fn.editableSelectInstances = function() {
    var ret = [];
    $(this).each(function() {
      if(typeof $(this).data('editable-selecter') != 'undefined') {
        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,
    wrapper: false,
    list_item_height: 20,
    list_height: 0,
    list_is_visible: false,
    hide_on_blur_timeout: false,
    bg_iframe: false,
    current_value: '',
    has_number: false,
    _debug: false,
    init: function(select, settings) {
      this.settings = settings;
      this.select = $(select);
      this.text = $('<input type="text">');
      this.hidden = $('<input type="hidden">');
      this.duplicate = $('<div class="editable-select-options">');

      var id = this.select.attr('id');
      if(!id) {
        id = 'editable-select'+ instances.length;
      };
      var selectClasses = this.select.attr('class');
      var addExplanation = this.select.hasClass('explain');
      var width = this.select.width();// because changing the ID also changes the *width*!
      var tabindex = this.select.attr('tabindex');

      this.select.attr('disabled', 'disabled');
      this.select.attr('id', id +'_hidden');
      this.select.attr('name', id +'_hidden');
      this.select.attr('tabindex', '');
      this.text.data('editable-selecter', this.select.data('editable-selecter'));
      this.text.attr('id', id+'_vis');
      this.text.attr('name', id+'_vis');
      this.text.attr('tabindex', tabindex);
      this.text.attr('autocomplete', 'off');
      this.text.addClass(selectClasses + ' text-field');
      this.text.attr('title', this.select.attr('title'));
      this.text.defaultText();
      this.hidden.attr('id', id);
      this.hidden.attr('name', id);
      this.duplicate.attr('id', id+'_dup');

      this.select.parent().append(this.duplicate);
      this.select.parent().after(this.hidden);
      this.initInputEvents(this.text);
      this.duplicateOptions(id, addExplanation);
      this.positionElements();
      this.setWidths(width);

      if(this.settings.bg_iframe) {
        this.createBackgroundIframe();
      };
    },
    duplicateOptions: function(id, addExplanation) {
      var context = this;
      var wrapper = $('#'+id+'_dup');
      if (addExplanation) {
          wrapper.append('<div class="explain">enter your postcode or choose ...</div>');// @TODO: make cleaner
      }
      var option_list = $('<ul></ul>');
      wrapper.append(option_list);
      var options = this.select.find('option');
      options.each(function() {
        if($(this).attr('selected')) {
          context.text.val($(this).text());
          context.text.defaultText();
          context.hidden.val($(this).val());
          context.current_value = $(this).val();
        };
        var li = $('<li>'+ $(this).text() +'</li>');
        li.addClass($(this).attr('class'));
        li.attr('id', id+'-'+$(this).val());
        context.initListItemEvents(li);
        option_list.append(li);
      });
      this.wrapper = wrapper;
      this.checkScroll();
    },
    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) {
      var li = $('<li>'+ value +'</li>');
      var option = $('<option>'+ value +'</option>');
      this.select.append(option);
      this.initListItemEvents(li);
      this.wrapper.find('ul').append(li);
      this.setWidths();
      this.checkScroll();
    },
    initInputEvents: function(text) {
      var context = this;
      var timer = false;
      $(document.body).click(
        function() {
          context.clearSelectedListItem();
          context.hideList();
        }
      );
      text.focus(
        function() {
          // Can't use the blur event to hide the list, because the blur event
          // is fired in some browsers when you scroll the list
          if (!context.has_number) context.showList();
          else context.hideIfPostcode();
          context.highlightSelected();
        }
      ).click(
        function(e) {
          e.stopPropagation();
          if (!context.has_number) context.showList();
          else context.hideIfPostcode();
          context.highlightSelected();
        }
      ).keydown(
        // Capture key events so the user can navigate through the list
        function(e) {
          if (context.has_number) {
            context.hideIfPostcode();
            return;
          }
          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:
              //if list is hidden, *do* submit
              if (context.listIsVisible()) e.preventDefault();
              context.pickListItem(context.selectedListItem());
              if (context.listIsVisible()) return false;
          };
        }
      ).keyup(
        function(e) {
          var re = new RegExp( '^[^0-9]*$' );
          if (context.text.val().match(re)) {
            context.has_number = false;
            if (e.keyCode != 13) context.showList();
          } else {
            context.has_number = true;
            context.hideIfPostcode();
          }
          // 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();
              };
            },
            200
          );
        }
      ).keypress(
        function(e) {
          /* on enter, *do* submit
          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 = li.next();
      } else {
        var sib = li.prev();
      };
      if(sib.length) {
        this.selectListItem(sib);
        this.scrollToListItem(sib);
        this.unselectListItem(li);
      };
    },
    selectListItem: function(list_item) {
        this.clearSelectedListItem();
        list_item.addClass('selected');
    },
    selectFirstListItem: function() {
      this.clearSelectedListItem();
      var first = this.wrapper.find('li:first');
      first.addClass('selected');
      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');
    },
    pickListItem: function(list_item) {
      this.hidden.val('');
      if(list_item.length) {
        var origVal = list_item.attr('id').substr(list_item.attr('id').indexOf('-')+1);
        this.text.defaultText();
        // temporary solution: main areas (central, north, etc) should not be selectable
        // when this gets removed, #area_dup li.level0 should also be removed from component.css
        if ( list_item.hasClass('level0') && (list_item.parent().parent().find('div.explain').hasClass('explain')) ) {
            this.text.val('');
            this.hidden.val('');
        } else {
            this.text.val(list_item.text());
            this.hidden.val(origVal);
        }
        this.current_value = this.text.val();
      };
      if(typeof this.settings.onSelect == 'function') {
        this.settings.onSelect.call(this, list_item);
      };
      this.hideList();
    },
    listIsVisible: function() {
      return this.list_is_visible;
    },
    showList: function() {
      this.wrapper.show();
      this.hideOtherLists();
      this.list_is_visible = true;
      // because some mpu ads have a z-index of 20000000
      this.text.parent().css('z-index', '20000001');
      if(this.settings.bg_iframe) {
        this.bg_iframe.show();
      };
      this.text.removeClass('no-select');
    },
    highlightSelected: function() {
      var context = this;
      var current_value = this.text.val();
      if(current_value.length < 0) {
        /*
        if(highlight_first) {
          this.selectFirstListItem();
        };
        */
        return;
      };
      if(current_value.length == 0) {
          context.hidden.val(current_value);
      };
      if(!context.settings.case_sensitive) {
        current_value = current_value.toLowerCase();
      };
      var best_candiate = false;
      var value_found = false;
      var list_items = this.wrapper.find('li');
      list_items.each(
        function() {
          if(!value_found) {
            var text = $(this).text();
            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.indexOf(current_value) === 0 && !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);
            };
          };
        }
      );
      var isArea = context.hidden[0].id == 'area';
      if (isArea) $('#postcode').val('');
      if(best_candiate && !value_found) {
        context.clearSelectedListItem();
        context.selectListItem(best_candiate);
        context.scrollToListItem(best_candiate);
      } else if(!best_candiate && !value_found) {
        //this.selectFirstListItem();
        context.clearSelectedListItem();
        context.hidden.val(current_value);
        if (isArea) $('#postcode').val(current_value);
      };
    },
    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;
      this.text.parent().css('z-index', '0');
      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 pos = this.select.position();
      pos.top += this.select.outerHeight();
      if (this.select.hasClass('hide')) {
          pos.top = pos.top + 20000;
          pos.left = pos.left + 20000;
      }
      this.select.after(this.text);
      this.select.hide();
      this.wrapper.css({top: pos.top +'px', left: pos.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].offsetHeight;
      this.wrapper.css('visibility', 'visible');
      this.wrapper.hide();
    },
    setWidths: function(width) {
      // The text input has a right margin because of the background arrow image
      // so we need to remove that from the width
      var padding_right = parseInt(this.text.css('padding-right').replace(/px/, ''), 10);
      this.text.width(width+1 - padding_right);
      this.wrapper.width(width + 4);
      if(this.bg_iframe) {
        this.bg_iframe.width(width + 4);
      };
    },
    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')});
      //?bg_iframe.css({top: this.wrapper.offsetTop, left: this.wrapper.offsetLeft});
      this.bg_iframe = bg_iframe;
    },
    hideIfPostcode: function() {
      var context = this;
      context.hideList();
      context.text.addClass('no-select');
    }
  };
})(jQuery);