var BrowserDetect = {
	       init: function () {
	          this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
	          this.version = this.searchVersion(navigator.userAgent)
	             || this.searchVersion(navigator.appVersion)
	             || "an unknown version";
	          this.OS = this.searchString(this.dataOS) || "an unknown OS";
	       },
	    searchString: function (data) {
	       for (var i=0;i<data.length;i++) {
	          var dataString = data[i].string;
	          var dataProp = data[i].prop;
	          this.versionSearchString = data[i].versionSearch || data[i].identity;
	          if (dataString) {
	             if (dataString.indexOf(data[i].subString) != -1)
	                return data[i].identity;
	          }
	          else if (dataProp)
	          return data[i].identity;
	       }
	    },
	    searchVersion: function (dataString) {
	       var index = dataString.indexOf(this.versionSearchString);
	       if (index == -1) return;
	       return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
	    },
	    dataBrowser: [
	       { string: navigator.userAgent,
	          subString: "OmniWeb",
	          versionSearch: "OmniWeb/",
	          identity: "OmniWeb"
	       },
	    {
	       string: navigator.vendor,
	       subString: "Apple",
	       identity: "Safari"
	    },
	    {
	       prop: window.opera,
	       identity: "Opera"
	    },
	    {
	       string: navigator.vendor,
	       subString: "iCab",
	       identity: "iCab"
	    },
	    {
	       string: navigator.vendor,
	       subString: "KDE",
	       identity: "Konqueror"
	    },
	    {
	       string: navigator.userAgent,
	       subString: "Firefox",
	       identity: "Firefox"
	    },
	    {
	       string: navigator.vendor,
	       subString: "Camino",
	       identity: "Camino"
	    },
	    { // for newer Netscapes (6+)
	       string: navigator.userAgent,
	       subString: "Netscape",
	       identity: "Netscape"
	    },
	    {
	       string: navigator.userAgent,
	       subString: "MSIE",
	       identity: "Explorer",
	       versionSearch: "MSIE"
	    },
	    {
	       string: navigator.userAgent,
	       subString: "Gecko",
	       identity: "Mozilla",
	       versionSearch: "rv"
	    },
	    { // for older Netscapes (4-)
	       string: navigator.userAgent,
	       subString: "Mozilla",
	       identity: "Netscape",
	       versionSearch: "Mozilla"
	    }
	    ],
	    dataOS : [
	    {
	       string: navigator.platform,
	       subString: "Win",
	       identity: "Windows"
	    },
	    {
	       string: navigator.platform,
	       subString: "Mac",
	       identity: "Mac"
	    },
	    {
	       string: navigator.platform,
	       subString: "Linux",
	       identity: "Linux"
	    }
	]
	
	};
	BrowserDetect.init(); 	
/**
 * author: Harris http://www.minube.com
 *
 * class: AutoComplete for Minube
 *
 * version: 1.0.0 

 * REFERENCES AND THANKS 
 * this class is based on the work in AutoSuggest.js of
 * Timothy Groves - http://www.brandspankingnew.net
 * and Antonio Ramirez http://webeaters.blogspot.com
 * and redesigned to work in minube.com systems
 *
 */

var AutoCompleteDB = Class.create();

