/*
 * Global variables 
 */
var map; //map object
var intervalid = 0; //playback interval
var race; //race object, contain participants, points
var current_time;//Time, where we are in playback
var bounds;
var uhuu;
function isNumber(x) 
{ 
  return ( (typeof x === typeof 1) && (null !== x) && isFinite(x) );
}
function format_timeline()//{{{
{
	var tl = race.timeline;
	var html = "<div id=wp_accordion>";
	for (var i=1;i<tl.length;i++)
	{
		var wp_tl = tl[i];
		wp_tl.sort(function(a, b)
				{
					return(a[1] - b[1]);
				});
		 
//html += "<table class=timeline><tr><th class='ui-widget-header' colspan=3 >Rajapunkt: "+i+"</th></tr>";
//
		html += "<h3><a href='#'>Rajapunkt: "+i+"</a></h3><div><table class=time>";
		for (var j=0;j<wp_tl.length;j++)
		{
			var imei = wp_tl[j][0];
			var epoch = wp_tl[j][1];
			var d = new Date();
			d.setTime((epoch - (d.getTimezoneOffset()*60 ))*1000);
			var time = d.toTimeString();
			var timestr = time.replace(/ .*/, "");

			html += "<tr><td>"+(j+1)+"</td><td>"+timestr+"</td><td>"+race.participants[imei].name+"</td></tr>";
		}
		html += "</tr></table></div>";
	}
	html += "</div>";
	return html;
}//}}}

function midpoint(point1, point2) //{{{
{

	var lat2 = toRad(point2.lat());
	var lng2 = point2.lng();
	var dLon = toRad(point2.lng() - point1.lng());
	var lat1 = toRad(point1.lat());
	var lng1 = toRad(point1.lng());
	var Bx = Math.cos(lat2) * Math.cos(dLon);
	var By = Math.cos(lat2) * Math.sin(dLon);
	var lat3 = Math.atan2(Math.sin(lat1)+Math.sin(lat2), Math.sqrt( (Math.cos(lat1)+Bx)*(Math.cos(lat1)+Bx) + By*By));
	var lng3 = lng1 + Math.atan2(By, Math.cos(lat1) + Bx);
	return new google.maps.LatLng(toDeg(lat3), toDeg(lng3)); 
}//}}}

