// JavaScript Document


var cf = { // cf = commutefrom
	// globals
	month_names : new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"),
	nxtWrkDate : new Date(),
	// ajax request related code
	ajax : {
		searchdone : false, //  has a search been done 
		searchRequestTimer : null, 
		popupRequestTimer: null,
		stationDataCache: new Hash(), // local cache of data so don't have to go to DB when doing popup
		searchResults: null,
		useCache: true, // use cache or not
		facebook: false, // is this page being viewed from facebook?
		init: function(){ // initialise the form submit
			$('searchform').action='ajax.php'
			$('searchform').onsubmit=cf.ajax.submitSearchRequest;
			$('searchform').getElementsByTagName('a')[0].onclick=cf.ajax.submitSearchRequest.bind($('searchform'));
			$('advancedsearchform').action='ajax.php'
			$('advancedsearchform').onsubmit=cf.ajax.submitSearchRequest;
			cf.ajax.facebook = window.location.search.test("fb_sig_api_key");			
		},
		submitSearchRequest: function(){ // submit search form
			var form = this;
			if(form.id == 'advancedsearchform')	{
				cf.search.hideAdvancedSearch(); // hide advanced form if it was submitted
				// populate searchform with details from advanced form. Would ideally set the time as well, creating a new select option if one not available
				$('searchform').tocode.value = form.tocode.value;
				var from = 'Anywhere';
				if(form.fromstation.value.toUpperCase() != "ANY") from = form.fromstation.value;
				else if(form.frompostcode.value.toUpperCase() != "ANY") from = form.frompostcode.value;
				else if(form.fromregion.value != "ANY") from = form.fromregion.value;
				$('searchform').from.value = from;
			}
			form.blur(); // remove focus from form so can use keys for map controls or dismissing popup
			
			if(!cf.search.validateForm(form)) return false;
			
			currSearchRequest = form.send({onComplete: cf.ajax.handleSearchResponse});
			// hide any stuf related to previous search
			if(cf.map.popup.showing()) cf.map.popup.hide();
			// what if response never comes back?
			cf.search.disableForm(true); // disable form so user doesn't submit new request whilst this one is still going
			$$('body').setStyle('cursor','wait'); // make cursor the hour glass
			$('map'+cf.map.currentmap).setStyle('cursor', 'wait');		
			cf.mainpopup.show("<h4>Searching...</h4>", false, true);
			cf.ajax.searchRequestTimer = (function(){ // if request takes too long, cancel it and re-enable the form
				 	currSearchRequest.cancel();
					cf.mainpopup.show("<h4>Request timed out</h4>Please try again later.", true);
					cf.search.disableForm(false); // enable form
					$$('body').setStyle('cursor','auto'); // reset cursor
					$('map'+cf.map.currentmap).setStyle('cursor', (window.gecko?'-moz-grab':'auto'));				
				}).delay(30000);

			pageTracker._trackPageview("/events/submitsearch"); // google analytics
			return false; // don't submit the form and reload the page as usual 
		},
		handleSearchResponse: function(response){ // handle search response - populate main table and map search table
			// [{"from":"Hayes","to":"Paddington","minTime":"00:19:00","minChanges":"0","avgPrice":"253,985"},...]
			$clear(cf.ajax.searchRequestTimer); // clear the timer which cancels the request if its taken too long.
			cf.map.clearMarkers(); // clear all the markers from a previous search
			var results;
			try{
				results = Json.evaluate(response);  
			}catch(e){
				cf.mainpopup.show("<h4>Error</h4>Please notify us in the feedback form and try again later. <p>Error Details: " + e.message + "</p>");
				return;
			}
			cf.ajax.searchResults = results;
			var uniqueStations = new Array(); // a list of stations we've already done so for mapsearchresults we don't have duplicates
			var j = 0; // counter for colouring mapsearchresults
			var table = "<table cellspacing='3'><thead><tr><th scope='col' colspan='5'>from</th><th scope='col'>to</th><th scope='col' colspan='3'>journey</th>" +
				"</tr><tr><th scope='col'>station</th><th scope='col'>postcode <div class='comment'>(click for map)</div></th>" +
				"<th scope='col'>avg house price <div class='comment'>(click for details)</div></th><th scope='col'>local info</th>" +
                "<th scope='col'>property for sale</th><th scope='col'>station</th><th scope='col'>total time <div class='comment'>(click for details)</div></th>" +
			  	"<th scope='col'>train changes</th><th scope='col'>train frequency</th></tr></thead><tbody>" ;
			// html for main table
			var mapsearchresults = "Search results:<table><tr><th>stations<div class='comment'>(click to show details on map)</div></th></tr><tbody>";
			// html for side bar map search 
			if(results.length > 0) {
				for(var i=0; i<results.length; i++) { // iterate over search results, populating tables
					if(!uniqueStations.contains(results[i].fromcode)){ // only create marker and add to map search results if we haven't already done this station to avoid duplicates
						j++;	// increment from station counter
						cf.map.createMarker(results[i].fromcode); // create a marker on map for each result
						mapsearchresults = mapsearchresults + "<tr " + (j%2==1?"class='odd'":"class='even'") + "><td>" + 
								cf.ajax.getPopupLink(results[i].fromcode, results[i].from) + "</td></tr>";
						uniqueStations.extend([results[i].fromcode]);
					}
				}
			} else { // search returned no results
				table = table + "<tr><td colspan='6'>No results found</td></tr>";
				mapsearchresults = mapsearchresults + "<tr><td>No results found</td></tr>";
			}
			$('tablecontainer').innerHTML = table + "</tbody></table>";// insert html into document
			$('mapsearchresults').innerHTML = mapsearchresults  + "</tbody></table>";
			
			if(uniqueStations.length > 1){ // only need to ensure coords in view if not poping up as popup will do its own ensure in view (see popup for one result below)
				cf.map.ensureCoordsInView(cf.map.markercoords, true, true); // ensure all the markers are in view
			}
			cf.search.disableForm(false); // enable form
			$$('body').setStyle('cursor','auto'); // reset cursor
			$('map'+cf.map.currentmap).setStyle('cursor', (window.gecko?'-moz-grab':'auto'));				
			var dest = $('searchform').tocode.value;
			if(dest != 'ANY') cf.map.createMarker(dest, true); // mark the destination in a different colour if selected
			cf.map.showMarkers();
			if(results.length == 0) { // search contained no results - lets see if we can popup a useful message about how the user could improve their query - DOESNT WORK WITH ADVANCED SEARCH
				var popupText = "<h4>No results found</h4>";
				if($('searchform').from.value.toUpperCase() != "ANYWHERE" && dest != 'ANY' && $('searchform').time.value != 'ANY')
					popupText = popupText + "There are no journeys from the places you selected in the time you selected. Try searching for 'from " 
						+ $('searchform').from.value + " to " + cf.util.getSelectedText($('searchform').tocode) + " in Any time' or 'from Anywhere "
						+ " to " + cf.util.getSelectedText($('searchform').tocode) + " in " + cf.util.getSelectedText($('searchform').time) + "'";
				else if($('searchform').from.value.toUpperCase() != "ANYWHERE" && dest != 'ANY') // selected journey you can't do
					popupText = popupText + "There are no journeys between the places you selected. Try searching for 'from " 
						+ $('searchform').from.value + " to Any', or 'from Anywhere to " + cf.util.getSelectedText($('searchform').tocode) + "'";
				else if($('searchform').from.value.toUpperCase() != "ANYWHERE" && $('searchform').time.value != 'ANY') // selected time not possible from specified start
					popupText = popupText + "There are no journeys from the place you selected in the time you selected. Try searching for 'from " 
						+ $('searchform').from.value + " in Any time'";
				else 
					popupText = popupText + "No results were found for your query. Check your spelling or try making your query less specific.";
				cf.mainpopup.show(popupText, true);	
			} else if(uniqueStations.length > 50) { // search contained too many results
				var popupText = "<h4>Many results found</h4>Your search returned " + results.length + " results. If that's too many, try ";
				if($('searchform').from.value.toUpperCase() == "ANYWHERE" && dest != 'ANY' && $('searchform').time.value != 'ANY')
					popupText = popupText + "including a 'from' value (e.g. 'south-west', 'TW1', or 'Twickenham'), or using the advanced search to specify more options.";
				else if($('searchform').from.value.toUpperCase() != "ANYWHERE" && dest != 'ANY' && $('searchform').time.value == 'ANY')
					popupText = popupText + "including a 'time' value, or using the advanced search to specify more options.";
				else
					popupText = popupText + "making your query more specific.";
				cf.mainpopup.show(popupText, true);
			} else cf.mainpopup.hide();
			if(!cf.table.showing()) cf.sidePanel.showSearchResults(); // show the results in the side bar if map is showing
			else cf.table.populate(); // if table is showing populate the table
			cf.ajax.searchdone = true;
			// Add data to cache (do at the end as non critical and don't want to slow down user inteface above)
			if(cf.ajax.useCache)cf.ajax.addSearchResultsToCache(results);
			if(uniqueStations.length == 1) { // search contained one result, might as well show the popup - do it after added data to cache
				cf.map.popup.showNoElem(results[0].fromcode);
			} 
		},
		addSearchResultsToCache: function(results) {
			if(results.length > 0) {
				var useTube = false;
				if(results[0].tubeCode != null) useTube = true;
				var currentKey = results[0].fromcode+(useTube?results[0].tubeCode:'ANY'); // get first key for comparison
				var listForKey = new Array();
				for(var i=0; i<results.length; i++) { // iterate over search results, populating tables
					var newKey = results[i].fromcode+(useTube?results[i].tubeCode:'ANY');
					if(newKey != currentKey) {
						cf.ajax.stationDataCache.set(currentKey, listForKey);
						listForKey = new Array();
						currentKey = newKey;
					}
					listForKey.extend([results[i]]);
				}
				cf.ajax.stationDataCache.set(currentKey, listForKey); // add the last one
			}
		},
		getStationData: function(station) { // gets data for a station for the popup. Looks in the cache first, else sends a request
			var key = station+$('searchform').tocode.value;
			if(cf.ajax.stationDataCache.hasKey(key))
				cf.ajax.populatePopup(cf.ajax.stationDataCache.get(key));
			else cf.ajax.submitPopupRequest(station);
		},
		submitPopupRequest: function(station) { // submit request for map popup
			var popupRequest = new Ajax('ajax.php', {
					 	method: 'post',
						postBody: {querytype: 'search', fromcode: station, tocode: $('searchform').tocode.value},
						onComplete: cf.ajax.handlePopupResponse
					 }).request();
			if(cf.ajax.popupRequestTimer == null){ // if we're not currently processing a popup request				
				cf.ajax.popupRequestTimer = (function(){ // if request takes too long, cancel it and re-enable the form
						popupRequest.cancel();
						cf.mainpopup.show("<h4>Request timed out</h4>Please try again later.", true);
						cf.map.popup.hide();
						cf.ajax.popupRequestTimer = null;
					}).delay(15000);
			}			
		},
		handlePopupResponse: function(response) { // handle response for map popup 
			cf.ajax.popupRequestTimer = $clear(cf.ajax.popupRequestTimer);
			var stationData;
			try{
				stationData = Json.evaluate(response);  
			}catch(e){
				cf.mainpopup.show("<h4>Error</h4>Please notify us in the feedback form and try again later. <p>Error Details: " + e.message + "</p>");
				return;
			}
			cf.ajax.populatePopup(stationData);
			if(stationData.length > 0 && cf.ajax.useCache)
				cf.ajax.stationDataCache.set(stationData[0].fromcode+$('searchform').tocode.value, stationData);// TODO: hmm is this the best way to do it? should get data from response incase someone changed the form
		},
		populatePopup: function(stationData) { // populate the popup div
			var html;
			var isDestSet = $('searchform').tocode.value != 'ANY';
			if(stationData.length > 0){ // if we got some data 
				html = "<h4>" + stationData[0].from + (cf.map.popup.stationOnMap?"":" (not on map)") + "</h4>" +
						"<table class='stationinfo'><tr><td><strong>postcode/map:</strong></td><td> " + cf.ajax.getMapLink(stationData[0].postcode, stationData[0].postcode) + 
							" <span class='comment'>(click for map)</span></td></tr>" +
						"<tr><td><strong>avg house price:</strong></td><td> " + cf.ajax.getHousePriceLink(stationData[0].postcode, stationData[0].avgPrice) + 
							" <span class='comment'>(click for details)</span></td></tr>" +
						"<tr><td><strong>local info:</strong></td><td> " + cf.ajax.getLocalInfoLink(stationData[0].postcode) + "</td></tr>" +
						"<tr><td><strong>property for sale:</strong></td><td> " + cf.ajax.getPropertyLink(stationData[0].postcode) + "</td></tr></table>";
				if(isDestSet){
					html = html + "<h4> to " + (stationData[0].tubeEnd?stationData[0].tubeEnd:stationData[0].to) + " takes " + stationData[0].totalTime + "</h4>" +
							"<table cellspacing=3><tr><th scope='col'>to</th><th scope='col' colspan='4'>journey</th></tr>" +
								"<tr><th scope='col'>station</th><th scope='col'>type</th><th scope='col'>min time <div class='comment'>(click for details)</div></th>" + 
								"<th scope='col'>changes</th><th scope='col'>frequency</th></tr>" + 
								"<tr class='even'><td>" + stationData[0].to + "</td><td>train" +
								"</td><td>" + cf.ajax.getTimetableLink('popup', stationData[0].fromcode, stationData[0].tocode, stationData[0].trainTimeMin) +
								"</td><td>" + stationData[0].minChanges + 
								"</td><td> every " + stationData[0].frequency + "</td></tr>";
								if(stationData[0].tubeEnd) {
									html = html + "<tr class='odd'><td>" + stationData[0].tubeEnd + "</td><td>tube" +
									"</td><td>" + cf.ajax.getTFLLink(stationData[0].tubeTimeMin, stationData[0].tubeStart, stationData[0].tubeEnd) +
									"</td><td> n/a </td><td> n/a </td></tr>";
								}
					html = html + "</table>";  
					html = html + "<span class='comment'><br/>The total time includes a 5 min transfer time from train to tube.</span>";
					if(cf.ajax.facebook) html = html + '<br/>' + cf.facebook.getSaveFacebookProfileLink(
						 stationData[0].fromcode + (stationData[0].tubeCode?stationData[0].tubeCode:stationData[0].tocode));
				} else {
					html = html + "<table cellspacing=3><tr><th scope='col'>to</th><th scope='col' colspan='3'>journey</th></tr>" +
							"<tr><th scope='col'>station</th><th scope='col'>min time <div class='comment'>(click for details)</div></th>" + 
							"<th scope='col'>changes</th><th scope='col'>frequency</th></tr>";
					for(i=0; i<stationData.length; i++) { // table row for each destination station
						html = html + 
									"<tr " + (i%2==1?"class='odd'":"class='even'") + " ><td>" + (stationData[i].tubeEnd?stationData[i].tubeEnd:stationData[i].to) +
									"</td><td>" + cf.ajax.getTimetableLink('popup', stationData[i].fromcode, stationData[i].tocode, stationData[i].totalTime) +
									"</td><td>" + stationData[i].minChanges + 
									"</td><td> every " + stationData[i].frequency + "</td></tr>";
					}
					html = html + "</table>";
					html = html + "<span class='comment'><br/>This shows the journeys to central London mainline stations.<br/> To view the journey to a " + 
						"tube station	, select one in the 'to'<br/> dropdown in the search facility above. </span>";
					if(cf.ajax.facebook) html = html + "<br/>Select a destination in the 'to' dropdown, to <br/>save your commute to your facebook profile"
				}
			} else html = "No results...";
			var mappopupcontent = $('mappopupcontent' + cf.map.currentmap);
			mappopupcontent.innerHTML = html;
			if(window.ie6){
				var width = mappopupcontent.offsetWidth;
				mappopupcontent.setStyle('width', (width-22)+'px'); // hack for ie6 which makes the popup as thin as possible when dragging unless a width is set
			}
			cf.map.ensurePopupInView(); 
		},
		getPopupLink: function(code, text) { // link to show station popup on map
			return "<a href=\"javascript:cf.map.popup.showNoElem('" + code + "');\" >"+ text + "</a>";
		},
		getMapLink: function(postcode, text) { // link to google maps
			return "<a href='http://maps.google.co.uk/maps?q=" + postcode +
					"' target='blank' onclick='pageTracker._trackPageview(\"/links/map\");'>" + text + "</a>";
		},
		getTimetableLink: function(prefix, fromcode, tocode, text) { // post link to nationalrail.co.uk
			var formname = "RAIL" + prefix + fromcode + tocode;
			//http://ojp.nationalrail.co.uk/en/pj/jp
			return "<form name='" + formname + "' action='http://ojp.nationalrail.co.uk/en/s/planjourney/plan' method='post' target='blank'>" +
				"<input type='hidden' name='from.searchTerm' value='"+fromcode+"'/>" +
				"<input type='hidden' name='to.searchTerm' value='"+tocode+"'/>" +
				"<input type='hidden' name='timeOfOutwardJourney.monthDay' value='"+cf.nxtWrkDate.getDate()+"/"+(cf.nxtWrkDate.getMonth()+1)+"/"+cf.nxtWrkDate.getFullYear()+"'/>" + 
				//"<input type='hidden' name='timeOfOutwardJourney.day' value='"+cf.nxtWrkDate.getDate()+"'/>" +
				//"<input type='hidden' name='timeOfOutwardJourney.month' value='"+cf.month_names[cf.nxtWrkDate.getMonth()]+"'/>" + 
				"<input type='hidden' name='timeOfOutwardJourney.hour' value='08'/>" +
				"<input type='hidden' name='timeOfOutwardJourney.minute' value='45'/>" +
				"<input type='hidden' name='timeOfOutwardJourney.arrivalOrDeparture' value='ARRIVE'/>" +
				"<input type='hidden' name='commandName' value='journeyPlannerCommand'/>" +
				"<input type='hidden' name='via.searchTerm' value=''/>" +
				"<input type='hidden' name='jpState' value='single'/>" +
				"<input type='hidden' name='_directTrains' value='on'/>" +
				"<input type='hidden' name='_includeOvertakenTrains' value='on'/>" +
				"<input type='hidden' name='_lookForSleeper' value='on'/>" +
				"<input type='hidden' name='_reduceTransfers' value='on'/>" +
				"<input type='hidden' name='offSetOption' value='0'/>" +
				"<input type='hidden' name='operator.code' value=''/>" +
				"<input type='hidden' name='operatorMode' value='SHOW'/>" +
				"<a href='javascript:document." + formname + ".submit()' onclick='pageTracker._trackPageview(\"/links/nationalrail\");'>" + text + "</a>" +
			 "</form>";
		},
		getTFLLink: function(text, from, to) {
			var month = (cf.nxtWrkDate.getMonth()+1);
			if(month < 10) month = "0" + month;
			return "<a href='http://journeyplanner.tfl.gov.uk/user/XSLT_TRIP_REQUEST2?language=en&name_origin=" + encodeURIComponent(from.replace(/'/,'')) +
				"+underground+station&nameDefaultText_origin=start&type_origin=stop&place_origin=London&name_destination=" + encodeURIComponent(to.replace(/'/,'')) + 
				"+underground+station&nameDefaultText_destination=end&type_destination=stop&place_destination=London&" + 
				"itdTripDateTimeDepArr=dep&itdDateDay=" + cf.nxtWrkDate.getDate() + "&itdDateYearMonth=" + (cf.nxtWrkDate.getFullYear()) + month + 
				"&itdTimeHour=8&itdTimeMinute=30&Submit=Search&routeType=LEASTTIME&changeSpeed=normal' target='blank' onclick='pageTracker._trackPageview(\"/links/tfl\");'>" + 
				text + "</a>";
		},
		getHousePriceLink: function(postcode, text) { // link to bbc house price list
			return "<a href='http://www.bbc.co.uk/cgi-perl/whereilive/server/bouncer_api.pl?type=news_la_hp&tab=&loc="  +
					postcode + "' target='blank' onclick='pageTracker._trackPageview(\"/links/houseprice\");'>&pound;" + text + "</a>";
		},
		getLocalInfoLink: function(postcode) {
			return "<a href='http://www.upmystreet.com/local/schools/l/" + 
					postcode + ".html' target='blank' onclick='pageTracker._trackPageview(\"/links/schools\");'>schools</a>, " + 
					"<a href='http://www.upmystreet.com/local/police-crime/l/" + 
					postcode + ".html' target='blank' onclick='pageTracker._trackPageview(\"/links/crime\");'>crime rates</a>, " + 
					"<a href='http://www.upmystreet.com/local/my-neighbours/l/" + 
					postcode + ".html' target='blank' onclick='pageTracker._trackPageview(\"/links/profile\");'>profile</a>";
		},
		getPropertyLink: function(postcode) {
			return "<a href='http://www.rightmove.co.uk/edit_search.rsp?s_lo=" +
					postcode + "&b=buy&psa=new&search=start+search'" + 
					" target='blank' onclick='pageTracker._trackPageview(\"/links/rightmove\");'>right move</a>, " + 
					"<a href='http://www.primelocation.com/uk-property-for-sale/search/?p=" +
					postcode.replace(/ .*/,"") + "' target='blank' onclick='pageTracker._trackPageview(\"/links/primelocation\");'>prime location</a>, " +
					"<br/><a href='http://www.fish4.co.uk/iad/homes/result?LOCATION=" +
					postcode + "' target='blank' onclick='pageTracker._trackPageview(\"/links/fish4\");'>fish4</a>, " +
					"<a href='http://www.propertyfinder.com/cgi-bin/rsearch?id=" +
					postcode.replace(/[A-Z]+$/,"") + "&a=qfp&cu=fn-pfc&t=res&pm=&px=&minbed=0&maxbed=&cat=&q=Search' target='blank' onclick='pageTracker._trackPageview(\"/links/propertyfinder\");'>property finder</a> " ;
					
					// postcode.replace(/ .*/,"")
		}
	},
	facebook : {
		getSaveFacebookProfileLink: function(handle) {
			return "<form id='savefacebookprofileform' action='facebook/savefacebookprofile.php'><input type='hidden' name='handle' value='" + 
				handle + "'/><br/><a onclick='cf.facebook.saveFacebookProfile();' href='#'>Save this journey as your commute on your facebook profile</a></form>.";
		},
		saveFacebookProfile: function(){
			$('savefacebookprofileform').send({onComplete: cf.facebook.handleSaveFacebookProfileResponse});
			cf.mainpopup.show("<h4>Saving profile...</h4>", false, true);
			return false; // don't submit the form and reload the page as usual 
		},
		handleSaveFacebookProfileResponse: function(response){
			var success;
			try{
				success = Json.evaluate(response);  
			}catch(e){
				cf.mainpopup.show("<h4>Error</h4>Please notify us in the feedback form and try again later. <p>Error Details: " + e.message + "</p>");
				return;
			}
			if(success) cf.mainpopup.show("<h4>Profile saved</h4>", true, false);
			else  cf.mainpopup.show("<h4>Saving profile failed</h4> Please make sure you've added this application to your facebook profile, and that you are logged on to facebook, then try again.", true, false);
		}
	},
	map : {
		width : new Array(),  // array containing the width of each map
		height : new Array(), // array containing the height of each map
		containerwidth : 0,   // map container div width
		containerheight : 0,  // map container div height
		currentmap: 1,        // which map is currently showing
		minmap: 1,			  // the minimum map zoom level
		maxmap: 3,			  // the maximum map zoom level
		markercoords: new Array(), // the max and min coords of the various markers currently shown [min-x, min-y, max-x, max-y]
		markers: new Array(), // list of html strings for markers on each map
		mapMoveEffect: new Array(), // the most recent move action's Effect
		init: function(){ // initialise map & dimentions
			// set up the map and limits at the start and if the window is resized
			window.addEvent('resize',cf.map.setSize);
			cf.map.setSize();
			// keyboard and wheel map controls
			document.addEvent('keydown',function(e){
					e = new Event(e);
					if(e.target.parentNode.id != 'searchform' && e.target.parentNode.id != 'advancedsearchform' ){// don't apply map controls if we are using arrow keys to move along a text box in the search form
						if(!cf.table.showing()){	
							if(e.key == 'up') cf.map.moveMapBy(0,70);
							if(e.key == 'left') cf.map.moveMapBy(70,0); 
							if(e.key == 'right')cf.map.moveMapBy(-70,0);
							if(e.key == 'down') cf.map.moveMapBy(0,-70);
							if(e.key == '+' || e.key == '=' || e.code == 187) cf.map.zoomIn();
							if(e.key == '-' || e.key == 'm' || e.code == 189) cf.map.zoomOut();
						}
						if(e.key == 'enter' && cf.mainpopup.showing()) cf.mainpopup.hide();
					}
				});
			document.addEvent('mousewheel', function(e){
					if(!cf.table.showing()){								 
						e = new Event(e);
						if(e.wheel > 0) cf.map.zoomIn();
						else if(e.wheel < 0) cf.map.zoomOut();
					}
			    });
			
			for(var i=cf.map.minmap;i<=cf.map.maxmap;i++){ // loop through each map layer and init it
				// centralise the map
				var startleft = Math.round(cf.map.containerwidth/2 - cf.map.width[i]/2);
				var starttop = Math.round(cf.map.containerheight/2 - cf.map.height[i]/2);
				$('map'+i).setStyles({'left':startleft+'px', 'top':starttop+'px'});
				$('mappopup'+i).setOpacity(0); // hide the popup initially
				if(window.gecko){ // set up map cursors
					$('map'+i).onmousedown=function(){this.style.cursor='-moz-grabbing';};
					$('map'+i).onmouseup=function(){this.style.cursor='-moz-grab';};				
				} else {
					$('map'+i).onmousedown=function(){this.style.cursor='move';}; 
					$('map'+i).onmouseup=function(){this.style.cursor='auto';};				
				}
				cf.map.markercoords[i] = new Array();
				cf.map.markers[i] = "";
				$('map'+i).ondblclick=function(){cf.map.zoomIn();};
			}
			new Tips('.tool-tip-elem', {showDelay:700, 
							timeout: 4000, 
							onShow: function(tip){cf.util.show(tip);},
							onHide: function(tip){cf.util.hide(tip);} });
			$$('.tool-tip').each(function(t){t.setOpacity(0);});
		},
		setSize: function(){
			cf.map.containerwidth = $('mapcontainer').offsetWidth; 
			cf.map.containerheight = $('mapcontainer').offsetHeight;		
			for(var i=cf.map.minmap;i<=cf.map.maxmap;i++){ // loop through each map layer and init it
				cf.map.width[i] = $('map'+i).offsetWidth;
				cf.map.height[i] = $('map'+i).offsetHeight;
				// set dragability and limits
				var xlimit = cf.map.width[i] - cf.map.containerwidth;
				var ylimit = cf.map.height[i] - cf.map.containerheight-3; // ie leaves a 3px gap cause it counts the border
				$('map'+i).makeDraggable({limit:{x:[-xlimit,0],
											   y:[-ylimit,0]}
									   });
			}
		},
		loadMapImages: function(){ // load map images in sequence not parallel, so first map downdoads quickest
			$('mapimage2').onload=function(){$('mapimage3').src="images/commutefrommap3.png";};
			$('mapimage2').src="images/commutefrommap2.png";
		},
		moveMapTo: function(left, top){ // move map to a particular point
			if(cf.map.mapMoveEffect[cf.map.currentmap] == null) 
				cf.map.mapMoveEffect[cf.map.currentmap] = $('map' + cf.map.currentmap)
					.effects({duration: 1000, transition: Fx.Transitions.quartInOut, wait: false});
			cf.map.mapMoveEffect[cf.map.currentmap].start({'left':left,'top':top});
		},
		moveMapBy: function(left, top){ // move map by a particular amount
			var newTop = parseInt($('map' + cf.map.currentmap).getStyle('top')) + top;
			var newLeft = parseInt($('map' + cf.map.currentmap).getStyle('left')) + left;
			var xlimit = cf.map.width[cf.map.currentmap] - cf.map.containerwidth;
			var ylimit = cf.map.height[cf.map.currentmap] - cf.map.containerheight;
			if(newTop > 0) newTop = 0;
			if(newTop < -ylimit) newTop = -ylimit;
			if(newLeft > 0) newLeft = 0;
			if(newLeft < -xlimit) newLeft = -xlimit;
			cf.map.moveMapTo(newLeft, newTop);
		},
		show: function(){  // show the map, hide the table, show the map search results if a search has been done
			if(cf.table.showing()){
				cf.util.hide('tablecontainer');
				if(cf.ajax.searchdone) cf.sidePanel.showSearchResults();
			}
		},
		ensurePopupInView: function(){ // ensure that the popup is in view
			var mapopup = $('mappopup' + cf.map.currentmap);
			var top = parseInt(mapopup.getStyle('top'));
			var height = mapopup.offsetHeight;
			var left = parseInt(mapopup.getStyle('left')); 
			var width = mapopup.offsetWidth;
			// ensure popup doesn't flow over the edge of the map, which would mean ensurePopupInView would move map beyond its limit
			if(top + height + 10 > cf.map.height[cf.map.currentmap]) {
				top = cf.map.height[cf.map.currentmap] - height - 10;
				mapopup.setStyle('top',top+'px');
			}
			if(left + width + 10 > cf.map.width[cf.map.currentmap]) {
				left = cf.map.width[cf.map.currentmap] - width - 10;
				mapopup.setStyle('left',left+'px');
			}
			var coords = new Array();
			coords[cf.map.currentmap]=[left, top, left + width, top + height];
			cf.map.ensureCoordsInView(coords, false, false);
		},
		ensureCoordsInView: function(coords, useZoom, center){ // ensure that a set of coords are in view, [map][min-x, min-y, max-x, max-y]
			coordsWidth = coords[cf.map.currentmap][2] - coords[cf.map.currentmap][0]; // height of the coordinates on current map
			coordsHeight = coords[cf.map.currentmap][3] - coords[cf.map.currentmap][1];
			if((coordsWidth > cf.map.containerwidth || coordsHeight > cf.map.containerheight) &&
			   	  cf.map.currentmap > cf.map.minmap && useZoom) { // coords are bigger than viewable area so zoom out.
						cf.map.zoomOut();
						cf.map.ensureCoordsInView(coords, useZoom, center); // call recursively incase need to zoom out more than once.
			}
			if(cf.map.currentmap < cf.map.maxmap && useZoom) {
				zoomedInCoordsWidth = coords[cf.map.currentmap+1][2] - coords[cf.map.currentmap+1][0]; // height of coordinates on zoomed in map
				zoomedInCoordsHeight = coords[cf.map.currentmap+1][3] - coords[cf.map.currentmap+1][1];
				if(zoomedInCoordsWidth < cf.map.containerwidth && zoomedInCoordsHeight < cf.map.containerheight) { // coords are smaller than viewable area on zoomed in map, so zoom in.
							cf.map.zoomIn();
							cf.map.ensureCoordsInView(coords, useZoom, center); // call recursively incase need to zoom in more than once.
				}
			}
			// can this be refactored as its doing exactly the same thing twice?
			var currentTop = -parseInt($('map' + cf.map.currentmap).getStyle('top'));
			var currentLeft = -parseInt($('map' + cf.map.currentmap).getStyle('left'));
			var newTop, newLeft;
			if(center) { // center the coordinates in the view
				newTop = (coords[cf.map.currentmap][3] + coords[cf.map.currentmap][1])/2 - cf.map.containerheight/2; // put newTop s.t. the center of the coordinates is in the center of the view
				if(newTop < 0) newTop = 0; // unless its right at the top of the map
				else if(newTop + cf.map.containerheight - cf.map.height[cf.map.currentmap] > 0) newTop = cf.map.height[cf.map.currentmap] - cf.map.containerheight; // or right at the bottom
				newLeft = (coords[cf.map.currentmap][2] + coords[cf.map.currentmap][0])/2 - cf.map.containerwidth/2;
				if(newLeft < 0) newLeft = 0;
				else if(newLeft + cf.map.containerwidth - cf.map.width[cf.map.currentmap] > 0) newLeft = cf.map.width[cf.map.currentmap] - cf.map.containerwidth;
			} else { // ensure the coordinates aren't out of view, if so bring into view
				var coordsBottom = 10 + coords[cf.map.currentmap][3] - cf.map.containerheight;
				var coordsTop = -10 + coords[cf.map.currentmap][1];	
				if(coordsBottom > currentTop || coordsTop < currentTop) { // if coordsTop or coordsBottom are out of view, need to move map
					if(coordsBottom > currentTop) {  // if the bottom of the coords is out of view
						if(coordsBottom - coordsTop > 0) newTop = coordsTop; // if coords are larger than container, align top of coords to top of container
						else newTop = coordsBottom; // otherwise just bring bottom of coords in view
					}
					else if(coordsTop < currentTop ) newTop = coordsTop; // if the top of coords is out of view move so top is in view
				} else newTop = currentTop; // otherwise don't move map 
				var coordsRight = 10 + coords[cf.map.currentmap][2] - cf.map.containerwidth;
				var coordsLeft = -10 + coords[cf.map.currentmap][0];
				if(coordsRight > currentLeft || coordsLeft < currentLeft) { // if coordsLeft or coordsRight are out of view, need to move map
					if(coordsRight > currentLeft) newLeft = coordsRight;
					else if(coordsLeft < currentLeft ) newLeft = coordsLeft;
				} else newLeft = currentLeft;
			}
			if(newTop != currentTop || newLeft != currentLeft) // only move if we need to
				cf.map.moveMapTo(-newLeft,-newTop);
		},
		zoomIn: function(){ // zoom in the map
			if(cf.map.currentmap < cf.map.maxmap){
				cf.map.popupZoom(cf.map.currentmap+1); // sort out popup
				cf.map.alignMap(cf.map.currentmap+1); // align this map with the next one
				cf.util.hide('map'+cf.map.currentmap); // hide the current map
				cf.map.currentmap++;					// set the current map to be the next zoomed in
			}
		},
		zoomOut: function(){ // zoom out the map
			if(cf.map.currentmap > cf.map.minmap){
				cf.map.popupZoom(cf.map.currentmap-1); 
				cf.map.alignMap(cf.map.currentmap-1); 
				cf.map.currentmap--;
				cf.util.show('map'+cf.map.currentmap);				
			}			
		},
		popupZoom: function(map){ // deal with popup during zoom - if popup is showing show on zoomed map
				if(cf.map.popup.showing()) {
					var currentPopup = $('mappopup' + cf.map.currentmap);
					var zoomedPopup = $('mappopup' + map);
					(function(){currentPopup.setOpacity(0)}).delay(500);
					var left = parseInt(currentPopup.getStyle('left'));
					var top = parseInt(currentPopup.getStyle('top'));
					left = left * cf.map.height[map] / cf.map.height[cf.map.currentmap];
					top  = top  * cf.map.width[map] /  cf.map.width[cf.map.currentmap];
					zoomedPopup.setStyles({'left':left+'px', 'top':top+'px'});
					$('mappopupcontent' + map).innerHTML = $('mappopupcontent' + cf.map.currentmap).innerHTML;
					if(window.ie6){
						var width = $('mappopupcontent' + cf.map.currentmap).offsetWidth;
						$('mappopupcontent' + map).setStyle('width', (width-22)+'px'); // hack for ie6 which makes the popup as thin as possible when dragging unless a width is set
					}
					zoomedPopup.setOpacity(1);
				}
		},
		alignMap: function(map){ // align a map we are zooming to with one we are zooming from. Assumes the maps cover the same geograpical area as uses the map dimentions as to determine desired top and left values
			var top = (parseInt($('map' + cf.map.currentmap).getStyle('top')) - cf.map.containerheight/2)
						* cf.map.height[map] / cf.map.height[cf.map.currentmap] + cf.map.containerheight/2;
			var left = (parseInt($('map' + cf.map.currentmap).getStyle('left')) - cf.map.containerwidth/2) 
						* cf.map.width[map] / cf.map.width[cf.map.currentmap] + cf.map.containerwidth/2;
			// if we are zooming out, and we were at the extremeties of the map, then centralising will put us outside the exremeties of the map we are zooming to. Adjust that here.
			if(top < -(cf.map.height[map] - cf.map.containerheight)) top = -(cf.map.height[map] - cf.map.containerheight);			
			if(top > 0) top = 0;
			if(left < -(cf.map.width[map] - cf.map.containerwidth)) left = -(cf.map.width[map] - cf.map.containerwidth);			
			if(left > 0) left = 0;
			$('map'+map).setStyles({'left':left+'px', 'top':top+'px'});
		},
		createMarker: function(station, dest){ // create a marker next to a station, must call showMarkers after calling this to display them
			for(var i=cf.map.minmap;i<=cf.map.maxmap;i++){ // loop through each map layer and put marker
				var coords = cf.util.getTopMiddleOfArea($('map'+i+station));
				if(coords.length > 0) { // if this station is on the map
					coords[0] -= 12;
					coords[1] -= 25; // place marker above station text
					if(cf.map.markercoords[i].length==0) cf.map.markercoords[i] = [coords[0], coords[1], coords[0] + 25, coords[1] + 25];
					else{ // check if these coords are greater than any of the previous ones, if so amend cf.map.markercoords, so it contains the marker extremities so we can ensure they are in view
						if(coords[0] < cf.map.markercoords[i][0]) cf.map.markercoords[i][0] = coords[0];
						if(coords[1] < cf.map.markercoords[i][1]) cf.map.markercoords[i][1] = coords[1];
						if(coords[0] + 25 > cf.map.markercoords[i][2]) cf.map.markercoords[i][2] = coords[0] + 25;
						if(coords[1] + 25 > cf.map.markercoords[i][3]) cf.map.markercoords[i][3] = coords[1] + 25;
					}
					// add marker HTML to existing marker HTML for this map
					cf.map.markers[i] = cf.map.markers[i] + "<div class='marker' style='left:"+coords[0]+"px;top:"+coords[1]+"px;'>" +
						"<a href='#' onClick='cf.map.popup.showNoElem(\""+station+"\");'><img src='images/marker" + (dest?"dest":"") +
						".gif' border=0 width=25 height=25/></a></div>;"
				}
			}
		},
		showMarkers: function(){ // display the markers that have been put in cf.map.markers
			for(var i=cf.map.minmap;i<=cf.map.maxmap;i++){ // loop through each map layer and put markers HTML into mapmarkers div
				$('mapmarkers'+i).innerHTML = cf.map.markers[i];
			}
		},
		clearMarkers: function(){ // clear any markers from the current search
			for(var i=cf.map.minmap;i<=cf.map.maxmap;i++){ // loop through each map layer and delete markers
				$('mapmarkers'+i).empty();
				cf.map.markercoords[i] = new Array(); // reset the marker extremities
				cf.map.markers[i] = ""; // reset markers html
			}
		},
		popup : {
			stationOnMap: true, // is the station that the popup has been requested for on the map or not
			station: null, // the station that the popup has been requested for
			show: function(element, station){ // show the popup, submit request for journey details, expects a map area element
				cf.map.popup.station = station;
				var popup = $('mappopup' + cf.map.currentmap); 
				var popupcontent = $('mappopupcontent' + cf.map.currentmap);
				popupcontent.setStyle('width','auto'); // unset any width set by ie6 hack
				popupcontent.innerHTML = 'Loading ...';
				var coords = cf.util.getMiddleOfArea(element);
				if(coords.length > 0){
					popup.setStyles({'left':(coords[0]+3)+'px', 'top':(coords[1]+3)+'px'});
					cf.map.popup.stationOnMap = true;
				} else { // station not on map, so whack popup in middle of map
					popup.setStyles({'left': ((cf.map.width[cf.map.currentmap]-popup.offsetWidth)/2)+'px', 
								     'top': ((cf.map.height[cf.map.currentmap]-popup.offsetHeight)/2)+'px'});
					cf.map.popup.stationOnMap = false;
				}
				cf.util.show(popup);
				cf.map.ensurePopupInView();
				if(cf.search.destinations.contains(station)) { // one of the destination stations has been selected
					popupcontent.innerHTML = "<h4><a href='#' onclick=\"$('searchform').tocode.value='"+station+"'; cf.map.popup.hide(); \">" + cf.util.getTextForValue($('searchform').tocode, station) + "</a></h4>" + 
					"<p>This is a destination station - you can <br/> search for journeys to here</p>" +
					"<a href='#' onclick=\"$('searchform').tocode.value='"+station+"'; cf.map.popup.hide(); \">click here to make this your destination" +
					"<br/> station</a>, then click on another station  <br/>to view journeys to this one, or use the<br/> search</p>"
				} else {
					cf.ajax.getStationData(station);
				}
				pageTracker._trackPageview("/events/showpopup"); // google analytics
			},
			showNoElem: function(station){ // show the popup as above, used when no access to area element
				cf.map.show(); // checks if map is showing first then shows map;
				cf.map.popup.show($('map' + cf.map.currentmap + station), station);
			},
			hide: function(){ // hide the popup
				cf.util.hide('mappopup' + cf.map.currentmap);	
				cf.map.popup.station = null;
			},
			resubmit: function() { // if tocode changed and popup is showing resubmit popup request to get tube journey details
				if(cf.map.popup.station != null)
					cf.map.popup.showNoElem(cf.map.popup.station);
			},
			showing: function(){return cf.map.popup.station != null /*$('mappopup' + cf.map.currentmap).getStyle('opacity') != 0*/} // check if map is showing
		}
	},
	table : {
		init: function(){ $('tablecontainer').setOpacity(0);  },
		show: function(){ // show table, hide map, hide map search results if any
			if(!cf.table.showing()){
				// populate the table if we've done a search and we haven't already populated it with the current search data
				if(cf.ajax.searchdone) {
					cf.table.populate(); // we populate the table every time even if it hasn't changed since we last populated it - potential performance implications
					cf.sidePanel.showInfo();
				}
				cf.util.show('tablecontainer');
			}
			pageTracker._trackPageview("/events/showtable"); // google analytics
		},
		showing: function(){return $('tablecontainer').getStyle('opacity') != 0}, // check if table is showing
		toggleDetailRow: function(rowId) { // show the time detail row in results table
			var style = (window.ie?'block':'table-row');
			$(rowId).setStyle('display',($(rowId).getStyle('display')=='none'?style:'none'));
		},
		populate: function(){
			var uniqueStations = new Array(); // a list of stations we've already done so for mapsearchresults we don't have duplicates
			var j = 0; // counter for colouring mapsearchresults
			var k = 0; // counter for destinations within a from set
			var table = "<table cellspacing='3'><thead><tr><th scope='col' colspan='5'>from</th><th scope='col'>to</th><th scope='col' colspan='3'>journey</th>" +
				"</tr><tr><th scope='col'>station</th><th scope='col'>postcode <div class='comment'>(click for map)</div></th>" +
				"<th scope='col'>avg house price <div class='comment'>(click for details)</div></th><th scope='col'>local info</th>" +
                "<th scope='col'>property for sale</th><th scope='col'>station</th><th scope='col'>total time <div class='comment'>(click for details)</div></th>" +
			  	"<th scope='col'>train changes</th><th scope='col'>train frequency</th></tr></thead><tbody>" ;
			// html for main table
			var results = cf.ajax.searchResults;
			if(results != null && results.length > 0) {
				for(var i=0; i<results.length; i++) { // iterate over search results, populating tables
					if(!uniqueStations.contains(results[i].fromcode)){
						j++;	// increment from station counter
						k=0;
					} else k++; // incrememt to station counter
					table = table + "<tr class='" + ((j+k)%2==0?"odd":"even") + (k==0?" newsection":"") + "'>"; // row colouring
					if(!uniqueStations.contains(results[i].fromcode)){
						// how may rows in the table with this 'from' station: 
						var rowCount = results.filter(function(item){return item.fromcode == results[i].fromcode;}).length;  // inefficient!
						table = table + "<td rowspan='" + rowCount + "' >"+ cf.ajax.getPopupLink(results[i].fromcode, results[i].from) +
						"</td><td rowspan='" + rowCount + "' >" + cf.ajax.getMapLink(results[i].postcode, results[i].postcode) +
						"</td><td rowspan='" + rowCount + "' >" + cf.ajax.getHousePriceLink(results[i].postcode, results[i].avgPrice) + 
						"</td><td rowspan='" + rowCount + "' >" + cf.ajax.getLocalInfoLink(results[i].postcode) +
						"</td><td rowspan='" + rowCount + "' >" + cf.ajax.getPropertyLink(results[i].postcode);
					}
					var rowId = "resultstable" + i;
					table = table + "</td><td>" + (results[i].tubeEnd?results[i].tubeEnd:results[i].to) +
						"</td><td>" + 
						(results[i].tubeCode?
							"<a href=\"javascript:cf.table.toggleDetailRow('" + rowId + "');\">" + results[i].totalTime + "</a>":
							cf.ajax.getTimetableLink('table', results[i].fromcode, results[i].tocode, results[i].trainTimeMin)) +
						"</td><td>" + results[i].minChanges +
						"</td><td> every " + results[i].frequency +						
						"</td></tr>";
					if(results[i].tubeCode) { // only show detail drop down if tube part to journey
						table = table + "<tr style='display:none;' id='" + rowId + "'><td colspan=9 align='right'>" +
							"<div style='position:relative;'><table cellspacing=2>" +
								"<tr><th scope='col'>from train station</th><th scope='col'>to train station</th><th scope='col'>min time <div class='comment'>(click for details)</div></th>" +
								"<th scope='col'>></th><th scope='col'>from tube station</th><th scope='col'>to tube station</th><th scope='col'>min time <div class='comment'>(click for details)</div></th>" +
								"</tr><tr class='even'><td>" + results[i].from + "</td><td>" + results[i].to + 
								"</td><td>" + cf.ajax.getTimetableLink('table', results[i].fromcode, results[i].tocode, results[i].trainTimeMin) +
								"<td>></td><td>" + results[i].tubeStart + "</td><td>" +  results[i].tubeEnd + 
								"</td><td>" + cf.ajax.getTFLLink(results[i].tubeTimeMin, results[i].tubeStart, results[i].tubeEnd) + "</td>" +
								"</tr></table><div class='popupclose'><a href=\"javascript:cf.table.toggleDetailRow('" + rowId + "');\">x</a></div></div>"; 
							"</td></tr>";
					}
					if(!uniqueStations.contains(results[i].fromcode)){ // only create marker and add to map search results if we haven't already done this station to avoid duplicates
						uniqueStations.extend([results[i].fromcode]);
					}
				}
			} else { // search returned no results
				table = table + "<tr><td colspan='6'>No results found</td></tr>";
			}
			$('tablecontainer').innerHTML = table + "</tbody></table>";// insert html into document
			// done with results, clear up memory and flag don't need to build table again until new search
		}
	},
	mainpopup : {
			init: function(){$('mainpopup').setOpacity(0); /* hide popup initially*/},
			show: function(text, ok, busy){ // show the popup
				var popupText = text;
				if(ok) popupText = popupText + " <div class='menu button'><a href='#' onClick='cf.mainpopup.hide();'>ok</a></div>";
				if(busy) popupText = popupText + " <img src='images/busy.gif' class='busy'/>";
				$('mainpopupcontent').innerHTML = popupText;
				cf.util.show('mainpopup');
			},
			hide: function(){ // hide the popup
				cf.util.hide('mainpopup');	
			},
			showing: function(){return $('mainpopup').getStyle('opacity') != 0} // check if map is showing
	},
	search : {
		destinations: null, // list of desintation station codes so popup knows if it is a destination station
		init: function(){
			$('advancedsearchcontainer').setOpacity(0); /* hide advancedsearch initially*/
			
			$('advancedsearchform').getFormElements().each(function(e){
					if(e.type=="text"){
						e.onfocus = function(){if(this.value=='Any')this.value='';};
						e.onblur = function(){if(this.value.length==0)this.value='Any';};
					}
				});
			cf.search.destinations = new Array($('searchform').tocode.options.length);
			$each($('searchform').tocode.options, function(option){cf.search.destinations[option.index]=option.value;});
			$('searchform').tocode.onchange=cf.map.popup.resubmit;
			cf.search.initFromQuery();
		},
		initFromQuery: function(){
			// convert a query string: "from=test&time=%2700%3A00%27+and+%2700%3A10%27"
			// into a JSON object: "{from: "test", time: "'00:00' and '00:10'"}
			var queryString = window.location.search;
			if(queryString.length > 0 && queryString.test("querytype=search")) {
				queryString = "{" + decodeURIComponent(queryString).slice(1)
					.replace(/\+/g, " ").replace(/&/g,"\", ").replace(/=/g,": \"") + "\"}";
				var query = Json.evaluate(queryString);
				// populate search form from query
				$('searchform').getFormElements().each(function(e){
									if(e.type!="submit")e.value = query[e.name];});
				// only search if one of the values is different from 'Any'
				if(!cf.util.formSetToAny('searchform'))
					(cf.ajax.submitSearchRequest.bind($('searchform')))();
			}
		},
		linkToSearch: function(){ // ideally if no search but popup showing would populate from in form from popup and then submit
			if(!cf.util.formSetToAny('searchform')) { // set form action to home page and submit...
				$('searchform').action='index.html';
				$('searchform').submit();
			} else {
				cf.mainpopup.show("<h4>No values selected</h4>Please select some values in the search facility.", true);
			}
			pageTracker._trackPageview("/events/linktosearch"); // google analytics
		},
		showAdvancedSearch: function(){ // populate advanced search with info from standard one, and show it
			$('advancedsearchform').tocode.value = $('searchform').tocode.value;
			var from = $('searchform').from.value;
			var fromregion = 'ANY';
			var frompostcode = 'Any';
			var fromstation = 'Any';
			if(from != 'Anywhere') {
				if(/(south|north|east|west|south.*east|north.*east|south.*west|north.*west)/i.test(from)){
					fromregion = from.replace(/[^a-zA-Z]/, "-").toLowerCase().trim();
				} else if(/^[A-Z]{1,2}[0-9]{0,2}( [0-9]{0,2}[A-Z]{1,2}){0,1}$/i.test(from)){
					frompostcode = from.toUpperCase();
				} else {
					fromstation = from;
				}
			}
			$('advancedsearchform').fromregion.value = fromregion;
			$('advancedsearchform').frompostcode.value = frompostcode;
			$('advancedsearchform').fromstation.value = fromstation;
			
			var time = $('searchform').time.value;
			if(time != 'ANY'){
				var times = time.match(/[0-9][0-9]:[0-9][0-9]/g);
				$('advancedsearchform').timemin.value = times[0];
				$('advancedsearchform').timemax.value = times[1];
			}
			
			cf.util.show('advancedsearchcontainer');
			(function(){$('search').setOpacity(0)}).delay(500); // hide standard search because in IE6 select boxes are always on top of advanced search
			pageTracker._trackPageview("/events/showadvancedsearch"); // google analytics
		},
		hideAdvancedSearch: function(){
			$('search').setOpacity(1);			
			cf.util.hide('advancedsearchcontainer');
		},
		validateForm: function(form){ // This would work much better as a class with methods for isFromSet etc. that were implemented differently for both forms
			if(cf.util.formSetToAny(form)) {
				cf.mainpopup.show("<h4>No values selected</h4>Please select some values in the search facility.", true);
				return false;
			}
			var fromset;
			if(form.from) {
				fromset = form.from.value.toUpperCase() != "ANYWHERE";
			} else {
				fromset = (form.fromstation.value.toUpperCase() != "ANY" || form.frompostcode.value.toUpperCase() != "ANY" 
								|| form.fromregion.value != "ANY");
			}
			var toset = (form.tocode.value != 'ANY');
			var timeset = (form.time?form.time.value != 'ANY':form.timemin.value.toUpperCase() != 'ANY' || form.timemax.value.toUpperCase() != 'ANY');
			
			if(!fromset && toset && !timeset){ // will this work for advanced search?
				cf.mainpopup.show("<h4>Only destination selected</h4>This will return all journeys from every station to this one. " 
								  + "Please select a time or 'from' value in the search facility e.g. 'south-west', 'TW1', or 'Twickenham'", true);
				return false;
			}
			if(!fromset && !toset && timeset){
				cf.mainpopup.show("<h4>Only time selected</h4>This will return all journeys that take this time from any station to any other station." 
								  + "Please select a 'to' or 'from' value in the search facility", true);
				return false;
			}
			return true;
		},
		exportData: function(){
			if(cf.ajax.searchdone) {
				$('exportform').data.value = Json.toString(cf.ajax.searchResults);
				$('exportform').submit();
				pageTracker._trackPageview("/events/export"); // google analytics
			} else {
				cf.mainpopup.show("<h4>No search done</h4>You can only export the data you searched for, if you've done a search." 
								  + "Please do a search and try export again.", true);

			}
		},
		disableForm: function(disable){$('searchform').getChildren().each(function(e){e.disabled = disable});}
	},
	sidePanel : {
		//init: function(){ $('mapsearchresultscontainer').setOpacity(0); },
		showSearchResults: function(){ cf.util.show('mapsearchresultscontainer'); },
		showInfo: function(){ cf.util.hide('mapsearchresultscontainer'); }
	},
	util : {
		show: function(e){  if($(e).getStyle('opacity') == 0) cf.util.setOpacity(e, 1);  }, // fade to hidden
		hide: function(e){  if($(e).getStyle('opacity') != 0) cf.util.setOpacity(e, 0);  }, // fade to showing
		setOpacity: function(e, opacity){ $(e).effect('opacity', {duration: 500}).start(opacity);  },  // fade to a particular opacity
		setNextWorkingDate: function() { // compute the next weekday
			cf.util.addDay(cf.nxtWrkDate); // tomorrow
			// next working date from tomorrow
			while(cf.nxtWrkDate.getDay() == 0 || cf.nxtWrkDate.getDay() == 6){
				cf.util.addDay(cf.nxtWrkDate); 
			}
		},
		addDay: function(d) {d.setTime(d.getTime()+24*60*60*1000);}, // adds a day to a date
		formSetToAny: function(form){ // returns true if every item in search form is 'Any'.
			return $(form).getFormElements().every(function(e){return e.type == "submit" || e.type == "hidden" ||e.value.toUpperCase().substring(0,3) == 'ANY' || e.value == ''});
		},
		getAreaCoords: function(e){ // gets the coordinates of a rectange area element
			if(e && e.tagName.toLowerCase() == 'area'){
				var coords = e.coords.split(',');
				return new Array(parseInt(coords[0]),parseInt(coords[1]),parseInt(coords[2]),parseInt(coords[3]));
			} else return new Array();
		},
		getMiddleOfArea: function(e){ // gets the middle of the coordinates of a rectangle area element
			var coords = cf.util.getAreaCoords(e);
			if(coords.length > 0) return new Array((coords[0]+coords[2])/2, (coords[1]+coords[3])/2);
			else return new Array();
		},
		getTopMiddleOfArea: function(e){ // gets the top middle of the coordinates of a rectangle area element
			var coords = cf.util.getAreaCoords(e);
			if(coords.length > 0) return new Array((coords[0]+coords[2])/2, coords[1]);
			else return new Array();
		},
		getSelectedText: function(e){ // get selected text (not value) from a select element
			//var text = null;
			//$each(e.options, function(option){if (option.selected) text = option.text;});
			//return text;
			return e.options[e.selectedIndex].text;
		},
		getTextForValue: function(e, value){ // get text for a given value from a select element
			var text = null;
			$A(e.options).every(function(option){if (option.value == value) {
												    text = option.text; 
												    return false;
											     } else return true;});
			return text;
		}
	},
	init: function(){ // initialise everything
		cf.map.init();
		cf.table.init();
		cf.ajax.init();
		cf.util.setNextWorkingDate();
		cf.mainpopup.init();
		cf.search.init();
		//cf.sidePanel.init();
	}
};
window.addEvent('domready',cf.init);
// shortcuts
function popup(element, station) { // shortcut as this is called hundreds of times from html
	cf.map.popup.show(element, station);
}


