

function pe(ol)
{
	s = "";
	for( var i=0,l=ol.length; i<l; i++ ) {
		s += String.fromCharCode(ol[i]);
	}
	return s;
}

var LogInEr = Class.create({
	initialize: function(loginForm, loginButton) {
		this.form = loginForm;
		this.submit = loginButton;

		this.form.onsubmit = (function() {
			this.doSubmit();
			return false;
		}).bind(this);
	}

	,doSubmit: function() {
		$('login_load_img').toggle();
		var url = this.form.action;
		new Ajax.Request(url, {
			parameters: this.form.serialize()
			,onSuccess:function(r) {
				var obj = r.responseJSON;
				if(obj && obj.success) {
					window.location = obj.redirect_to;
				}
				else {
					$('error').innerHTML = obj.message;
				}
			}
			,onFailuer:function() {
				$('error').innerHTML = "An error occurred during login. Please try again in a few minutes.";
			}
			,onComplete:function() { $('login_load_img').toggle(); }
		});
	}

});


var lastLogUrl = "";

function logLink(url,errorMsg,refresh) {
	if( refresh ) {
		new Ajax.Request(url,{onFailure:function(){alert(errorMsg)},onSuccess:function(){
			logLink(lastLogUrl,errorMsg);
		}});
	}
	else {
		lastLogUrl = url;
		return ajaxUpdater(url,$('log_body'),$('log_loading'),errorMsg);
	}
}


function togglePortlet(portletId) {
	var open = togglePortletBar(('pb_' + portletId),('pl_' + portletId));
	//new Ajax.Request('/profile/set_portlet_open/' + portletId + '/' + open);
}

function togglePortletBar(barId,contId,imgId,onPath,offPath) {
	var bo = $(barId);
	var co = $(contId);
	if( co.style.display == 'none' ) {
		co.show();
		if(imgId && $(imgId)) {
			$(imgId).src = offPath;
		}
		return 1;
	}
	//Opening
	else
	{
		co.hide();
		if(imgId && $(imgId)) {
			$(imgId).src = onPath;
		}
		return 0;
	}
}


function ajaxUpdater(url,updateElement,loadingElement,errorMsg) {
	new Ajax.Updater(
		{success:updateElement},
		url,
		{	asynchronous:true,
			evalScripts:true,
			onFailure:function(){alert(errorMsg)},
			onLoaded: function(){loadingElement.hide()},
			onLoading:function(){loadingElement.show()}
		}
	);
	return false;
}


////////////////////////////////////////////////////////
////////		Running Log Functions				////
////////////////////////////////////////////////////////

var RunMapPoint = Class.create();
RunMapPoint.prototype = {
	initialize: function(latlng,overlay,index,elevation) {
		this.latlng = latlng;
		this.overlay = overlay;
		this.elevation = elevation > 0 ? elevation : -1;
		this.index = index;
		this.meters = -1;
	}

	,getElevation: function()
	{
		if(this.elevation == -1 || !this.elevation) {
			var p = {0:this.latlng.toUrlValue()};
			//console.log("Getting elevation for " + this.index);
			var url = "/log/get_elevations";
			new Ajax.Request(url, { parameters:p,
						onSuccess: this.setElevation.bind(this),
						onFailure: function(){alert("Couldn't get elevation point!");}
					}
			);
		}
	}

	,setElevation:function(ajaxObj)
	{
		var jsonObj = ajaxObj.responseText.evalJSON();
		for( index in jsonObj) {
			this.elevation = jsonObj[index];
		}
	}
}



