function isValidDate(strDate) {
     // (\d{1,2}) means 4 or 12
     // (\/|-) means either (/ or -), 4-12 or 4/12 
     // NOTE: we have to escape / (\/)
     // or else pattern matching will interpret it to mean the end instead of the literal "/"
     // \2 use the 2nd placeholder (\/|-) "here"
     // (\d{2}|\d{4}) means 02 or 2002
     var datePat = /^(\d{1,2})(\/|-)(\d{1,2})\2(\d{2}|\d{4})$/;
     var matchArray = strDate.match(datePat);

     if (matchArray == null) return false;

     // matchArray[0] will be the original entire string, for example, 4-12-02 or 4/12/2002
     var month = matchArray[3];     // (\d{1,2}) - 1st parenthesis set - 4
     var day = matchArray[1];         // (\d{1,2}) - 3rd parenthesis set - 12
     var year = matchArray[4];        // (\d{2}|\d{4}) - 5th parenthesis set - 02 or 2002

     if (month < 1 || month > 12) return false;
     if (day < 1 || day > 31) return false;
     if ((month == 4 || month == 6 || month==9 || month == 11) && day == 31) return false;
     if (month == 2) {
          var isleap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));

          if (day > 29 || (day == 29 && !isleap)) return false;
     }
     return true;
}

function scorri_slot(id){
	var n_pixel_delta = 150;
	var n_li = $('#id_slot_' + id + ' li').length;
	h_tot = n_li * 25;
	var pos = $('#id_slot_' + id).position();
	var mtop = $('#id_slot_' + id).css('marginTop').replace('px','');
	
	if(mtop == -(h_tot-250)){
		mtop = n_pixel_delta;
	}
	
	mtop = mtop - n_pixel_delta;

		

	if((h_tot-250)>(-mtop)){
		$('#id_slot_' + id).animate( { marginTop: mtop + "px"}, 2000 );	
	}else{
		mtop = -(h_tot-250);
		$('#id_slot_' + id).animate( { marginTop: mtop + "px"}, 2000 );	
	}
	

	

}
function pop_under_voli(){
	var winfeatures="left=150,top=200,width=510,height=326,scrollbars=0,resizable=1,toolbar=0,location=0,menubar=0,status=1,directories=0";

	//
	/*http://www.lastminute.com/site/commercial/ratefinder_popunder.html?skin=itit.lastminute.com&s_theDomain=www.it.lastminute.com&s_selectedForm=flights_search&a_formPrefix=air&a_fromDate=20&a_fromMonth=6&startYear=2009&endDate=4&endMonth=7&endYear=2009&s_theAdtoShow=ad3&s_dest=ZZZ&s_ratefinderLocale=it_IT&noOfAdults=1&orig=oli_(
	*/
	var tmp=$('#depDateFrom')[0].value.split('/');	
	var a_fromDate = tmp[0];	
	var a_fromMonth = tmp[1];
	var startYear =  tmp[2];
	var s_orig = $('#volo_partenza')[0].value;
	

	var tmp=$('#depDateTo')[0].value.split('/');	
	
	var a_endDate = tmp[0];
	var a_endMonth = tmp[1];
	var endYear =  tmp[2];
	var s_dest = $('#volo_arrivo')[0].value;
	
	var adults = $('#adulti').val();
		
	var s_urlString = 'http://www.lastminute.com/site/commercial/ratefinder_popunder.html?skin=itit.lastminute.com&s_theDomain=www.it.lastminute.com&s_selectedForm=flights_search&a_formPrefix=air&a_fromDate='+a_fromDate+'&a_fromMonth='+a_fromMonth+'&startYear='+startYear+'&endDate='+a_endDate+'&endMonth='+a_endMonth+'&endYear='+endYear+'&s_theAdtoShow=ad3&s_dest='+s_dest+'&s_ratefinderLocale=it_IT&noOfAdults='+adults+'&orig='+s_orig;


	 win2=window.open(s_urlString,"adWindow",winfeatures);
	 win2.blur();
//	 win2.screenX = 150;
//	 win2.screenY = 200;
	 window.focus();

}
/*
function pop_under_voli(){
	var winfeatures="left=150,top=200,width=510,height=326,scrollbars=0,resizable=1,toolbar=0,location=0,menubar=0,status=1,directories=0";

	//
//http://www.lastminute.com/site/commercial/ratefinder_popunder.html?skin=itit.lastminute.com&s_theDomain=www.it.lastminute.com&s_selectedForm=flights_search&a_formPrefix=air&a_fromDate=20&a_fromMonth=6&startYear=2009&endDate=4&endMonth=7&endYear=2009&s_theAdtoShow=ad3&s_dest=ZZZ&s_ratefinderLocale=it_IT&noOfAdults=1&orig=oli_(
 
	var tmp=$('#depDateFrom')[0].value.split('/');	
	var a_fromDate = tmp[0];	
	var a_fromMonth = tmp[1];
	var startYear =  tmp[2];
	

	var tmp=$('#depDateTo')[0].value.split('/');	
	
	var a_endDate = tmp[0];
	var a_endMonth = tmp[1];
	var endYear =  tmp[2];
	var s_dest = $('#volo_arrivo')[0].value;
		
	var s_urlString = 'http://www.lastminute.com/site/commercial/ratefinder_popunder.html?skin=itit.lastminute.com&s_theDomain=www.it.lastminute.com&s_selectedForm=flights_search&a_formPrefix=air&a_fromDate='+a_fromDate+'&a_fromMonth='+a_fromMonth+'&startYear='+startYear+'&endDate='+a_endDate+'&endMonth='+a_endMonth+'&endYear='+endYear+'&s_theAdtoShow=ad3&s_dest='+s_dest+'&s_ratefinderLocale=it_IT';

	 win2=window.open(s_urlString,"adWindow",winfeatures);
	 win2.blur();
//	 win2.screenX = 150;
	// win2.screenY = 200;
	 window.focus();

}
*/
function select_content(id_a,id_contenuto){
	$('#main .box_html_largo .contenuti').hide();
	$('#main .box_html_largo #' + id_contenuto).show();
	
	$('#main .box_html_largo li a').removeClass('attivo');
	$('#' + id_a).addClass('attivo');
}

