window.addEvent('unload', function(){
	return;
});

/*
 * Show a bottom overlay that contains all the info 
 * (FeatureInfo and mp3 info )
 */
currentDetail = null;
var HintOverlay = new Class({
	Implements: [Events,Chain,Options],

	initialize: function( dom_el ){
		this.imVisible = false;
		return;
	},

	vote: function(_id, rate, rate_total, _val){
		var geoJsonRequest = new RiderGeoJson();
		geoJsonRequest.rate( _id ,_val); 

		/*
		 * Show Voting rank 
		 */

		var pct = (rate || 0) / ((rate_total+1) || 1)*100;
		if(pct > 100)
		   pct = 100;
		
		if(_val == 'p'){
			$('#label-vote-no').hide();
		}else{
			$('#label-vote-yes').hide();
		}
		$('.d3-tooltip .text:contains("Sim"), .d3-tooltip .text:contains("Não")').parent().remove();
		$('#vote-stats').show().text(parseInt(pct) + '%');
		$('#voting h3').text('Obrigado!');

		 //$('#voting-thanks #vote-stats').html('{pct}% acharam legal essa dica!'.substitute({'pct': pct}));
		 //$('#voting-thanks #voting').css('display','none');
		 //$('#voting-thanks #voting-thanks').css('display','block');
	},

	/*
	 * Get the feature detail and call your function
	 * with that details
	 */
	getRawFeatureDetail: function( _id, cb){
		var geoJsonRequest = new RiderGeoJson( {'onFeatureDetail': cb} );
		geoJsonRequest.getFeatureDetail( _id ); 
	},

	/*
	 * Get a feature detail and already fill
	 * the info on the right-pane
	 */
	getFeatureDetail: function( _id , markerInfo ){

		var onFeatureDetail = function( detail ){
			currentDetail = detail;

			var state = ($('#localization input:checked').val() || "SP");
			track('/theride/home/cidade{city}/{categoria}/{id}/saibamais/clicou'.substitute({'city': state,'categoria': currentDetail.category , 'id': currentDetail.id }));
			/*
			 * Fill the data
			 */
			switch( detail.category ) {
				case 'baladas':
					colorCateg = "#a675ef";							
				break;
				case 'comer':
					colorCateg = "#32bfdb";							
				break;
				case 'compras':
					colorCateg = "#f19a15";							
				break;
				case 'cultura':
					colorCateg = "#BF2406";							
				break;
				case 'lazer':
					colorCateg = "#7aba36";							
				break;																												
			}
			
			$( '#place-details h1' ).css( 'color', colorCateg ).html( fixCategoryLabel( detail.category ) );
			$( '#place-details h2' ).html( detail.title );															   
			
			var city =  ($('#localization input:checked').parent().find('abbr').attr('title') || "São Paulo");
			var state = ($('#localization input:checked').val() || "SP");

			var addr =  '';
				addr += (detail.address || ''  ) + ', ';
				addr += (detail.number || '' ) + '<br />';
				addr += (detail.neighbourhood || '' ) + ' - ' + $('#localization input:checked').parent().find('abbr').attr('title') + ' - ' + $('#localization input:checked').val() + '<br />';

				var tel = (detail.phone || 'S/N') + '';
				addr += tel.substring(0,4) + '-' + tel.substring(4,8) + '<br /><br />';
				if(detail.website)
					addr += '<a href="'+ detail.website +'" target="_blank">' + detail.website.replace('http://','') + '</a>';
			
			$( '#hint-address'	   ).html( addr				  );
			$( '#hint-description' ).html( detail.description );
			

			if(detail.photo.length)
				$( 'figure img').attr('src', "/media/" + detail.photo[0].photo );

			/*
			 * Set rating buttons
			 */
				$('#label-vote-yes').unbind('click');
				$('#label-vote-no').unbind('click');
				
				$('#vote-stats').hide();
				$('#label-vote-yes, #label-vote-no').show();
				$('#voting h3').text('Gostou?');
				
			 	$('#label-vote-yes').bind('click',function(){
					var state = ($('#localization input:checked').val() || "SP");
					track('/theride/home/cidade{city}/{categoria}/{id}/saibamais/gostou/sim/clicou'.substitute({'city': state,'categoria': currentDetail.category , 'id': currentDetail.id }));

					$(this).unbind('click');
					var a = new HintOverlay();
					a.vote(detail.id, detail.rate+1, detail.rate_total, 'p');
				});
				
				$('#label-vote-no').bind('click',function(){
					var state = ($('#localization input:checked').val() || "SP");
					track('/theride/home/cidade{city}/{categoria}/{id}/saibamais/gostou/nao/clicou'.substitute({'city': state,'categoria': currentDetail.category , 'id': currentDetail.id }));

					$(this).unbind('click');
					var b = new HintOverlay();
					b.vote(detail.id, detail.rate, detail.rate_total, 'm');
				});
				
			 /*
			  * Set sharing buttons
			  */
			  //updateShareLink('twitter');
			  //updateShareLink('facebook');
			  
			  $( '#title-description' ).addClass( 'active' );						  
			  $( '#hint-description'  ).show();
			  
		};

		var geoJsonRequest = new RiderGeoJson( {'onFeatureDetail': onFeatureDetail.bind(this)} );
		geoJsonRequest.getFeatureDetail( _id ); 
	},

	toggle: function(){
		if(this.imVisible) this.hide();
		else			   this.show();
	},

	show: function( ){
		trace("show", arguments);
		$('#main-bar').css('visibility','visible');

		var fx = new Fx.Tween( $E('#main-bar'), {duration: 800} ); 
		fx.start('height', ['10px','348px']);

		this.imVisible = !this.imVisible;
	},

	hide: function(){
		var fx = new Fx.Tween( $E('#main-bar'), {duration: 800} ); 
		fx.start('height', ['348px','10px']);

		this.imVisible = !this.imVisible;
	}
});
var hintDetail = new HintOverlay();