var RunMap = Class.create();
RunMap.prototype = {

	initialize: function(runId) {
		this.map        = null;
		this.runId		= runId;
		this.geocoder   = null;
		this.points     = [];
		this.miles      = [];
		this.mileIcons  = {};
		this.startIcon  = this.createIcon("/img/log/start.png", new GSize(20,31), new GPoint(10,31));
		this.pointIcon  = this.createIcon("/img/log/point.png", new GSize(10,10), new GPoint(5,5));
		this.endIcon    = this.createIcon("/img/log/finish.png", new GSize(20,31), new GPoint(10,31));
		this.distanceCont = null;
		this.elevationCont = null;
		this.metersInMile = 1609.344;
		this.routeOverlay = null;
		this.elevationChart = null;
		this.calculate = false;
        this.readonly = false;
	}

	,setDistanceContainer: function(id) {
		this.distanceCont = $(id);
	}

	,setElevationContainer: function(id) {
		this.elevationCont = $(id);
	}

	,createIcon: function(path,size,anchor) {
		var i = new GIcon(null,path);
		i.iconSize = size;
		i.shadow = null;
		i.shadowSize = null;
		if(!anchor) {
			anchor = new GPoint(parseInt(size.width/2), parseInt(size.height/2));
		}
		i.iconAnchor = anchor;
		return i;
	}

	,createMap : function(canvas, onMapLoad, addressFn) {
		this.map = new GMap2($(canvas));
		this.map.addControl(new GLargeMapControl());
		this.map.addControl(new GMapTypeControl());
		this.geocoder = new GClientGeocoder();

		// Attach Event Listeners
		var me = this;
        if( !this.readonly ) {
            GEvent.addListener(this.map, "click", function(overlay,point){ me.mapClicked(overlay,point);} );
        }
		if(onMapLoad) GEvent.addListener(this.map, "load", onMapLoad );
        if(addressFn) {
            addressFn(this);
        }
	}

	,setAddress : function( address, city, state, country, zoom) {
		zoom = zoom ? zoom : 13;
		if( address )
			var pts = [address,city,state];
		else
			var pts = [city,state];
		if( country ) pts.push(country);
		var addr = pts.join(", ");
		var me = this;
		this.geocoder.getLatLng(addr, function(point) {
			if(point){
				me.map.setCenter(point,zoom);
			}
			else {
				alert('Cant find address: ' + addr);
			}
		});
	}

	,mapClicked: function(overlay,point) {
		this.addPoint(point.x, point.y,true);
	}

	,clearMap: function() {
		this.map.clearOverlays();
		this.points = [];
		this.miles  = [];
		this.doCalculations();
	}

	,addPoint: function(x,y,pan,elevation) {
		var ico = this.pointIcon;
		var zproc = (function(){return 1;});
		if( this.points.length == 0 ) {
			ico = this.startIcon;
			var zproc = (function(){return 2;});
		}
		var gl = new GLatLng(y,x);
		var gm = new GMarker(gl, {icon:ico,clickable:false,zIndexProcess:zproc});
		this.map.addOverlay(gm);
		var mp = new RunMapPoint(gl,gm,this.points.length,elevation);
		mp.getElevation();
		this.points.push( mp );
		if(this.calculate) {
			if(pan){
				this.map.panTo(gl);
			}
			this.doCalculations();
		}
	}

	,removeLastPoint : function() {
		var lastPoint = this.points[ this.points.length - 1];
		if( lastPoint && lastPoint.overlay ) {
			this.map.removeOverlay(lastPoint.overlay);
			var len = this.points.length;
			delete lastPoint;
			delete this.points[len - 1];
			this.points.length = len - 1;
			this.doCalculations();
		}
	}

	,clearMiles : function() {
		var me = this;
		this.miles.each(function(m){
			me.map.removeOverlay(m.overlay);
		});
		this.miles = [];
	}

	,doCalculations : function() {
		this.calcDistance();
		this.drawOverlay();
	}

	,drawOverlay : function() {
		if( this.routeOverlay ) {
			this.map.removeOverlay(this.routeOverlay);
		}
		var pnts = [];
		this.points.each(function(p) {
			pnts.push(p.latlng);
		});
		var pline = new GPolyline(pnts, "#336699",5);
		this.map.addOverlay(pline);
		this.routeOverlay = pline;
	}

	,showElevationChart : function() {
		if(this.elevationChart) {
			this.elevationCont.innerHTML = "";
			delete this.elevationChart;
		}
		this.getElevationPoints();
	}

	,fireChartXML : function() {
		var url = "/log/elevation_chart_xml"
		var d = {};
		this.points.each(function(p){
			d[p.index] = [p.elevation,p.meters].join(",");
		});
		var me = this;
		new Ajax.Request(url,{parameters:d, onFailure: function(){alert("Couldn't get chart xml!");},
					onSuccess: function(ajaxObj) {
						me.elevationChart = new FusionCharts("/img/Charts/Line.swf", "ChartId", "600", "200", "0", "0");
						me.elevationChart.setDataXML(ajaxObj.responseText);
						me.elevationChart.render(me.elevationCont.id);
					}
				});

	}

	,pointsWithoutElevation: function() {
		var d = {};
		var needElevation = false;
		this.points.each(function(p) {
			if( p.elevation == -1 ) {
				d[p.index] = p.latlng.toUrlValue();
				needElevation = true;
			}
		});
		return {needElevation:needElevation,points:d};
	}

	,getElevationPoints: function() {
		var r = this.pointsWithoutElevation();
		if( r.needElevation ) {
			var url = "/log/get_elevations";
			new Ajax.Request(url, { parameters:r.points,
									onSuccess: this.retElevationPoints.bind(this),
									onFailure: function(){alert("Couldn't get elevation points!");},
									onLoading: function(){ $('log_elevation_loading').show(); },
									onLoaded: function(){ $('log_elevation_loading').hide(); }
					} );
		}
		else {
			this.fireChartXML();
		}
	}

	,retElevationPoints: function(ajaxObj) {
		var jsonObj = ajaxObj.responseText.evalJSON();
		for( index in jsonObj) {
			this.points[index].elevation = jsonObj[index];
		}
		this.fireChartXML();
	}

	,metersToMiles: function(m) {
		return m / this.metersInMile;
	}

	,calcDistance : function() {
		var meters = 0;
		this.clearMiles();
		var last = null;
		var me = this;
		this.points.each(function(p) {
			if(last) {
			var nextLeg = last.distanceFrom(p.latlng);
			// miles at a,b
			var mia = me.metersToMiles(meters);
			var mib = me.metersToMiles(meters + nextLeg);
			//We crossed a mile marker, find lat/lng point for mile
			if( Math.floor(mia) < Math.floor(mib) ) {
				var dist = Math.floor(mib) - Math.floor(mia);
				for( var i=1; i<=dist; i++) {
				var xa, ya, xb, yb, rat, lat, lng;
				//xa&b=miles, ya,b=lat|lng, rat=the mile we're on (Math.floor(mib) + i
				xa  = mia;
				xb  = mib;
				rat = Math.floor(mia) + i; // Current mile we're calculating
				// Calc lerp for lat
				ya  = last.lat();
				yb  = p.latlng.lat();
				lat = me.lerp(xa,ya,xb,yb,rat);
				ya  = last.lng();
				yb  = p.latlng.lng();
				lng = me.lerp(xa,ya,xb,yb,rat);
				me.addMileMarker( rat, lat, lng );
				}
			}
			meters += nextLeg;
			}
			last = p.latlng;
			p.meters = meters;
		});
		var miles = Math.round(this.metersToMiles(meters)*100)/100;
		this.setDistance(miles);
	}

	,setDistance : function(miles) {
        if(!this.distanceCont) return;
		this.distanceCont.innerHTML = miles + " miles";
		$('distance').value = miles;
	}

	,getMileIcon   : function(mile) {
		mile = mile % 100;
		if( !this.mileIcons[mile] )  {
			var mi  = this.createIcon("/img/log/marker" + mile + ".png", new GSize(15,15), new GPoint(7,7));
			this.mileIcons[mile] = mi;
		}
		return this.mileIcons[mile];
	}

	,addMileMarker : function(mile, lat, lng) {
		var gl = new GLatLng(lat,lng);
		var gm = new GMarker(gl, {icon:this.getMileIcon(mile),clickable:false,zIndexProcess:(function(){return 2;})});
		this.map.addOverlay(gm);
		this.miles.push( new RunMapPoint(gl,gm) );
	}

	,lerp : function(xa, ya, xb, yb, x) {
		return ya + ((x - xa) * (yb - ya))/(xb - xa)
	}

	,setOptimalViewport : function() {
		var bounds = this.calcOptimalViewport();
		if(!bounds) return;
		var center = bounds.getCenter();
		var zoom   = this.map.getBoundsZoomLevel(bounds);
		this.map.setZoom(zoom);
		this.map.panTo(center);
	}

	,calcOptimalViewport : function() {
		if( !this.points.length ) return null;
		var maxN=-99999, maxE=-99999, maxS=99999, maxW=99999;
		this.points.each(function(p){
			maxN = Math.max(maxN,p.latlng.lat());
			maxS = Math.min(maxS,p.latlng.lat());
			maxE = Math.max(maxE,p.latlng.lng());
			maxW = Math.min(maxW,p.latlng.lng());
		});
		var sw = new GLatLng(maxS,maxW);
		var ne = new GLatLng(maxN,maxE);
		return new GLatLngBounds( sw, ne );
	}

	,saveRun: function() {
		try {
			if( !($F('name') && $F('distance')) )
			{
				alert('Please include a name and a distance before saving.');
				return false;
			}
			var fo = $('route_form');
			var fo_fields = fo.serialize();
			fo_fields += '&points=' + escape(this.serializePoints());
			var url = '/log/save_run'
			if( this.runId ) url += '/' + this.runId;
			new Ajax.Request(url,{parameters:fo_fields,onSuccess:this.saveRunBack.bind(this)});
		}
		catch(e) {
			alert(e);
		}
		return false;
	}

	,saveRunBack: function(ajaxObj, jsonObj) {
		window.location = "/log/routes";
	}

	,serializePoints:function() {
		var points = [];
		this.points.each(function(p){
			points[p.index] = {lat:p.latlng.lat(), lng:p.latlng.lng(), elevation:p.elevation};
		});
		return Object.toJSON(points);
	}
}













/////////////////////////////////////////////////////////////////////
/////		DATE DROP OBJECT     ////////////////////////////////////
/////////////////////////////////////////////////////////////////////