function espandi_form_voli(){
	$('#search_dx_riassunto').hide();
	$('#search_dx_form').show(); 
	jQuery("#volo_arrivo").suggest("/select_citta_voli.php",{delay: 50 });	
	jQuery("#volo_partenza").suggest("/select_citta_voli.php",{delay: 50 });
	jQuery("#vacanza").suggest("/select_dest.php",{delay: 50 });
	 
}

$(document).ready(function(){
	$("#depDateFrom").datepicker(
		{
			monthNames: ['gennaio','febbraio','marzo','aprile','maggio','giugno','luglio','agosto','settembre','ottobre','novembre','dicembre'],
			dayNamesMin: ['Do','Lu', 'Ma', 'Me', 'Gio', 'Ve', 'Sa'] ,
			minDate: '+1d',
			firstDay: '+1',
			dateFormat: 'd/m/yy',
			showOn: 'button', buttonImage: '/immagini/ico_calendar.gif', buttonImageOnly: true,
			onSelect: function(dateText) {
				var arr=dateText.split('/');
				$('#da_gg')[0].selectedIndex = parseInt(arr[0])-1;
				$('#da_mm')[0].selectedIndex = parseInt(arr[1])-1;
			}

			}
		);
	$("#depDateTo").datepicker(
		{
			monthNames: ['gennaio','febbraio','marzo','aprile','maggio','giugno','luglio','agosto','settembre','ottobre','novembre','dicembre'],
			dayNamesMin: ['Do','Lu', 'Ma', 'Me', 'Gio', 'Ve', 'Sa'] ,
			 minDate: '+1d',
		dateFormat: 'd/m/yy',
					firstDay: '+1',
			showOn: 'button', buttonImage: '/immagini/ico_calendar.gif', buttonImageOnly: true,
			onSelect: function(dateText) {
				var arr=dateText.split('/');
			 
				$('#a_gg')[0].selectedIndex = parseInt(arr[0]);
				$('#a_mm')[0].selectedIndex = parseInt(arr[1]);
			}

		}
	);
	
	$("#depDateFrom_v").datepicker(
		{
			monthNames: ['gennaio','febbraio','marzo','aprile','maggio','giugno','luglio','agosto','settembre','ottobre','novembre','dicembre'],
		dayNamesMin: ['Do','Lu', 'Ma', 'Me', 'Gio', 'Ve', 'Sa'] ,
			 minDate: '+1d',
		dateFormat: 'd/m/yy',
					firstDay: '+1',
			showOn: 'button', buttonImage: '/immagini/ico_calendar.gif', buttonImageOnly: true,
			onSelect: function(dateText) {
				var arr=dateText.split('/');
				$('#da_gg_v')[0].selectedIndex = parseInt(arr[0])-1;
				$('#da_mm_v')[0].selectedIndex = parseInt(arr[1])-1;
			}

			}
		);
	$("#depDateTo_v").datepicker(
		{
			monthNames: ['gennaio','febbraio','marzo','aprile','maggio','giugno','luglio','agosto','settembre','ottobre','novembre','dicembre'],
			dayNamesMin: ['Do','Lu', 'Ma', 'Me', 'Gio', 'Ve', 'Sa'] ,
			 minDate: '+1d',
			 			firstDay: '+1',
			dateFormat: 'd/m/yy',
			showOn: 'button', buttonImage: '/immagini/ico_calendar.gif', buttonImageOnly: true,
			onSelect: function(dateText) {
				var arr=dateText.split('/');
	 
				$('#a_gg_v')[0].selectedIndex = parseInt(arr[0]);
				$('#a_mm_v')[0].selectedIndex = parseInt(arr[1]);
			}

		}
	);
	
	$("#depDateFrom_dp").datepicker(
		{
			monthNames: ['gennaio','febbraio','marzo','aprile','maggio','giugno','luglio','agosto','settembre','ottobre','novembre','dicembre'],
			dayNamesMin: ['Do','Lu', 'Ma', 'Me', 'Gio', 'Ve', 'Sa'] ,
			 minDate: '+1d',
			 			firstDay: '+1',
			dateFormat: 'd/m/yy',
			showOn: 'button', buttonImage: '/immagini/ico_calendar.gif', buttonImageOnly: true,
			onSelect: function(dateText) {
				var arr=dateText.split('/');
				$('#da_gg_dp')[0].selectedIndex = parseInt(arr[0])-1;
				$('#da_mm_dp')[0].selectedIndex = parseInt(arr[1])-1;
			}

			}
		);
	$("#depDateTo_dp").datepicker(
		{
			monthNames: ['gennaio','febbraio','marzo','aprile','maggio','giugno','luglio','agosto','settembre','ottobre','novembre','dicembre'],
			dayNamesMin: ['Do','Lu', 'Ma', 'Me', 'Gio', 'Ve', 'Sa'] ,
			 minDate: '+1d',
			 			firstDay: '+1',
		dateFormat: 'd/m/yy',
			showOn: 'button', buttonImage: '/immagini/ico_calendar.gif', buttonImageOnly: true,
			onSelect: function(dateText) {
				var arr=dateText.split('/');
				$('#a_gg_dp')[0].selectedIndex = parseInt(arr[0]);
				$('#a_mm_dp')[0].selectedIndex = parseInt(arr[1]);
			}

		}
	);
	
	/**/
	$('a.cut').each(
		function(){
			this.innerHTML = this.innerHTML.substring(0,18)+"...";
		}
	)
	/**/
	
});