function init_race()//{{{
{
	//
	// Construct start and end time string
	//
	//[% race.get_start_time.strftime("%d.%m.%y %H:%M") %] - [% IF (race.get_start_time.dmy == race.get_end_time.dmy);race.get_end_time.strftime("%H:%M"); ELSE; race.get_end_time.strftime("%d.%m.%y %H:%M"); END %]
	
	var start = new Date(race.start_js_str);
	var end = new Date(race.end_js_str);
	var start_date_str = start.dateFormat("DD.MM.YY");
	var end_date_str = end.dateFormat("DD.MM.YY");
	var start_end_str = start_date_str+" "+start.timeFormat("HH:mm")+" - "+(start_date_str == end_date_str ? end.timeFormat("HH:mm") : end_date_str+" "+end.timeFormat("HH:mm"));
	$("#start_end_string").html(start_end_str);

	//put race track on map
	/*race.temp_waypoint = new google.maps.MarkerImage("/static/images/ajutine.png",	
			         new google.maps.Size(5, 5),
			         new google.maps.Point(0,0),
			         new google.maps.Point(3, 3)
			        );*/
	race.temp_waypoint = document.getElementById("wp_image");
	race.finished = 0;
	race.course = new Array();
	race.course2 = new Array();

	race.coursemarkers = new Array();
	race.timeline = new Array();
	for (var i=0; i<race.track.length;i++)
	{	
		var wp = new Object();
		race.timeline[i] = new Array();
		//create points
		wp.points = new Array();
		for (var j=0; j<race.track[i].points.length;j++)
		{
			var p=race.track[i].points[j];
			var gpoint = new google.maps.LatLng(p[0], p[1]);
			gpoint.time = p[2];
			wp.points.push(gpoint);
		}
		if (race.track[i].points2)
		{
			wp.points2 = new Array();
			for (var j=0; j<race.track[i].points2.length;j++)
			{
				var p=race.track[i].points2[j];
				var gpoint = new google.maps.LatLng(p[0], p[1]);
				gpoint.time = p[2];
				wp.points2.push(gpoint);
			}
		}
		race.course2[i] = wp;
		if(race.track[i].lat2)
		{
			var point1 = wp.points[0];
			var point2 = wp.points2[0];
			var temp_point = midpoint(point1, point2);
			temp_point.point1 = point1;
			temp_point.point2 = point2;
			if (!point1 || !point2)
				continue;
			if (race.track[i].imei)
			{
				race.coursemarkers[i+"_"+race.track[i].imei] = new CustomMarker({
				position: point1,
				map: map,
				image: race.temp_waypoint,
				labelclass: "coursemarker",
				text: race.track[i].displayname
				});
			}
			else
			{
				race.coursemarkers[i+"_p1"] = new CustomMarker({
				position: point1,
				map: map,
				image: race.temp_waypoint,
				labelclass: "coursemarker",
				text: race.track[i].displayname
				});
			}
			if (race.track[i].imei2)
			{
				race.coursemarkers[i+"_"+race.track[i].imei2] = new CustomMarker({
				position: point2,
				map: map,
				image: race.temp_waypoint,
				labelclass: "coursemarker",
				text: race.track[i].displayname2
				});
			}
			else
			{
				race.coursemarkers[i+"_p2"] = new CustomMarker({
				position: point2,
				map: map,
				image: race.temp_waypoint,
				labelclass: "coursemarker",
				text: race.track[i].displayname2
				});
			}	
			race.track[i].point_line = new google.maps.Polyline({
				path: new Array(point1, point2),
				clickable: false,
				strokeColor: "#111111",
				strokeOpacity: 0.1,
				strokeWeight: 1,
				map: map
			});
		}
		else
		{
			var temp_point = new google.maps.LatLng(race.track[i].lat, race.track[i].lng);
			temp_point.isdual = false;
			var m_id;
			if (race.track[i].imei)
				m_id = i+"_"+race.track[i].imei;
			else
				m_id = i;
			race.coursemarkers[m_id] = new CustomMarker({
				position: temp_point,
				map: map,
				image: race.temp_waypoint,
				labelclass: "coursemarker",
				text: race.track[i].displayname
				});
		}
		bounds.extend(temp_point);
	}
	
	
    var track = getCurrentTrack(0);
    var tmpcourse = new Array();
    for (i in track)
    {
    	var c = track[i].point;
    		tmpcourse.push(new google.maps.LatLng(c.lat(), c.lng()));

    	if (race.track[i].point_line)
    	{
    		race.track[i].point_line.setPath(new Array(track[i].p1, track[i].p2));
    	}
    }
    race.start_point = tmpcourse[0];
    race.courseline = new google.maps.Polyline({
    path: tmpcourse,
    clickable: false,
    strokeColor: "#0000AA",
    strokeOpacity: 0.4,
    strokeWeight: 1,
    map: map
    });
	race.racedistance = google.maps.geometry.spherical.computeLength(tmpcourse) / 1000;
    if(!$("#generaalkurss").attr("checked"))
   		race.courseline.setMap(null);

		
	for (var participant in race.participants)
	{
		part = new Participant(race.participants[participant]);
		delete(race.participants[participant]);
		race.participants[participant] = part;
		var start_point = race.start_point;
		start_point.time = ""+(parseInt(race.start_epoch));
		start_point.distance_from_previous = 0;
		start_point.distance_to_finish = 0;
		start_point.distance_traveled = 0;
		start_point.speed = 0;
		start_point.next_wp = 1;
		
		if (!race.no_course)
			fillPointParams(start_point, null, participant);
		part.gpoints.push(start_point);
		part.index = 0;
		part.next_wp = 1; //should actaully be 0, to count for start point, but whatever
		
		
		//Contstruct google LatLng objects, and set marker and polyline, if we have points...
		if (part.points.length > 0)
		{
			var next_wp = 1; //don't count first waypoint
			for (var i=0; i < part.points.length; i++)
			{	
				var point = new google.maps.LatLng(part.points[i][0], part.points[i][1]);
				point.time = part.points[i][2];
				point.distance_from_previous = part.points[i][3];
				point.speed = parseFloat(part.points[i][4]);
				point.bearing = part.points[i][5];

				part.gpoints.push(point);
				if (!race.no_course)	
					fillPointParams(point, part.gpoints[i], participant);
			}
			delete part.points;	
		}
		var markerpos;
		markerpos = part.gpoints[0];
		if ($.browser.msie)
		{
			//fetch colored image for a participant, catalyst can't handle # in url, so that must go 
			var icon = new google.maps.MarkerImage("/circle/"+part.color.replace('#', ''),	
				 new google.maps.Size(11, 11),
				 new google.maps.Point(0,0),
				 new google.maps.Point(5, 5)
				);

			part.marker =  new google.maps.Marker({
							position: markerpos,
							map: map,
							icon: icon,
							title: part.name,
							clickable: false
							});
		}
		else
		{
			part.img = document.getElementById("boat_"+part.v_id);
			var pname = part.name;
			part.marker = new CustomMarker({
				position: markerpos, 
				image: part.img,
				text: pname.replace(/ \(.*/g, ""), 
				map: map
			});
		}
		var polyOptions = {
			clickable: false,
			path: new Array(), //must put something here, or else poly won't initialize
				strokeColor: part.color,
				strokeOpacity: 0.5,
			strokeWeight: 2,
			map: map
		};

		part.poly = new google.maps.Polyline(polyOptions);
				
	}

	map.fitBounds(bounds);
	if (map.getZoom() > 16)
		map.setZoom(16);

	map.setCenter(bounds.getCenter());
	current_time=race.start_epoch;
	showTime(current_time);
	
	$(function() 
	{
		race.slider = $("#slider").slider
		({
					min: race.start_epoch,
					max: race.end_epoch,
					range: "min",
					change: function(event, ui)
					{ 
						moveTracks(ui.value);
					},
					stop: function(event, ui)
					{ 
					},
					slide: function(event, ui) { showTime(ui.value);},
					step:1
		});
	

	});
	if(islive)
	{
		//create progressbar
		setTimeout("toEnd()", 1000);
		//it's live, so scroll to end and set interval to fetch coordinates...
		for (var part in race.participants)
		{
			var p = race.participants[part];
			if (!p.gpoints || p.gpoints.length < 2)
				continue;
			var latlng = p.gpoints[p.gpoints.length-1];
			var speed = 0;
			if (race.units == "m")
			{
				speed = Math.round(latlng.speed*5.39956803)/10;
			}
			else
			{
				speed = Math.round(latlng.speed*10)/10;
			}
			$("#speed_"+part).html(speed);
		}
		if (!race.no_course)
			sortTable();
	}
		//init slider
			
	$('#loading').fadeOut(100);

	
	if(islive)
	{
		setTimeout("listen('','')", 3000);
		
		var tilesloaded_lstnr = google.maps.event.addListener(map, 'tilesloaded', function(evnt) {
   			toEnd();
			google.maps.event.removeListener(tilesloaded_lstnr);
		});	

	}
}//}}}


/*
 * returns array of time and speed for imei
 */

function getTimeSpeed(imei)
{
	var points = race.participants[imei].gpoints;
	var timespeed = new Array();
	for (var i in points)
	{
		var ts = new Array((parseInt(points[i].time)+10800+10800)*1000, points[i].speed);
		timespeed.push(ts);
	}
	return timespeed;
}

/*
 * returns array of track in time...
 */
function getCurrentTrack(time) //{{{
{
	var track = new Array();
	for (var i=0; i<race.course2.length;i++)
	{	
		var curpoint = new Object();
		var min=0;
		var max=race.course2[i].points.length-1;
		var pindex = 0;
		while (min < max)
		{
			var middle = Math.floor(((max - min)/2) + min);
			var point = race.course2[i].points[middle];
			if (max - min <= 1)
			{
				pindex=min;
				break;
			}
			if (point.time > time)
			{
				max = middle;
			}
			else if (point.time < time)
			{
				min = middle;
			}
			else 
			{
				pindex = middle;
				break;
			}
		}
		curpoint.p1 = race.course2[i].points[pindex];	
		if (race.course2[i].points2)
		{
			min=0;
		    max=race.course2[i].points2.length-1;
			pindex = 0;
			while (min < max)
			{
				var middle = Math.floor(((max - min)/2) + min);
				var point = race.course2[i].points2[middle];
				if (max - min <= 1)
				{
					pindex=min;
					break;
				}
				if (point.time > time)
				{
					max = middle;
				}
				else if (point.time < time)
				{
					min = middle;
				}
				else 
				{
					pindex = middle;
					break;
				}
			}
			curpoint.p2 = race.course2[i].points2[pindex];
			curpoint.point = midpoint(curpoint.p1, curpoint.p2);
		}
		else
			curpoint.point = curpoint.p1;

		track.push(curpoint);
	}
	return track;
}//}}}

function listen(last_modified, etag) {
       $.ajax({
           'beforeSend': function(xhr) {
               xhr.setRequestHeader("If-None-Match", etag);
               xhr.setRequestHeader("If-Modified-Since", last_modified);
           },
           url: '/comet?id=666',
           dataType: 'json',
           type: 'get',
           cache: 'false',
           success: function(response, textStatus, xhr) {
               etag = xhr.getResponseHeader('Etag');
               last_modified = xhr.getResponseHeader('Last-Modified');
               	if(response)
				{
					if (response.uiaction)
					{
						if (response.uiaction == "reload" && response.r_id == race.id)
							setTimeout("forceReload();", 20000);
					}
					else
					{
						for (var i in response)
						{
							var resp = response[i];
							var imei = resp[0];
							var lat = resp[1];
							var lng = resp[2];
							var time = resp[3];
							var distance = resp[4];
							var speed = resp[5];
							var heading = resp[6];
							var p = race.participants[imei];
							if (p)
							{
								//dont push a coordinate if resp.time is less than time of last gpoint
								var i = p.gpoints.length;
								var lastpoint = p.gpoints[i-1];
								if (!lastpoint || lastpoint.time < time)
								{
									var point= new google.maps.LatLng(lat, lng);
									point.time = time;
									point.distance_from_previous = distance;
									point.speed = speed;
									point.bearing = heading;
									
									p.gpoints.push(point);
									if (!race.no_course)
									{
										if (i > 0)
											fillPointParams(p.gpoints[i], p.gpoints[i-1], resp.imei);
										else
											fillPointParams(p.gpoints[i], null, resp.imei);	
									}
								}
							}
							else
							{
								
								for (var i=0; i<race.track.length;i++)
								{
									var changed = false;
									var newpoint;
		
									if (race.track[i].imei && race.track[i].imei == imei)
									{
										var latlng =  new google.maps.LatLng(lat, lng);
										latlng.time = time;
										race.course2[i].points.push(latlng);
										delete latlng
										changed=true;
									}
									if (race.track[i].imei2 && race.track[i].imei2 == imei)
									{
										var latlng =  new google.maps.LatLng(lat, lng);
										latlng.time = time;
										race.course2[i].points2.push(latlng);
										delete latlng;
										changed=true;
									}
										
								}
							}
						}
						toEnd();
					}
				}

               /* Start the next long poll. */
	   			listen(last_modified, (etag > 0? etag : ''));
           },
           error: function(xhr, textStatus, errorThrown) {
               //console.log(textStatus + ' | ' + errorThrown);
                           listen(last_modified, etag);
           }
       });
}

function race_subscribe() //{{{
{
	race.subscription = new nginx_subscribe(
			"/comet?id=666",
			function success(resparr) {
				// the script calls success function direct
						},
			function failure(resp) {
				if (window.console) {
					console.log('subscribe failure');
				}
			}
			);
}//}}}

function intersects (p1, p2, p3, p4) //{{{
{
	var x1 = p1.lat();
	var y1 = p1.lng();
	var x2 = p2.lat();
	var y2 = p2.lng();
	var x3 = p3.lat();
	var y3 = p3.lng();
	var x4 = p4.lat();
	var y4 = p4.lng();
	var a;
	var b;
	try
	{
		a = (((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3)))/(((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1)));
	}
	catch(err)
	{ 
		return false;
	}
	
	try {
		b = (((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3)))/(((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1)));
	}
	catch(err)
	{
		return false;
	}
	if ( a && b && a < 1 && a > 0 && b < 1 && b > 0)
	{
		return true;
	}
}//}}}

function fillPointParams (curpoint, prevpoint, imei) //{{{
{
		
	var track = getCurrentTrack(curpoint.time);

	var courselen = track.length;

	if (!prevpoint)
	{
		curpoint.next_wp = 1;
		curpoint.speed=0;
		curpoint.distance_from_previous=0;
		curpoint.distance_to_next_wp = distance(curpoint, track[1].point);
		curpoint.distance_to_finish = google.maps.geometry.spherical.computeLength(race.courseline.getPath()) / 1000;
	}
	else
	{
		//distance from previous point
		curpoint.distance_from_previous = distance(prevpoint, curpoint);
		//total traveled
		curpoint.distance_traveled = curpoint.distance_from_previous + prevpoint.distance_traveled;
		//bearing
		curpoint.bearing = bearing(prevpoint.lat(),prevpoint.lng(),curpoint.lat(),curpoint.lng());
		//speed
		if (!curpoint.speed)
		{
			if (curpoint.time != prevpoint.time)
				curpoint.speed = curpoint.distance_from_previous/(curpoint.time - prevpoint.time)*3600;
			else
				curpoint.speed = 0;
		}

		curpoint.distance_to_finish = 0;
		
		if (prevpoint.next_wp >= courselen)
		{
			curpoint.distance_to_finish = 0;
			curpoint.next_wp = prevpoint.next_wp;
			if (prevpoint.finished)
				curpoint.finished = prevpoint.finished;

			return;
		}
		for (var i = 0; i<prevpoint.next_wp ;i++)
		{
			track.shift();
		}
		if(!track[0])
			alert(prevpoint.next_wp);
		
		curpoint.distance_to_next_wp = distance(curpoint, track[0].point);
		
		if (curpoint.time >= race.real_start &&
			track[0] && 
			track[0].p2 && intersects(track[0].p1, track[0].p2, prevpoint, curpoint)
			)
		{
			race.timeline[prevpoint.next_wp].push(new Array(imei, curpoint.time, curpoint));
			curpoint.next_wp =  prevpoint.next_wp+1;
			if (curpoint.next_wp < courselen)
			{
				curpoint.distance_to_next_wp = distance(curpoint, track[1].point);
			}
			else
			{
				curpoint.finished = curpoint.time;
			}
			track.shift();

		}
		else if (curpoint.time >= race.real_start &&
				curpoint.distance_to_next_wp < race.radius && 
				prevpoint.next_wp < courselen && 
				prevpoint.distance_to_next_wp < curpoint.distance_to_next_wp)
		{
			race.timeline[prevpoint.next_wp].push(new Array(imei, prevpoint.time, curpoint));
			curpoint.next_wp =  prevpoint.next_wp+1;
			if (curpoint.next_wp < courselen)
			{
				curpoint.distance_to_next_wp = distance(curpoint, track[1].point);
			}
			else
			{
				curpoint.finished = curpoint.time;
			}

			track.shift();
			
		}
		else
		{
			curpoint.next_wp = prevpoint.next_wp;
		}
		var distance_to_finish = curpoint.distance_to_next_wp;
		for (var i=0;i < track.length -1 ;i++)
		{
			distance_to_finish += distance(track[i].point, track[i+1].point);
		}

		curpoint.distance_to_finish = distance_to_finish;
		delete (track);	
	}
}//}}}


function toRad(deg) {return deg * Math.PI/180;}

function toDeg(rad) {return rad * 180 / Math.PI;}

function bearing(latitude1, longtitude1, latitude2, longtitude2) {//{{{
	var lat1 = toRad(latitude1), lat2 = toRad(latitude2);
	var dLon = toRad((longtitude2-longtitude1));

	var y = Math.sin(dLon) * Math.cos(lat2);
	var x = Math.cos(lat1)*Math.sin(lat2) -
		Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
	var brng = Math.atan2(y, x);
	return Math.round((toDeg(brng)+360) % 360);
}//}}}

function line_distance(line_array) //{{{
{
	var total = 0;
	var length = line_array.length;
	for (i = 1; i<length; i++)
	{
		var elem1 = line_array[i-1];
		var elem2 = line_array[i];
		total += distance(elem1, elem2);
	}
	return Math.round(total *100)/100;
}//}}}

function distance(l1, l2) {//{{{
	var R = 6371; // km
	if (!l1 || !l2)
		return 0;
	var lat1 = l1.lat();
	var lon1 = l1.lng();
	var lat2 = l2.lat();
	var lon2 = l2.lng();
	var dLat = toRad(lat2-lat1);
	var dLon = toRad(lon2-lon1);
	if(lat1 == lat2 && lon1 == lon2)
		return 0;
	var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
		Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * 
		Math.sin(dLon/2) * Math.sin(dLon/2); 
	var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
	var d = R * c;
	return d;
}//}}}

function toEnd()//{{{
{
	var last = current_time;
	for (var part in race.participants)
	{
		var p = race.participants[part];
		var elem = p.gpoints[p.gpoints.length - 1];
		if (!elem)
			continue;
		if (elem.time > last)
		{
			last = elem.time;
		}
	}
	$("#slider").slider("value", last);
}//}}}

function time2str(seconds)//{{{
{
	var d = new Date();
	d.setTime((seconds - (d.getTimezoneOffset()*60 ))*1000);
	return d.toTimeString();
}//}}}

Number.prototype.pad = function(l, s){
	var str = new String(this);
	return (l -= str.length) > 0 
	        ? (s = new Array(Math.ceil(l / s.length) + 1).join(s)).substr(0, s.length) + str + s.substr(0, l - s.length) 
    	    : str;
};
function showTime(seconds)//{{{
{
	seconds = seconds - race.real_start;

	var negative = false;

	if (seconds < 0)
	{
		seconds *= -1;
		negative = true;
	}
	var h = parseInt(seconds / 3600);
	var m = parseInt(seconds / 60 - (h * 60));
	var s = seconds % 60;
	
	$("#current_time").html((negative?"- ":"")+h.pad(2, "0")+":"+m.pad(2, "0")+":"+s.pad(2, "0"));
}//}}}


function fitMapToMarkers()//{{{
{
	var bounds = new google.maps.LatLngBounds();
	for (var participant in race.participants)
	{
		var p = race.participants[participant];
		if (!p.gpoints || p.gpoints.length == 0)
			continue;
		if (!p.marker.getVisible())
			continue;
		bounds.extend(p.marker.getPosition());
	}
	map.fitBounds(bounds);
	if (map.getZoom() > 16)
		map.setZoom(16);
	map.setCenter(bounds.getCenter());

}//}}}

function repaintTrack(time){
	var track = getCurrentTrack(time);

	for (var i=0;i<track.length;i++)
	{
		if(track[i].p2)
		{
			if(race.track[i].imei)
				race.coursemarkers[i+"_"+race.track[i].imei].setPosition(track[i].p1);
			else
				race.coursemarkers[i+"_p1"].setPosition(track[i].p1);

			if(race.track[i].imei2)
				race.coursemarkers[i+"_"+race.track[i].imei2].setPosition(track[i].p2);
			else
				race.coursemarkers[i+"_p2"].setPosition(track[i].p2);
			if (race.track[i].point_line)
			{
				race.track[i].point_line.setPath(new Array(track[i].p1, track[i].p2));
			}

		}
		else
		{
			if (race.track[i].imei)
				race.coursemarkers[i+"_"+race.track[i].imei].setPosition(track[i].point);
			else
			{
				if (race.coursemarkers[i])
					race.coursemarkers[i].setPosition(track[i].point);
				else
					alert (i);
			}
		}			
	}
	if(race.show_course)
	{
		var tmpcourse = new Array();
		for (i in track)
		{
			var c = track[i].point;
				tmpcourse.push(new google.maps.LatLng(c.lat(), c.lng()));

		}
		race.courseline.setPath(tmpcourse);

	}

}

function moveTracks(newval) //{{{
{
	var courselen = race.course.length;
	var tail_tmp = Math.round(Math.pow(2, ($('#tail_slider').slider("value")/60)));
	var fulltail = false;
	var current_bounds = map.getBounds();
	var suum = false;

	if ($('#tail_slider').slider("value") == $('#tail_slider').slider("option", "max"))
	{
		fulltail = true;
	}
	
	if (newval > current_time)
	{	
		for (var participant in race.participants)
		{	
			if (!race.participants[participant].gpoints || race.participants[participant].gpoints.length ==0)
				continue;
			var p = race.participants[participant];
			var len = p.gpoints.length;
			var latlng;
			for (var i=p.index; i < len; i++)
			{
				latlng = p.gpoints[i];
				p.index=i;	
				if (latlng.time <= newval)
				{}
				else
				{
					if (p.index > 0)
					{
						p.index--;
						latlng=p.gpoints[p.index];
					}
					break;
				}	
			}
			

			var speed = 0;
			if (newval - latlng.time < 90)
			{
				if (race.units == "m")
				{
					speed = latlng.speed*0.539956803;
				}
				else
				{
					speed = latlng.speed;
				}
				if(speed)
					$("#speed_"+participant).html(parseFloat(speed).toFixed(1));
			}
			else if (latlng.distance_to_finish != 0)
				$("#speed_"+participant).html("<span style='color:red;' title='"+time2str(latlng.time)+"'>!!!</span>");
			
			if (fulltail)
			{
				p.poly.setPath(p.gpoints.slice(1, p.index+1));
			}
			else
			{
				var tail_len = tail_tmp;
				if (p.index < tail_len)
				{
					tail_len = p.index-1;
				}
				var tail = p.gpoints.slice(p.index - tail_len, p.index+1);
				p.poly.setPath(tail);
			}
			var distance_to_finish = latlng.distance_to_finish;
						
			p.marker.setPosition(latlng);
			if (!current_bounds.contains(latlng))
					suum=true;
			delete distance_to_finish;
			delete latlng;
		}
		//move waypoints
		repaintTrack(newval);		
		
	}
	else if (newval < current_time)
	{
		for (var participant in race.participants)
		{
			var p = race.participants[participant];
			var latlng;
			for (var i=p.index; i >= 0; i--)
			{
				var latlng = p.gpoints[i];
				p.index=i;
				if (latlng.time >= newval)
				{}
				else
				{
					break;
				}	
			}
			var latlng = p.gpoints[p.index];
			if (latlng)
				p.marker.setPosition(latlng);
			else
				continue;

			if (race.units == "m")
			{
				speed = latlng.speed*0.539956803;
			}
			else
			{
				speed = latlng.speed;
			}
			$("#speed_"+participant).html(speed.toFixed(1));

			//$("#traveled_"+participant).html(line_distance(p.gpoints.slice(0, i)));
			if (fulltail)
			{
				p.poly.setPath(p.gpoints.slice(0, p.index+1));
;
			}
			else
			{
				var tail_len = tail_tmp;
				if (p.index < tail_len)
				{
					tail_len = p.index-1;
				}
				var tail = p.gpoints.slice(p.index - tail_len, p.index+1);
				p.poly.setPath(tail);
			}
			p.marker.setPosition(latlng);
			if (!current_bounds.contains(latlng))
					suum=true;

			delete distance_to_finish;
			delete latlng;

		}
	}
	else if ( newval == current_time )
	{
		return;
	}
	current_time = newval;
	showTime(current_time);
	
	if(!$.browser.msie && !race.no_course)
		sortTable();
	if ($('#autozoom').attr("checked") && suum == true)
		fitMapToMarkers();

}//}}}

function forward(value)//{{{
{
	var newtime = current_time + value;
	$("#slider").slider("value", newtime); 
}//}}}

function play(value)//{{{
{
	//clearInterval(intervalid);
	var newtime = current_time + value;
	$("#slider").slider("value", newtime); 
	//intervalid = setInterval("play(30)", 400);
}//}}}

function reverse(value)//{{{
{
	var newtime = current_time - value;
	$("#slider").slider("value", newtime);
}//}}}

function toggle_marker_all()//{{{
{
	if ($("#marker_all").attr("checked"))
	{
		$(".markertoggle").checkbox("check");
	}
	else
	{
		$(".markertoggle").checkbox("uncheck");
	}
	for (var i in race.participants)
	{
		var p = race.participants[i];
		if ($("#marker_all").attr("checked"))
		{
			//$("#marker_"+i).attr("checked", "checked");
			if (p.marker)
				p.marker.setVisible(true);
			if (p.poly)
				p.poly.setMap(map);
		}
		else	
		{
			//$("#marker_"+i).attr("aria-checked", "false");	
			if (p.marker)
				p.marker.setVisible(false);
			if (p.poly)
				p.poly.setMap(null);

		}
	}
}//}}}

function toggle_marker(imei)//{{{
{
	var p = race.participants[imei];
	if ($("#marker_"+imei).attr("checked"))
	{
		$("#track_"+imei).attr("checked", "checked");
		if (p.marker)
			p.marker.setVisible(true);
		if (p.poly)
			p.poly.setMap(map);
	}
	else
	{
		if (p.marker)
			p.marker.setVisible(false);
		if (p.poly)
			p.poly.setMap(null);
	}

}//}}}

function loophandler() { //{{{
	if (intervalid != 0)
	{
		clearInterval( intervalid );
		intervalid = 0;
		$('#playbutton').removeClass("ui-icon-pause");
		$('#playbutton').addClass("ui-icon-play");

	}
	else
	{
		var new_speed = 1100 - $('#speed_slider').slider("value");
		var hop = race.interval;
		intervalid = setInterval("play("+hop+")", new_speed);
		$('#playbutton').removeClass("ui-icon-play");
		$('#playbutton').addClass("ui-icon-pause");
	}
}//}}}

function changespeed() { //{{{
	if (intervalid != 0)
	{
		clearInterval( intervalid );
		intervalid = 0;
		var new_speed = 1100 - $('#speed_slider').slider("value");
		var hop = race.interval;
		intervalid = setInterval("play("+hop+")", new_speed);
	}
}//}}}


function sortTable() //{{{
{
	if (race.no_course)
		return;
	$('#part_table').each(function() {
		var $table = $(this);	
		var arr = $table.find('tbody > tr').get();
		var changed;
		var j, i, v, h, k;
		var length = arr.length-1;
		for (h=1; h < length; h=3*h+1);
		while (h=(h-1)/3)
		{
			for (i=h, j=i, v=arr[i]; i<=length; arr[j+h]=v, i++, j=i, v=arr[i])
			{
				while
				(
					(j-=h) >= 0
					&& 
					(
					 	( 
						 	!$(arr[j]).data("participant").currentpoint().finished && !$(v).data("participant").currentpoint().finished &&
					 		$(arr[j]).data("participant").currentpoint().distance_to_finish > 
							$(v).data("participant").currentpoint().distance_to_finish
						)
						||
							!$(arr[j]).data("participant").currentpoint().finished && $(v).data("participant").currentpoint().finished 
						||
						(
						 	$(v).data("participant").currentpoint().finished <
						  	$(arr[j]).data("participant").currentpoint().finished
						)

					)
				)
				{
					changed=true;
					arr[j+h]=arr[j];
				}
			}
		}
		
		var leaderdistance;
		for (var i = 0; i < arr.length;i++) 
		{
			var part = $(arr[i]).data("participant");
			if(!part)
				console.log(arr[i]);
			if (i == 0)
			{
				leaderdistance = part.getDistance2Finish();
				part.distanceelment.html(leaderdistance);
				if (islive)
					$("#progress").progressbar("value", part.getCoveredPercent());
			}
			else
			{
				part.distanceelment.html("+"+(part.getDistance2Finish() - leaderdistance).toFixed(2));
			}
			part.positionelement.html((i+1>9) ? (i+1) : "&nbsp;"+(i+1));

			if (changed)
			{
				$table.children('tbody').eq(0).append(arr[i]);
			}
		}
	});
}//}}}

function part_over(imei)//{{{
{
	var p = race.participants[imei];
	p.tempmarker.setVisible(true);
	p.tempmarker.setPosition(p.gpoints[p.index]);
}//}}}

function part_out(imei)//{{{
{
	var p = race.participants[imei];
	p.tempmarker.setVisible(false);
}//}}}