function DateFields(containerNode,prefix,date,includeTime,yearRange,todayBtn,className)
{
	this.prefix = prefix;
	var year,month,day,hour,minutes,seconds,ampm;
	if( date && date.toLowerCase() == 'now' ) {
		year 	= parseInt((new Date()).getFullYear());
		month 	= parseInt((new Date()).getMonth() + 1);
		day 	= parseInt((new Date()).getDate());
		hour	= parseInt((new Date()).getHours());
		minutes	= parseInt((new Date()).getMinutes());
		seconds	= parseInt((new Date()).getSeconds());
	}
	else
	if( date && date.length > 0 && date.indexOf('-') > -1) {
		if( date.indexOf(' ') > -1 ) {
			var dp = date.split(' ');
			date = dp[0];
			time = dp[1];
			ampm = "am";
			var tp = time.split(':');
			for( var i=0; i<tp.length; i++ )
				if( tp[i].charAt(0) == '0' )
					tp[i] = tp[i].substr(1);
			hour   = parseInt(tp[0]);
			if( hour >= 13 ) {
				ampm = "pm";
				hour -= 12;
			}
			if( hour == 0 ) {
				ampm = "am";
				hour = 12;
			}
			minutes = parseInt(tp[1]);
			seconds = parseInt(tp[2]);
		}
		var parts = date.split('-');
		for( var i=1; i<3; i++) {
			if( parts[i].charAt(0) == '0') {
				parts[i] = parts[i].substr(1);
			}
		}
		year 	  = parseInt(parts[0]);
		month 	  = parseInt(parts[1]);
		day 	  = parseInt(parts[2]);
		//alert( year + ' ' + month + ' ' + day + ' ' + hour + ' ' + minutes + ' ' + seconds );
	}

	var yearRange = yearRange ? yearRange : [3,3];
	var startYear = parseInt((new Date()).getFullYear());
	var classNameAttrib = className ? " class=\""+className+"\"" : "";

	var html =  '<select name="'+prefix+'_yr" id="'+prefix+'_yr"'+classNameAttrib+'></select>-' +
				'<select name="'+prefix+'_mo" id="'+prefix+'_mo"'+classNameAttrib+'></select>-' +
				'<select name="'+prefix+'_day" id="'+prefix+'_day"'+classNameAttrib+'></select>';

	if( includeTime ) {
		html += ' &nbsp; <select name="'+prefix+'_hr" id="'+prefix+'_hr"'+classNameAttrib+'></select>:' +
				'<select name="'+prefix+'_min" id="'+prefix+'_min"'+classNameAttrib+'></select>:' +
				'<select name="'+prefix+'_sec" id="'+prefix+'_sec"'+classNameAttrib+'></select>' +
				'<select name="'+prefix+'_ampm" id="'+prefix+'_ampm"'+classNameAttrib+'></select>';
	}

	if( todayBtn ) {
		html += '<input id="'+prefix+'_today" type="button" value="today" style="font: 10px verdana; width: 40px;" />';
	}

	var yearObj  	= null;
	var monthObj 	= null;
	var dayObj   	= null;
	var todayObj	= null;
	var me 			= this;

	this.render = function() {
		if( containerNode ) {
			containerNode.innerHTML = html;
		}
		else {
			document.write(html);
		}

		yearObj  	= this.yearObj  = document.getElementById(prefix+'_yr');
		monthObj 	= this.monthObj = document.getElementById(prefix+'_mo');
		dayObj   	= this.dayObj   = document.getElementById(prefix+'_day');
		hrObj   	= this.hrObj    = document.getElementById(prefix+'_hr');
		minObj   	= this.minObj   = document.getElementById(prefix+'_min');
		secObj   	= this.secObj   = document.getElementById(prefix+'_sec');
		ampmObj   	= this.ampmObj  = document.getElementById(prefix+'_ampm');
		todayObj	= this.todayObj = document.getElementById(prefix+'_today');

		this.yearObj.options[0] = new Option( '', '' );
		var ys = 0;
		for( var i=(startYear+yearRange[1]); i>=(startYear-yearRange[0]); i--) {
			if( i==year ) ys = this.yearObj.options.length;
			this.yearObj.options[this.yearObj.options.length] = new Option(i,i);
		}
		this.yearObj.selectedIndex = ys;

		this.monthObj.options[0] = new Option( '', '' );
		var ms = 0;
		for( var i=1; i<=12; i++ ) {
			if( i==month ) ms = this.monthObj.options.length;
			this.monthObj.options[this.monthObj.options.length] = new Option(this.pad(i),i);
		}
		this.monthObj.selectedIndex = ms;

		this.dayObj.options[0] = new Option( '', '' );
		var ds = 0;
		for( var i=1; i<=31; i++ ) {
			if( i==day ) ds = this.dayObj.options.length;
			this.dayObj.options[this.dayObj.options.length] = new Option(this.pad(i),i);
		}
		this.dayObj.selectedIndex = ds;

		if( this.todayObj ) {
			this.todayObj.onclick = function() {
				me.today();
			}
		}

		if( hrObj ) {
			var hs = 0;
			hrObj.options[0] = new Option( '', '' );
			for( var i=1; i<=12; i++ ) {
				if( i==hour ) hs = hrObj.options.length;
				hrObj.options[hrObj.options.length] = new Option(i,i);
			}
			hrObj.selectedIndex = hs;
		}

		if( minObj ) {
			var ms = 0;
			minObj.options[0] = new Option( '', '' );
			for( var i=0; i<=59; i++ ) {
				if( i==minutes ) ms = minObj.options.length;
				minObj.options[minObj.options.length] = new Option(this.pad(i),i);
			}
			minObj.selectedIndex = ms;
		}

		if( secObj ) {
			var ss = 0;
			secObj.options[0] = new Option( '', '' );
			for( var i=0; i<=59; i++ ) {
				if( i==seconds ) ss = secObj.options.length;
				secObj.options[secObj.options.length] = new Option(this.pad(i),i);
			}
			secObj.selectedIndex = ss;
		}

		if( ampmObj ) {
			ampmObj.options[ampmObj.options.length] = new Option("AM","am");
			ampmObj.options[ampmObj.options.length] = new Option("PM","pm");
			ampmObj.selectedIndex = ampm == "am" ? 0 : 1;
		}

		yearObj.onchange  = function() {me.setDays();}
		monthObj.onchange = function() {me.setDays();}
		this.setDays();

	}

	this.pad = function(num) {
		if(num.toString().length == 1) {
			return "0" + num.toString();
		}
		return num.toString();
	}


	this.clear = function()
	{
		this.yearObj.selectedIndex = -1;
		this.monthObj.selectedIndex = -1;
		this.dayObj.selectedIndex = -1;
	}

	this.getValue = function()
	{
		if( !(this.yearObj.value && this.monthObj.value && this.dayObj.value) ) return null;
		return this.yearObj.value+"-"+this.monthObj.value+"-"+this.dayObj.value;
	}

	this.getDate = function()
	{
		if( !(this.yearObj.value || this.monthObj.value || this.dayObj.value) ) return null;
		return new Date( this.yearObj.value, this.monthObj.value, this.dayObj.value );
	}

	this.today = function()
	{
		var year 	= (new Date()).getFullYear();
		var month 	= (new Date()).getMonth() + 1;
		var day 	= (new Date()).getDate();

		for( var ix=0; ix<this.yearObj.options.length; ix++ ) {
			if( this.yearObj.options[ix].value == year ) {
				this.yearObj.options[ix].selected = true;
				this.yearObj.selectedIndex = ix;
			}
		}

		for( var ix=0; ix<this.monthObj.options.length; ix++ ) {
			if( this.monthObj.options[ix].value == month ) {
				this.monthObj.options[ix].selected = true;
				this.monthObj.selectedIndex = ix;
			}
		}

		for( var ix=0; ix<this.dayObj.options.length; ix++ ) {
			if( this.dayObj.options[ix].value == day ) {
				this.dayObj.options[ix].selected = true;
				this.dayObj.selectedIndex = ix;
			}
		}
	}

	this.setDays = function()
	{
		var y = me.yearObj.options[me.yearObj.selectedIndex].value;
		var m = me.monthObj.options[me.monthObj.selectedIndex].value;
		var totalDays = 0;

		// find number of days in current month
		if ( (m == 4) || (m == 6) || (m == 9) || (m == 11) ) {
			totalDays = 30;
		}
		else if (m == 2) {
			if ( (Math.floor(y/4) == (y/4)) && ((Math.floor(y/100) != (y/100)) || (Math.floor(y/400) == (y/400))) )
				totalDays = 29
			else
				totalDays =  28
		}
		else {
			totalDays = 31;
		}

		var lastDay = me.dayObj.options[ me.dayObj.options.length -1].value;
		// if (days in new month > current days) then we must add the extra days
		if (totalDays > lastDay) {
			for (i = lastDay; i <= totalDays; i++) {
				me.dayObj.options[me.dayObj.options.length] = new Option(i,i,false,false);
			}
		}

		// if (days in new month < current days) then we must delete the extra days
		if (totalDays < lastDay) {
			me.dayObj.length = me.dayObj.length - (lastDay -totalDays);
			if (me.dayObj.selectedIndex == -1) {
				me.dayObj.selectedIndex = totalDays - 1;
			}
		}
	}

}