function aggiorna_date_vacanze(anno_corrente){
	if(isValidDate($('#da_gg')[0].value + '/' + $('#da_mm')[0].value + '/' + anno_corrente)){
		$("#depDateFrom")[0].value=$('#da_gg')[0].value + '/' + $('#da_mm')[0].value + '/' + anno_corrente;
	}
	else{
		$("#dialog")[0].innerHTML = "<br>Data di partenza non valida";
		$("#dialog")[0].title="Errore";
		$("#dialog").dialog(
				{modal: true, position: ['center',400]}
			);
		$("#dialog").dialog('open');
		$('#da_gg')[0].selectedIndex = 1;
		return false;
	}
	
	if(isValidDate($('#a_gg')[0].value + '/' + $('#a_mm')[0].value + '/' + anno_corrente))
		$("#depDateTo")[0].value=$('#a_gg')[0].value + '/' + $('#a_mm')[0].value + '/' + anno_corrente;
	else{
		$("#dialog")[0].innerHTML = "<br>Data di ritorno non valida";
		$("#dialog")[0].title="Errore";
		$("#dialog").dialog(
				{modal: true, position: ['center',400]}
			);
		$("#dialog").dialog('open');
		$('#a_gg')[0].selectedIndex = 1;
		return false;
	}
	
}
function aggiorna_date_vacanze_v(anno_corrente){
	d1 = $('#da_gg_v')[0].value + '/' + $('#da_mm_v')[0].value + '/' + anno_corrente;
	d2 = $('#a_gg_v')[0].value + '/' + $('#a_mm_v')[0].value + '/' + anno_corrente;
	if(isValidDate(d1)){
		$("#depDateFrom_v")[0].value= d1;
	}else{
		$("#dialog")[0].innerHTML = "<br>Data di partenza non valida";
		$("#dialog")[0].title="Errore";
		$("#dialog").dialog(
				{modal: true, position: ['center',400]}
			);
		$("#dialog").dialog('open');
		$('#da_gg_v')[0].selectedIndex = 0;
	}
	
	if(isValidDate(d2)){
		$("#depDateTo_v")[0].value=d2;
	}else{
		$("#dialog")[0].innerHTML = "<br>Data di partenza non valida";
		$("#dialog")[0].title="Errore";
		$("#dialog").dialog(
				{modal: true, position: ['center',400]}
			);
		$("#dialog").dialog('open');
		$('#a_gg_v')[0].selectedIndex = 1;
	}
}
function aggiorna_date_vacanze_dp(anno_corrente){
	d1=$('#da_gg_dp')[0].value + '/' + $('#da_mm_dp')[0].value + '/' + anno_corrente;
	
	
	if(isValidDate(d1)){
		$("#depDateFrom_dp")[0].value=d1;
	}else{
		$("#dialog")[0].innerHTML = "<br>Data di partenza non valida";
		$("#dialog")[0].title="Errore";
		$("#dialog").dialog(
				{modal: true, position: ['center',400]}
			);
		$("#dialog").dialog('open');
		$('#da_gg_dp')[0].selectedIndex = 1;
	}	

	d2=$('#a_gg_dp')[0].value + '/' + $('#a_mm_dp')[0].value + '/' + anno_corrente;	
	if(isValidDate(d2)){
		$("#depDateTo_dp")[0].value=d2;
	}else{
		$("#dialog")[0].innerHTML = "<br>Data di ritorno non valida";
		$("#dialog")[0].title="Errore";
		$("#dialog").dialog(
				{modal: true, position: ['center',400]}
			);
		$("#dialog").dialog('open');
		$('#a_gg_dp')[0].selectedIndex = 1;
	}
	
}