AutoCompleteDB.prototype = {
  Version: '1.0.0',
  REQUIRED_PROTOTYPE: '1.4.0',
 
  initialize: function (id, param)
  {
  	// check whether we have the appropiate javascript libraries
  	this.PROTOTYPE_CHECK();

  	// get field
	this.fld = $(id);
	
	// Overlay Info
	this.iframe = id + "_iframe";
	this.needOverlay = this.checkOverlaySupport();

	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 with Tmp Data
	this.actualRoot = "";	// el valor de los caracteres en minchar;
	this.iHigh 	= 0;		// level of list selection 
	this.keyTimer = "";
	this.cache = new Array();	
	this.isMouseBlur = false;
	this.selected = true;
	this.noResultsFound = false;
	
	// parameters object
	this.options = param ? param : {};
	
	// defaults	
	var k, def = {preloadedOptions:new Array(), valueSep:';', minchars:1, meth:"get", varname:"input", className:"autocomplete", timeout:3000, delay:500, offsety:-5, shownoresults: true, noresults: "-", maxheight: 250, maxentries: 25, onAjaxError:null, setWidth: false, minWidth: 100, maxWidth: 200, useNotifier: true, textToDelete: ""};
	
	// vamos a utilizar minchar como valor para establecer la busqueda a bbdd
	// SOLO SE BUSCARA CUANDO EL INPUT DE ENTRADA TENGA minchars CARACTERES y A PARTIR DE AHI SE CACHEARA
	// CUANDO VUELVA A TENER minchars SE VUELVE A BUSCAR 
	
	for (k in def) 
	{
		if (typeof(this.options[k]) != typeof(def[k]))
			this.options[k] = def[k];
	}
	
	//si tenemos preloadedOptions formateamos las llaves para poder realizar búsquedas
	var formattedPreloadedOptions = new Array();
	this.options.preloadedOptions.each
	(
		function(entry)
		{
			var key = this.cleanString(entry);
			formattedPreloadedOptions.push({formatted:key,value:entry});
		}.bind(this)
	);
	this.options.preloadedOptions = formattedPreloadedOptions;
	
	if (this.options.useNotifier) this.fld.addClassName('ac_field');
	
	// set keyup handler for field
	// and prevent AutoComplete from client
	// NOTE: not using addEventListener because UpArrow fired twice in Safari
	this.initializeEvents(this);
	this.fld.setAttribute("AutoComplete","off");
	if (this.fld.value != "")
		this.fld.setStyle({backgroundImage:'none'});

  },
  
  initializeEvents: function(objetoActual) {
	this.fld.onkeypress 	= function(ev){ return objetoActual.onKeyPress(ev); };
	this.fld.onkeyup 		= function(ev){ return objetoActual.onKeyUp(ev); };
	this.fld.onblur			= function(ev){ return objetoActual.onBlur(ev); };
	this.fld.onclick		= function(ev){ objetoActual.fld.focus(); };	
	this.fld.onfocus		= function(ev){
		if (objetoActual.options.textToDelete != "" && objetoActual.fld.value==objetoActual.options.textToDelete) {
			objetoActual.fld.value = "";
		}
		objetoActual.selected=true; objetoActual.fld.select(); }; 
  },
  
  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);
  },
  
  onBlur: function (e) {
  	if (!this.isMouseBlur && !this.noResultsFound ) {
		this.iHigh = 1;
		this.setHighlight(1);	  		
		this.setHighlightedValue();
		this.selected = false;		
	}
  },
  
  onKeyPress: function (e) {
  	if (!e) e = window.event;
  	var key = e.keyCode || e.which;
  	
	// 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 sets the first item in list

	switch(key)
	{
		case Event.KEY_RETURN:
			this.setHighlightedValue();
			Event.stop(e);
			break;
		case Event.KEY_TAB: // Si tabuleamos se elige el primero;		
			this.fld.blur();		
			Event.stop(e);
			break;
		case Event.KEY_ESC:
			this.clearSuggestions();
			break;
	}
	return true;
  },
  
  onKeyUp: function (e) {
 	if (!e) e = window.event;
  	var key = e.keyCode || e.which;
  	
  	if (key == Event.KEY_UP || key == Event.KEY_DOWN) {
  		this.changeHighlight(key);
  		Event.stop(e);
  	} else {
		this.getSuggestions(this.fld.value,this);
	}
	return true;
  },
  
  cleanString: function(val)
  {
	//Formateando entrada para comparar
	var formattedVal = val.toLowerCase();
	formattedVal = formattedVal.gsub(/[áàäâ]/,'a');
	formattedVal = formattedVal.gsub(/[éèëê]/,'e');
	formattedVal = formattedVal.gsub(/[íìïî]/,'i');
	formattedVal = formattedVal.gsub(/[óòöô]/,'o');
	formattedVal = formattedVal.gsub(/[úùüû]/,'u');
	formattedVal = formattedVal.gsub(/[ñÑ]/,'n');		
	formattedVal = formattedVal.gsub(/[çÇ]/,'c');		
	return formattedVal;
  },
  
  getSuggestions: function(val,objetoActual) {
  	this.noResultsFound = false;
  	
  	// input the same? -> do nothing
  	if(val==this.sInp) return false;
  	
  	// kill the old list
  	if($(this.acID)) $(this.acID).remove();
  	
  	this.sInp = val;
  	
  	if (val.length < this.options.minchars)
  	{
	  	// input length is less than the min required to trigger a request -> do nothing  		
  		this.aSug 	= [];
		this.aFullSug = [];
  		this.nInpC	= val.length; 
  		this.actualRoot = "";  		
  	}
  	else
  	{
		//Buscamos en la lista precargada, si existe
		//LLamarla autocomplete_entries o similar, declarandola como un array en un js externo
		//Si existe, buscar en ella
		//De todos modos, disparar la Ajax Request para que llegue con la lista mas completa despues de un rato
		//Las opciones se almacenan en "this.aSug"
		//Y la lista se crea en "this.createList(this.aSug,this);"
		
		//Devuelve JSON de la forma:
		//{"results": [{"id": "1428", "value": "Barcelonnette, Francia - Barcelonnette (BAE)"}]}
		
		var coincidences = new Array();
		objetoActual.options.preloadedOptions.each
		(
			function(entry,index)
			{
				var formattedVal = this.cleanString(val);
				if(entry.formatted.indexOf(formattedVal)>=0)
				{	coincidences.push({id:index,value:entry.value});	}
			}.bind(objetoActual)
		);
		if(coincidences.length > 0)
		{
			//Usar setSuggestions en lugar de createList directamente
			objetoActual.setSuggestions(coincidences,val);
		}
		
		this.nInpC	= val.length;   		
  		clearTimeout(this.keyTimer); 
  		this.keyTimer = setTimeout( 
			  function () {
			  	if ( objetoActual.fld.value.length == objetoActual.nInpC ) {
			  		cached = objetoActual.findInCache(val);
			  		if (cached)
			  		  	objetoActual.createList(cached,objetoActual);
			  		else
			  		  	objetoActual.doAjaxRequest(val,objetoActual);			  		
				}
		} , this.options.delay);
	}
	
  	return false;
  },
  
  
    
  doAjaxRequest: function(input,objetoActual)  {  	 	
  	// create ajax request
  	// do we need to call a function to recreate the url?
  	var cleanInput = this.cleanString(input);
  	if (typeof this.options.script == 'function')
  		var url = this.options.script(encodeURIComponent(cleanInput));
  	else
	  	var url = this.options.script+this.options.varname+'='+cleanInput;
  		//var url = this.options.script+this.options.varname+'='+encodeURIComponent(cleanInput);
  	
  	if(!url) return false;
  	  	
  	var m = this.options.meth;  // get or post?
  	if (this.options.useNotifier){this.fld.setStyle({backgroundImage:'url(/autocomplete/images/autocomplete_spinner.gif)'});} 
  	
  	var options = {
  		method: m,
  		onSuccess: function (req) 
  		{
  				if (objetoActual.options.useNotifier){objetoActual.fld.setStyle({backgroundImage:'none'});} 
				// response in json format?
				suggestions = objetoActual.processSuggestions(req.responseText);
  				objetoActual.setSuggestions(suggestions,input);
  		},
  		onFailure: (typeof objetoActual.options.onAjaxError == 'function')? function (status) {if (objetoActual.options.useNotifier){objetoActual.fld.setStyle({backgroundImage:'url(/autocomplete/images/autocomplete_leftcap.gif)'});} objetoActual.options.onAjaxError(status)} :  function (status) {if (objetoActual.options.useNotifier){objetoActual.fld.setStyle({backgroundImage:'url(/autocomplete/images/autocomplete_leftcap.gif)'});} alert("AJAX error: "+status); }
  	}
  	// make new ajax request
  	new Ajax.Request(url, options);
  },
  
  //Receives JSON code, parses it to an array and returns it
  processSuggestions : function (json)
  {
	 var aSugTmp = [];
	 var jsondata = eval('(' + json + ')');
	
	 for (var i=0; i<jsondata.results.length;i++)
	 {
	  aSugTmp.push({'id':jsondata.results[i].id, 'value':jsondata.results[i].value, 'info':jsondata.results[i].info});
	 }
	 
	 return aSugTmp;
  },
  
  setSuggestions: function (suggestionsArray,input)
  {
  	// compruebo lo necesario para tirar la request a la basura si se nos ha quedado anticuada...  	
	var aSugTmp = suggestionsArray;
	
	this.cacheResults(input, this.aSugTmp);
	if (input != this.fld.value)
		return false;	
		
	this.aSug = aSugTmp;
	aSugTmp = [];
			
	this.acID = 'ac_'+this.fld.id;	
 	this.actualRoot = input; 
 	if (!this.selected) {
		this.iHigh = 1;
		this.setHighlight(1);			
		this.setHighlightedValue(); 				
	} else
	  	this.createList(this.aSug,this); 
  },
  
  cacheResults: function (input, values) {
	this.cache.push({c:input,v:values});
  },
  
  findInCache: function (needle) {
  	for (i=0,len=this.cache.length; i<len; i++) {
		if (this.cache[i].c.toLowerCase() == needle.toLowerCase())
			return this.cache[i].v;
	}
	return false;
  },
  
  clearCacheResults: function() {
	this.cache.clear();	
  },
  
  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, objetoLocal)
  {
	// get rid of the old list if any  
  	if($(this.acID)) $(this.acID).remove();
  	  	
  	// 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', {id:this.acID, className:this.options.className});
  	
  	// 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
	var ul	= this.createDOMElement('ul', {id:'ac_ul'});
	// no results?
	if (arr.length == 0 && this.options.shownoresults)
	{		
		var li = this.createDOMElement('li', {className: 'ac_warning'}, this.options.noresults ,true);
		ul.appendChild(li);
		this.noResultsFound = true;		
	}
	else
	{
		// loop through arr of suggestions creating an LI element for each of them
		// render must be limited by this.options.limit if it is lower than arr.length
		var limit = this.options.maxentries < arr.length? this.options.maxentries: arr.length;
		var val;
		for (var i=0; i<limit; i++)
		{
			// format output with the input enclosed in a EM elementFromPoint
			// (as HTML not DOM)
			val 	= arr[i].value+'';
			var st 		= 0; // Capado a que muestre solo los resultados por delante
			st=val.toLowerCase().indexOf(this.sInp.toLowerCase());
			if(st!=-1)				
				var output 	= val.substring(0,st) + '<em>' + val.substring(st,st+this.sInp.length) + '</em>' + val.substring(st+this.sInp.length);
			else
				var output 	= val;
			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:'javascript:void(0);'});
			
			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 (){ objetoLocal.setHighlightedValue(); };
			a.onmouseover	= function () { objetoLocal.isMouseBlur = true; objetoLocal.setHighlight(this.name); }; 
			a.onmouseout	= function () { objetoLocal.isMouseBlur = false; }; 			
			
			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 
	
	var pos		= Position.cumulativeOffset(this.fld); // deprecated in Prototype 1.6, use instead this.fld.cumulativeOffset()
	div.style.left 		= pos[0] - this.fld.offsetLeft + this.options.hOffset + "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";
		
	// add DIV to document
	document.getElementsByTagName("body")[0].appendChild(div);
	$(this.acID).show();	
	this.handleOverlay(0);	
	this.handleOverlay(1);
	
	// highlight first item, if there are items to highlight
	if( arr.length > 0 )
	{
		this.iHigh = 1;
		this.setHighlight(1);
		if (!this.selected) {
			this.setHighlightedValue();		
		}
	}
  },
  
  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 assumedd 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);
  	
  	list.childNodes[this.iHigh-1].className = 'ac_highlight';	
  },
  
  clearHighlight:	function()
  {
  	var list = $('ac_ul');
  	
  	if(!list) return false;
  	
  	if(this.iHigh > 0)
  	{
  		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;
  		
  		// Old way
  		this.sInp	= this.fld.value = this.aSug[ this.iHigh -1 ].value;
  		
		// 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
		
  		this.clearSuggestions();
  	}
  },

  clearSuggestions:	function ()
  {
	// if ($(this.acID)){this.fadeOut(50,function () {$(this.acID).remove();});}
	// probando sin fade
	if ($(this.acID)){
		$(this.acID).remove(); 
		this.handleOverlay(0);
		this.aSug = [];		
	}
  },

	/*************** IE 5.5 & 6.0 HACK ****************/
	// mira si es necesaria la construcción de un iframe de overlay
    checkOverlaySupport: function() {   	
		if (BrowserDetect.browser == "Explorer" && (BrowserDetect.version > 5 && BrowserDetect.version < 7)){								
        	return true;
        } else 
		return false;
    }, 
    
    // maneja la construcción del overlay
    handleOverlay: function(bVis) {
        if ( this.needOverlay) {
            switch (bVis) {
                case 1 :
					if (!$(this.iframe)){
					  	// create holding iframe
					
						var iframe = '<iframe id="' + this.iframe + '" frameborder="0" scrolling="no" src="about:blank" style="display:none;position:absolute;border:0px none;z-index:15000; background-color:transparent; filter: alpha(opacity=10); opacity: 0.1"></iframe>';
						document.getElementsByTagName("body")[0].id = "superbody";
						new Insertion.Bottom("superbody", iframe);
						
						var pos		= Position.cumulativeOffset(this.fld); // deprecated in Prototype 1.6, use instead this.fld.cumulativeOffset()		
						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;
						var h = $(this.acID).getHeight() + (2*this.options.offsety);
						
						$(this.iframe).setStyle({			
							left:pos[0] - this.fld.offsetLeft + "px",
							top: pos[1] + (-2 * this.options.offsety) +  this.fld.offsetHeight + "px",
							width: w + "px",
							height: h + "px"							
						});								
						$(this.iframe).show();
					}
                break;
                case 0 :
					if ($(this.iframe)){
						$(this.iframe).hide();						
						$(this.iframe).remove();
					}
                break;
             }
        }
    }      
}
