function ajaxSuggestFbml(obj_id, options) {
  this.obj = f$(obj_id);
  //alert(f$(obj_id).getValue());
	// Setup the events we're listening to
	this.obj.addEventListener('focus', this.onfocus.bind(this))
		.addEventListener('blur', this.onblur.bind(this))
		.addEventListener('keyup', this.onkeyup.bind(this))
		.addEventListener('keydown', this.onkeydown.bind(this))
		.addEventListener('keypress', this.onkeypress.bind(this));

  // Create the dropdown list that contains our suggestions
  var list = bantr.create_element('div');
  this.list = list;
	this.list.setClassName('suggest_list').setStyle({display: 'none'});
  this.obj.getParentNode().insertBefore(this.list,this.obj);
  this.originalOffsetLeft = this.obj.getAbsoluteLeft();
  // Various flags
  this.focused = true;
  this.growingWidth = 5;
	this.options = options;
	this.selectedindex = -1;
	this.delayTime = options.delayTime == null ? 700 : options.delayTime;
	this.preMsgTxt = options.preMsgTxt == null ? 'type for suggestions' : options.preMsgTxt;
  if (this.options.rewrite_id) {
    this.options.focus = false;
  }
  if (!this.options.min_length) {
    this.options.min_length = 0
  }

  this.cache = {};
	
	if(!this.options.focus) {
    this.preMsg = true;
		this.obj.setValue(this.preMsgTxt);
		this.obj.addClassName('suggest_pretext');
	} else {
    this.obj.focus();
		this.preMsg = false;
		this.update_results();
  }

}

// Show suggestions when the user focuses the text field
ajaxSuggestFbml.prototype.onfocus = function(event) {
	if(this.preMsg) {
		this.obj.setValue('');
		this.obj.removeClassName('suggest_pretext');
    this.preMsg = false;
	}
	this.focused = true;
	//this.update_results();
	this.obj.removeClassName('suggest_found');
};

// ...and hide it when they leave the text field
ajaxSuggestFbml.prototype.onblur = function() {
	if(this.obj.getValue() == '') {
		this.preMsg = true;
		this.obj.setValue(this.preMsgTxt);
		this.obj.addClassName('suggest_pretext');
	}
	this.focused = false;
	this.hide();
};

// Every keypress updates the suggestions
ajaxSuggestFbml.prototype.onkeyup = function(event) {
  switch (event.keyCode) {
		case 27: // escape
			this.hide();
			this.obj.removeClassName('suggest_found');
			break;
		case 13: // enter
			if(this.options!=null && this.options.onEnter!=null) {
        this.hide();
				this.options.onEnter(event);
				
				if(this.options.clearOnEnter == true) {
					this.obj.setValue('');
					this.hide();
					this.obj.removeClassName('suggest_found');
				}
			}
			break;
		case 0:  
		case 37: // left
		case 9:  // tab
		case 38: // up
		case 39: // right
		case 40: // down
			break;
		default:
      this.selectedindex = -1;
      if (this.options.growWidth) {
        if (event.keyCode == 8) {
          this.growingWidth--;
        } else {
          this.growingWidth++;
        }
        this.obj.setStyle('width',(this.growingWidth/2)+'em');
      }
			this.update_results();
			this.obj.removeClassName('suggest_found');
			break;
	}
};

// We want interactive stuff to happen on keydown to make it feel snappy
ajaxSuggestFbml.prototype.onkeydown = function(event) {
  switch (event.keyCode) {
    case 9: // tab
		case 13: // enter
			if (this.results[this.selectedindex]) {
				this.obj.addClassName('suggest_found').setValue(this.results[this.selectedindex]['title']);
        this.item_selected(this.results[this.selectedindex]['id']);
        this.hide();
				event.preventDefault();
			} else {
        this.item_selected(0);  
      }
			break;

		case 38: // up
			this.select(this.selectedindex - 1);
			event.preventDefault();
			break;

		case 40: // down
			this.select(this.selectedindex + 1);
			event.preventDefault();
			break;
	}
};

// Override these events so they don't actually do anything
ajaxSuggestFbml.prototype.onkeypress = function(event) {
  switch (event.keyCode) {
	    case 13: // return
		case 9:  // tab
		case 38: // up
		case 40: // down
      		event.preventDefault();
      		break;
	}
};