function cerca_dp(){
	var msg="";
	if(!$('#destination')[0].value){
		msg="Indicare la <b>destinazione</b>";
	}
 
	var oggi = new Date();
	var arr_andata = $('#depDateFrom_dp')[0].value.split('/');
 	var arr_ritono = $('#depDateTo_dp')[0].value.split('/');

			
	var andata = new Date(arr_andata[2], arr_andata[1]-1, arr_andata[0]);
	var ritorno = new Date(arr_ritono[2], arr_ritono[1]-1, arr_ritono[0]);
 
	if(andata.getTime()<oggi.getTime()){
		msg=msg+"<br>Data di partenza non valida";		
	}
 	
	if(andata.getTime()>ritorno.getTime()){
		msg=msg+"<br>Data di ritorno non valida";		
	}
	if(msg!=""){
		$("#dialog")[0].innerHTML = msg;
		$("#dialog")[0].title="Errore";
		$("#dialog").dialog(
				{modal: true, position: ['center',400]}
			);
		$("#dialog").dialog('open');
		return false;
	}
 		$('#depDateFrom_dp').attr("disabled", true);		
		$('#depDateTo_dp').attr("disabled", true);

	$('#form_search_volo_hotel')[0].submit();	
}

function cerca_vacanze(){
	var msg="";
	if(!$('#vacanza')[0].value){
		msg="Indicare la <b>destinazione</b>";
	}
 
	var oggi = new Date();
	var arr_andata = $('#depDateFrom_v')[0].value.split('/');
 
	
	var andata = new Date(arr_andata[2], arr_andata[1]-1, arr_andata[0]);
 
	if(andata.getTime()<oggi.getTime()){
		msg=msg+"<br>Data di partenza non valida";		
	}
 	
	if(msg!=""){
		$("#dialog")[0].innerHTML = msg;
		$("#dialog")[0].title="Errore";
		$("#dialog").dialog(
				{modal: true, position: ['center',400]}
			);
		$("#dialog").dialog('open');
		return false;
	}
 
	$('#form_search_vacanze')[0].action="/cerca_vacanze.php";
	$('#form_search_vacanze')[0].submit();	
}

