
//funcion principal que arma las url
function writeUrl(porCercania) {
	var url = writeBaseUrl();
	url=writeDonde(url);
	var params = setParamKeys(url);
	url=writeParams(params);
	if(porCercania){
		url=writeCercania(url);
	}	
	return url;
}

//funcion basica
function writeBaseUrl()  {
	var url ="";
	var idLocality=$.cookie(COOKIE_SEARCH_LOCALITYID);
	var localityFriendly=$("#friendlyLocality").val();
	var locality=$("#locality").val(); 
	if(!isEmpty(idLocality)&&isEmpty(localityFriendly)){
		var urlSubDomainFriendly=GLOBAL_BASE_PATH.substring(7);
		localityFriendly=urlSubDomainFriendly.substring(0,urlSubDomainFriendly.indexOf("."));
	}
	var domain=GLOBAL_HOME_PATH;
	var subDomain=GLOBAL_SUB_DOMAIN;
	if (!isEmpty(idLocality)&&!isEmpty(localityFriendly)) {
		url="http://"+localityFriendly;
	}else if (!isEmpty(locality) && isEmpty(idLocality)&&$("#searchType").val()!=5) {
		url="http://listado";
	}else{
		return domain;
	}
	return url=url+subDomain;
}

function writeDonde(url){
	//setemos la ubicacion adelante de cualquier parametro 
	//si la misma fue ingresada sin suggest 
	var locality=$("#locality").val(); 
	if($("#searchType").val()!=5){
		var idLocality=$.cookie(COOKIE_SEARCH_LOCALITYID);
		if (locality!="" && isEmpty(idLocality)) {
			url=url+"/"+friendlyClean(locality);
		}
	}else{
		$.cookie(COOKIE_PRODUCT_LOCALITY,friendlyClean(locality), COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
	}
	return url;
}

function writeCercania(url){
	var direccion=$("#calleAltura").val(); 
	var radio=$("#radio").val();
	if (!isEmpty(radio) && !isEmpty(direccion)) {
		url=url+"/c_"+friendlyClean(direccion)+"_r_"+radio;
	} 
	return url;
}

function setParamKeys(url){
	var params = [];
	var searchType=$("#searchType").val();
	var keyword = $("#keyword").val().toLowerCase();
	if (searchType=="") {url=url+"/q";params[1]=friendlyClean(keyword);}else{
		if (searchType=="1") {url=url+"/e";params[1]=friendlyClean(keyword);}
		if (searchType=="2") {url=url+"/r";params[1]=friendlyClean(keyword);}
		if (searchType=="3") {url=url+"/k";params[1]=friendlyClean(keyword);}	
		if (searchType=="4") {url=url+"/m";params[1]=friendlyClean(keyword);}
		if (searchType=="5") {url=url+"/precios";params[1]=friendlyClean(keyword);}
	}
	//defino la url con los parametros que va contener
	params[0]=url;
	return params;
}

function writeParams(params){
	var url=params[0];
	//le agrego a la url el parametro correspondiente 
	//obtenido en setParamkeys
	if(params.length>1){url=url+"/"+params[1];} 
	return url;
}


function isEmpty(key){
	if( key!= undefined && key!=null && key!=""){return false;}return true;
}

//TODO: tratar de utilizar siempre esta funcion para el suggest
function friendlyClean(word){
	var originales = [/[áàäâ]/g,/[éèëê]/g,/[íìïî]/g,/[óòöô]/g,/[úùüû]/g,/[ñ]/g];
	var reemplazos = ["a","e","i","o","u","n"];
	var w=word.toLowerCase(); //hacemos todo en minuscula;
	for (i = 0; i < originales.length; i++) {w = w.replace(originales[i],reemplazos[i]);}
	return w
	.replace(/^\s+|\s+$/g, "")//quito los espacios		
	.replace(/[_|\s]+/g, "-") // cambios los espacios y los guiones bajos por guiones
	.replace(/[^a-z0-9-]+/g, "") // remuevo todos los caracteres que no son alfanumericos menos el guion
	.replace(/[-]+/g, "-") // reemplazo varias instancias de un guion con una sola
	.replace(/^-+|-+$/g, "") // quito espacios entre los guiones 		
	; 

}

function labels(){
	if($("input#keyword").val().length != 0){
		$("#labelKeyword").addClass("off").removeClass("on");
	}else{
		$("#labelKeyword").addClass("on").removeClass("off");
	}
	//no se saca mas de la cookie. Lo que resuelva locality se coloca en el input
	//var valCKLoc = $.cookie(COOKIE_SEARCH_LOCALITY);
	var valCKLoc = GLOBAL_LOCALITY;
	if(valCKLoc != null && valCKLoc.length != 0 || $("input#locality").val().length != 0){
		$("#labelLocality").addClass("off").removeClass("on");
	}else{
		$("#labelLocality").addClass("on").removeClass("off");
	}
}
function abrirPopupError($errMsg){
	// Asignar la función de cierre
	$(".cerrarMinimoPopup").click(function(){
		$("#fondoTranslucido").css("display","none");
		$("#searchMinimoPopup").css("display","none");
		$("#validateCalleAltura").css("display","none");
	});
	$("#fondoTranslucido").css("display","block");
	$errMsg.css("display","block");
}
// retorna la localidad seleccionada teniendo en cuenta facetas seleccionadas
// que tienen precedencia sobre la localidad escrita en el input de búsqueda
function getSelectedLocality(){
	
	if(isEmpty(localityBreadCrum)){
		localityBreadCrum=$("#locality").val();
	}
	
	if (!localityBreadCrum.toLowerCase().match(/argentina/)){
		localityBreadCrum = localityBreadCrum; //+" argentina"
	}
	
	return localityBreadCrum.toLowerCase().replace(/,/g," ");
}

// retorna la dirección a buscar en google
function getGeocodeAddress(){
	var calleAltura = $("#calleAltura").val();
	var searchText = calleAltura+" "+getSelectedLocality();
	//searchText = "calle "+searchText;
	return searchText;
}

// actualiza el tooltip con el valor que tiene withElement
// y si está en blanco le pone el valor original
// Recive objeto jquery ballon y objeto jquery input
function updateBalloon(balloonObject, inputObject){
	var b = balloonObject;
	if (inputObject.val() != ""){
		b.find(".balloonText").text(inputObject.val());
	} else {
		b.find(".balloonText").text(
				b.find(".balloonText").data("original")
		);
	}
}

//Recive objeto jquery ballon
function redrawBalloon(balloonObject){
	var b = balloonObject;
	if (b.hasClass("inputHover") || b.hasClass("labelHover")
			|| b.hasClass("focused")){
		// ocultar otros tooltips
		// $(".balloon").not(balloonElement).hide();
	    	// Se ocultan solo los conocidos para mejorar perfomance en IE
	    	$("#keywordBalloon, #localityBalloon, #addressBalloon").not(balloonObject).hide();
		b.show();
	} else {
		// ocultar
		b.hide();
	}
}

/* Función que crea un tooltip para el input element a partir del texto contenido en balloonElement
 * Al elemento balloonElement hay que definirle la posición tomando como referencia la esquina
 * inferior izquierda, para que pueda crecer libremente hacia la derecha y hacia abajo (en caso
 * que se le haya limitado el ancho al balloonElement) 
 */
function balloon(element, balloonElement){

	// dar estilo al balloon
	var b = $(balloonElement);
	var inputObject = $(element);
	
	// check if initialized
	if (b.find(".balloonText").length == 0){
		// initialize
		var txt = b.text();
		var input = inputObject;
		b.addClass("balloon");
		b.text("");
		b.append("<div class='balloonText'>"+(input.val()==""?txt:input.val())+"</div>"+
			"<div class='tlBalloon'></div><div class='trBalloon'></div><div class='blBalloon'></div><div class='brBalloon'></div><div class='sayBalloon'></div>"+
			"<div class='rightBalloon'></div><div class='leftBalloon'></div><div class='topBalloon'></div><div class='bottomBalloon'></div>");
		b.find(".balloonText").data("original", txt);
	}

	// mostrar el tooltip al pasar por encima con el mouse y ocultarlo al salir
	inputObject.mouseout(function(){
		b.removeClass("inputHover");
		redrawBalloon(b);
	}).mouseover(function(){
		b.addClass("inputHover");
		updateBalloon(b, inputObject);
		redrawBalloon(b);
	});
	
	// mostrar el tooltip al entrar en el elemento y ocultarlo al salir
	inputObject.focus(function(){ 
		updateBalloon(b, inputObject);
		b.addClass("focused");
		redrawBalloon(b);
		// este es un fix para ocultar el label de localidad cuando se borra el
		// texto de localidad luego de hacer una búsqueda por localidad
		if (inputObject.prev().is("label") && inputObject.val() == ""){
		    inputObject.prev().hide();
		}
	});
	inputObject.blur(function(){ 
		b.removeClass("focused");
		redrawBalloon(b);
		// este es un fix para mostrar el label de localidad cuando se borra el
		// texto de localidad luego de hacer una búsqueda por localidad
		if (inputObject.prev().is("label") && inputObject.val() == ""){
		    inputObject.prev().show();
		}
	});
	
	// mostrarlo cuando se pasa sobre el label (porque se ubica sobre el input)
	if (inputObject.prev().is("label")){
	    inputObject.prev().mouseout(function(){
			b.removeClass("labelHover");
			redrawBalloon(b);
		}).mouseover(function(){
			b.addClass("labelHover");
			redrawBalloon(b);
		});
	}
	
	// actualizar el valor del tooltip al tipear
	inputObject.keyup(function(){ updateBalloon(b, inputObject) });
}

/**
 * Función que ejecuta el submit, se separó de preetySubmit para que 
 * se pueda ejecutar luego de la llamada asyncrónica a google maps
 * @param $form
 * @return
 */
function doSubmit($form,forzarChequeoCercania){
	// guardo la respuesta xml en una variable para que no sea serializada
	var gmapsResponse = $form.find("#gmapsXmlResponse").val();
	// y luego limpio su valor
	$form.find("#gmapsXmlResponse").val("");
	
	//logica para armar urls dependiendo del lugar en que este
	var url="";// estoy en el caso de buscar por cercania desde el filtro
	if(forzarChequeoCercania && page=="results"){
		var urlFriendly = $("#urlCercania").val();
		url=$.trim(urlFriendly);
		url=url.replace("reemplazar-calle",friendlyClean($("#calleAltura").val())).replace("reemplazar-radio",$("#radio").val());
	}else {// para el caso de estar en la home por cercania o haber realizado
			// una busqueda normal
		url=writeUrl(forzarChequeoCercania);
	}
	
	$form.attr("action",url);
	
	//Seteo la cookie de search name con el valor original de la keyword (sin friendly)
	$.cookie(COOKIE_SEARCH_NAME, escape($("#keyword").val()), COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
	// actualizar la cookie de searchType con el valor actual
	$.cookie(COOKIE_SEARCH_TYPE, $("#searchType").val(), COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
	$form.unbind("submit");
	// se reestablece el valor y se lo escapa para enviarlo al server
	$form.find("#gmapsXmlResponse").val(escape(gmapsResponse));
	$form.submit();

}

$(document).ready(function(){
	labels();
	// deshabilitar el autocomplete del browser
	$("#searchForm input").attr("autocomplete", "off");
	$("#searchFormCercania input").attr("autocomplete", "off");
	
	$("#rdPorPalabra").click(function(){
		$("#keyword").css("display","inline");
		$("#keyword").focus();
		$("#labelKeyword").css("display","");
		$("#alojamientos").css("display","none");
		$("#category").attr("value","");
	});
	$("#rdPorCategoria").click(function(){		
		prenderCategorias();
	});
	$("#alojamientos").click(function(){
		var valueSelected = $(this).attr("value");
		$("#category").attr("value",valueSelected);		
	});

	$("#calleAltura").focus(function(){
	    $("#suggest").removeClass("open").addClass("close");
	    //$(".suggestions").removeClass("suggestOn").addClass("suggestOff");
	    //$(".suggestionsLocality").removeClass("suggestOn").addClass("suggestOff");
	    $("#suggestions, #suggestionsLocality").removeClass("suggestOn").addClass("suggestOff");
	});
	
	$("#radio").focus(function(){
	    $("#suggest").removeClass("open").addClass("close");
	    //$(".suggestions").removeClass("suggestOn").addClass("suggestOff");
	    //$(".suggestionsLocality").removeClass("suggestOn").addClass("suggestOff");
	    $("#suggestions, #suggestionsLocality").removeClass("suggestOn").addClass("suggestOff");
	});
	
	/* asegurarse que cuando el usuario escribe o modifica algo en el campo keyword
	 * el tipo de búsqueda sea seteado a keyword (3) */
	$("#keyword").bind("keydown", function(event){
		var code = event.which == 0 ? event.keyCode : event.which;
		if(GLOBAL_KEYWORD != $("#keyword").val()){			
			$("#searchType").val("");
		}
		if (code != 13) {
			$("#labelKeyword").removeClass("onInline");
			$("#labelKeyword").addClass("off");
		}
	});
	
	var suggest_keyword = $("#keyword").Yell_Suggester(
			$("#suggest"), $("#suggestions"),GLOBAL_BASE_PATH + 'suggestionAction.action?keyword=');
			suggest_keyword.bind("suggestSelected", function(e,data){
				$(this).val(data.valueSelected); // coloca el valor clickeado en el input del campo QUE
				$("#locality").focus(); // pone el foco en el input de localidad
				$("#searchType").val(data.classSelected);//setea el tipo devuelto por el suggest
				
			});

	//no se saca mas de la cookie. Lo que resuelva locality se coloca en el input
	//var valCKLoc = $.cookie(COOKIE_SEARCH_LOCALITY);
	var valCKloc = GLOBAL_LOCALITY;
	if( valCKloc != null && valCKloc != ""){
		$("#locality").val(valCKloc.replace(/"/g, ""));
	}
	var suggest_location = $("#locality").Yell_Suggester(
			$("#suggest"), $("#suggestionsLocality"),GLOBAL_BASE_PATH + 'suggestLocation.action?keyword=');
	suggest_location.bind("suggestSelected", function(e,data){
				// Se guarda en cookies el valor seleccionado
				GLOBAL_LOCALITY=data.valueSelected;
				GLOBAL_LOCALITY_ID=data.classSelected;
				$("#friendlyLocality").val(data.friendlySelected);
				$(this).val(data.valueSelected); // coloca el valor clickeado en el input del campo DONDE
				$("#buscar").focus(); // ponel el foco en el boton de buscar
				GLOBAL_LOCALITY_CHANGED = true;
	});

	$("input#locality").change( function() {
		GLOBAL_LOCALITY_ID=null;
		
	});
	
			
	if($("#rdPorCategoria").attr("checked")){
		prenderCategorias();
	}
	
	/* Tooltips del cajón de búsqueda */
	balloon('#keyword', '#keywordBalloon');
	balloon('#locality', '#localityBalloon');
	balloon('#calleAltura', '#addressBalloon');
	
	// $form es el formulario jquery a chequear y forzarChequeoCercania un booleano
	var preetySubmit = function ($form, forzarChequeoCercania){
		if ($form.length > 0) $form.submit(function(){
			
			// si se fuerza la búsqueda por cercanía o si hay un valor para calle y altura, debe hacerse la búsqueda por cercania
			var chequearCercania = forzarChequeoCercania; /* || $("input#calleAltura").length > 0 && $("input#calleAltura").val().length > 0;
			El || no va más porque este preetySubmit se usa sólo en los formularios y no en las facetas como se usaba antes
			*/
			$form.find("input").each(function(){
				$(this).blur();
			});
			
			var localityInput = $("input#locality").val();
			var localityCookie = $.cookie(COOKIE_SEARCH_LOCALITY);
			 	
			// guardar la localidad en una cookie
			if (localityInput.length > 0){
				$.cookie(COOKIE_SEARCH_LOCALITY, localityInput, COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
			}else{
				// limpiar las cookies de localidad y id de localidad
				$.cookie(COOKIE_SEARCH_LOCALITY, "", COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
				$.cookie(COOKIE_SEARCH_LOCALITYID, "", COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
			}
			
			//si cambio por algo escrito a mano pero no se quito el foco del campo donde
			if(localityInput.length > 0 && GLOBAL_LOCALITY  != localityInput){
				$.cookie(COOKIE_SEARCH_LOCALITYID, "", COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
			}else // si la localidad cambió, o si no cambio y ya tenia un id, guardo la cookie con el ID nuevamente
				if (GLOBAL_LOCALITY_CHANGED || (!GLOBAL_LOCALITY_CHANGED && GLOBAL_LOCALITY_ID!=null && GLOBAL_LOCALITY_ID!="")){
				if (GLOBAL_LOCALITY_ID == null){
					GLOBAL_LOCALITY_ID = "";
				}
				$.cookie(COOKIE_SEARCH_LOCALITYID, GLOBAL_LOCALITY_ID, COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
			}
			
			var key = $("input#keyword").val();
			var calleAltura = $("input#calleAltura").val();
			var provincia = $("input#locality").val();
			if (chequearCercania){
				if (calleAltura.length > 0){
					// se guarda la cookie para preservar el texto sin normalizar
					$.cookie(COOKIE_SEARCH_PROXIMITY, calleAltura, COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
				}
			}
			
			if(key.length < 2){
				abrirPopupError($("#searchMinimoPopup"));
				return false;
			}else if(chequearCercania && (calleAltura.length == 0 || (provincia.length == 0 && page=="homePorCercania" ))){
			    	// La localidad la toma de localityBreadCrumWithoutQuotes que esta siempre cargada
			    	// (sino no cargaría la solapa)
				abrirPopupError($("#validateCalleAltura"));
				return false;
			}
			
			$("#buscar").attr("disabled","disabled");
			$("#buscarCercania").attr("disabled","disabled");
			
			if (chequearCercania && calleAltura.length > 0 && (provincia.length > 0 || localityBreadCrum.length > 0)){
				// hacer la petición a google para resolver las direccions
				var geocoder = new google.maps.Geocoder();
				var address=getGeocodeAddress();
				geocoder.geocode( { 'address': address }, 
				    function(results, status) {
					    if (status == google.maps.GeocoderStatus.OK) {
					    	// estandarizar la latitud y longitud (ver http://groups.google.com/group/google-maps-js-api-v3/browse_thread/thread/29cbde36c418e54d/8140167681fa8365?lnk=gst&q=geometry)
					    	for (i in results){
					    	    var geometry = results[i].geometry;
					    	    results[i].geometry.location.latitude = geometry.location.lat();
					    	    results[i].geometry.location.longitude = geometry.location.lng();
					    	}
					    	// setear el valor en el hidden del formulario
					    	$("#gmapsXmlResponse").val(JSON.stringify(results));
					    } else {
					    	$("#gmapsXmlResponse").val("Geocoder FAIL. Response status = "+status+
					    			". Respuesta obtenida: \n"+JSON.stringify(results));
					    }
					    doSubmit($form,forzarChequeoCercania);
					});				
			} else {
				//ante una nueva busqueda limpio la faceta de provincia
				$.cookie(COOKIE_MOSTRAR_PROVINCIA, "", COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
				doSubmit($form,forzarChequeoCercania);
			}
			return false;
		});
	}
	// cambiar el submit de los formularios para que sea preety
	preetySubmit($("#searchFormCercania"), true);
	preetySubmit($("#searchForm"), false);
	
	// habilitar nuevamente el botón buscar (MNP-118)
	$("#buscar").attr("disabled", false);
	
	// toggle suggest
	$("#tab #btOpenSuggest").click(function(){
		var suggest = $.Yell.lastSuggestFocused;
		if (suggest.isOpen()){
			suggest.closeSuggest();
		} else {
			suggest.$el.focus();
			suggest.openSuggest();
		}
	});
	
	$("input#buscar").focus(function(){
		suggest_location.getYell_Suggester().closeSuggest();
	});
	
	if ($("#sort").length > 0){
		$("#sort a").click(function(){
			$.cookie(COOKIE_SEARCH_NAME, GLOBAL_KEYWORD, COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
		});
	}
	$("input#buscarCercania").click(function(){
		$.cookie(COOKIE_FILTER_NAME,"expand-results", COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
	});
	
});

function prenderCategorias(){
	$("#keyword").attr("value","");
	$("#keyword").css("display","none");
	$("#labelKeyword").css("display","none");
	$("#alojamientos").css("display","inline");
}

/* 
 * Crea un suggest que observe el input watch y se despliegue en displayOn dentro de container
 * Los tres parámetros deben ser objetos JQuery
 * Sobre el elemento watch se disparan los eventos del suggest para que otros puedan escuchar
 * La función retorna el watch para que se pueda guardar `como` una referencia a suggester y 
 * escuchar sus eventos. Se puede obtener el suggest nuevamente con el método getYell_Suggester
 * Depende del script cookies_utils.js
 */
(function($){
    if(!$.Yell){
        $.Yell = new Object();
        
        /* Acá se definen algunas propiedades globales a los suggests */
        // leer de la cookie
        $.Yell.isSuggestEnabled = $.cookie(COOKIE_SEARCH_NAME+"_suggest");
        // caso particular cuando la cookie no está seteada
        if ($.Yell.isSuggestEnabled == null) {
        	$.Yell.isSuggestEnabled = true;
        }
        // obtener el último sobre el que se hizo foco
        $.Yell.lastSuggestFocused = null;
    };
    
    $.Yell.Suggester = function(el, container, displayOn, suggestUrl, options){
        // To avoid scope issues, use 'base' instead of 'this'
        // to reference this class from internal events and functions.
        var base = this;
        if ($.Yell.lastSuggestFocused == null){
        	$.Yell.lastSuggestFocused = base;
        }
        
        // Access to jQuery and DOM versions of element
        base.$el = $(el);
        base.el = el;
        // timer para disparar la búsqueda
    	base.lastKeyTimer = 0;
    	// variable para saber si tiene el foco
    	base.isFocused = false;
        
        // Add a reverse reference to the DOM object
        base.$el.data("Yell.Suggester", base);
        
        // inicialización
        base.init = function(){
            base.container = container;
            base.displayOn = displayOn;
            base.suggestUrl = suggestUrl;
            
            base.options = $.extend({},$.Yell.Suggester.defaultOptions, options);
	        // registrar funciones
            // las teclas arriba y abajo en IE deben ser capturadas en el evento keydown
           
            $.browser.chrome = /chrome/.test(navigator.userAgent.toLowerCase());
            if ($.browser.msie || $.browser.chrome){
	        	base.$el.keydown(function(event){
	            	var code = event.which == 0 ? event.keyCode : event.which;
	            	// flechas arriba y abajo
	            	if(code == 40 || code == 38){
	        			base.handleNavegacionPorTeclas(event);
	        		}
	            	else if(code == 8 || code == 46){
	        			// retroceso o delete se manejan en keydown (para IE)
	        			if(base.reevaluateSuggestByKeyCode(event))
	        				base.updateSuggest(event);	        		
	        		}
	            });
            }
            // @ref http://api.jquery.com/keypress/
        	base.$el.keypress(function(event){
        		var code = event.which == 0 ? event.keyCode : event.which;
        		// flechas arriba y abajo (excepto para IE)
        		if(!$.browser.msie && (code == 40 || code == 38)){
        			base.handleNavegacionPorTeclas(event);
        		} else if (code == 13) {
        			// si presionó Enter no hacer submit
        			event.preventDefault();
        			event.stopPropagation();
        			// chequear si hay una sugerencia seleccionada visible
        			if (base.displayOn.find(".suggestSelected").length > 0 && !base.displayOn.hasClass("suggestOff")){
        				// ejecutar lo mismo que al hacer click
        				base.displayOn.find(".suggestSelected").click();
        			} else {
        				// submitear si el usuario presiona enter sin navegar por teclado
        				$(base.el.form).submit();
        			}
        		} else if (base.reevaluateSuggestByKeyCode(event)){
        			base.updateSuggest(event);
        		}
        	});
        	
        	// otros códigos que deben ser tratados en el evento "key up"
        	base.$el.keyup(function(event){
        		var code = event.which == 0 ? event.keyCode : event.which;
            	// ctrl+v (pegar)
            	if (code == 17){
            		base.updateSuggest(event);
            	}
        	});
        	
        	base.$el.focus(function(){
        		// si el que estaba seleccionado perdió el foco, cerrarlo
        		if ($.Yell.lastSuggestFocused.isOpen() && !$.Yell.lastSuggestFocused.isFocused){
        			$.Yell.lastSuggestFocused.closeSuggest();
        		}
        		// actualizar cual fue el último que obtuvo el foco
        		$.Yell.lastSuggestFocused = base;
        		base.isFocused = true;
        	}).blur(function(){
        		base.isFocused = false;
        	});
        };
        
        /*
         * Funciones
         */
        
        /* updateSuggest: cuando llega el evento, la palabra aún no fue actualizada
	     * por eso se chequea por menor igual a 3 y mayor igual a 2
         */
        base.updateSuggest = function(event){
        	var code = event.which == 0 ? event.keyCode : event.which;
        	if (base.isEnabled()){
	    		var keyword = base.$el.val();
	    		if (code==9){
	    			// cuando se presiona tab no hay que traer el suggest
	    			clearTimeout(base.lastKeyTimer);
	    		} else if(keyword.length >= 2){
	    			base.$el.trigger('suggestChanged');
	    			clearTimeout(base.lastKeyTimer);
	    			base.lastKeyTimer = setTimeout(base.actualizarSuggest, 300);
	    		} else {
	    			base.$el.trigger('suggestChanged');
	    			clearTimeout(base.lastKeyTimer);
	    			base.closeSuggest();
	    		}
        	}
    	}
        
        /* actualizarSuggest: cuando llega el evento, la palabra ya fue actualizada
         */
        base.actualizarSuggest = function(){
        	// vuelvo a chequear si está habilitado por si quedó encolada una invocación
        	if (base.isEnabled()){
	    		// vuelvo a obtener la keyword por que se puedo modificar de cuando programé la función
	    		var keyword = base.$el.val();
	    		// vuelvo a chequear el largo de la keyword porque se pudo modificar
	    		// y además chequeo si tengo el foco para no abrir cuando se está haciendo otra cosa
	    		if(keyword.length >= 3 && base.isFocused){
	    			$.get(base.suggestUrl + encodeURIComponent(keyword),
	    				function(data) {
	    					if (!base.isFocused) return;
	    					base.displayOn.html(data);
	    					//chequeo si no trajo resultados el motor del suggest
	    					if(base.displayOn.find("ul").find("li").length >0){
	    						base.openSuggest();
	    						// agregar funcionalidad al contenido nuevo
		    					// listaSuggest debe ser devuelto en data
		    					var scrollParameters = {scrollbarWidth:14, showArrows:true, arrowSize:0, wheelSpeed:10, dragMinHeight:18, dragMaxHeight:20};
		    					base.displayOn.find(".listaSuggest").jScrollPane(scrollParameters);
		    					base.displayOn.find(".listaSuggest li").mouseover(function(){
		    						base.displayOn.find(".listaSuggest li.suggestSelected").removeClass("suggestSelected");
		    				        $(this).addClass("suggestSelected");
		    					}).mouseout(function(){
		    					      $(this).removeClass("suggestSelected");
		    					}).click(function(){
		    						var friendlyValue = $(this).find(".friendlyValue");
		    						var friendly = friendlyValue.text();
		    						if(friendly!=""){
		    							friendly = $.trim(friendly);
		    						}
		    						base.$el.trigger('suggestSelected', {
		    							friendlySelected: friendly,
		    							valueSelected: $(this).attr("id"), 
		    							classSelected: 
		    								// comprobamos si es un suggest de localidad o de palabra
		    								// Nota: esto no debería ser dependiente del suggest en cuestión
		    								(($(this).attr("class") != null && $(this).attr("class").match(/tipo./))? 
		    										$(this).attr("class").substring(4,5) // se obtiene el número del tipo del nombre de la clase: tipo1, tipo2, tipo3
		    										:$(this).find('input').val() // se busca el id de la localidad
		    								)
	    							});
		    						base.closeSuggest();
		    					});
		    					
		    					base.displayOn.find(".onoff").click(function(){
		    						base.closeSuggest();
		    						base.disable();
		    					});
	    					}else{
	    						base.closeSuggest();
	    					}
	    				});
	    		} else {
	    			//base.container.addClass("suggestOff");
	    			base.closeSuggest();
	    		}
        	}
    	}
        
        // esta función evalúa si al presionar una tecla el suggest debe consultarse de nuevo
        base.reevaluateSuggestByKeyCode = function(event){
        	var code = event.which==0?event.keyCode:event.which;
        	// 8 == retroceso
        	// 9 == tab
        	return  code == 8 || code == 9 || code == 32 || (code >= 46 && code <= 90) ||  (code >= 78 && code <= 122) || 
        			(code >= 186 && code <= 192) || (code >= 219 && code <= 222) || code == 225 || code == 233 ||
        			code == 237 || code == 243 || code == 250;
        }

        base.handleNavegacionPorTeclas = function(event) {
        	if (base.isEnabled()){
        		var code = event.which == 0 ? event.keyCode : event.which;
	    		var prevSelected = base.displayOn.find(".suggestSelected");
	    		var newSelected;
	    		
	    		// key down
	    		if(code == 40){
	    			if (prevSelected.length == 0){
	    				base.$el.data("dataInput", base.$el.val());
	    				newSelected = base.displayOn.find("li:first");
	    				newSelected.focus();
	    			} else {
	    				newSelected = prevSelected.next();
	    			}
	    			
	    			if (newSelected.length == 0){
	    				// Si se va para abajo estando en el último el nuevo seleccionado sigue siendo el último
	    				newSelected = prevSelected;
	    			}
	    		}
	    		// key up
	    		if(code == 38){
	    			if (prevSelected.length == 0){
	    				// no seleccionar nada
	    				newSelected = base.displayOn.find("li:first").prev();
	    			} else {
	    				newSelected = prevSelected.prev();
	    			}
	    			if (newSelected.length == 0){
	    				// Si se va hacia arriba estando en el primero, no se hace nada
	    				base.$el.val(base.$el.data("dataInput"));
	    			}
	    		}
	    		// for both up and down:
	    		prevSelected.removeClass("suggestSelected");
	    		if (newSelected.length > 0){
	    			newSelected.addClass("suggestSelected");
    				base.$el.val(newSelected.attr("id"));
    				if (!base.isLiVisible(newSelected)){
    					// esto no está muy lindo, pero hace su trabajo 
    					base.displayOn.find("ul")[0].scrollTo(
							newSelected.position().top
						);
    				}
	    		}
        	}
    	}
        
        base.closeSuggest = function(){
        	base.container.addClass("suggestOff").removeClass("suggestOn");
        	base.container.removeClass("open").addClass("close");
        	base.displayOn.addClass("suggestOff").removeClass("suggestOn");
        	$("#main").removeClass("suggestOppen");
        }
        
        base.isOpen = function(){
        	return base.isEnabled() && !base.container.hasClass("suggestOff");
        }
        
        base.openSuggest = function(){
        	// si no estaba habilitado lo habilito y lo actualizo
        	if (!base.isEnabled()) { 
        		base.enable();
        		base.actualizarSuggest();
    		}
        	var hasResults = base.displayOn.find("ul li").length >0;
        	if (base.isFocused){
        	    base.container.removeClass("suggestOff").addClass("suggestOn");
        	    base.container.removeClass("close").addClass("open");
        	    if(hasResults){
        		base.displayOn.removeClass("suggestOff").addClass("suggestOn");
        	    }
        	    base.$el.focus();
        	}
        	if(hasResults){
        	    $("#main").addClass("suggestOppen");
        	}
        }
        
        base.disable = function(){
        	$.Yell.isSuggestEnabled = false;
        	$.cookie(COOKIE_SEARCH_NAME+"_suggest", false, COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
        }
        
        base.enable = function(){
        	$.Yell.isSuggestEnabled = true;
        	$.cookie(COOKIE_SEARCH_NAME+"_suggest", true, COOKIE_CROSS_SUB_DOMAIN_OPTIONS);
        }
        
        base.isEnabled = function(){
        	return $.Yell.isSuggestEnabled == true || "true" == $.Yell.isSuggestEnabled;
        }
        
        base.isLiVisible = function(elem){
        	var lista = $(base.displayOn).find("ul");
        	var scroller = lista.parent();
            var docViewTop = lista.position().top * -1;
            var docViewBottom = docViewTop + scroller.height();

            var elemTop = $(elem).position().top;
            var elemBottom = elemTop + $(elem).height();
            
            return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom)
              && (elemBottom <= docViewBottom) &&  (elemTop >= docViewTop) );
        }
        
        // Run initializer
        base.init();
    };
    
    $.Yell.Suggester.defaultOptions = {
    };
    
    $.fn.Yell_Suggester = function(container, displayOn, options){
        return this.each(function(){
            (new $.Yell.Suggester(this, container, displayOn, options));
        });
    };
    
	// Getter para obtener el suggester a partir del elemento
	$.fn.getYell_Suggester = function(){
		return this.data("Yell.Suggester");
	};
})(jQuery);