/*
 * FixCategoryLabel
 *
 * Fixes the category name that may be inconsistent to what should be displayed.
 * PS: HARD-CODED, ugly!
 */
var fixCategoryLabel = function( cat ){
	switch(cat){
		case 'baladas':
			return 'Night';
			break;
		case 'comer':
			return 'Comes & Bebes';
			break;
	}

	return cat;
};



/*
 * Update hint-of-the-day based on the current
 * city
 */
var updateHintOfTheDay = function( city_acronym ){
	trace( city_acronym );
	this.updateDOM = function(hint_json){
		trace(hint_json);

		function prettyStringEllipsis(text, maxLength ){
			if(text.length > maxLength)
				return text.substring(0,maxLength) + ' ...';
			else
				return text;
		}

		// Overwrite class
		$('#places-most-viewed').attr('class', hint_json['category'].toLowerCase() );

		// Put name of the hint
		$('#places-most-viewed dt').html( "<span id=\"cat\">{category}:</span> {title}".substitute( 
																										{'category': fixCategoryLabel( hint_json['category'] ).capitalize(), 
																										 'title':	 hint_json['title'].capitalize() 
																										 })  );

		// The content and correct link, plz
		$('#places-most-viewed dd').html( prettyStringEllipsis( hint_json['description'] , 180 ) );
		$('#hint_of_the_day').attr('href', '/walker/share/{id}'.substitute({'id': hint_json['id']}));

		return;
	};


	this.requestHint = function(_city){
		var geoJsonRequest = new RiderGeoJson( {'onNewHintOfTheDay': this.updateDOM} );
		geoJsonRequest.getHintOfTheDay( _city ); 
	};

	this.requestHint( city_acronym );
}; 

/*
 * Given a city acronym (used in divs ids)
 * change to map to the correct city
 */