//Training Log stuff
var IntervalManager = Class.create();
IntervalManager.prototype = {
	initialize: function() {

		this.intervals = [];

		this.tbody = $('int_tbody');
		this.etype = $('int_type');
		this.edistance = $('int_distance');
		this.eunits = $('int_distance_unit');
		this.edurration = $('int_durration');
		this.enotes = $('int_notes');
		this.addButton = $('int_add_button');

		this.checkAddDisabled();

		//attach events
		Event.observe(this.edistance, 'keyup', this.checkAddDisabled.bind(this));
		Event.observe(this.edurration, 'keyup', this.checkAddDisabled.bind(this));
		Event.observe(this.addButton, 'click', this.addButtonClicked.bind(this));

		Event.observe($('dur_h'),'keyup', this.calcPace.bind(this));
		Event.observe($('dur_m'),'keyup', this.calcPace.bind(this));
		Event.observe($('dur_s'),'keyup', this.calcPace.bind(this));
		Event.observe($('distance'),'keyup', this.calcPace.bind(this));
	}

	,calcPace: function()
	{
		//var durr = $F('durration');
		var dist = $F('distance');
		var du = $F('distance_unit');

		var secs = this.parseTime();
		if( secs == -1 ) {
			$('pace').value = 'N/A';
			$('pace_units').value = '';
		}
		else
		{
			var meters = 0;
			switch(du) {
				case 'Miles':
					meters = parseInt(dist * 1609.344);

				break;
				case 'Kilometers':
					meters = dist * 1000;
				break;
				case 'Meters':
					meters = dist;
				break;
			}
			var miles = meters / 1609.344;
			var spm = secs / miles;
			var spm_min = parseInt(spm / 60);
			var spm_secs = parseInt((spm - spm_min * 60));
			if( spm_secs < 10 ) {
				spm_secs = "0" + spm_secs;
			}
			$('pace').value = spm_min + ':' + spm_secs;
			$('pace_units').value = 'per mile';
		}
	}

	,parseTime: function(timeStr) {
		var _h = $F('dur_h');
		var _m = $F('dur_m');
		var _s = $F('dur_s');
		var secs = 0;
		if( _h ) {
			secs += parseInt(_h,10) * 3600;
		}
		if( _m ) {
			secs += parseInt(_m,10) * 60;
		}
		if( _s ) {
			secs += parseInt(_s,10);
		}
		return secs;
	}

	,parseTime2: function(timeStr)
	{
		if(timeStr.indexOf(':') > -1)
		{
			var pcs = timeStr.split(':');
			if( pcs.length == 2 )
			{
				secs = parseInt(pcs[0]) * 60;
				secs += parseInt(pcs[1])
				return secs;
			}
			else if(pcs.length > 2) {
				secs = parseInt(pcs[0]) * 3600;
				secs += parseInt(pcs[1]) * 60;
				secs += parseInt(pcs[2])
				return secs;
			}
		}
		else
		{
			var secs = parseInt(timeStr);
			if(secs) return secs;
		}
		return -1;
	}


	,addButtonClicked: function()
	{
		this.addInterval($F(this.etype), $F(this.edistance), $F(this.eunits), $F(this.edurration), $F(this.enotes));
		this.edurration.clear();
		this.enotes.clear();
		this.checkAddDisabled();
		this.autoSum();
		return false;
	}

	,autoSum : function()
	{
		var s = 0;
		this.intervals.each(function(interval) {
			var d = parseInt(interval['distance']);
			var du = interval['distance_units'];
			var meters = 0;
			switch(du) {
				case 'Miles':
					meters = parseInt(d * 1609.344);
				break;
				case 'Kilometers':
					meters = d * 1000;
				break;
				case 'Meters':
					meters = d;
				break;
			}
			s += meters;
		});

		var final_s = 0;
		var tu = $F('distance_unit');
		switch(tu) {
			case 'Miles':
				final_s = Math.round(s / 1609.344,2);
			break;
			case 'Kilometers':
				final_s = Math.round(s / 1000,2);
			break;
			case 'Meters':
				final_s = s;
			break;
		}
		$('distance').value = final_s;
	}

	,addObj:function(obj)
	{
		this.addInterval(obj.type, obj.distance, obj.distance_units, obj.durration, obj.notes);
	}

	,addInterval: function(type, distance, dist_units, time, notes)
	{
		var tr = document.createElement('tr');
		var td_type = document.createElement('td');
		var td_distance = document.createElement('td');
		var	td_time = document.createElement('td');
		var td_notes = document.createElement('td');
		var td_actions = document.createElement('td');
		tr.appendChild(td_type);
		tr.appendChild(td_distance);
		tr.appendChild(td_time);
		tr.appendChild(td_notes);
		tr.appendChild(td_actions);

		$(td_type).insert(type);
		$(td_distance).insert( distance + ' ' + dist_units );
		$(td_time).insert(time);
		$(td_notes).insert(notes);

		var intIndex = this.intervals.length;

		var interval = {
			type:type,
			distance:distance,
			distance_units:dist_units,
			durration:time,
			notes:notes,
			tr:tr,
			delbtn:null,
			upbtn:null,
			td_actions:td_actions,
			index:intIndex
		}

		var delbtn = document.createElement('input');
		delbtn.type = "button";
		delbtn.value = "Del";
		Event.observe(delbtn,'click',(function(){ this.delInterval(interval);}).bind(this));
		$(td_actions).insert(delbtn);
		interval.delbtn = delbtn;

		if(intIndex > 0 ) {
			var upbtn = document.createElement('input');
			upbtn.type = "button"
			upbtn.value = "Up";
			Event.observe(upbtn,'click',(function(){ this.moveUp(interval);}).bind(this));
			$(td_actions).insert(upbtn);
			interval.upbtn=upbtn;
		}

		this.tbody.insert(tr);
		this.intervals.push(interval);
	}

	,delInterval: function(interval)
	{
		$(interval.tr).remove();
		this.intervals = this.intervals.without(interval);
		for( var ix=0; ix<this.intervals.length; ix++) {
			this.intervals[ix].index = ix;
		}
	}

	,moveUp: function(interval)
	{
		var row = interval.index;
		if(row<1)return;
		var currentInt = interval
		var prevInt    = this.intervals[row-1];
		var currentTr  = currentInt.tr;
		var prevTr	   = prevInt.tr;

		$(prevTr).remove();
		$(currentTr).insert({after:prevTr});

		this.intervals[row] = prevInt;
		this.intervals[row-1] = currentInt;
		prevInt.index = row;
		currentInt.index = row-1;

		if( row-1 == 0 ) {
			currentInt.upbtn.remove();
			var upbtn = document.createElement('button');
			$(upbtn).insert("Up");
			Event.observe(upbtn,'click',(function(){ this.moveUp(prevInt);}).bind(this));
			$(prevInt.td_actions).insert(upbtn);
			prevInt.upbtn = upbtn;
			currentInt.upbtn = null;
		}
	}

	,onSubmit: function()
	{
		this.calcPace()
		$('intervals').value = this.intervals.toJSON();
		return true;
	}

	,checkAddDisabled: function()
	{
		if(this.edurration.value && this.edistance.value) {
			this.addButton.disabled = false;
		}
		else {
			this.addButton.disabled = true;
		}
	}
}