// Select a given index
ajaxSuggestFbml.prototype.select = function(index) {
	var children = this.list.getChildNodes();
	
	if(children != null && children.length > 0) {
		if(index<0)
			index = 0;
		if(index >= children.length)
			index = children.length - 1;
		
		if(this.selectedindex >=0 && this.selectedindex < children.length )
			children[this.selectedindex].removeClassName('suggest_selected');
		children[(this.selectedindex=index)].addClassName('suggest_selected');
	}
};

// on successfull ajax response.
ajaxSuggestFbml.prototype.onajaxdone = function(data) {
  // f$('debugger').setTextValue('ajax done 1');
  var_data = data;
  // if its valid, update the UI
  if (this.options.rewrite_id) {
    this.cache[this.get_norm_typed()].results = var_data;
    this.cache[this.get_norm_typed()].curRequest = null;
    this.draw_results(var_data, '');
  } else {
    //// f$('debugger').setTextValue('ajax done 2 - saving to cache | data.fortext: ' + data.fortext + ' | data.results: '+ data.results);
    // save to the cache
    this.cache[data.fortext].results = data.results;
    this.cache[data.fortext].curRequest = null;
    if(this.get_norm_typed() == data.fortext) {
      // f$('debugger').setTextValue('ajax done 3 - drawing results');
      this.draw_results(data.results, data.fortext);
      this.results = data.results;
    }
  }
  
  // show and hide a few things.
  if (this.options.show_while_loading) {
    if (f$(this.options.show_while_loading) != null) {
      f$(this.options.show_while_loading).setStyle('display','none');
    }
  }
  if (f$(this.options.rewrite_id) != null) {
    f$(this.options.rewrite_id).setStyle('display','block');
  }
};

ajaxSuggestFbml.prototype.get_norm_typed = function() {
	return this.obj.getValue().toLowerCase();
};

ajaxSuggestFbml.prototype.getValue = function() {
	return (this.preMsg?'':this.obj.getValue());
};

// draw the results
ajaxSuggestFbml.prototype.draw_results = function(results, typed) {
  if (this.options.rewrite_id) {
    if (bantr.script_location == bantr.APP_ID_FACEBOOK) {
      f$(this.options.rewrite_id).setInnerFBML(results);
    } else {
      $('#'+this.options.rewrite_id).html(results); 
    }
  } else {
    this.list.setTextValue('');
    if(results == null || results.length == 0) {
      this.list.setStyle('display','none');
      return;
    }
    this.show();
    if (this.options.checkParentWidth) {
      var textAreaWidth = this.obj.getParentNode().getParentNode().getOffsetWidth();
      var listWidth = this.list.getOffsetWidth();
      var objMoveLeft = this.obj.getAbsoluteLeft() - this.originalOffsetLeft;
      if ((objMoveLeft+listWidth) > textAreaWidth) {
        var margin = (objMoveLeft+listWidth) - textAreaWidth;
        //f$('debugger').setTextValue('textAreaWidth: '+ textAreaWidth + ' objMoveLeft: ' + objMoveLeft + ' listWidth: ' + listWidth + ' margin: ' + margin);
        this.list.setStyle('marginLeft','-' + margin +'px');
      } else {
        this.list.setStyle('marginLeft','0');
      }
    }
    for( var i = 0; i < results.length; i++ ) {
      var item = bantr.create_element('div');
      item.setClassName('suggest_suggestion');
      // f$('debugger').setTextValue('drawing results - created div');
      item.addEventListener('mouseover', 
            function() {
              this[0].select(this[1]); 
            }.bind([this, i]));
      item.addEventListener('mousedown', 
            function(event) {
              this.obj.addClassName('suggest_found');
              this.obj.setValue(this.results[this.selectedindex]['title']);
              this.item_selected(this.results[this.selectedindex]['id']);
              this.hide();
            }.bind(this));
      // f$('debugger').setTextValue('drawing results - added listeners');
      var begins = results[i]['title'].toLowerCase().indexOf(typed);
      if (begins == -1) {
        var span_item = bantr.create_element('span');
        span_item.setTextValue(results[i]['title']);
        item.appendChild(span_item);
      } else {
        if (begins > 0) {
          item.appendChild(bantr.create_element('span').setTextValue(results[i]['title'].substring(0, begins)));
        }
        var em_item = bantr.create_element('strong');
        em_item.setTextValue(results[i]['title'].substring(begins, begins + typed.length));
        item.appendChild(em_item);
    
        var span_item = bantr.create_element('span');
        span_item.setTextValue(results[i]['title'].substring(begins + typed.length));
        item.appendChild(span_item);
      }
      this.list.appendChild(item);
    }
  }
};