function cerca_voli(forza_esterno){
	if(forza_esterno == undefined)
		forza_esterno = false;
	
	var msg="";
	if((!$('#volo_partenza')[0].value)&&($('#OW').val()!=1)){
		msg="Indicare <b>citt&agrave; di partenza</b>";
	}
	if(!$('#volo_arrivo')[0].value){
		msg=msg+"<br>Indicare <b>citt&agrave; di arrivo</b>";		
	}
	
	var oggi = new Date();
	var arr_andata = $('#depDateFrom')[0].value.split('/');
	var arr_ritono = $('#depDateTo')[0].value.split('/');
	
	var andata = new Date(arr_andata[2], arr_andata[1]-1, arr_andata[0]);
	var ritorno = new Date(arr_ritono[2], arr_ritono[1]-1, arr_ritono[0]);
	
	if(andata.getTime()<oggi.getTime()){
		msg=msg+"<br>Data di partenza non valida";		
	}
	if(!$('#sola_andata')[0].checked){
		if(andata.getTime()>ritorno.getTime()){
			msg=msg+"<br>Data di ritorno non valida";		
		}
	}	
	if(msg!=""){
		$("#dialog")[0].innerHTML = msg;
		$("#dialog")[0].title="Errore";
		$("#dialog").dialog(
				{modal: true, position: ['center',400]  }
			);
		$("#dialog").dialog('open');
		return false;
	}
	if(($('#sola_andata')[0].checked)&&($('#OW').val()!=1) && forza_esterno == false){
		// ricerca interna
		var action = "/cerca_voli.php";
	}else{
		if($('#volo_partenza')[0].value.toLowerCase()=="oporto")
			$('#volo_partenza')[0].value = "Porto";
		if($('#volo_arrivo')[0].value.toLowerCase()=="oporto")
			$('#volo_arrivo')[0].value = "Porto";
		// ricerca su istituzionale
		//if(forza_esterno == false)
			$('#sola_andata').attr("disabled", true);
		if($('#volo_partenza').value=="")	
			$('#volo_partenza').attr("value", "Milano");
		//alert("Changed");
		$('#depDateTo').attr("disabled", true);		
		$('#depDateFrom').attr("disabled", true);
		$('#search_base').attr("disabled", true);
		
		//var action = "http://www.it.lastminute.com/site/viaggi/voli/transport-results.html";
		var action = "http://www.it.lastminute.com/site/viaggi/voli/interstitial_page.html"
		//	$('#form_search_voli')[0].target='_blank';	
	}
	pop_under_voli();
	
	$('#form_search_voli')[0].action=action;
	$('#form_search_voli')[0].submit();	
}

function extCheck(flag) {
	if(flag) {
		$('#OW').val(1);
		$('input[name=sola_andata]').attr('checked', true);
		//alert("EXT");
		cerca_voli();
	}
}


function solo_andata(is_solo_andata){
	if(is_solo_andata){
		$('#campi_ritorno').hide();
		$('#campi_ritorno2').hide();
	}else{
		$('#campi_ritorno').show();
		$('#campi_ritorno2').show();
	}		
}
function goto_voli(id_volo){
	var x=$('#url_selezionato_volo_' + id_volo)[0].value;
	window.document.location=x;
}

function reorder(o){
	$.ajax({
		type: "POST",
		url: "/_order.php",
		data: "o=" + o,
		success: function(msg){
			window.document.location.reload();
			}
		}
	);
}