var LimitedTextArea = Class.create();
LimitedTextArea.prototype = {
	initialize: function(textAreaId, maxLength) {
		this.textArea = $(textAreaId);
		this.textAreaCountDiv = $(textAreaId + '_count');
		this.maxLength = maxLength;
		Event.observe(this.textArea, 'keypress', this.textAreaKeyPressed.bindAsEventListener(this));
		this.updateCount();
	}

	,textAreaKeyPressed : function(event)
	{
		this.updateCount();
		if(this.textArea.value.length > this.maxLength - 1)
		{
			var code;
			if(!event) var event = window.event;
			if(event.keyCode) code = event.keyCode;
			else if (event.which) code = event.which;
			switch(code) {
				case Event.KEY_BACKSPACE:
				case Event.KEY_DELETE:
				case Event.KEY_HOME:
				case Event.KEY_END:
					break;
				default: Event.stop(event);
				break;
			}
		}

	}

	,updateCount: function()
	{
		var cnt = this.textArea.value.length + 1;
		this.textAreaCountDiv.innerHTML = cnt + "/" + this.maxLength;
		return cnt;
	}
}


var SmartSearchBox = Class.create();
SmartSearchBox.prototype = {
	initialize: function(textBox, searchText) {
		this.textBox = textBox;
		this.searchText = searchText;
		if(this.textBox.value == "") this.textBox.value = searchText;
		Event.observe(this.textBox, 'focus', this.textBoxFocus.bindAsEventListener(this) );
		Event.observe(this.textBox, 'blur', this.textBoxBlur.bindAsEventListener(this) );
	}
	,textBoxFocus: function(event)
	{
		if( this.textBox.value == this.searchText )
			this.textBox.value = "";
	}
	,textBoxBlur: function(event)
	{
		if(this.textBox.value == "")
			this.textBox.value = this.searchText;
	}
}

var Paginator = Class.create();
Paginator.prototype = {
	 initialize: function(meth,args,block_size,total,template,container, nextButton,prevButton,infoContainer) {
		this.meth = meth;
		this.args = Object.toJSON(args);
		this.total = total;
		this.block_size = block_size;
		this.current = 0;
		this.nextCurrent = null;
		this.container = container;
		this.nextButton = nextButton;
		this.prevButton = prevButton;
		this.infoContainer = infoContainer;
		this.template = template;

		Event.observe(this.nextButton, 'click', this.next.bind(this));
		Event.observe(this.prevButton, 'click', this.prev.bind(this));
		this.update();
	}

	,next: function() {
		var from = (this.current + this.block_size);
		var by   = this.block_size;
		var url = "/pag/" + this.meth;
		this.nextCurrent = this.current + this.block_size;

		new Ajax.Request(url, {
			 parameters: {args:this.args, from:from, by:by, template:this.template}
			,onSuccess:this.updateContainer.bind(this)
			,onFailure:function(ajaxObj) {
				alert(ajaxObj.responseText);
			}
		});
	}

	,prev: function() {
		var from = (this.current - this.block_size);
		var by   = this.block_size;
		var url = "/pag/" + this.meth;
		this.nextCurrent = this.current - this.block_size;

		new Ajax.Request(url, {
			 parameters: {args:this.args, from:from, by:by, template:this.template}
			,onSuccess:this.updateContainer.bind(this)
			,onFailure:function(ajaxObj) {
				alert(ajaxObj.responseText);
			}
		});
	}

	,update: function() {
		if(this.current - this.block_size < 0 ) {
			this.prevButton.hide();
		}
		else {
			this.prevButton.show();
		}

		if(this.current + this.block_size > this.total) {
			this.nextButton.hide();
		}
		else {
			this.nextButton.show();
		}

		if(this.current - this.block_size < 0 && this.current + this.block_size > this.total) {
			this.infoContainer.hide();
			return;
		}
		else {
			this.infoContainer.show();
		}

		var a = this.current;
		if(a == 0 ) a = 1;
		var ofb = this.current + this.block_size;
		if(ofb > this.total) ofb = this.total;
		var ns = a + " - " + ofb + " of " + this.total;
		this.infoContainer.innerHTML = ns;
	}

	,updateContainer: function(ajaxObj) {
		var obj = ajaxObj.responseText.evalJSON();
		if( !obj.success ) {
			alert(obj.msg);
		}
		else {
			this.current = this.nextCurrent;
			this.container.innerHTML = obj.html;
			this.update();
		}

	}
}


var ForumPaginator = Class.create(Paginator, {
	 initialize: function(url,block_size, total, container, nextButton, prevButton, infoContainer) {
		this.total = total;
		this.url = url;
		this.block_size = block_size;
		this.current = 0;
		this.nextCurrent = null;
		this.container = container;
		this.nextButton = nextButton;
		this.prevButton = prevButton;
		this.infoContainer = infoContainer;

		Event.observe(this.nextButton, 'click', this.next.bind(this));
		Event.observe(this.prevButton, 'click', this.prev.bind(this));
		this.update();
	}

	,next: function() {
		var from = (this.current + this.block_size);
		var by   = this.block_size;
		this.nextCurrent = this.current + this.block_size;

		new Ajax.Request(this.url, {
			 parameters: {f:from, c:by}
			,onSuccess:this.updateContainer.bind(this)
			,onFailure:function(ajaxObj) {
				alert(ajaxObj.responseText);
			}
		});
	}

	,prev: function() {
		var from = (this.current - this.block_size);
		var by   = this.block_size;
		this.nextCurrent = this.current - this.block_size;

		new Ajax.Request(this.url, {
			 parameters: {f:from, c:by}
			,onSuccess:this.updateContainer.bind(this)
			,onFailure:function(ajaxObj) {
				alert(ajaxObj.responseText);
			}
		});
	}

	,updateContainer: function(ajaxObj) {
		this.current = this.nextCurrent;
		this.container.innerHTML = ajaxObj.responseText;
		this.update();

	}
});



/**
 * @author Ryan Johnson <http://saucytiger.com/>
 * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
 * @package LivePipe UI
 * @license MIT
 * @url http://livepipe.net/core
 * @require prototype.js
 */

if(typeof(Control) == 'undefined')
	Control = {};

var $proc = function(proc){
	return typeof(proc) == 'function' ? proc : function(){return proc};
};

var $value = function(value){
	return typeof(value) == 'function' ? value() : value;
};