// send ajax
ajaxSuggestFbml.prototype.send_ajrequest = function(val) {
  // show hide a few things
  if (f$(this.options.show_while_loading) != null) {
    f$(this.options.show_while_loading).setStyle('display','block');
  }
  if (f$(this.options.hide_while_loading) != null) {
    f$(this.options.hide_while_loading).setStyle('display','none');
  }
  if (f$(this.options.rewrite_id) != null) {
    //f$(this.options.rewrite_id).setStyle('display','none');
  }

  // ajax query
  if (bantr.script_location == bantr.APP_ID_FACEBOOK) {
    var request = new Ajax();
    request.requireLogin = true;
    if (this.options.rewrite_id) {
      request.responseType = Ajax.FBML;
    } else {
      request.responseType = Ajax.JSON;
    }
    request.ondone = this.onajaxdone.bind(this);
    this.cache[val] = {curRequest: request};
    this.cache[val].curRequest.post(this.options.ajaxUrl, {suggest_typed: val, action_type: this.options.action_type});
  } else {
    if (this.options.rewrite_id) {
      $.post(this.options.ajaxUrl, {suggest_typed: val, action_type: this.options.action_type} , this.onajaxdone.bind(this));
    } else {
      $.getJSON(this.options.ajaxUrl, {suggest_typed: val, action_type: this.options.action_type} , this.onajaxdone.bind(this));
    }
    this.cache[val] = {};
  }
};

// This is called every keypress to update the suggestions
ajaxSuggestFbml.prototype.update_results = function() {
	// Search the list of potential results and find ones that match what we have so far
  var val = this.get_norm_typed();
  if (val.length >= this.options.min_length) {
    if(this.cache[val] != null) {
      // pull from el cache
      this.draw_results(this.cache[val].results,val);
      this.results = this.cache[val].results;
    } else if(this.cache[val] == null || this.cache[val].curRequest == null){
      if(this.requestTimer != null)
        clearTimeout(this.requestTimer);
      this.requestTimer = setTimeout(
        function() { 
          this.send_ajrequest(val); 
          this.requestTimer = null;
        }.bind(this), this.delayTime);
    }
  }
};

ajaxSuggestFbml.prototype.cleanup = function() {
	this.hide();
	this.obj.setValue('');
  this.obj.removeClassName('suggest_found');
};

ajaxSuggestFbml.prototype.show = function() {
	this.list.setStyle('display', 'block');
};

ajaxSuggestFbml.prototype.hide = function() {
	this.selectedindex = -1;
	this.list.setStyle('display', 'none');
};

// something has been chosen. add it to a seperate hidden input, and display it, and show a cancel button.
ajaxSuggestFbml.prototype.item_selected = function(id) {
  if (this.options.multiSelect) {
    // f$('debugger').setTextValue('mouse clicked selected');
    var item = bantr.create_element('div');
    item.setClassName('selected_item');
    var input_text_holder = this.obj.getParentNode();
    if (id == 0) {
      var span_item = bantr.create_element('input');
      span_item.setName('product_names[]');
      span_item.setValue(this.obj.getValue());
      span_item.setType('hidden');
      item.appendChild(span_item);
    } else {
      var span_item = bantr.create_element('input');
      span_item.setName('product_ids[]');
      span_item.setValue(id);
      span_item.setType('hidden');
      item.appendChild(span_item);
    }
    var span_item = bantr.create_element('div');
    span_item.setClassName('selected_item_left');
    item.appendChild(span_item);
    var strValue = this.obj.getValue()+' ';
    strValue.substring(0,40);
    var span = bantr.create_element('div').setTextValue(strValue);
    span.setClassName('selected_item_inner_text');
    span_item.appendChild(span);
    var remover = bantr.create_element('div');
    remover.setClassName('selected_item_remove');
    remover.setTitle('click to remove');
    remover.addEventListener('mousedown', 
              function(event) {
                var parent = input_text_holder.getParentNode();
                parent.removeChild(item);
              }.bind(this));
    span_item.appendChild(remover);
    var span_item = bantr.create_element('div');
    span_item.setClassName('selected_item_right');
    item.appendChild(span_item);
    input_text_holder.getParentNode().insertBefore(item, input_text_holder);
    this.obj.setValue('');
    this.list.setTextValue('');
    this.obj.focus();
    this.growingWidth = 5;
    this.obj.setStyle('width',(this.growingWidth)+'em');
  } else if (this.options.forwardToOnSelect) {
    bantr.set_location(this.options.forwardToOnSelect + id);
  }
  //product_inputer_hidden
};