var changeToCity = function(city_name, auto_load_features){
	var gmap = $E('#gmaps-holder').retrieve('map_api');
    $('#gmaps-holder').css({display: 'block'});
	$('#route .block').hide();
	/*
	 * Get the correct name of the city (compost name).
	 *
	 * HACK: Gosh, some browsers convert to unicode, others just urlencode.
	 * However, urlencoding does not preserve the correct UTF-8 character.
	 * Since we only use SPACES and Á,Ó,É , let's do manually.
	 */
	if (city_name == "Sao Paulo") city_name = "São Paulo"; //also, we had to remove unicode for ie and bit.ly
	var city = {
		'raw':		city_name,
		'name':		city_name.replace(/%20/g,' ').replace(/%E3/g, 'ã').replace(/%C3%A3/g,'ã'),
		'acronym':  'sp'
	};

	var min_city	= ['sp','rj'];
	['São Paulo', 'Rio de Janeiro'].filter( function(item,ix){
		if(city['name'].toLowerCase() == min_city[ix] || city['name'].toLowerCase() == item.toLowerCase()){
			city['name']	= item;
			city['acronym'] = min_city[ix];  
			return true;
		}
		return false;	 
	});
	
	//track('/theride/home/cidade/' + city['acronym'] + '/carregou');
	// Reload city and show nearby features
		gmap.loadCity( city['name'] );
		updateHintOfTheDay( city['acronym'] );

		if(auto_load_features){
			var user_pos = new google.maps.LatLng( gmap.user.lat, gmap.user.lng );
			gmap.showFeaturesNearby( user_pos );
		}

	// Reflect the changes in the interface
		// change top buttons
		$('#localization input ~ abbr[title="' + city + '"]').siblings().attr('checked','true');
		$('#localization input ~ abbr[title="'+ city +'"]').trigger('click');

		$('.d3-tooltip .text:contains("Descubra onde estou")').parent().remove();
		$('#whereami').d3_tooltip({ offset: [-15,-50], direction: 'top', text: 'Descubra onde estou' });

		$('#route-from').val( gmap.user.addr );
		$('#route-to').val( '' ); 

		// Clean overlay (if open..)
		$('.overlay .close').trigger('click');
};


/*
 * Change category visibility (triggered by the
 * middle flash circle)
 */
var gmap_changedCategory = function(obj){
	 var humanObj = {
		 'comer':	obj[0],
		 'cultura': obj[1],
		 'lazer':	obj[2],
		 'compras': obj[3],
		 'baladas':	obj[4]
	 };


	 var gmap = $E('#gmaps-holder').retrieve('map_api');
	 for(var key in humanObj){
		 if(humanObj[key]){
			 track('/theride/home/filtro/{category}/enabled'.substitute({'category': key}));	
			 gmap.enableCategory( key);
		 }else{
			 track('/theride/home/filtro/{category}/disabled'.substitute({'category': key}));	
			 gmap.disableCategory(key);
		}
	 }
};

/*
 * Show a specific tip
 * @params latlng
 *         hint_id
 */
var gmap_goToHint = function(hint_latlng, hint_id){
	var gmap = $E('#gmaps-holder').retrieve('map_api');

	/*
	 * Utter hack.
	 * We need a route or at least a POI. So, 
	 * we fake one around this hint
	 */
	current_hint = hint_id; 

	var point = new GLatLng( hint_latlng.lat() + 0.001, hint_latlng.lng() + 0.001);
	gmap.showFeaturesNearby( point );
};