Object.Event = {
	extend: function(object){
		object._objectEventSetup = function(event_name){
			this._observers = this._observers || {};
			this._observers[event_name] = this._observers[event_name] || [];
		};
		object.observe = function(event_name,observer){
			if(typeof(event_name) == 'string' && typeof(observer) != 'undefined'){
				this._objectEventSetup(event_name);
				if(!this._observers[event_name].include(observer))
					this._observers[event_name].push(observer);
			}else
				for(var e in event_name)
					this.observe(e,event_name[e]);
		};
		object.stopObserving = function(event_name,observer){
			this._objectEventSetup(event_name);
			if(event_name && observer)
				this._observers[event_name] = this._observers[event_name].without(observer);
			else if(event_name)
				this._observers[event_name] = [];
			else
				this._observers = {};
		};
		object.observeOnce = function(event_name,outer_observer){
			var inner_observer = function(){
				outer_observer.apply(this,arguments);
				this.stopObserving(event_name,inner_observer);
			}.bind(this);
			this._objectEventSetup(event_name);
			this._observers[event_name].push(inner_observer);
		};
		object.notify = function(event_name){
			this._objectEventSetup(event_name);
			var collected_return_values = [];
			var args = $A(arguments).slice(1);
			try{
				for(var i = 0; i < this._observers[event_name].length; ++i)
					collected_return_values.push(this._observers[event_name][i].apply(this._observers[event_name][i],args) || null);
			}catch(e){
				if(e == $break)
					return false;
				else
					throw e;
			}
			return collected_return_values;
		};
		if(object.prototype){
			object.prototype._objectEventSetup = object._objectEventSetup;
			object.prototype.observe = object.observe;
			object.prototype.stopObserving = object.stopObserving;
			object.prototype.observeOnce = object.observeOnce;
			object.prototype.notify = function(event_name){
				if(object.notify){
					var args = $A(arguments).slice(1);
					args.unshift(this);
					args.unshift(event_name);
					object.notify.apply(object,args);
				}
				this._objectEventSetup(event_name);
				var args = $A(arguments).slice(1);
				var collected_return_values = [];
				try{
					if(this.options && this.options[event_name] && typeof(this.options[event_name]) == 'function')
						collected_return_values.push(this.options[event_name].apply(this,args) || null);
					for(var i = 0; i < this._observers[event_name].length; ++i)
						collected_return_values.push(this._observers[event_name][i].apply(this._observers[event_name][i],args) || null);
				}catch(e){
					if(e == $break)
						return false;
					else
						throw e;
				}
				return collected_return_values;
			};
		}
	}
};

/* Begin Core Extensions */

//Element.observeOnce
Element.addMethods({
	observeOnce: function(element,event_name,outer_callback){
		var inner_callback = function(){
			outer_callback.apply(this,arguments);
			Element.stopObserving(element,event_name,inner_callback);
		};
		Element.observe(element,event_name,inner_callback);
	}
});

//mouseenter, mouseleave
//from http://dev.rubyonrails.org/attachment/ticket/8354/event_mouseenter_106rc1.patch
Object.extend(Event, (function() {
	var cache = Event.cache;

	function getEventID(element) {
		if (element._prototypeEventID) return element._prototypeEventID[0];
		arguments.callee.id = arguments.callee.id || 1;
		return element._prototypeEventID = [++arguments.callee.id];
	}

	function getDOMEventName(eventName) {
		if (eventName && eventName.include(':')) return "dataavailable";
		//begin extension
		if(!Prototype.Browser.IE){
			eventName = {
				mouseenter: 'mouseover',
				mouseleave: 'mouseout'
			}[eventName] || eventName;
		}
		//end extension
		return eventName;
	}

	function getCacheForID(id) {
		return cache[id] = cache[id] || { };
	}

	function getWrappersForEventName(id, eventName) {
		var c = getCacheForID(id);
		return c[eventName] = c[eventName] || [];
	}

	function createWrapper(element, eventName, handler) {
		var id = getEventID(element);
		var c = getWrappersForEventName(id, eventName);
		if (c.pluck("handler").include(handler)) return false;

		var wrapper = function(event) {
			if (!Event || !Event.extend ||
				(event.eventName && event.eventName != eventName))
					return false;

			Event.extend(event);
			handler.call(element, event);
		};

		//begin extension
		if(!(Prototype.Browser.IE) && ['mouseenter','mouseleave'].include(eventName)){
			wrapper = wrapper.wrap(function(proceed,event) {
				var rel = event.relatedTarget;
				var cur = event.currentTarget;
				if(rel && rel.nodeType == Node.TEXT_NODE)
					rel = rel.parentNode;
				if(rel && rel != cur && !rel.descendantOf(cur))
					return proceed(event);
			});
		}
		//end extension

		wrapper.handler = handler;
		c.push(wrapper);
		return wrapper;
	}

	function findWrapper(id, eventName, handler) {
		var c = getWrappersForEventName(id, eventName);
		return c.find(function(wrapper) { return wrapper.handler == handler });
	}

	function destroyWrapper(id, eventName, handler) {
		var c = getCacheForID(id);
		if (!c[eventName]) return false;
		c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
	}

	function destroyCache() {
		for (var id in cache)
			for (var eventName in cache[id])
				cache[id][eventName] = null;
	}

	if (window.attachEvent) {
		window.attachEvent("onunload", destroyCache);
	}

	return {
		observe: function(element, eventName, handler) {
			element = $(element);
			var name = getDOMEventName(eventName);

			var wrapper = createWrapper(element, eventName, handler);
			if (!wrapper) return element;

			if (element.addEventListener) {
				element.addEventListener(name, wrapper, false);
			} else {
				element.attachEvent("on" + name, wrapper);
			}

			return element;
		},

		stopObserving: function(element, eventName, handler) {
			element = $(element);
			var id = getEventID(element), name = getDOMEventName(eventName);

			if (!handler && eventName) {
				getWrappersForEventName(id, eventName).each(function(wrapper) {
					element.stopObserving(eventName, wrapper.handler);
				});
				return element;

			} else if (!eventName) {
				Object.keys(getCacheForID(id)).each(function(eventName) {
					element.stopObserving(eventName);
				});
				return element;
			}

			var wrapper = findWrapper(id, eventName, handler);
			if (!wrapper) return element;

			if (element.removeEventListener) {
				element.removeEventListener(name, wrapper, false);
			} else {
				element.detachEvent("on" + name, wrapper);
			}

			destroyWrapper(id, eventName, handler);

			return element;
		},

		fire: function(element, eventName, memo) {
			element = $(element);
			if (element == document && document.createEvent && !element.dispatchEvent)
				element = document.documentElement;

			var event;
			if (document.createEvent) {
				event = document.createEvent("HTMLEvents");
				event.initEvent("dataavailable", true, true);
			} else {
				event = document.createEventObject();
				event.eventType = "ondataavailable";
			}

			event.eventName = eventName;
			event.memo = memo || { };

			if (document.createEvent) {
				element.dispatchEvent(event);
			} else {
				element.fireEvent(event.eventType, event);
			}

			return Event.extend(event);
		}
	};
})());

Object.extend(Event, Event.Methods);

Element.addMethods({
	fire:			Event.fire,
	observe:		Event.observe,
	stopObserving:	Event.stopObserving
});

Object.extend(document, {
	fire:			Element.Methods.fire.methodize(),
	observe:		Element.Methods.observe.methodize(),
	stopObserving:	Element.Methods.stopObserving.methodize()
});

//mouse:wheel
(function(){
	function wheel(event){
		var delta;
		// normalize the delta
		if(event.wheelDelta) // IE & Opera
			delta = event.wheelDelta / 120;
		else if (event.detail) // W3C
			delta =- event.detail / 3;
		if(!delta)
			return;
		var custom_event = event.element().fire('mouse:wheel',{
			delta: delta
		});
		if(custom_event.stopped){
			event.stop();
			return false;
		}
	}
	document.observe('mousewheel',wheel);
	document.observe('DOMMouseScroll',wheel);
})();

/* End Core Extensions */

//from PrototypeUI
var IframeShim = Class.create({
	initialize: function() {
		this.element = new Element('iframe',{
			style: 'position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);display:none',
			src: 'javascript:void(0);',
			frameborder: 0
		});
		$(document.body).insert(this.element);
	},
	hide: function() {
		this.element.hide();
		return this;
	},
	show: function() {
		this.element.show();
		return this;
	},
	positionUnder: function(element) {
		var element = $(element);
		var offset = element.cumulativeOffset();
		var dimensions = element.getDimensions();
		this.element.setStyle({
			left: offset[0] + 'px',
			top: offset[1] + 'px',
			width: dimensions.width + 'px',
			height: dimensions.height + 'px',
			zIndex: element.getStyle('zIndex') - 1
		}).show();
		return this;
	},
	setBounds: function(bounds) {
		for(prop in bounds)
			bounds[prop] += 'px';
		this.element.setStyle(bounds);
		return this;
	},
	destroy: function() {
		if(this.element)
			this.element.remove();
		return this;
	}
});