function attiva_voli(){
 	switch_form(1);
 }
 
 function attiva_vacanze(){
 	switch_form(0);
 	jQuery("#vacanza").suggest("/select_dest.php",{delay: 50 });
 }

 function attiva_volo_hotel(){
 	switch_form(2);
 }
  
 function switch_form(voli){
 	if(voli==1){
 		$('#sel_form_voli')[0].checked=true;
	 	$('#form_ricerca_voli').show();
		$('#form_ricerca_viaggi').hide();
		$('#form_ricerca_volo_hotel').hide();
		$('#titolo-form-voli').show();
	 	$('#titolo-form-vacanze').hide();
	 	$('#titolo-form-volo-hotel').hide();
		$('#link_form_voli').addClass('attivo');
		$('#link_form_viaggi').removeClass('attivo');
		$('#link_form_volo_hotel').removeClass('attivo');
	}
	if(voli==0){
		$('#sel_form_vacanze')[0].checked=true;
	 	$('#form_ricerca_voli').hide();
	 	$('#form_ricerca_volo_hotel').hide();
		$('#form_ricerca_viaggi').show();
		$('#titolo-form-voli').hide();
	 	$('#titolo-form-vacanze').show();
	 	$('#titolo-form-volo-hotel').hide();
		$('#link_form_viaggi').addClass('attivo');
		$('#link_form_voli').removeClass('attivo');
		$('#link_form_volo_hotel').removeClass('attivo');
	}
	if(voli==2){
		$('#sel_form_hotel')[0].checked=true;
		$('#form_ricerca_volo_hotel').show();
	 	$('#form_ricerca_voli').hide();
		$('#form_ricerca_viaggi').hide();
		$('#titolo-form-voli').hide();
	 	$('#titolo-form-vacanze').hide();
	 	$('#titolo-form-volo-hotel').show();
		$('#link_form_volo_hotel').addClass('attivo');
		$('#link_form_voli').removeClass('attivo');
		$('#link_form_viaggi').removeClass('attivo');
	}
 }
 
function inviaform(form)
{ 
	if(!form.privacy.checked)
	{
	  alert( "Per procedere assicurati di aver accettato i termini e condizioni per l'iscrizione");
	  return false;
	}
	else
	{
	return true;
	}
}