window.addEvent('domready', function(){ 
	var gmap = new RiderGMap( {map_el: '#gmaps-holder', direction_el: '#directions-bar', onSuccessLoad: gmapLoaded });
    $E('#gmaps-holder').store('map_api',gmap);

	function gmapLoaded(){
		var gmap = $E('#gmaps-holder').retrieve('map_api');
		
		/*
	     * RJ/SP Buttons 
	     */
	     $('#localization input').each( function(e,t){
		 	var rd = this;
	         $( this ).parent().bind('click', function(x){
	               var city_acronym = rd.value;
				   trace('@', city_acronym);
				   
				   // Reload page with the correct city name
					var city = {
						'raw':		city_acronym,
						'name':		city_acronym.replace(/%20/g,' ').replace(/%E3/g, 'ã').replace(/%C3%A3/g,'ã'),
						'acronym':  'sp'
					};

					var min_city	= ['sp','rj'];
					['São Paulo', 'Rio de Janeiro'].filter( function(item,ix){
						if(city['name'].toLowerCase() == min_city[ix] || city['name'].toLowerCase() == item.toLowerCase()){
							city['name']	= item;
							city['acronym'] = min_city[ix];  
							return true;
						}
						return false;	 
					});
					track('/theride/home/cidade/{city}/clicou'.substitute({'city': city['acronym']}) );

					// Finally reload
					location.hash = '#c:' + city['name'];
					location.reload();
				    
	               //changeToCity( city_acronym , true);
	         });

	     });

	      /*
	       * Share
	       */
	       updateShareLink = function( service){
				var twitShareURI = "http://twitter.com/home?status={mQuote}";
				var fbShareURI = "http://www.facebook.com/sharer.php?u={trim_uri}&t={mQuote}";

				var quotes = [
					"Olha o que eu descobri no meu caminho #the_ride {uri}",
					"Entre um clique e outro olha o que surgiu #the_ride {uri}",
					"Esse lugar estava na minha cara e eu nunca tinha percebido #the_ride {uri}",
					"Se liga no que eu achei #the_ride {uri}",
					"Estava no meu caminho e eu nunca me liguei #the_ride {uri}"
				];
				
	           //OBS YVES: Tem hora que não pega a URI correta, aí o link passado pro twitter não funfa qdo aberto.
			   var mQuote = quotes[$random(0,quotes.length-1)].substitute({uri: current_mini});
			   if (service == "twitter") {
					$("#share_twitt").attr('href', twitShareURI.substitute({
						'mQuote': encodeURIComponent(mQuote),
						'trim_uri': current_mini
					}));
					$("#share_twitt").click(function() {
						/* TRACKING */
						track('/theride/home/compartilhar/twitter/clicou');
					});
			   } else if (service == "facebook") {
			   	
			   		// Facebook needs some metatags to do their magic.
					// The SHARE page will do that metatag. {tip_cid} is
					// the encoded tip ID, {tip_id} is the unencoded...
					var fbShare = "{host}/walker/share/{tip_id}/".substitute({
						'host': window.location.host,
						'tip_id': $getDeepUri()[2]
					});
					
					$("#share_fb").attr('href', fbShareURI.substitute({
						'mQuote': encodeURIComponent(mQuote),
						'trim_uri': encodeURIComponent(fbShare)
					}));
					
					$("#share_fb").click(function() {
						/* TRACKING */
						track('/theride/home/compartilhar/facebook/clicou');
					});
				}
	       };


	     /*
	      * Default values
	      */
		  var submit = function(fromCorrected, toCorrected){
		
				/* TRACKING */
				track('/theride/home/trajeto/OK/clicou');
		
				var fromOK = fromCorrected || false;
				var toOK   = toCorrected || false;
				var from = $("#route-from").val().trim();
				var to   = $("#route-to").val().trim();

				var showWrongAddress = function(element, data){
					// Show a sad face indicating that we don't have any idea
					// of this address...
					$('#error_overlay #icon').attr('src','/media/img/icone_sad.png');
					$E('#error_overlay #caption').innerHTML = "Que pena, não conseguimos encontrar o lugar que você procurou. Tente ser mais específico: Rua - Bairro" 
					ui.map.error_overlay.wink(3000);
					trace('wrongAddress',element,from,to);
					return;
				};
				
				var showAddressOptions = function(element, data, cb){
					var div = $('#route_options');
					div.hide().css("margin-top",-div.height());
					var selectAddress = function(el) {
						var addr = $(this).find('.text').text();
						$(element).val(addr);
						div.animate({marginTop: -div.height()}, {duration: 600, complete:function() {div.hide();}});
						cb.apply(null,[]);
					}
					var list = div.find('ul');
					list.find('li').remove();
					for (var i = 0; i < 4 && i < data.length; i ++) {
						var li = $('<li />').appendTo(list).click(selectAddress);
						$('<div />').addClass('bullet').appendTo(li);
						$('<div />').addClass('text').text(data[i]).appendTo(li);
					}
					div.show().animate({marginTop: 0}, 600);
				};

				var showWrongCity = function(element, data){

					// Show a blinking face to the user who wanna use other city 
					$('#error_overlay #icon').attr('src','/media/img/icone_piscada.png');
					$E('#error_overlay #caption').innerHTML = "Opa, por enquanto as dicas do the ride estão disponíveis nas cidades de São Paulo e Rio de Janeiro.";
					ui.map.error_overlay.wink(3000);

				};

				var matchCity = function( details, cityName ) {
					if (details.Country) {
						return details.Country && details.Country.AdministrativeArea &&
							(details.Country.AdministrativeArea.AdministrativeAreaName == gmap.user.city ||
							(details.Country.AdministrativeArea.Locality &&
							details.Country.AdministrativeArea.Locality.LocalityName == gmap.user.city));
					} else if (details.split && details.split('-').length == 3) {
						return cityName == details.split('-')[1].trim();
					}
					return details.indexOf && details.indexOf(cityName) > -1;
				}


				var validateFromAddress = function(addr_isunique, addr_list){
					var filtered = addr_list.filter(function(el){return matchCity(el, gmap.user.city);});
					if( addr_isunique ){

						/*
				  		 * Check city
						 */
						var addressDetails = addr_list[0].AddressDetails;
						if(!matchCity(addressDetails, gmap.user.city)){
							showWrongCity(null,null);
							return;
						} else 
							fromOK = true;


						if(toOK && to != ""){
							// POI along a route
							gmap.getPath( from, to ); 
						} else if(toOK){
							// POI along a pont	
							var point = new GLatLng( addr_list[0].Point.coordinates[1], addr_list[0].Point.coordinates[0] );
							gmap.showFeaturesNearby( point );
						}
					} else if (filtered.length > 1) { //multiple matches
						showAddressOptions('#route-from', filtered, function(addr) { submit(true, toOK); });
						return;
						//showWrongAddress('#route-from', addr_list);
					} else {
						showWrongAddress('#route-from', addr_list);
						return;
					}
				};

				var validateToAddress = function(addr_isunique, addr_list){
					var filtered = addr_list.filter(function(el){return matchCity(el, gmap.user.city);});
					if( addr_isunique ){

						/*
				  		 * Check city
						 */
						var addressDetails = addr_list[0].AddressDetails;
						if(!matchCity(addressDetails,gmap.user.city)){
							showWrongCity(null,null);
							return;
						} else 
							toOK = true;

						if(fromOK && from != ""){
							// POI along a route
							gmap.getPath( from, to ); 
						} else if(fromOK){
							// POI along a point	
							var point = new GLatLng( addr_list[0].Point.coordinates[1], addr_list[0].Point.coordinates[0] );
							gmap.showFeaturesNearby( point );
						}
					} else if (filtered.length > 1) {
						showAddressOptions( '#route-to', filtered, function() { submit(fromOK, true); } );
						return;
					} else {
						showWrongAddress( '#route-to', addr_list);
						return;
					}
				};


				var gmap = $E('#gmaps-holder').retrieve('map_api');
				if(from != "" && to != ""){
					gmap.didYouMean(  from,   {onAddress: validateFromAddress.bind(this)} );
					gmap.didYouMean(  to,     {onAddress: validateToAddress.bind(this)} );
				}else{

					// User only wanna POI around a point, not a route.
					if(from != "" && to == ""){
						// Search only "FROM"
						toOK = true;
						gmap.didYouMean( from, {onAddress: validateFromAddress.bind(this)});
					} else if(to != "" && from == ""){
						// Search only "TO"
						fromOK = true;
						gmap.didYouMean(  to,     {onAddress: validateToAddress.bind(this)} );
					} else {
						// Ask them to fill the From/To fields...
						showWrongAddress(null,null);
					} 

					return false;
				}

				return false;
	      }
		  
		  
	      $('#route-submit').bind('click', submit);

		 /*
		  * Ooook, lets start google maps
		  * Beware with events: this is actually a gmap instance!
		  */
		  gmap.addEvents( {
			  'ready': function(){
					
					var setUIEvents = function(){
					
						GEvent.addListener(gmap.map, "moveend", function() {
							refreshMapTooltips();
						});
						
						/*
						* ScrollBar
						*/
						GEvent.addListener( gmap.map, "zoomend", function(oldZoom, newZoom ){


							if(oldZoom-newZoom > 0)
								gmap.control.decrease();
							else
								gmap.control.increase()

							// Close tooltip (if open) 
                            gmap.map.closeExtInfoWindow();
						});
					

						/*
						* WhereAmI Button
						*/ 
						var geo = new RiderGeoLocate(null);
						if(geo.supportGeoApi())
							gmap.control.whereami.addEvent('click', function(){
								gmap.getClientLocation( true ); 
							});

						// Tip of the day link
						$('#hint_of_the_day').bind('click', function(){

							/* TRACKING */
							track('/theride/home/pitstop/clicou');
							
							var hintOfTheDay = $('#hint_of_the_day').attr('href').split('/')[3];

							// Show only a tip	
							window.current_hint = hintOfTheDay;
							hintDetail.getRawFeatureDetail( current_hint, function(featureDetail){

								// Change to correct city
								//changeToCity( featureDetail.city.name, false );						
								var latlng = new GLatLng( featureDetail.gmaps_loc.coords[0].y , featureDetail.gmaps_loc.coords[0].x );
								gmap_goToHint( latlng, featureDetail.id );	

								$('#route-from').val( featureDetail.address + ' - ' + featureDetail.neighbourhood );
								$('#route-to').val( '' ); 

								$('html,body').animate( {scrollTop: 0}, {duration: 500, easing: 'easeOutQuad', queue: false} );
							});

							return false;
						});
					};

					setUIEvents();

					$('#map > header #route').css({'opacity': 1.0});

					 /*
					  * Permalinks
					  */
					 var coords = $getDeepUri();

					 if(coords){
						
						current_hint = coords[2]; 
						city		 = coords[3];
				
						// Set gmaps to this specific city
						if(city){
							//$('#localization abbr[title="' + city + '"]').trigger('click');
							changeToCity( city, false );
						}

						// Have start and end point (aka, is a route)
						if(coords[0] && coords[1]){

							gmap.getRawPath( coords[0], coords[1] );

						// Have just one point (aka, is a POI) 
						}else if(coords[0] || coords[1]){

							var coord = coords[0] || coords[1];	
							gmap.showFeaturesNearby( coord );	

						// There is just a hint (no POI).
						} else if(current_hint){
							
							trace('$',current_hint);
							// Fake a POI. 
							hintDetail.getRawFeatureDetail( current_hint, function(featureDetail){
								var latlng = new GLatLng( featureDetail.gmaps_loc.coords[0].y , featureDetail.gmaps_loc.coords[0].x );
								gmap_goToHint( latlng, featureDetail.id );	
								$('#route-from').val( featureDetail.address + ' - ' + featureDetail.neighbourhood );
								$('#route-to').val( '' ); 
							});

						} else if(city){
							changeToCity(city,true);
						}

					 } else {

						 gmap.getClientLocation( false );
						
						/*
						 * Preload map or show interface for selecting the current city 
						 */
						var city		   = gmap.user.city;
						
						if(city == 'sp'){
							$('#localization abbr[title="São Paulo"]').trigger('click');
							changeToCity( city , true);
						}else if(city == 'rj'){
							$('#localization abbr[title="Rio de Janeiro"]').trigger('click');
							changeToCity( city , true);
						} else {
						
							/*
							 * SHow a overlay asking for user to choose
							 * a city
							 */
							$('#map > header #route').css({'opacity': 0.4});

							$('#city-helper').remove();
							$('<span id="city-helper"></span>').hide().appendTo('body');
							$('#city-helper').data('objToAppend','#choose-city').bind( 'click' , ui.map.overlay.large.open );
							$('#city-helper').trigger('click');

							// Hide close button for choose-city page 
							$('#overlay_close').css('display','none');

							$('#choose-city .maps label').unbind('click');
							$('#choose-city .maps label').bind('click',function(){
								var nCity = $(this).find('input').val();
								$('#localization input[type=radio]').removeAttr('checked');
								if(nCity == 'sp') {
									$('#localization #sp').attr('checked','checked');
									//$('#localization abbr[title="São Paulo"]').trigger('click');
								}else if(nCity == 'rj'){
									$('#localization #rj').attr('checked','checked');
									//$('#localization abbr[title="Rio de Janeiro"]').trigger('click');
								}
								changeToCity(nCity, true);
							});

							// Hide km and time info
							$('#road-infos .data').css('display','none');
							$('#road-infos .label').css('display', 'block');
						}
					}

					// Make copyright smaller
					window.setTimeout( "$E('#gmaps-holder').retrieve('map_api').hackGMapsApi()", 5000);

			  },

			  'featureClick': function( featureClickInfo ){

					var latlng		  = featureClickInfo.latlng;
					var category	  = featureClickInfo.category;

					trace(category);
					var marker		  = this.featureManager.getMarkerByCategory( category.acronym, featureClickInfo.marker );
					var category_gss  = this.gss[category.gss];

					var featureInfo	   = ( marker != null ? marker.info	   : '');
					var gMarker		   = ( marker != null ? marker.marker  : '');
					
					var descriptionTooltip = featureInfo.description; 
					var infoName		   = featureInfo.title;
					
					function prettyStringEllipsis(text, maxLength ){
						if(text.length > maxLength)
							return text.substring(0,maxLength) + ' ...';
						else
							return text;
					}
					descriptionTooltip = prettyStringEllipsis( descriptionTooltip, 100 );

					// InfoName font is monospace...
					infoName		   = prettyStringEllipsis( infoName, 24 );

					gMarker.openExtInfoWindow( this.map, "HintTooltip",
					
						$E('#tooltip_skel').innerHTML.substitute({
							'category_name':		fixCategoryLabel(category.acronym).capitalize(),
							'hint_name':			infoName, 
							'hint_description':		descriptionTooltip,

							'hint_name_title':				featureInfo.title.replace("\"","\\\""),	
							'hint_description_title':	    featureInfo.description.replace("\"","\\\"")

							//'feature_id':			  featureInfo.id
						}),
						{cssClass: 'box{categoria}'.substitute( {'categoria': category.acronym.capitalize() } )}
					 );

					// Store the marker data!
					$E('#HintTooltip').store('marker', marker ); 

					// Let me Align the map					
					this.map.panTo( latlng ); 

					// Hide the HintOverlay (if opened)
					ui.map.sidebar.close();
					$('#bt-details').hide();

					/*
					 * Mark the current marker as "clicked".
					 */
					if(this.featureManager.currentClickedMarker){
						var oldMarker = this.featureManager.getMarker( this.featureManager.currentClickedMarker ); 

						try {
							oldMarker.marker.state = 'mouseout';
							oldMarker.marker.setImage( oldMarker.marker.gss.icon_src );
						} catch(err){
							//console.log("Can't clean oldMarker. Bypassing...");
						}
					}

					marker.marker.state = 'clicked'; 
					this.featureManager.currentClickedMarker = marker.marker; 

					var state = ($('#localization input:checked').val() || "SP");
					track('/theride/home/cidade{city}/{categoria}/{id}/tooltip'.substitute({'city': state,'categoria': category.acronym , 'id': featureInfo.id }));
			  },

			  'onTooltipOpen': function(){
					
					$E('#HintTooltip .hint_close').addEvent('click', function(){
                        gmap.map.closeExtInfoWindow();
                    });

					$E('#HintTooltip a').addEvent('click', function(){

						//var state = ($('#localization input:checked').val() || "SP");
						//track('/theride/home/cidade{city}/{categoria}/{id}/saibamais/clicou'.substitute({'city': state,'categoria': currentDetail.category , 'id': currentDetail.id }));

						var self   = $E('#HintTooltip');
						var marker = self.retrieve('marker');
						
						if(gmap.route.start.pos && gmap.route.end.pos)
							$setDeepUri( gmap.route.start.pos, gmap.route.end.pos, marker.info.id , gmap.user.city );
						else if(gmap.route.whereami.pos)
							$setDeepUri( gmap.route.whereami.pos, null, marker.info.id , gmap.user.city );

						// Open the right pane with the details 
						hintDetail.getFeatureDetail( marker.info.id, null );

						$( '#wrapper-place-player .open-close-player #button-detail-place' ).show();
						ui.map.sidebar.open('#place-details');

						// And hide this tooltip
						var ctop = $('#HintTooltip').offset().top;
						$('#HintTooltip').animate({
							'marginTop': 30,
							'opacity':  0
						}, {duration: 500, easing: 'easeInBack', queue: false, complete: function(){
							$('#HintTooltip').css({'display': ''});	
                                gmap.map.closeExtInfoWindow();
						}});
					});
					

					/*
					 * Coda bubble style appearing
					 */
					var ctop = $('#HintTooltip').offset().top;
					$('#HintTooltip').css({
												'opacity': '0',
												'display': '',
												'marginTop':  30
					}).animate({
						'marginTop':	0,
						'opacity':		1.0
					}, {duration: 500, easing: 'easeOutBack', queue: false});
			  },

			  'onTooltipClose': function(){
                    //gmap.map.closeExtInfoWindow();
                    trace("tooltip hidden")
                    $('#HintTooltip').css('display','none');
			   },

			  'onGrantedUserLocation': function( user_location ){

					var user_loc	  = new google.maps.LatLng( user_location.lat, user_location.lng );

					this.showFeaturesNearby( user_loc , 1500 );
					 if(user_location.addr)
						$('#route-from').val( user_location.addr );
			   },

			   'onUserLocationBadAccuracy': function( acc ){
				   trace("Sorry, we can't find where are you with good accuracy.");
				   return;
			   },
			   'onDeniedUserLocation': function(err){
				   trace("Sorry, we can't find where are you with good accuracy.");
				   return;
			   },
			   'onUserLocationFatalError': function( georesponse ){
				   trace("Sorry, we can't find where are you with good accuracy.");
				   return;
			   },

			   // Start/end Marker (Route markers) dragndrop release
			   'onSEMarkerRelease': function( points ){

					// Close tooltip (if open) 
					gmap.map.closeExtInfoWindow();

					this.getPath( points.start, points.end );
			   },

			   // Single marker(POI) dragndrop release
			   'onSingleMarkerRelease': function( point ){

					// Close tooltip (if open) 
					gmap.map.closeExtInfoWindow();

					// POI along a point	
					gmap.showFeaturesNearby( point );
			   },

			'onFeaturesNearby': function( info ){
				$setDeepUri( info.pos, null , null , gmap.user.city );

				/* Hide mp3 and km info */ 
				 $('#player-info .data').hide();							 
				 $('#player-info .label').show();														 

				 $('#road-infos .data').css('display','none');
				 $('#road-infos .label').css('display', 'block');

				 ui.map.sidebar.hide();
				 $('#bt-player').hide();
			},

			'onNewPath':		function( info ){
				var bb   = info['bb'];
				var poly = info['poly'];

				/*
				 * Set Permalink
				 */
				 $setDeepUri( this.route.start.pos, this.route.end.pos , null , this.user.city );

				/* 
				 * So, you got the fucking path? Fine,
				 * But now, we need ANOTHER query to get the cool things around
				 * your route. Geez!
				 */

				/*
				 * Fit the route on your screen... 
				 */
				 var center = this.route.getAppropriateZoomLevel( this.map );				
				 var cZoom  = (center['zoom'] >= this.app.zoom.min ? 
								(center['zoom'] <= this.app.zoom.max ? center['zoom'] : this.app.zoom.max ) :
							 this.app.zoom.min );
				 var p      = (cZoom == center['zoom'] ? center['center'] : this.route.start.pos );

				this.map.setZoom( cZoom );
				this.map.panTo( p );
				 

				 /*
				  * Get the cool things around your route 
				  */
				if(this.markerClusterer) 
					this.markerClusterer.clearMarkers();
				
				this.getCoolFeaturesNearbyRoute( poly, 0.005);
				
				ui.map.sidebar.show();

				/*
 				 * Show distance and time
 				 */
				 var distance = this.dir.getDistance().meters;
				 var dur	  = this.dir.getDuration().html;							 

				 var mainDuration	= dur.split('.')[0].split(' ');
				 var timeMult = (mainDuration[1].indexOf("hora") == 0 ? (60*60) : 60 );

				 // Show mp3 info
				 player.getMusic( 'relax', parseInt( mainDuration[0] )*timeMult );
				 $( '#timing-route' ).val( parseInt( mainDuration[0] )*timeMult ); 
				 
				 $('#player-info .data').show();							 
				 $('#player-info .label').hide();														 
				 $('#bt-player').show();

				 $('#road-infos .data').css('display','block');
				 $('#road-infos .label').css('display', 'none');
                 if (distance < 1000) {
				    $E('#route_distance_label').innerHTML = "{distance} <small>metros</small>".substitute({'distance': distance});
                 } else {
                     distance = (distance/1000).toFixed(2);
				    $E('#route_distance_label').innerHTML = "{distance} <small>quil&ocirc;metros</small>".substitute({'distance': distance});
                 }

				 var hour = min = '00';
				 if(mainDuration[1] == 'hora' || mainDuration[1] == 'horas'){
					 hour = ( parseInt(mainDuration[0]) < 10 && '0'+mainDuration[0] ) || mainDuration[0];

					 if(mainDuration.length > 2)
						min = ( parseInt(mainDuration[2]) < 10 && '0'+mainDuration[2] ) || mainDuration[2];
				}else if(mainDuration[1] == 'min'){
					 min = ( parseInt(mainDuration[0]) < 10 && '0'+mainDuration[0] ) || mainDuration[0];
				 }

				 $E('#route_time_min_label').innerHTML	   = '{time_hora}<small>{cp}</small>'.substitute({'time_hora': hour, 'cp': ( hour > 1 ? "horas" : "hora" ) });
				 $E('#route_time_label').innerHTML		   = '{time_min}<small>{cp}</small>'.substitute({'time_min': min, 'cp': ( min > 1 ? "min." : "min." ) });

				/*
				 * Update from-to textbox to show the current location
				 */
				 $('#route-from').val( this.route.start.addr );
				 $('#route-to').val( this.route.end.addr );
			},

			'onNewPathError': function( e ){
				$('#error_overlay #icon').attr('src','/media/img/icone_sad.png');
				$E('#error_overlay #caption').innerHTML = e.description; 

				ui.map.error_overlay.wink(3000);
			}

		  });

		gmap.run();

	};
});
