/**
 * author: Antonio Ramirez http://webeaters.blogspot.com
 *
 * class: AutoComplete for Prototype 1.5.1
 *
 * version: 1.2.0 - 2007-11-11
 * 		(based on AutoSuggest 2.1.3 - 2007-07-19)
 *
 *
 * REFERENCES AND THANKS
 * this class is based on the work in AutoSuggest.js of
 * Timothy Groves - http://www.brandspankingnew.net
 * and adapted for use with prototype 1.5.1
 *
 */

var AutoComplete = Class.create();


AutoComplete.prototype = {
  Version: '1.0.0_beta',
  REQUIRED_PROTOTYPE: '1.5.1',

  initialize: function (id, param)
  {
  	// check whether we have the appropiate javascript libraries
  	this.PROTOTYPE_CHECK();


  	// get field
	//
	this.key = 0;
	this.fld = $(id);
	this.acID = 'autocomplete';

	if (!this.fld)
	{
		throw("AutoComplete requires a field id to initialize");
	}

	// init variables
	//
	this.sInp 	= ""; 	// input value
	this.nInpC 	= 0;	// input value length
	//this.aSug 	= []; 	// suggestions array
	var str = "";
	this.aSug = [];
	this.aOrig = [];
	for(i=0; i < arr.length;i++){
		var vals = arr[i];
		var ids = i+1;
		str = {"id": ids, "value": vals, "info": "" };
		/*if(i < arr.length - 1)
			str += ",";*/
		this.aSug.push(str);
		this.aOrig.push(str);
	}
	
	this.iHigh 	= 0;	// level of list selection

	// parameters object
	this.options = param ? param : {};

	// defaults
	var k, def = {minchars:1, meth:"get", varname:"input", className:"autocomplete", timeout:2500, delay:500, offsety:-5, shownoresults: true, noresults: "No suggestions", maxheight: 50, cache: true, maxentries: 550, onAjaxError:null, setWidth: false, minWidth: 100, maxWidth: 200, useNotifier: true};
	for (k in def)
	{
		if (typeof(this.options[k]) != typeof(def[k]))
			this.options[k] = def[k];
	}

	if (this.options.useNotifier) this.fld.addClassName('ac_field');
	// set keyup handler for field
	// and prevent AutoComplete from client
	//
	var p = this;

	// NOTE: not using addEventListener because UpArrow fired twice in Safari
	document.onmousedown	= function(ev){
		if (!ev)
			ev = window.event;
			//alert(Event.element(ev).innerHTML.toLowerCase());
			if(Event.element(ev).parentNode.innerHTML.toLowerCase().indexOf('<em>') == -1 && Event.element(ev).id != id){
					cls = document.getElementsByTagName('div');
					for(i=0; i < cls.length; i++){
						if( (cls[i].className == "autocomplete") || (cls[i].className == "autocomplete acContainer"))
							cls[i].style.display = 'none';
					}
					 }};
	this.fld.onkeydown 	= function(ev){return p.onKeyDown(ev); };
	//this.fld.onkeypress 	= function(ev){return p.onKeyPress(ev); };
	this.fld.onkeyup 		= function(ev){ return p.onKeyUp(ev); };
	//this.fld.onblur			= function(ev){ return p.onKeyBlur(ev);} // to implement? i did not think so but... up to you

	this.fld.setAttribute("AutoComplete","off");

  },
  convertVersionString: function (versionString){
      var r = versionString.split('.');
      return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
  },
  PROTOTYPE_CHECK: function() {
    if((typeof Prototype=='undefined') ||
       (typeof Element == 'undefined') ||
       (typeof Element.Methods=='undefined') ||
       (this.convertVersionString(Prototype.Version) <
        this.convertVersionString(this.REQUIRED_PROTOTYPE)))
       throw("AutoComplete requires the Prototype JavaScript framework >= " +
        this.REQUIRED_PROTOTYPE);
  },
  onKeyDown: function (e)
    {
    	if (!e) e = window.event;
  
    	var key	= e.keyCode || e.wich;
  	
  
  	// set responses to keypress events in the field
  	// this allows the user to use the arrow keys to scroll through the results
  	// ESCAPE clears the list
  	// RETURN sets the current highlighted value
  	// TAB moves to next item in the list
  	
  	
  	if(this.fld.value != ""){
  		
		switch(key)
			{
				case Event.KEY_DOWN:
					this.changeHighlight(key);
					Event.stop(e);
					break;
				case Event.KEY_UP:
					this.changeHighlight(key);
					Event.stop(e);
					break;
				case 8:
					this.doAjaxRequest(this.fld.value);
					break;
				case Event.KEY_RETURN:
					this.setHighlightedValue();
					Event.stop(e);
					break;
				case Event.KEY_TAB: // if we wish to use this, we need to use it here, keyup fails
					this.changeHighlight(key);
					Event.stop(e);
					break;
				case Event.KEY_ESC:
					this.clearSuggestions();
					break;
			}
			return true;
  	}
  },
  onKeyPress: function (e)
  {
  	if (!e) e = window.event;

  	var key	= e.keyCode || e.wich;


	// set responses to keypress events in the field
	// this allows the user to use the arrow keys to scroll through the results
	// ESCAPE clears the list
	// RETURN sets the current highlighted value
	// TAB moves to next item in the list

	switch(key)
	{
		case 8:
			this.doAjaxRequest(this.fld.value);
			break;
		case Event.KEY_RETURN:
			this.setHighlightedValue();
			Event.stop(e);
			break;
		case Event.KEY_TAB: // if we wish to use this, we need to use it here, keyup fails
			this.changeHighlight(key);
			Event.stop(e);
			break;
		case Event.KEY_ESC:
			this.clearSuggestions();
			break;
	}
	return true;
  },
  onKeyUp: function (e)
  {
  	if (!e) e = window.event;

  	this.key = e.keyCode || e.wich;

	if(this.fld.value != ""){
  	/*if (this.key == Event.KEY_UP || this.key == Event.KEY_DOWN)
  	{
  		this.changeHighlight(this.key);
  		Event.stop(e);
  	}*/
  	if(this.key == 8){
  		//this.doAjaxRequest(this.fld.value);
  		this.getSuggestions(this.fld.value);
  		Event.stop(e);
  	}

  	else { 
			this.key = 8;
			this.getSuggestions(this.fld.value);
			Event.stop(e);
	}

	return true;
	}
	else{
		cls = document.getElementsByTagName('div');
				for(i=0; i < cls.length; i++){
					if( (cls[i].className == "autocomplete") || (cls[i].className == "autocomplete acContainer"))
						cls[i].style.display = 'none';
		}
		this.doAjaxRequest(this.fld.value);
	}
  },
  onKeyBlur: function (e)
  {
  	cls = document.getElementsByTagName('div');
					for(i=0; i < cls.length; i++){
						if( (cls[i].className == "autocomplete") || (cls[i].className == "autocomplete acContainer"))
							cls[i].style.display = 'none';
		}
		this.fld.value = "";
  },
  getSuggestions: function(val)
  {

  	// input the same? do nothing
  	if(val==this.sInp) return false;
  	if(val.length <= this.sInp.length){ this.nInpC = 0; }

  	// kill the old list
  	if($(this.acID)) $(this.acID).remove();

  	this.sInp = val;

  	// input length is less than the min required to trigger a request
  	// do nothing
  	if (val.length < this.options.minchars)
  	{
  		this.aSug 	= [];
  		this.nInpC	= val.length;
  		return false;
  	}


  	// here we will detect if there is a comma and the splitted value has a value to check
  	// comma stars a new search and val is converted to the new value after the comma
  	var ol	= this.nInpC; // old length

  	this.nInpC	= val.length ? val.length : 0;

  	// if caching enabled, and user is typing (ie. length of input is increasing)
  	// filter results out of suggestions from last request
  	this.aSug = this.aOrig;
  	var l = this.aSug.length;
  	//alert(l);

	if($('globalSearchLabel').innerHTML == "Search Merchant")
	{
		if(this.key == 8){
			//alert("if " + this.key);
			//alert(this.aOrig.length);
					  		var arr = [];
					  		for (var i=0;i<this.aOrig.length;i++)
					  		{
					  			if(this.aOrig[i].value.substr(0,val.length).toLowerCase() == val.toLowerCase()){
					  				arr.push(this.aOrig[i]);
								}
								if(val.length >2 && this.aOrig[i].value.toLowerCase().indexOf( val) != -1 && this.found(arr,this.aOrig[i].value) == false){
									arr.push(this.aOrig[i]);
								}
					  		}
					  		this.aSug = arr;

					  		// recreate the list
		  		this.createList(this.aSug);
		}
		else{
  		if (this.nInpC > ol && l < this.options.maxentries && this.options.cache)
		  	{ 
		  		//alert("if " + this.key);
		  		var arr = [];
		  		for (var i=0;i<l;i++)
		  		{
		  			if(this.aSug[i].value.substr(0,val.length).toLowerCase() == val.toLowerCase()){
		  				arr.push(this.aSug[i]);
					}
		  		}
				for (var i=0;i<this.aOrig.length;i++){
		  			if(val.length >2 && this.aOrig[i].value.toLowerCase().indexOf(val) != -1 &&                    this.found(arr,this.aOrig[i].value) == false){
						arr.push(this.aOrig[i]);
					}
				}
		  		this.aSug = arr;
		  		// recreate the list
		  		this.createList(this.aSug);
		  	}
		  	else
		  	{
		  	this.getSuggestions(this.fld.value);
				//this.doAjaxRequest(this.fld.value);
				//this.getSuggestions(this.fld.value);
		  		// do new request
		  		var p = this;
		  		var input	= this.sInp; // send the converted new value (comma)

		  		//clearTimeout(this.ajID); // ajax id timer
		  		this.ajID = setTimeout( function () {p.doAjaxRequest(p.sInp)}, this.options.delay);
		  		var arr = [];
						  		for (var i=0;i<l;i++)
						  		{
						  			if(this.aSug[i].value.substr(0,val.length).toLowerCase() == val.toLowerCase()){
						  				arr.push(this.aSug[i]);
									}
						  		}
						  		this.aSug = arr;

						  		// recreate the list
		  		this.createList(this.aSug);
  	}}

  	}
	else{
		cls = document.getElementsByTagName('div');
		for(i=0; i < cls.length; i++){
			if( (cls[i].className == "autocomplete") || (cls[i].className == "autocomplete acContainer"))
				cls[i].style.display = 'none';
		}
	}
  	return false;
  },
  found: function(arr, item)
  {
  	found = false;
  	for(i=0; i < arr.length; i++){
  		if(arr[i].value.toLowerCase() == item.toLowerCase()){
  			found = true;
  			break;
  		}
  	}
  	return found;
  },
  doAjaxRequest: function (input)
  {
  	// we have to check here if there is a new splitted value (, or ;)
  	// always check against the last part of the comma and then check
  	// saved input is still the value of the field
  	if($(this.acID))
		$(this.acID).remove();
  	if (input != this.fld.value)
  		return false;

  	// create ajax request
  	// do we need to call a function to recreate the url?
  	if (typeof this.options.script == 'function')
  		var url = this.options.script(encodeURIComponent(this.sInp));
  	else
  		var url = this.options.script+this.options.varname+'='+encodeURIComponent(this.sInp);

  	if(!url) return false;

/* Why is it necessary to create a new AutoComplete object? 
   I'm not sure but this breaks in an ugly way if you don't.
	 This just seems wrong, though. */
	var options = {
			script:'',
			varname:'input',
			json:true,
			shownoresults:true,
			maxresults:16,
			callback: function (obj) { }
			};
merchant_search=new AutoComplete(this.fld.id,options);return true;

  	var p = this;
  	var m = this.options.meth;  // get or post?
  	if (this.options.useNotifier){this.fld.setStyle({backgroundImage:'url(images/AutoComplete/AutoComplete_spinner.gif)'});}

  	var options = {
  		method: m,
  		onSuccess: function (req)
  		{
  				if (p.options.useNotifier){p.fld.setStyle({backgroundImage:'url(images/AutoComplete/AutoComplete_leftcap.gif)'});}
  				p.setSuggestions(req,input);
  		},
  		onFailure: (typeof p.options.onAjaxError == 'function')? function (status) {if (p.options.useNotifier){p.fld.setStyle({backgroundImage:'url(images/AutoComplete/AutoComplete_leftcap.gif)'});} p.options.onAjaxError(status)} :  function (status) {if (p.options.useNotifier){p.fld.setStyle({backgroundImage:'url(images/AutoComplete/AutoComplete_leftcap.gif)'});} alert("AJAX error: "+status); }
  	}
  	// make new ajax request
  	new Ajax.Request(url, options);
  },
  setSuggestions: function (req, input)
  {
	  // if field input no longer matches what was passed to the request
	  // don't show the suggestions
	  // here we need to check against the splitted values if any (, or ;)
	if (input != this.fld.value)
		return false;

	this.aSug = [];

	if(this.options.json) // response in json format?
	{
		var jsondata = eval('(' + req.responseText + ')');

		for (var i=0; i<jsondata.results.length;i++)
		{
			this.aSug.push({'id':jsondata.results[i].id, 'value':jsondata.results[i].value, 'info':jsondata.results[i].info});
		}
	}
	else	// response in xml format?
	{


		var results = req.responseXML.getElementsByTagName('results')[0].childNodes;

		for(var i=0;i<results.length;i++)
		{
			if(results[i].hasChildNodes())
				this.aSug.push(  { 'id':results[i].getAttribute('id'), 'value':results[i].childNodes[0].nodeValue, 'info':results[i].getAttribute('info') }  );
		}
	}
	this.acID = 'ac_'+this.fld.id;

	this.createList(this.aSug);
  },
  createDOMElement: function ( type, attr, cont, html )
  {
	var ne = document.createElement( type );

	if (!ne)
		return 0;

	for (var a in attr)
		ne[a] = attr[a];

	var t = typeof(cont);

	if (t == "string" && !html)
		ne.appendChild( document.createTextNode(cont) );
	else if (t == "string" && html)
		ne.innerHTML = cont;
	else if (t == "object")
		ne.appendChild( cont );

	return ne;
  },
  createList:	function(arr)
  {
	 if(arr.length > 0){
	// get rid of the old list if any
  	if($(this.acID)) $(this.acID).remove();

  	// clear list removal timeout
  	this.killTimeout();

  	// if no results, and shownoresults is false, do nothing
  	if (arr.length == 0 && !this.options.shownoresults) return false;

  	// create holding div
  	var div	= this.createDOMElement('div', {className:this.options.className});
	div.setAttribute('id','autocomplete');
	
  	// create div header
  	var hcorner = this.createDOMElement('div', {className: 'ac_corner'});
  	var hbar	= this.createDOMElement('div', {className: 'ac_bar'});
  	var header	= this.createDOMElement('div', {className: 'ac_header'});
  	header.appendChild(hcorner);
  	header.appendChild(hbar);
  	div.appendChild(header);

	// create and populate ul
	if($('ac_ul')){
		$('ac_ul').style.display = 'none';
		$('ac_ul').setAttribute('id','');
	}
	var ul	= this.createDOMElement('ul', {id:'ac_ul'});
	var p 	= this; // pointer that we will need later on
	// no results?
	if (arr.length == 0 && this.options.shownoresults)
	{
		var li = this.createDOMElement('li', {className: 'ac_warning'}, this.options.noresults );
		ul.appendChild(li);
	}
	else
	{
		// loop through arr of suggestions creating an LI element for each of them
		for (var i=0,l = arr.length; i<l; i++)
		{
			// format output with the input enclosed in a EM elementFromPoint
			// (as HTML not DOM)
			var val 	= arr[i].value;
			var st 		= val.toLowerCase().indexOf(this.sInp.toLowerCase()); // HERE WE CHECK AGAINST THE SPLITTED VALUE IF ANY***
			var output 	= val.substring(0,st) + '<em>' + val.substring(st,st+this.sInp.length) + '</em>' + val.substring(st+this.sInp.length);

			var span	= this.createDOMElement('span',{},output,true); // type of, properties, output, isHTML?

			if(arr[i].info != '') // do we need to add extra info?
			{
				var br	= this.createDOMElement('br',{});
				span.appendChild(br);

				var small = this.createDOMElement('small',{}, arr[i].info);
				span.appendChild(small);
			}
			var a 	= this.createDOMElement('a',{href:'#'});

			//var tl	= this.createDOMElement('span',{className:'tl'},'&nbsp;',true);
			//var tr	= this.createDOMElement('span',{className:'tr'},'&nbsp;',true);

			/* a.appendChild(tl);
			a.appendChild(tr); */
			a.appendChild(span); // add the object span into the link

			a.name = i+1;

			a.onclick 		= function (){ p.setHighlightedValue();return false; };
			a.onmouseover	= function () { p.setHighlight(this.name); };

			var li = this.createDOMElement('li',{}, a); // add the link element to a li element

			// finally add the newly created li element to the ul element
			ul.appendChild(li);
		}
	}

	div.appendChild(ul); // add the newly created list to the div element

	// create div footer
	var fcorner = this.createDOMElement('div', {className: 'ac_corner'});
  	var fbar	= this.createDOMElement('div', {className: 'ac_bar'});
  	var footer	= this.createDOMElement('div', {className: 'ac_footer'});
  	footer.appendChild(fcorner);
  	footer.appendChild(fbar);
  	div.appendChild(footer);

  	// get position of target textfield
	// position holding div below it
	// set width of holding div to width of field
	// if

	var pos		= Position.cumulativeOffset(this.fld);
	div.style.position = "absolute";
	//div.style.left = "50%";
	//div.style.marginLeft = "-190px";
	div.style.zIndex = 100;
	div.style.left 		= pos[0]  + "px";
	div.style.top 		= pos[1] + this.fld.offsetHeight  + "px";

	var w 		= (this.options.setWidth && this.fld.offsetWidth < this.options.minWidth)? this.options.minWidth : (this.options.setWidth && this.fld.offsetWidth > this.options.maxWidth)? this.options.maxWidth : this.fld.offsetWidth;

	div.style.width 	= w + "px";
	//div.style.height 	= "310px";
	// TODO: REMOVE THIS CONDITIONAL AFTER DEPLOYING UPDATED SITE NAV
	if (this.fld.id == "sitesearchfield") {
		$(div).addClassName("acContainer");
	}else{
		div.style.height 	= "135px";
	}

	// set mouseover functions for div
	// when mouse pointer leaves div, set a timeout to remove the list after an interval
	// when mouse enters div, kill the timeout so the list won't be removed
	//
	div.onmouseover 	= function(){ p.killTimeout() };

	div.onmouseout 		= function(){ p.resetTimeout() };

	// add DIV to document
	document.getElementsByTagName("body")[0].appendChild(div);

	// highlight first item
	this.iHigh = 0;
	//this.setHighlight(0);

	// remove list after interval
	this.toID	= setTimeout(function () {p.clearSuggestions() },this.options.timeout);
	 }
	 else{
		 	cls = document.getElementsByTagName('div');
			for(i=0; i < cls.length; i++){
				if( (cls[i].className == "autocomplete") || (cls[i].className == "autocomplete acContainer"))
					cls[i].style.display = 'none';
	 		}
	 }
  },
  changeHighlight:	function(key)
  {
  	var list = $("ac_ul");
	if (!list)
		return false;

	var n;

	n = (key == Event.KEY_DOWN || key == Event.KEY_TAB)? this.iHigh + 1 : this.iHigh - 1; // false assumed to be Event.KEY_UP

	n = (n > list.childNodes.length)? list.childNodes.length : ((n < 1)? 1 : n);

	this.setHighlight(n);
  },
  setHighlight:		function(n)
  {
  	var list = $('ac_ul');

  	if (!list) return false;

  	if (this.iHigh > 0) this.clearHighlight();

  	this.iHigh = Number(n);

	if(list){
  		list.childNodes[this.iHigh-1].className = 'ac_highlight';
  		var anchors;
		var lis = document.getElementsByTagName('li');
		for(i=0;i < lis.length;i++){
			if(lis[i].className  == 'ac_highlight'){
				anchors = lis[i].getElementsByTagName('a'); 
				lis[i].scrollIntoView(false);
				
			}
		}
		
		
		//$('autocomplete').scrollTop = 135;
		//alert($('autocomplete').scrollHeight);
		//$('autocomplete').scrollTop = $('autocomplete').scrollHeight;
		//list.childNodes[this.iHigh-1].scrollHeight = list.childNodes[this.iHigh-1].offsetHeight;
		
  	}

  	this.killTimeout();
  },
  clearHighlight:	function()
  {
  	var list = $('ac_ul');

  	if(!list) return false;

  	if(this.iHigh > 0)
  	{
		if(list)
  			list.childNodes[this.iHigh-1].className = '';
  		this.iHigh = 0;
  	}

  },
  setHighlightedValue:	function()
  {
  	if (this.iHigh)
  	{
  		// HERE WE NEED TO IMPLEMENT THE GMAIL LIKE SPLITTED VALUE
  		if (!this.aSug[this.iHigh - 1]) return;
  		this.sInp	= this.fld.value = this.aSug[ this.iHigh -1 ].value;

  		// move cursor to end of input (safari)
  		this.fld.focus();
  		if(this.fld.selectionStart)
  			this.fld.setSelectionRange(this.sInp.length, this.sInp.length);

  		this.clearSuggestions();

  		// pass selected object to callback function, if exists
  		if (typeof this.options.callback == 'function')
  			this.options.callback(this.aSug[this.iHigh-1]); // the object has the properties we want, it will depend of
		goSearch(true);
  	}
  	else
		goSearch(false);
  },
  killTimeout:	function()
  {
  	clearTimeout(this.toID);
  },
  resetTimeout:	function()
  {
  	this.killTimeout();
  	var p = this;
  	this.toID = setTimeout(function () { p.clearSuggestions();});
  },
  clearSuggestions:	function ()
  {

	this.killTimeout();

  },
  fadeOut:	function (milliseconds, callback)
  {
  	this._fadeFrom 	= 1;
  	this._fadeTo	= 0;
  	this._afterUpdateInternal = callback;

  	this._fadeDuration	= milliseconds;
  	this._fadeInterval = 50;
  	this._fadeTime = 0;
  	var p = this;
  	this._fadeIntervalID = setInterval(function() {p._changeOpacity()},this._fadeInterval);

  },
  _changeOpacity: function() {

 	if (!$(this.acID)){
  		this._fadeIntervalID=clearInterval(this._fadeIntervalID);
  		return;
  	}
  	this._fadeTime += this._fadeInterval;

  	var ieop = Math.round( (this._fadeFrom + ((this._fadeTo - this._fadeFrom) * (this._fadeTime/this._fadeDuration))) * 100)
  	var op = ieop / 100;


  	var el = $(this.acID);
  	if (el.filters) // internet explorer
	{
		try
		{
			el.filters.item("DXImageTransform.Microsoft.Alpha").opacity = ieop;
		} catch (e) {
			// If it is not set initially, the browser will throw an error.  This will set it if it is not set yet.
			el.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity='+ieop+')';
		}
	}
	else // other browsers
	{
		el.style.opacity = op;
	}

	if (this._fadeTime >= this._fadeDuration)
	{
		clearInterval( this._fadeIntervalID );
		if (typeof this._afterUpdateInternal == 'function')
			this._afterUpdateInternal();
	}

  }

}