/**
 * @author Ryan Johnson <http://saucytiger.com/>
 * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
 * @package LivePipe UI
 * @license MIT
 * @url http://livepipe.net/control/textarea
 * @require prototype.js, livepipe.js
 */

if(typeof(Prototype) == "undefined")
	throw "Control.TextArea requires Prototype to be loaded.";
if(typeof(Object.Event) == "undefined")
	throw "Control.TextArea requires Object.Event to be loaded.";

Control.TextArea = Class.create({
	initialize: function(textarea){
		this.onChangeTimeout = false;
		this.element = $(textarea);
		$(this.element).observe('keyup',this.doOnChange.bindAsEventListener(this));
		$(this.element).observe('paste',this.doOnChange.bindAsEventListener(this));
		$(this.element).observe('input',this.doOnChange.bindAsEventListener(this));
		if(!!document.selection){
			$(this.element).observe('mouseup',this.saveRange.bindAsEventListener(this));
			$(this.element).observe('keyup',this.saveRange.bindAsEventListener(this));
		}
	},
	doOnChange: function(event){
		if(this.onChangeTimeout)
			window.clearTimeout(this.onChangeTimeout);
		this.onChangeTimeout = window.setTimeout(function(){
			this.notify('change',this.getValue());
		}.bind(this),Control.TextArea.onChangeTimeoutLength);
	},
	saveRange: function(){
		this.range = document.selection.createRange();
	},
	getValue: function(){
		return this.element.value;
	},
	getSelection: function(){
		if(!!document.selection)
			return document.selection.createRange().text;
		else if(!!this.element.setSelectionRange)
			return this.element.value.substring(this.element.selectionStart,this.element.selectionEnd);
		else
			return false;
	},
	replaceSelection: function(text){
		var scroll_top = this.element.scrollTop;
		if(!!document.selection){
			this.element.focus();
			var range = (this.range) ? this.range : document.selection.createRange();
			range.text = text;
			range.select();
		}else if(!!this.element.setSelectionRange){
			var selection_start = this.element.selectionStart;
			this.element.value = this.element.value.substring(0,selection_start) + text + this.element.value.substring(this.element.selectionEnd);
			this.element.setSelectionRange(selection_start + text.length,selection_start + text.length);
		}
		this.doOnChange();
		this.element.focus();
		this.element.scrollTop = scroll_top;
	},
	wrapSelection: function(before,after){
		this.replaceSelection(before + this.getSelection() + after);
	},
	insertBeforeSelection: function(text){
		this.replaceSelection(text + this.getSelection());
	},
	insertAfterSelection: function(text){
		this.replaceSelection(this.getSelection() + text);
	},
	collectFromEachSelectedLine: function(callback,before,after){
		this.replaceSelection((before || '') + $A(this.getSelection().split("\n")).collect(callback).join("\n") + (after || ''));
	},
	insertBeforeEachSelectedLine: function(text,before,after){
		this.collectFromEachSelectedLine(function(line){
		},before,after);
	}
});
Object.extend(Control.TextArea,{
	onChangeTimeoutLength: 500
});
Object.Event.extend(Control.TextArea);

Control.TextArea.ToolBar = Class.create(	{
	initialize: function(textarea,toolbar){
		this.textarea = textarea;
		if(toolbar)
			this.container = $(toolbar);
		else{
			this.container = $(document.createElement('ul'));
			this.textarea.element.parentNode.insertBefore(this.container,this.textarea.element);
		}
	},
	attachButton: function(node,callback){
		node.onclick = function(){return false;}
		$(node).observe('click',callback.bindAsEventListener(this.textarea));
	},
	addButton: function(link_text,callback,attrs){
		var li = document.createElement('li');
		var a = document.createElement('a');
		a.href = '#';
		this.attachButton(a,callback);
		li.appendChild(a);
		Object.extend(a,attrs || {});
		if(link_text){
			var span = document.createElement('span');
			span.innerHTML = link_text;
			a.appendChild(span);
		}
		this.container.appendChild(li);
	}
});



var ForumCommands = {
	bold: {html:'<b>B</b>', help:'Bold text [b]text[/b]', mask:'[b]{0}[/b]'}
   ,italic:	{html:'<i>i</i>', help:'Italic text [i]text[/i]', mask:'[i]{0}[/i]'}
   ,underline: {html:'<u>u</u>', help:'Underline text [u]text[/u]', mask:'[u]{0}[/u]'}
   ,quote: {html:'Quote', help:'Quote text [quote]text[/quote]', mask:'[quote]{0}[/quote]'}
   ,url: {html:'Url', help:'Insert url [url]http://url[/url]', mask:'[url]{0}[/url]'}
   ,img: {html:'Image', help:'Insert image [img]http://image_url[/img]', mask:'[img]{0}[/img]'}
   ,flash: {html:'Flash', help:'Flash: [flash=width,height]http://flash_url[/flash]', mask:'[flash=]{0}[/flash]'}
};

var ForumPostRTB = Class.create();
ForumPostRTB.prototype = {

	initialize: function(buttonContainer, bodyTextArea) {
		this.buttonContainer = buttonContainer;
		this.bodyTextArea = new Control.TextArea(bodyTextArea);
		this.buildButtons();
	}


	,buildButtons: function() {
		var me = this;
		Object.keys(ForumCommands).each(function(cmd) {
			var co = ForumCommands[cmd];
			var newButton = document.createElement('button');
			newButton.innerHTML = co.html;
			newButton.title = co.help;
			Event.observe(newButton,'click', function(event){ me.onCommandPressed(event,cmd) });
			me.buttonContainer.appendChild(newButton);
		});

	}

	,onCommandPressed: function(event, command) {
		var co = ForumCommands[command];
		var mp = co.mask.split('{0}');
		this.bodyTextArea.wrapSelection(mp[0],mp[1]);
		Event.stop(event);
	}
};