function link_to(destination) {
	location.href = destination;
}


	
	/*
	 *	jquery.suggest 1.1 - 2007-08-06
	 *	
	 *	Uses code and techniques from following libraries:
	 *	1. http://www.dyve.net/jquery/?autocomplete
	 *	2. http://dev.jquery.com/browser/trunk/plugins/interface/iautocompleter.js	
	 *
	 *	All the new stuff written by Peter Vulgaris (www.vulgarisoip.com)	
	 *	Feel free to do whatever you want with this file
	 *
	 */
	
	(function($) {

		$.suggest = function(input, options) {
	
			var $input = $(input).attr("autocomplete", "off");
			var $results = $(document.createElement("ul"));

			var timeout = false;		// hold timeout ID for suggestion results to appear	
			var prevLength = 0;			// last recorded length of $input.val()
			var cache = [];				// cache MRU list
			var cacheSize = 0;			// size of cache in chars (bytes?)
			
			$results.addClass(options.resultsClass).appendTo('body');
				

			resetPosition();
			$(window)
				.load(resetPosition)		// just in case user is changing size of page while loading
				.resize(resetPosition);

			$input.blur(function() {
				setTimeout(function() { $results.hide() }, 200);
			});
			
			
			// help IE users if possible
			try {
				$results.bgiframe();
			} catch(e) { }


			// I really hate browser detection, but I don't see any other way
			if ($.browser.mozilla)
				$input.keypress(processKey);	// onkeypress repeats arrow keys in Mozilla/Opera
			else
				$input.keydown(processKey);		// onkeydown repeats arrow keys in IE/Safari
			



			function resetPosition() {
				// requires jquery.dimension plugin
				var offset = $input.offset();
				$results.css({
					top: (offset.top + input.offsetHeight) + 'px',
					left: offset.left + 'px'
				});
			}
			
			
			function processKey(e) {
				
				// handling up/down/escape requires results to be visible
				// handling enter/tab requires that AND a result to be selected
				if ((/27$|38$|40$/.test(e.keyCode) && $results.is(':visible')) ||
					(/^13$|^9$/.test(e.keyCode) && getCurrentResult())) {
		            
		            if (e.preventDefault)
		                e.preventDefault();
					if (e.stopPropagation)
		                e.stopPropagation();

					e.cancelBubble = true;
					e.returnValue = false;
				
					switch(e.keyCode) {
	
						case 38: // up
							prevResult();
							break;
				
						case 40: // down
							nextResult();
							break;
	
						case 9:  // tab
						case 13: // return
							selectCurrentResult();
							break;
							
						case 27: //	escape
							$results.hide();
							break;
	
					}
					
				} else if ($input.val().length != prevLength) {

					if (timeout) 
						clearTimeout(timeout);
					timeout = setTimeout(suggest, options.delay);
					prevLength = $input.val().length;
					
				}			
					
				
			}
			
			
			function suggest() {
			
				var q = $.trim($input.val());

				if (q.length >= options.minchars) {
					
					cached = checkCache(q);
					
					if (cached) {
					
						displayItems(cached['items']);
						
					} else {
					
						$.get(options.source, {q: q}, function(txt) {

							$results.hide();
							
							var items = parseTxt(txt, q);
							
							displayItems(items);
							addToCache(q, items, txt.length);
							
						});
						
					}
					
				} else {
				
					$results.hide();
					
				}
					
			}
			
			
			function checkCache(q) {

				for (var i = 0; i < cache.length; i++)
					if (cache[i]['q'] == q) {
						cache.unshift(cache.splice(i, 1)[0]);
						return cache[0];
					}
				
				return false;
			
			}
			
			function addToCache(q, items, size) {

				while (cache.length && (cacheSize + size > options.maxCacheSize)) {
					var cached = cache.pop();
					cacheSize -= cached['size'];
				}
				
				cache.push({
					q: q,
					size: size,
					items: items
					});
					
				cacheSize += size;
			
			}
			
			function displayItems(items) {
				
				if (!items)
					return;
					
				if (!items.length) {
					$results.hide();
					return;
				}
				
				var html = '';
				for (var i = 0; i < items.length; i++)
					html += '<li>' + items[i] + '</li>';

				$results.html(html).show();
				
				$results
					.children('li')
					.mouseover(function() {
						$results.children('li').removeClass(options.selectClass);
						$(this).addClass(options.selectClass);
					})
					.click(function(e) {
						e.preventDefault(); 
						e.stopPropagation();
						selectCurrentResult();
					});
							
			}
			
			function parseTxt(txt, q) {
				
				var items = [];
				var tokens = txt.split(options.delimiter);
				
				// parse returned data for non-empty items
				for (var i = 0; i < tokens.length; i++) {
					var token = $.trim(tokens[i]);
					if (token) {
						token = token.replace(
							new RegExp(q, 'ig'), 
							function(q) { return '<span class="' + options.matchClass + '">' + q + '</span>' }
							);
						items[items.length] = token;
					}
				}
				
				return items;
			}
			
			function getCurrentResult() {
			
				if (!$results.is(':visible'))
					return false;
			
				var $currentResult = $results.children('li.' + options.selectClass);
				
				if (!$currentResult.length)
					$currentResult = false;
					
				return $currentResult;

			}
			
			function selectCurrentResult() {
			
				$currentResult = getCurrentResult();
			
				if ($currentResult) {
					$input.val($currentResult.text());
					$results.hide();
					
					if (options.onSelect)
						options.onSelect.apply($input[0]);
						
				}
			
			}
			
			function nextResult() {
			
				$currentResult = getCurrentResult();
			
				if ($currentResult)
					$currentResult
						.removeClass(options.selectClass)
						.next()
							.addClass(options.selectClass);
				else
					$results.children('li:first-child').addClass(options.selectClass);
			
			}
			
			function prevResult() {
			
				$currentResult = getCurrentResult();
			
				if ($currentResult)
					$currentResult
						.removeClass(options.selectClass)
						.prev()
							.addClass(options.selectClass);
				else
					$results.children('li:last-child').addClass(options.selectClass);
			
			}
	
		}
		
		$.fn.suggest = function(source, options) {
		
			if (!source)
				return;
		
			options = options || {};
			options.source = source;
			options.delay = options.delay || 100;
			options.resultsClass = options.resultsClass || 'ac_results';
			options.selectClass = options.selectClass || 'ac_over';
			options.matchClass = options.matchClass || 'ac_match';
			options.minchars = options.minchars || 2;
			options.delimiter = options.delimiter || '\n';
			options.onSelect = options.onSelect || false;
			options.maxCacheSize = options.maxCacheSize || 65536;
	
			this.each(function() {
				new $.suggest(this, options);
			});
	
			return this;
			
		};
		
	})(jQuery);
	