var RunFinder = Class.create();
RunFinder.prototype = {
    initialize: function()
    {
        this.map			= null;
		this.geocoder		= null;
		this.startIcon		= this.createIcon("/img/log/start.png", new GSize(20,31), new GPoint(10,31));
		this.searchTimeout	= null;
		this.routes			= {};
		this.lastFilter	    = null;
		this.coverage = {
			n:0, e:0, s:0, w:0
		};
		this.extra_latlng = .9;
    }

	,reset: function()
	{
		this.routes	  = {};
		this.coverage = {
			n:0, e:0, s:0, w:0
		};
		this.map.clearOverlays();
	}

    ,createIcon: function(path,size,anchor) {
		var i = new GIcon(null,path);
		i.iconSize = size;
		i.shadow = null;
		i.shadowSize = null;
		if(!anchor) {
			anchor = new GPoint(parseInt(size.width/2), parseInt(size.height/2));
		}
		i.iconAnchor = anchor;
		return i;
	}

    ,createMap : function(canvas, onMapLoad, addressFn) {
		this.canvas = canvas;
		this.map = new GMap2($(canvas));
		this.map.addControl(new GLargeMapControl());
		this.map.addControl(new GMapTypeControl());
		this.geocoder = new GClientGeocoder();

		// Attach Event Listeners
		var me = this;
		GEvent.addListener(this.map, "moveend", this.mapViewChanged.bind(this));
		GEvent.addListener(this.map, "zoomend", this.mapViewChanged.bind(this));
		if(onMapLoad) GEvent.addListener(this.map, "load", onMapLoad );
        if(addressFn) {
            addressFn(this);
        }
	}

	,getFilter : function()
	{
		var filter = {
			 cb_me: $F('cb_me')
			,cb_friends: $F('cb_friends')
			,cb_anyone: $F('cb_anyone')
			,rating: filterStars.average
			,db_low: $F('db_low')
			,db_high: $F('db_high')
		};
		return Object.toQueryString(filter);
	}

	,setAddress : function( address, city, state, country, zoom) {
		zoom = zoom ? zoom : 13;
		if( address )
			var pts = [address,city,state];
		else
			var pts = [city,state];
		if( country ) pts.push(country);
		var addr = pts.join(", ");
		var me = this;
		this.geocoder.getLatLng(addr, function(point) {
			if(point){
				me.map.setCenter(point,zoom);
			}
			else {
				alert('Cant find address: ' + addr);
			}
		});
	}

	,getNewBounds: function()
	{
		var bounds = this.map.getBounds();
		var ne = bounds.getNorthEast();
		var sw = bounds.getSouthWest();
		var gridc = {n:ne.lat(),e:ne.lng(),s:sw.lat(),w:sw.lng()};
		var newc = Object.clone(this.coverage);
		var changes = [];
		for( var x in this.coverage )
		{
			if( x == 'n' || x == 'e' ) {
				if( gridc[x] > this.coverage[x] || this.coverage[x] == 0 ) {
					var nv = gridc[x] + this.extra_latlng;
					newc[x] = nv;
					changes.push(x);
				}

			}
			if( x == 's' || x == 'w' ) {
				if( gridc[x] < this.coverage[x] || this.coverage[x] == 0 ) {
					var nv = gridc[x] - this.extra_latlng;
					newc[x] = nv;
					changes.push(x);
				}
			}
		}

		//Optimizations
		if( changes.length > 0 ) {
			return newc;
		}

		return false;
	}

	,updateCoverage: function(newBounds)
	{
		if(this.coverage.n == 0) {
			this.coverage = Object.clone(newBounds);
			return;
		}
		this.coverage.n = Math.max(newBounds.n, this.coverage.n);
		this.coverage.s = Math.min(newBounds.s, this.coverage.s);
		this.coverage.e = Math.max(newBounds.e, this.coverage.e);
		this.coverage.w = Math.min(newBounds.w, this.coverage.w);

	}


	,mapViewChanged: function()
	{
		if(this.searchTimeout) {
			clearTimeout(this.searchTimeout);
		}

		var searchFilter = this.getFilter();
		if(searchFilter != this.lastFilter) {
			this.lastFilter = searchFilter;
			this.reset();
		}

		var boundsToQuery = this.getNewBounds();
		if( boundsToQuery == false ) {
			return;
		}


		this.searchTimeout = setTimeout( (function() {

			this.updateCoverage(boundsToQuery);
			var params = new Hash(boundsToQuery);
			params = params.merge({loaded_runs:Object.keys(this.routes).join(',')});
			paramString = Object.toQueryString(params);
			paramString += "&" + this.lastFilter;

			new Ajax.Request('/log/search_route_by_lat_lng', {
				parameters: paramString
				,onSuccess: this.loadFoundRoutes.bind(this)
				,onFailure: function() { alert("Search Failed"); }
				,onLoading: this.setLoading.bind(this)
				,onComplete: (function() { this.stopLoading(); this.searchTimeout = null; }).bind(this)
			});

		}).bind(this), 1000);

	}


	,loadFoundRoutes: function(ajax) {
		var resp = ajax.responseText.evalJSON();
		for( var x in resp )
		{
			if( this.routes[x] ) continue;

			this.routes[x] = resp[x];
			var route = resp[x];

			var gl = new GLatLng(route.lat,route.lng);
			var gm = new GMarker(gl, {icon:this.startIcon,title:route.name,clickable:true});
			var fm = function(me,overlay,route) {
				return function(obj) {
					me.runOverlayClicked(overlay,route);
				}
			}
			GEvent.addListener(gm, "click", fm(this,gm,route) );
			this.map.addOverlay(gm);
		}

	}

	,runOverlayClicked: function(overlay,route) {
		var starNode = Builder.node('div', {style:"height:25px;"});
		new Starbox(starNode, route.rating, {locked:true});
		var on_div = Builder.node('div',{style:"padding:2px;"});
		on_div.innerHTML = "by " + route.owner_name;
		var c = Builder.node('div',[
			 Builder.node('div', {style:"font-weight:bold;"}, route.name)
			,Builder.node('div', {style:"font-weight:bold;"}, route.distance+"")
			,Builder.node('div', {style:"font-weight:bold;"}, [
				Builder.node('span',"Rating"), starNode
			])
			,on_div
			,Builder.node('div', route.description)
			,Builder.node('a', {href:"/log/route/"+route.run_id},"View Route")
		]);

		this.map.openInfoWindow( overlay.getLatLng(), c );
	}

	,setLoading: function() {
		this.canvas.style.border = "2px solid red";
	}

	,stopLoading: function() {
		this.canvas.style.border = "1px solid black";
	}
};


var RatingStarBox = Class.create();
RatingStarBox.prototype = {
    initialize: function(container, avgRating, total, userRating, objectId, objectType, lock) {
        var o = {
             rated: userRating > 0 ? userRating : false
            ,locked: userRating > 0 || lock
            ,effect: {mouseover:true,mouseout:true}
            ,total:total
            ,onRate: function(element, memo) {
                var rating = memo.rated;
                new Ajax.Request('/profile/give_rating', {
                     parameters:{t:objectType,o:objectId,r:rating}
                    ,onFailure:function() {alert("Rating Failed!");}
                    ,onSuccess:function(ro) {
                        var rob = ro.responseText.evalJSON();
                        if( !rob.success ) {
                            alert(rob.reason);
                        }
                    }
                });
            }
        };
        new Starbox(container, avgRating, o);
    }
};


var BannerRotator = Class.create({
	initialize: function(container, imageList, options) {
		this.opts = {};
		this.imageList = imageList;
		this.container = container;
		this.setOptions(options);
		if( !( this.imageList && this.imageList.length > 0) ) {
			alert("Can't rotate an empty image list");
			return;
		}
		this.currentIndex = 0;
		this.imgCache = {};
		this.start();
	}

	,setOptions: function(opts) {
		if(!opts) opts = {};
		this.opts["delay"] = opts["delay"] ? opts["delay"] : 2000;
	}

	,start: function() {

		this.container.innerHTML = "";
		this.container.appendChild( this.getImg(this.currentIndex) );
		setInterval(this.rotate.bind(this), this.opts["delay"]);
	}

	,rotate: function() {
		this.currentIndex += 1;
		if( this.currentIndex >= this.imageList.length ) {
			this.currentIndex = 0;
		}
		var nextImg = this.getImg(this.currentIndex);
		var currImg = this.container.firstChild;
		//this.container.replaceChild( nextImg, this.container.firstChild);

		new Effect.Opacity(this.container,
			{duration: 0.75,
			 from: 1.0, to: 0.0,
			 afterFinish: (function() {
				this.container.replaceChild( nextImg, currImg );
				new Effect.Opacity(this.container,
					{duraction: 0.71,
					 from: 0.0, to: 1.0});

			 }).bind(this)
			});

	}

	,getImg: function(ix) {
		var img = null;
		if(this.imgCache[ix]) {
			img = this.imgCache[ix];
		}
		else {
			var imgSrc = this.imageList[this.currentIndex];
			img = new Image();
			img.src = imgSrc;
			this.imgCache[ix] = img;
		}
		return img;
	}

});

