// JavaScript Document

//***********************************************************************/
//*	DATA STRUCTURES							*/
//***********************************************************************/

	function info() 
	{
	  var w = window.open("info-calculator.html",
	                      "information",
	                      "scrollbars,width=550,height=445",
	                      "replace",
	                      "resize");
	  w.document.close();
	  w.focus();
	}

//*********************************************************************/

	function month(name, numdays, abbr) 
	{
		this.name = name;
		this.numdays = numdays;
		this.abbr = abbr;
	}

//*********************************************************************/

	function ans(daySave,value)
	{
		this.daySave = daySave;
		this.value = value;
	}

//*********************************************************************/

	function city(name, lat, lng, height, zoneHr) 
	{
		this.name = name;
		this.lat = lat;
		this.lng = lng;
		this.height = height;
		this.zoneHr = zoneHr;
	}


//***********************************************************************/
//*	Data for Selectbox Controls					*/
//***********************************************************************/

	var monthList = new Array();	//	list of months and days for non-leap year
	var i = 0;
	monthList[i++] = new month("Januari", 31, "Jan");
	monthList[i++] = new month("Februari", 28, "Feb");
	monthList[i++] = new month("Maart", 31, "Mrt");
	monthList[i++] = new month("April", 30, "Apr");
	monthList[i++] = new month("Mei", 31, "Mei");
	monthList[i++] = new month("Juni", 30, "Jun");
	monthList[i++] = new month("Juli", 31, "Jul");
	monthList[i++] = new month("Augustus", 31, "Aug");
	monthList[i++] = new month("September", 30, "Sep");
	monthList[i++] = new month("Oktober", 31, "Okt");
	monthList[i++] = new month("November", 30, "Nov");
	monthList[i++] = new month("December", 31, "Dec");

//*********************************************************************/

	var YesNo = new Array();	//Daylight Saving array	
	i=0;
	YesNo[i++] = new ans("Neen",0);
	YesNo[i++] = new ans("Ja",60);

//*********************************************************************/

	var City = new Array();

	j = 0;
	City[j++] = new city("Kies een lokatie",0,0,0,0);
	City[j++] = new city("",0,0,0,0);
	City[j++] = new city("Aalten",51.9276,6.5802,0,+1);
	City[j++] = new city("Alkmaar",52.6321,4.7505,0,+1);
	City[j++] = new city("Almelo",52.3598,6.6657,0,+1);
	City[j++] = new city("Almere-Stad",52.3727,5.2219,0,+1);
	City[j++] = new city("Alphen a/d Rijn",52.1294,4.6613,0,+1);
	City[j++] = new city("Amersfoort",52.1560,5.3871,0,+1);
	City[j++] = new city("Amstelveen",52.2947,4.8340,0,+1);
	City[j++] = new city("Amsterdam",52.3753,4.8838,0,+1);
	City[j++] = new city("Apeldoorn",52.2211,5.9548,0,+1);
	City[j++] = new city("Arnhem",51.9796,5.9093,0,+1);
	City[j++] = new city("Assen",52.9939,6.5603,0,+1);
	City[j++] = new city("Baarn",52.2125,5.2920,0,+1);
	City[j++] = new city("Bergen op Zoom",51.4952,4.2880,0,+1);
	City[j++] = new city("Beverwijk",52.4866,4.6584,0,+1);
	City[j++] = new city("Boxtel",51.5906,5.3264,0,+1);
	City[j++] = new city("Breda",51.5901,4.7754,0,+1);
	City[j++] = new city("Bussum",52.2735,5.1635,0,+1);
	City[j++] = new city("Capelle a/d IJssel",51.9268,4.5954,0,+1);
	City[j++] = new city("Castricum",52.5473,4.6643,0,+1);
	City[j++] = new city("Coevorden",52.6621,6.7395,0,+1);
	City[j++] = new city("Culemborg",51.9578,5.2258,0,+1);
	City[j++] = new city("De Bilt (KNMI)",52.1024,5.1784,0,+1);
	City[j++] = new city("Delft",52.0126,4.3602,0,+1);
	City[j++] = new city("Den Haag",52.0779,4.3066,0,+1);
	City[j++] = new city("Den Helder",52.9619,4.7678,0,+1);
	City[j++] = new city("Deventer",52.2528,6.1542,0,+1);
	City[j++] = new city("Dieren",52.0436,6.1075,0,+1);
	City[j++] = new city("Doetinchem",51.9662,6.2875,0,+1);
	City[j++] = new city("Dordrecht",51.8149,4.6602,0,+1);
	City[j++] = new city("Drachten",53.1079,6.1018,0,+1);
	City[j++] = new city("Ede",52.0465,5.6709,0,+1);
	City[j++] = new city("Eindhoven",51.4383,5.4795,0,+1);
	City[j++] = new city("Emmen",52.7867,6.8941,0,+1);
	City[j++] = new city("Enkhuizen",52.7045,5.2927,0,+1);
	City[j++] = new city("Enschede",52.2219,6.8952,0,+1);
	City[j++] = new city("Geldrop",51.4233,5.5570,0,+1);
	City[j++] = new city("Geleen",50.9738,5.8426,0,+1);
	City[j++] = new city("Goes",51.5044,3.8904,0,+1);
	City[j++] = new city("Gorinchem",51.8299,4.9730,0,+1);
	City[j++] = new city("Gouda",52.0112,4.7107,0,+1);
	City[j++] = new city("Groningen",53.2205,6.5686,0,+1);
	City[j++] = new city("Haarlem",52.3819,4.6375,0,+1);
	City[j++] = new city("Harderwijk",52.3501,5.6158,0,+1);
	City[j++] = new city("Harlingen",53.1750,5.4131,0,+1);
	City[j++] = new city("Heemstede",52.3529,4.6132,0,+1);
	City[j++] = new city("Heerenveen",52.9623,5.9211,0,+1);
	City[j++] = new city("Heerhugowaard",52.6712,4.8440,0,+1);
	City[j++] = new city("Heerlen",50.8899,5.9786,0,+1);
	City[j++] = new city("Helmond",51.4792,5.6548,0,+1);
	City[j++] = new city("Hengelo",52.2667,6.7932,0,+1);
	City[j++] = new city("'s-Hertogenbosch",51.6884,5.3077,0,+1);
	City[j++] = new city("Hillegom",52.2921,4.5780,0,+1);
	City[j++] = new city("Hilversum",52.2227,5.1789,0,+1);
	City[j++] = new city("Hoofddorp",52.3088,4.6895,0,+1);
	City[j++] = new city("Hoogeveen",52.7271,6.4808,0,+1);
	City[j++] = new city("Hoogvlied",51.8653,4.3587,0,+1);
	City[j++] = new city("Hoorn",52.6405,5.0578,0,+1);
	City[j++] = new city("Houten",52.0281,5.1616,0,+1);
	City[j++] = new city("Kampen",52.5600,5.9160,0,+1);
	City[j++] = new city("Kerkrade",50.8671,6.0597,0,+1);
	City[j++] = new city("Leeuwarden",53.2042,5.7904,0,+1);
	City[j++] = new city("Leiden",52.1568,4.4917,0,+1);
	City[j++] = new city("Leiden (Sterrewacht)",52.1556,4.4839,0,+1);
	City[j++] = new city("Lelystad",52.5195,5.4842,0,+1);
	City[j++] = new city("Maarssen",52.1408,5.0400,0,+1);
	City[j++] = new city("Maastricht",50.8499,5.6872,0,+1);
	City[j++] = new city("Meppel",52.6985,6.1905,0,+1);
	City[j++] = new city("Middelburg",51.5000,3.6147,0,+1);
	City[j++] = new city("Middelharnis",51.7586,4.1653,0,+1);
	City[j++] = new city("Mijdrecht",52.2093,4.8681,0,+1);
	City[j++] = new city("Naarden",52.2966,5.1625,0,+1);
	City[j++] = new city("Nieuwegein",52.0294,5.0853,0,+1);
	City[j++] = new city("Nijkerk",52.2238,5.4828,0,+1);
	City[j++] = new city("Nijmegen",51.8487,5.8616,0,+1);
	City[j++] = new city("Nijverdal",52.3668,6.4632,0,+1);
	City[j++] = new city("Oldenzaal",52.3138,6.9275,0,+1);
	City[j++] = new city("Oss",51.7695,5.5212,0,+1);
	City[j++] = new city("Rhenen",51.9575,5.5640,0,+1);
	City[j++] = new city("Roermond",51.1972,5.9824,0,+1);
	City[j++] = new city("Roosendaal",51.5384,4.4637,0,+1);
	City[j++] = new city("Rotterdam",51.9221,4.4851,0,+1);
	City[j++] = new city("Schagen",52.7873,4.7970,0,+1);
	City[j++] = new city("Schiedam",51.9129,4.3988,0,+1);
	City[j++] = new city("Sittard",51.0012,5.8678,0,+1);
	City[j++] = new city("Sliedrecht",51.8227,4.7718,0,+1);
	City[j++] = new city("Sneek",53.0333,5.6589,0,+1);
	City[j++] = new city("Stadskanaal",53.0072,6.9243,0,+1);
	City[j++] = new city("Steenwijk",52.7889,6.1154,0,+1);
	City[j++] = new city("Terneuzen",51.3376,3.8270,0,+1);
	City[j++] = new city("Tiel",51.8854,5.4334,0,+1);
	City[j++] = new city("Tilburg",51.5559,5.0859,0,+1);
	City[j++] = new city("Utrecht",52.0913,5.1214,0,+1);
	City[j++] = new city("Utrecht (Sterrenwacht)",52.0860,5.1293,0,+1);
	City[j++] = new city("Valkenswaard",51.3508,5.4586,0,+1);
	City[j++] = new city("Veenendaal",52.0264,5.5533,0,+1);
	City[j++] = new city("Veghel",51.6179,5.5410,0,+1);
	City[j++] = new city("Veldhoven",51.4051,5.3976,0,+1);
	City[j++] = new city("Venlo",51.3718,6.1703,0,+1);
	City[j++] = new city("Venray",51.5284,5.9741,0,+1);
	City[j++] = new city("Vianen",51.9928,5.0935,0,+1);
	City[j++] = new city("Vlaardingen",51.9087,4.3426,0,+1);
	City[j++] = new city("Vlissingen",51.4431,3.5740,0,+1);
	City[j++] = new city("Waalwijk",51.6943,5.0921,0,+1);
	City[j++] = new city("Wageningen",51.9653,5.6621,0,+1);
	City[j++] = new city("Wassenaar",52.1459,4.3905,0,+1);
	City[j++] = new city("Weert",51.2549,5.7067,0,+1);
	City[j++] = new city("Weesp",52.3081,5.0424,0,+1);
	City[j++] = new city("Westerbork (Sterrenwacht)",52.9170,6.6040,0,+1);
	City[j++] = new city("Winschoten",53.1438,7.0391,0,+1);
	City[j++] = new city("Winterswijk",51.9728,6.7198,0,+1);
	City[j++] = new city("Woerden",52.0866,4.8839,0,+1);
	City[j++] = new city("Zaandam",52.4409,4.8267,0,+1);
	City[j++] = new city("Zaltbommel",51.8122,5.2520,0,+1);
	City[j++] = new city("Zevenaar",51.9140,6.0822,0,+1);
	City[j++] = new city("Zierikzee",51.6510,3.9150,0,+1);
	City[j++] = new city("Zoetermeer",52.0568,4.4957,0,+1);
	City[j++] = new city("Zutphen",52.1405,6.1944,0,+1);
	City[j++] = new city("Zwijndrecht",51.8200,4.6546,0,+1);
	City[j++] = new city("Zwolle",52.5133,6.0897,0,+1);

//*********************************************************************/



//*********************************************************************/


	function setLatLong(f, index)
	{
		// Decimal degrees are passed in the array.  Temporarily store these 
		// degs in lat and lon deg and have convLatLong modify them.

		f["latDeg"].value = City[index].lat;
		f["lonDeg"].value = City[index].lng;
	
		// These are needed to prevent iterative adding of min and sec when 
		// set button is clicked.

		f["latMin"].value = 0;
		f["latSec"].value = 0;
		f["lonMin"].value = 0;
		f["lonSec"].value = 0;
		
		//call convLatLong to convert decimal degrees into table form.

		convLatLong(f);

		//Local time zone value set in table

		f["height"].value = City[index].height;
		f["hrsToGMT"].value =  City[index].zoneHr;
	}


//*********************************************************************/

// isLeapYear returns 1 if the 4-digit yr is a leap year, 0 if it is not

	function isLeapYear(yr) 
	{
		return ((yr % 4 == 0 && yr % 100 != 0) || yr % 400 == 0);
	}


//*********************************************************************/

// isPosInteger returns false if the value is not a positive integer, true is
// returned otherwise.

	function isPosInteger(inputVal) 
	{
		inputStr = ("" + inputVal);
		for (var i = 0; i < inputStr.length; i++) {
			var oneChar = inputStr.charAt(i);
			if (oneChar < "0" || oneChar > "9")
				return false;
		}
		return true;
	}

//*********************************************************************/

	function isInteger(inputVal) 
	{
		inputStr = "" + inputVal;
		if(inputStr == "NaN") return false;
		if(inputStr == "-NaN") return false;
		for (var i = 0; i < inputStr.length; i++) 
		{
			var oneChar = inputStr.charAt(i);
			if (i == 0 && (oneChar == "-" || oneChar == "+"))
			{
				continue;
			}
			if (oneChar < "0" || oneChar > "9")
			{
				return false;
			}
		}
		return true;
	}


//*********************************************************************/

	function isNumber(inputVal) 
	{
		var oneDecimal = false;
		var inputStr = "" + inputVal;
		for (var i = 0; i < inputStr.length; i++) 
		{
			var oneChar = inputStr.charAt(i);
			if (i == 0 && (oneChar == "-" || oneChar == "+"))
			{
				continue;
			}
			if (oneChar == "." && !oneDecimal) 
			{
				oneDecimal = true;
				continue;
			}
			if (oneChar < "0" || oneChar > "9")
			{
				return false;
			}
		}
		return true;
	}


//*********************************************************************/

// isValidInput makes sure valid input is entered before going ahead to 
// calculate the sunrise and sunset.  False is returned if an invalid entry 
// was made, true is the entry is valid.

	function isValidInput(f, index, latLongForm) 
	{
		if ((latLongForm["height"].value < 0))
		{
			alert("De hoogte boven het niveau van de lokale horizon moet liggen tussen 0 en 10000 meter. \nDe hoogte zal worden veranderd in \"0\" meter.");
			latLongForm["height"].value = 0;
		}
		else if ((latLongForm["height"].value > 10000))
		{
			alert("De hoogte boven het niveau van de lokale horizon moet liggen tussen 0 en 10000 meter. \nDe hoogte zal worden veranderd in \"10000\" meter.");
			latLongForm["height"].value = 10000;
		}
		else if (f["day"].value == "") 
		{	//	see if the day field is empty
			alert("Alvorens de berekening kan worden uitgevoerd dient U een datum in te voeren.");
			return false;
		}
		else if (f["year"].value == "") 
		{	//	 see if the year field is empty
			alert("Alvorens de berekening kan worden uitgevoerd dient U een datum in te voeren.");
			return false;
		}
		else if (!isPosInteger(f["day"].value) || f["day"].value == 0)
		{
			alert("De dag moet een positief geheel getal zijn.");
			return false;
		}
		else if (!isInteger(f["year"].value)) 
		{
			alert("Het jaartal moet een geheel getal zijn.");
			return false;
		}
		else if ( (f["year"].value < 1600) || (f["year"].value > 2200) )
		{
			alert("Berekening kunnen alleen worden uitgevoerd voor de jaren tussen 1600 en 2200.");
			return false;
		}

		//	For the non-February months see if the day entered is greater than
		//	the number of days in the selected month

		else if ((index != 1) && (f["day"].value > monthList[index].numdays)) 
		{
			alert("De maand " + monthList[index].name + " telt slechts " + monthList[index].numdays + " dagen.");
			return false;
		}
		else if ((index != 1) && (f["day"].value > monthList[index].numdays)) 
		{
			alert("De maand " + monthList[index].name + " telt slechts " + monthList[index].numdays + " dagen.");
			return false;
		}

		//	First see if the year entered is a leap year.  If so we have to make sure
		//	the days entered is <= 29.  If not a leap year we make sure that the days
		//	entered is <= 28.

		else if (index == 1) 
		{	//	 month selected is February the screwball month
			if (isLeapYear(f["year"].value)) {	//	year entered is a leap year
				if (f["day"].value > (monthList[index].numdays + 1)) 
				{
					alert("De maand " + monthList[index].name + " telt slechts " + (monthList[index].numdays + 1) + " dagen.");
					return false;
				}
				else
					return true;
			}
			else 
			{	//	year entered is not a leap year
				if (f["day"].value > monthList[index].numdays) 
				{
					alert("De maand " + monthList[index].name + " telt slechts " + monthList[index].numdays + " dagen.");
					return false;
				}
				else
					return true;
			}
		}
		else 
			return true;	
	}

//*********************************************************************/

//convLatLong converts any type of lat/long input
//into  the table form and then handles bad input
//it is nested in the calcSun function.

	function convLatLong(f)
	{
		if(f["latDeg"].value == "")
		{
			f["latDeg"].value = 0;
		}
		if(f["latMin"].value == "")
		{
			f["latMin"].value = 0;
		}
		if(f["latSec"].value == "")
		{
			f["latSec"].value = 0;
		}
		if(f["lonDeg"].value == "")
		{
			f["lonDeg"].value = 0;
		}
		if(f["lonMin"].value == "")
		{
			f["lonMin"].value = 0;
		}
		if(f["lonSec"].value == "")
		{
			f["lonSec"].value = 0;
		}

		var neg = 0;
		if(f["latDeg"].value.charAt(0) == '-') 
		{
			neg = 1;
		}

		if(neg != 1)
		{
			var latSeconds = (parseFloat(f["latDeg"].value))*3600 
				+ parseFloat(f["latMin"].value)*60 
				+ parseFloat(f["latSec"].value)*1;

			f["latDeg"].value = Math.floor(latSeconds/3600);
			f["latMin"].value = Math.floor((latSeconds
				- (parseFloat(f["latDeg"].value)*3600))/60);
			f["latSec"].value = Math.floor((latSeconds
				- (parseFloat(f["latDeg"].value)*3600) 
				- (parseFloat(f["latMin"].value)*60)) + 0.5);
		}
		else if(parseFloat(f["latDeg"].value) > -1)
		{
			var latSeconds = parseFloat(f["latDeg"].value)*3600 
				- parseFloat(f["latMin"].value)*60 
				- parseFloat(f["latSec"].value)*1;

			f["latDeg"].value = "-0";
			f["latMin"].value = Math.floor((-latSeconds)/60);
			f["latSec"].value = Math.floor( (-latSeconds 
				- (parseFloat(f["latMin"].value)*60)) + 0.5);

		}
		else
		{
			var latSeconds = parseFloat(f["latDeg"].value)*3600 
				- parseFloat(f["latMin"].value)*60 
				- parseFloat(f["latSec"].value)*1;

			f["latDeg"].value = Math.ceil(latSeconds/3600);
			f["latMin"].value = Math.floor((-latSeconds
				+ (parseFloat(f["latDeg"].value)*3600))/60);
			f["latSec"].value = Math.floor((-latSeconds
				+ (parseFloat(f["latDeg"].value)*3600) 
				- (parseFloat(f["latMin"].value)*60)) + 0.5);
		}

		neg = 0;
		if(f["lonDeg"].value.charAt(0) == '-') 
		{
			neg = 1;
		}

		if(neg != 1)
		{
			var lonSeconds = parseFloat(f["lonDeg"].value)*3600 
				+ parseFloat(f["lonMin"].value)*60 
				+ parseFloat(f["lonSec"].value)*1;
			f["lonDeg"].value = Math.floor(lonSeconds/3600);
			f["lonMin"].value = Math.floor((lonSeconds
				- (parseFloat(f["lonDeg"].value)*3600))/60);
			f["lonSec"].value = Math.floor((lonSeconds
				- (parseFloat(f["lonDeg"].value)*3600)
				- (parseFloat(f["lonMin"].value))*60) + 0.5);
		}
		else if(parseFloat(f["lonDeg"].value) > -1)
		{
			var lonSeconds = parseFloat(f["lonDeg"].value)*3600 
				- parseFloat(f["lonMin"].value)*60 
				- parseFloat(f["lonSec"].value)*1;

			f["lonDeg"].value = "-0";
			f["lonMin"].value = Math.floor((-lonSeconds)/60);
			f["lonSec"].value = Math.floor((-lonSeconds
				- (parseFloat(f["lonMin"].value)*60)) + 0.5);
		}
		else
		{
			var lonSeconds = parseFloat(f["lonDeg"].value)*3600 
				- parseFloat(f["lonMin"].value)*60 
				- parseFloat(f["lonSec"].value)*1;
			f["lonDeg"].value = Math.ceil(lonSeconds/3600);
			f["lonMin"].value = Math.floor((-lonSeconds
				+ (parseFloat(f["lonDeg"].value)*3600))/60);
			f["lonSec"].value = Math.floor((-lonSeconds
				+ (parseFloat(f["lonDeg"].value)*3600)
				- (parseFloat(f["lonMin"].value)*60)) + 0.5);
		}

		//Test for invalid lat/long input

		if(latSeconds > 324000)
		{
			alert("U heeft een onjuiste breedtegraad ingevoerd.\nDe geografische breedte zal worden veranderd in 89°.8 NB.");
			f["latDeg"].value = 89.8;
			f["latMin"].value = 0;
			f["latSec"].value = 0;
		}
		if(latSeconds < -324000)
		{
			alert("U heeft een onjuiste breedtegraad ingevoerd.\nDe geografische breedte zal worden veranderd in 89°.8 ZB.");
			f["latDeg"].value = -89.8;
			f["latMin"].value = 0;
			f["latSec"].value = 0;
		}
		if(lonSeconds > 648000)
		{
			alert("U heeft een onjuiste lengtegraad ingevoerd.\nDe geografische lengte zal worden veranderd in 180° OL.");
			f["lonDeg"].value = 180;
			f["lonMin"].value = 0;
			f["lonSec"].value = 0;
		}
		if(lonSeconds < -648000)
		{
			alert("U heeft een onjuiste lengtegraad ingevoerd.\nDe geografische lengte zal worden veranderd in 180° WL.");
			f["lonDeg"].value = -180;
			f["lonMin"].value = 0;
			f["lonSec"].value =0;
		}
	}



//***********************************************************************/
//***********************************************************************/
//*									*/
//*This section contains subroutines used in calculating solar position */
//*Reference: J. Meeus, 'Astronomical Algorithms', Richmond (1991);	*/
//*           P.K. Seidelmann, 'Explanatory Supplement to the           */
//*           Astronomical Almanac', Mill Valley (1992);                */
//*           P. Bretagnon and G. Francou, 'Planetary theories in       */
//*           rectangular and spherical variables. VSOP87 solutions',   */
//*           Astronomy & Astrophysics, vol. 202, p. 309-315 (1988).    */
//*									*/
//***********************************************************************/
//***********************************************************************/

// Convert radian angle to degrees

	function radToDeg(angleRad) 
	{
		return (180.0 * angleRad / Math.PI);
	}

//*********************************************************************/

// Convert degree angle to radians

	function degToRad(angleDeg) 
	{
		return (Math.PI * angleDeg / 180.0);
	}

//*********************************************************************/


//***********************************************************************/
//* Name:    calcDayOfYear						*/
//* Type:    Function							*/
//* Purpose: Finds numerical day-of-year from mn, day and lp year info  */
//* Arguments:								*/
//*   month: Januari = 1						*/
//*   day  : 1 - 31							*/
//*   lpyr : 1 if leap year, 0 if not					*/
//* Return value:							*/
//*   The numerical day of year						*/
//***********************************************************************/

	function calcDayOfYear(mn, dy, lpyr) 
	{
		var k = (lpyr ? 1 : 2);
		var doy = Math.floor((275 * mn)/9) - k * Math.floor((mn + 9)/12) + dy -30;
		return doy;
	}


//***********************************************************************/
//* Name:    calcJD							*/
//* Type:    Function							*/
//* Purpose: Julian day from calendar day				*/
//* Arguments:								*/
//*   year : 4 digit year						*/
//*   month: Januari = 1						*/
//*   day  : 1 - 31							*/
//* Return value:							*/
//*   The Julian day corresponding to the date				*/
//* Note:								*/
//*   Number is returned for start of day.  Fractional days should be	*/
//*   added later.							*/
//***********************************************************************/

	function calcJD(year, month, day)
	{
		if (month <= 2) {
			year -= 1;
			month += 12;
		}
		var A = Math.floor(year/100);
		var B = 2 - A + Math.floor(A/4);

		var JD = Math.floor(365.25*(year + 4716)) + Math.floor(30.6001*(month+1)) + day + B - 1524.5;
		return JD;
	}



//***********************************************************************/
//* Name:    calcDateFromJD						*/
//* Type:    Function							*/
//* Purpose: Calendar date from Julian Day				*/
//* Arguments:								*/
//*   jd   : Julian Day							*/
//* Return value:							*/
//*   String date in the form DD-MONTHNAME-YYYY				*/
//* Note:								*/
//***********************************************************************/

	function calcDateFromJD(jd)
	{
		var z = Math.floor(jd + 0.5);
		var f = (jd + 0.5) - z;

		if (z < 2299161) {
			var A = z;
		} else {
			alpha = Math.floor((z - 1867216.25)/36524.25);
			var A = z + 1 + alpha - Math.floor(alpha/4);
		}

		var B = A + 1524;
		var C = Math.floor((B - 122.1)/365.25);
		var D = Math.floor(365.25 * C);
		var E = Math.floor((B - D)/30.6001);

		var day = B - D - Math.floor(30.6001 * E) + f;
		var month = (E < 14) ? E - 1 : E - 13;
		var year = (month > 2) ? C - 4716 : C - 4715;

		return (day + "-" + monthList[month-1].name + "-" + year);
	}


//***********************************************************************/
//* Name:    calcDayFromJD						*/
//* Type:    Function							*/
//* Purpose: Calendar day (minus year) from Julian Day			*/
//* Arguments:								*/
//*   jd   : Julian Day							*/
//* Return value:							*/
//*   String date in the form DD-MONTH					*/
//***********************************************************************/

	function calcDayFromJD(jd)
	{
		var z = Math.floor(jd + 0.5);
		var f = (jd + 0.5) - z;

		if (z < 2299161) {
			var A = z;
		} else {
			alpha = Math.floor((z - 1867216.25)/36524.25);
			var A = z + 1 + alpha - Math.floor(alpha/4);
		}

		var B = A + 1524;
		var C = Math.floor((B - 122.1)/365.25);
		var D = Math.floor(365.25 * C);
		var E = Math.floor((B - D)/30.6001);

		var day = B - D - Math.floor(30.6001 * E) + f;
		var month = (E < 14) ? E - 1 : E - 13;
		var year = (month > 2) ? C - 4716 : C - 4715;

		return ((day<10 ? "0" : "") + day + monthList[month-1].abbr);
	}


//***********************************************************************/
//* Name:    calcTimeJulianCent						*/
//* Type:    Function							*/
//* Purpose: convert Julian Day to centuries since J2000.0.		*/
//* Arguments:								*/
//*   jd : the Julian Day to convert					*/
//* Return value:							*/
//*   the T value corresponding to the Julian Day			*/
//***********************************************************************/

	function calcTimeJulianCent(jd)
	{
		var T = (jd - 2451545.0)/36525.0;
		return T;
	}


//***********************************************************************/
//* Name:    calcJDFromJulianCent					*/
//* Type:    Function							*/
//* Purpose: convert centuries since J2000.0 to Julian Day.		*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//* Return value:							*/
//*   the Julian Day corresponding to the t value			*/
//***********************************************************************/

	function calcJDFromJulianCent(t)
	{
		var JD = t * 36525.0 + 2451545.0;
		return JD;
	}


//***********************************************************************/
//* Name:    calGeomMeanLongSun						*/
//* Type:    Function							*/
//* Purpose: calculate the Geometric Mean Longitude of the Sun		*/
//* Source:  VSOP87	Reference: FK5					*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//* Return value:							*/
//*   the Geometric Mean Longitude of the Sun in degrees		*/
//***********************************************************************/

	function calcGeomMeanLongSun(t)
	{
		var L0 = 280.466431580 + 36000.7698277856 * t + 0.00030320279 * t * t + 0.000000020028 * t * t * t - 0.0000000065365 * t * t * t * t - 0.00000000000503 * t * t * t * t * t;

		while(L0 > 360.0)
		{
			L0 -= 360.0;
		}
		while(L0 < 0.0)
		{
			L0 += 360.0;
		}
		return L0;		// in degrees
	}

//***********************************************************************/
//* Name:    calcSunTrueLong						*/
//* Type:    Function							*/
//* Purpose: calculate the true longitude of the sun			*/
//* Source:  VSOP87	Reference: FK5					*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//* Return value:							*/
//*   sun's true longitude in degrees					*/
//***********************************************************************/


	function calcSunTrueLong(t)
	{
		var O = 280.466431580 + 
			  1.914628115 * Math.cos(4.66925680417 +  628.30758499914 * t) +
			  0.019992947 * Math.cos(4.62610241759 + 1256.61516999828 * t) +
			  0.002003665 * Math.cos(2.74411800971 +  575.33848848968 * t) +
			  0.001958124 * Math.cos(2.82886579606 +    0.35231183490 * t) +
			  0.001796736 * Math.cos(3.62767041758 + 7771.37714681205 * t) +
			  0.001533360 * Math.cos(4.41808351397 +  786.04193924392 * t) +
			  0.001342261 * Math.cos(6.13516237631 +  393.02096962196 * t) +
			  0.000758763 * Math.cos(0.74246356352 + 1150.67697697936 * t) +
			  0.000729470 * Math.cos(2.03709655772 +   52.96909650946 * t) +
			  0.000687072 * Math.cos(1.10962944315 +  157.73435424478 * t) +
			  0.000567371 * Math.cos(5.23268129594 +  588.49268465832 * t) +
			  0.000516725 * Math.cos(2.04505443513 +    2.62983197998 * t) +
			  0.000491153 * Math.cos(3.50849156957 +   39.81490034082 * t) +
			  0.000446784 * Math.cos(1.17882652114 +  522.36939198022 * t) +
			  0.000431518 * Math.cos(2.53339053818 +  550.75532386674 * t) +
			  0.000289495 * Math.cos(4.58292563052 + 1884.92275499742 * t) +
			  0.000282112 * Math.cos(4.20506639861 +   77.55226113240 * t) +
			  0.000204348 * Math.cos(2.91954116867 +    0.00673103028 * t) +
			  0.000181677 * Math.cos(5.84901952218 + 1179.06290886588 * t) +
			  0.000162792 * Math.cos(1.89869034186 +   79.62980068164 * t) +
			  0.000155294 * Math.cos(0.31488607649 + 1097.70788046990 * t) +
			  0.000139120 * Math.cos(0.34481140906 +  548.67778431750 * t) +
			  0.000118121 * Math.cos(4.80646606059 +  254.43144198834 * t) +
			  0.000117677 * Math.cos(1.86947813692 +  557.31428014331 * t) +
			  0.000115887 * Math.cos(2.45767795458 +  606.97767545534 * t) +
			  0.000089104 * Math.cos(0.83306073807 +   21.32990954380 * t) +
			  0.000075752 * Math.cos(3.41118275555 +  294.24634232916 * t) +
			  0.000072298 * Math.cos(1.08302630210 +    2.07753954924 * t) +
			  0.000065966 * Math.cos(0.64544911683 +    0.09803210682 * t) +
			  0.000058929 * Math.cos(0.63599846727 +  469.40029547076 * t) +
			  0.000058382 * Math.cos(0.97569221824 + 1572.08387848784 * t) +
			  0.000058284 * Math.cos(4.26679821365 +    0.71135470008 * t) +
			  0.000056841 * Math.cos(6.20992940258 +  214.61654164752 * t) +
			  0.000055925 * Math.cos(0.68101272270 +   15.54203994342 * t) +
			  0.000049161 * Math.cos(5.98322631256 +16100.06857376740 * t) +
			  0.000048775 * Math.cos(1.29870743025 +  627.59623029906 * t) +
			  0.000048536 * Math.cos(3.67080093025 + 7143.06956181290 * t) +
			  0.000045629 * Math.cos(1.80791330700 + 1726.01546546904 * t) +
			  0.000045124 * Math.cos(3.03698313141 + 1203.64607348882 * t) +
			  0.000042772 * Math.cos(1.75508916159 +  508.86288397668 * t) +
			  0.000042327 * Math.cos(3.50319443167 +  315.46870848956 * t) +
			  0.000042139 * Math.cos(4.67926565481 +   80.18209311238 * t) +
			  0.000039893 * Math.cos(0.83297596966 +  943.77629348870 * t) +
			  0.000035781 * Math.cos(3.97763880587 +  882.73902698748 * t) +
			  0.000035035 * Math.cos(1.81839811024 +  708.48967811152 * t) +
			  0.000032637 * Math.cos(2.78430398043 +  628.65989683404 * t) +
			  0.000032152 * Math.cos(4.38694880779 + 1414.34952424306 * t) +
			  0.000031843 * Math.cos(3.47006009062 +  627.95527316424 * t) +
			  0.000029789 * Math.cos(0.18914945834 + 1213.95535091068 * t) +
			  0.000029567 * Math.cos(1.33282746983 +  174.80164130670 * t) +
			  0.000029304 * Math.cos(0.28306864501 +  585.64776591154 * t) +
			  0.000028075 * Math.cos(0.48735065033 +  119.44470102246 * t) +
			  0.000023512 * Math.cos(5.36817351402 +  842.92412664666 * t) +
			  0.000023456 * Math.cos(2.39850881707 + 1965.10484810980 * t) +
			  0.000022460 * Math.cos(6.16832995016 + 1044.73878396044 * t) +
			  0.000021068 * Math.cos(6.04133859347 + 1021.32855462110 * t) +
			  0.000020968 * Math.cos(2.56955238628 +  105.93819301892 * t) +
			  0.000020600 * Math.cos(1.70876111898 +  235.28661537718 * t) +
			  0.000020378 * Math.cos(1.77597314691 +  681.27668150860 * t) +
			  0.000019074 * Math.cos(0.59309499459 + 1778.98456197850 * t) +
			  0.000017425 * Math.cos(0.44294464135 + 8399.68473181118 * t) +
			  0.000017216 * Math.cos(2.73975123935 +  134.98674096588 * t) +
			  0.000014526 * Math.cos(3.16470953405 +  469.04798363586 * t) +
		      36000.7698277856 * t +
			  0.0118063032 * t * Math.cos(2.67823455584 +  628.30758499914 * t) +
			  0.0002465684 * t * Math.cos(2.63512650414 + 1256.61516999828 * t) +
			  0.0000243658 * t * Math.cos(1.59046980729 +    0.35231183490 * t) +
			  0.0000068332 * t * Math.cos(5.79557487799 +    2.62983197998 * t) +
			  0.0000062439 * t * Math.cos(2.96618001993 +  157.73435424478 * t) +
			  0.0000053559 * t * Math.cos(2.59212835365 + 1884.92275499742 * t) +
			  0.0000041323 * t * Math.cos(1.13846158196 +   52.96909650946 * t) +
			  0.0000038828 * t * Math.cos(1.87472304791 +   39.81490034082 * t) +
			  0.0000038576 * t * Math.cos(4.40918235168 +  550.75532386674 * t) +
			  0.0000033820 * t * Math.cos(2.88797038460 +  522.36939198022 * t) +
			  0.0000032072 * t * Math.cos(2.17471680261 +   15.54203994342 * t) +
			  0.0000026016 * t * Math.cos(0.39803079805 +   79.62980068164 * t) +
			  0.0000020838 * t * Math.cos(0.46624739835 +   77.55226113240 * t) +
			  0.0000016592 * t * Math.cos(2.64707383882 +    0.71135470008 * t) +
			  0.0000011943 * t * Math.cos(5.34138275149 +    0.09803210682 * t) +
			  0.0000010942 * t * Math.cos(1.84628332577 +  548.67778431750 * t) +
			  0.0000010604 * t * Math.cos(4.96855124577 +   21.32990954380 * t) +
			  0.0000009908 * t * Math.cos(2.99116864949 +  627.59623029906 * t) +
			  0.0000009301 * t * Math.cos(0.03216483047 +  254.43144198834 * t) +
			  0.0000009071 * t * Math.cos(1.43049285325 +  214.61654164752 * t) +
			  0.0000008374 * t * Math.cos(1.20532366323 + 1097.70788046990 * t) +
			  0.0000007140 * t * Math.cos(2.83432285512 +  174.80164130670 * t) +
			  0.0000006805 * t * Math.cos(3.25804815607 +  508.86288397668 * t) +
			  0.0000006765 * t * Math.cos(5.27379790480 +  119.44470102246 * t) +
			  0.0000006597 * t * Math.cos(2.07502418155 +  469.40029547076 * t) +
			  0.0000006097 * t * Math.cos(0.76614199202 +   55.35694028424 * t) +
			  0.0000005712 * t * Math.cos(1.30262991097 +  628.65989683404 * t) +
			  0.0000005570 * t * Math.cos(4.23925472239 +  134.98674096588 * t) +
			  0.0000005416 * t * Math.cos(2.69957062864 +   24.27286039740 * t) +
			  0.0000004914 * t * Math.cos(5.64475868067 +   95.17184062506 * t) +
			  0.0000004341 * t * Math.cos(5.30062664886 +  235.28661537718 * t) +
			  0.0000003658 * t * Math.cos(2.65033984967 +  943.77629348870 * t) +
			  0.0000003496 * t * Math.cos(4.66632584188 +  469.04798363586 * t) +
			  0.00030320279 * t * t +
			  0.00004996099 * t * t * Math.cos(1.07209665242 +  628.30758499914 * t) +
			  0.00000177116 * t * t * Math.cos(0.86728818832 + 1256.61516999828 * t) +
			  0.00000015664 * t * t * Math.cos(0.05297871691 +    0.35231183490 * t) +
			  0.00000009359 * t * t * Math.cos(5.18826691036 +    2.62983197998 * t) +
			  0.00000009025 * t * t * Math.cos(3.68457889430 +   15.54203994342 * t) +
			  0.00000005467 * t * t * Math.cos(0.75742297675 + 1884.92275499742 * t) +
			  0.00000005121 * t * t * Math.cos(2.05705419118 + 7771.37714681205 * t) +
			  0.00000003983 * t * t * Math.cos(0.82673305410 +   77.55226113240 * t) +
			  0.00000002901 * t * t * Math.cos(4.66284525271 +  157.73435424478 * t) +
			  0.00000002327 * t * t * Math.cos(1.03057162962 +    0.71135470008 * t) +
			  0.00000002183 * t * t * Math.cos(3.44050803490 +  557.31428014331 * t) +
			  0.00000001984 * t * t * Math.cos(5.14074632811 +   79.62980068164 * t) +
			  0.00000001816 * t * t * Math.cos(6.05291851171 +  550.75532386674 * t) +
			  0.00000001730 * t * t * Math.cos(1.19246506441 +   24.27286039740 * t) +
			  0.00000001654 * t * t * Math.cos(6.11652627155 +   52.96909650946 * t) +
			  0.00000001555 * t * t * Math.cos(0.30637881025 +   39.81490034082 * t) +
			  0.00000001454 * t * t * Math.cos(2.27992810679 +   55.35694028424 * t) +
			  0.00000001358 * t * t * Math.cos(4.38118838167 +  522.36939198022 * t) +
			  0.00000001191 * t * t * Math.cos(3.75435330484 +    0.09803210682 * t) +
			  0.000000165714 * t * t * t * Math.cos(5.84384198723 + 628.30758499914 * t) +
			  0.000000020028 * t * t * t +
			  0.000000009637 * t * t * t * Math.cos(5.48766912348 + 1256.61516999828 * t) +
			  0.000000001697 * t * t * t * Math.cos(5.19577265202 +   15.54203994342 * t) +
			  0.000000000738 * t * t * t * Math.cos(4.72200252235 +    0.35231183490 * t) +
			  0.000000000409 * t * t * t * Math.cos(5.30045809128 + 1884.92275499742 * t) +
			  0.000000000364 * t * t * t * Math.cos(5.96925937141 +   24.27286039740 * t) +
			 -0.0000000065365 * t * t * t * t +
			  0.0000000004422 * t * t * t * t * Math.cos(4.13446589358 +  628.30758499914 * t) +
			  0.0000000000438 * t * t * t * t * Math.cos(3.83803776214 + 1256.61516999828 * t) +
			 -0.00000000000503 * t * t * t * t * t;
		return O;		// in degrees
	}

//***********************************************************************/
//* Name:    calcSunTrueLat						*/
//* Type:    Function							*/
//* Purpose: calculate the true latitude of the sun			*/
//* Source:  VSOP87							*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//* Return value:							*/
//*   sun's true latitude in degrees					*/
//***********************************************************************/


	function calcSunTrueLat(t)
	{
		var B = 0.000160210 * Math.cos(3.19870156017 + 8433.46615813082 * t) +
			0.000058237 * Math.cos(5.42248619256 +  550.75532386674 * t) +
			0.000046092 * Math.cos(3.88013204458 +  522.36939198022 * t) +
			0.000025099 * Math.cos(3.70444689758 +  235.28661537718 * t) +
			0.000018296 * Math.cos(4.00026369781 +  157.73435424478 * t) +
			0.0000005174 * t * Math.cos(3.89729061890 + 550.75532386674 * t) +
			0.0000003539 * t * Math.cos(1.73038850355 + 522.36939198022 * t);
		return B;		// in degrees
	}

//***********************************************************************/
//* Name:    calcSunRadVector						*/
//* Type:    Function							*/
//* Purpose: calculate the distance to the sun in AU			*/
//* Source:  VSOP87							*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//* Return value:							*/
//*   sun radius vector in AUs						*/
//***********************************************************************/

	function calcSunRadVector(t)
	{
		var R = 1.00013989 + 
			0.01670700 * Math.cos(3.098464 +  628.30758500 * t) + 
			0.00013956 * Math.cos(3.055246 + 1256.61517000 * t) + 
			0.00003084 * Math.cos(5.198467 + 7771.37714681 * t) + 
			0.00001628 * Math.cos(1.173877 +  575.33848849 * t) + 
			0.00001576 * Math.cos(2.846852 +  786.04193924 * t) + 
			0.00000925 * Math.cos(5.452922 + 1150.67697698 * t) + 
			0.00000542 * Math.cos(4.564091 +  393.02096962 * t) + 
			0.000103019 * t * Math.cos(1.107490 +  628.30758500 * t) + 
			0.000001721 * t * Math.cos(1.064423 + 1256.61517000 * t) + 
			0.0000004359 * t * t * Math.cos(5.784551 + 628.30758500 * t);
		return R;		// in AUs
	}

//***********************************************************************/
//* Name:    calcSunApparentLong					*/
//* Type:    Function							*/
//* Purpose: calculate the apparent longitude of the sun		*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//* Return value:							*/
//*   sun's apparent longitude in degrees				*/
//***********************************************************************/

	function calcSunApparentLong(t)
	{
		var O = calcSunTrueLong(t);
		var R = calcSunRadVector(t);

		var omega = 125.0445222 -   1934.1362608 * t + 0.0020708 * t * t + 0.0000022 * t * t * t;
		var L     = 280.4660694 +  36000.7697972 * t + 0.0003025 * t * t;
		var LA    = 218.3164325 + 481267.8812772 * t - 0.0016117 * t * t + 0.0000053 * t * t * t;

		var lambda = O - (0.0056916 / R) +
			     -0.0047777 * Math.sin(degToRad(omega)) +
			     -0.0003663 * Math.sin(degToRad(2 * L)) +
			     -0.0000632 * Math.sin(degToRad(2 * LA)) +
			      0.0000573 * Math.sin(degToRad(2 * omega));

		return lambda;		// in degrees
	}

//***********************************************************************/
//* Name:    calcMeanObliquityOfEcliptic				*/
//* Type:    Function							*/
//* Purpose: calculate the mean obliquity of the ecliptic		*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//* Return value:							*/
//*   mean obliquity in degrees						*/
//***********************************************************************/

	function calcMeanObliquityOfEcliptic(t)
	{
		var seconds = 21.448 - 46.8150 * t - 0.00059 * t * t + 0.001813 * t * t * t;
		var e0 = 23.0 + (26.0 + (seconds / 60.0)) / 60.0;
		return e0;		// in degrees
	}

//***********************************************************************/
//* Name:    calcObliquityCorrection					*/
//* Type:    Function							*/
//* Purpose: calculate the corrected obliquity of the ecliptic		*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//* Return value:							*/
//*   corrected obliquity in degrees					*/
//***********************************************************************/


	function calcObliquityCorrection(t)
	{
		var e0 = calcMeanObliquityOfEcliptic(t);

		var omega = 125.0445222 -   1934.1362608 * t + 0.0020708 * t * t + 0.0000022 * t * t * t;
		var L     = 280.4660694 +  36000.7697972 * t + 0.0003025 * t * t;
		var LA    = 218.3164325 + 481267.8812772 * t - 0.0016117 * t * t + 0.0000053 * t * t * t;

		var e = e0 + 0.0025563 * Math.cos(degToRad(omega)) + 
			     0.0001593 * Math.cos(degToRad(2 * L)) + 
			     0.0000271 * Math.cos(degToRad(2 * LA)) +
			    -0.0000249 * Math.cos(degToRad(2 * omega));

		return e;		// in degrees
	}

//***********************************************************************/
//* Name:    calcSunRtAscensionGeo					*/
//* Type:    Function							*/
//* Purpose: calculate the right ascension of the sun			*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//* Return value:							*/
//*   sun's right ascension in degrees					*/
//***********************************************************************/

	function calcSunRtAscensionGeo(t)
	{
		var e = calcObliquityCorrection(t);
		var lambda = calcSunApparentLong(t);
		var beta = -1 * calcSunTrueLat(t);
 
		var tananum = (Math.cos(degToRad(e)) * Math.sin(degToRad(lambda)) - Math.tan(degToRad(beta)) * Math.sin(degToRad(e)));
		var tanadenom = (Math.cos(degToRad(lambda)));
		var alpha = radToDeg(Math.atan2(tananum, tanadenom));

		while(alpha > 360.0)
		{
			alpha -= 360.0;
		}
		while(alpha < 0.0)
		{
			alpha += 360.0;
		}

		return alpha;		// in degrees
	}

//***********************************************************************/
//* Name:    calcSunDeclinationGeo					*/
//* Type:    Function							*/
//* Purpose: calculate the declination of the sun			*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//* Return value:							*/
//*   sun's declination in degrees					*/
//***********************************************************************/

	function calcSunDeclinationGeo(t)
	{
		var e = calcObliquityCorrection(t);
		var lambda = calcSunApparentLong(t);
		var beta = -1 * calcSunTrueLat(t);

		var sint = Math.sin(degToRad(beta)) * Math.cos(degToRad(e)) + Math.cos(degToRad(beta)) * Math.sin(degToRad(e)) * Math.sin(degToRad(lambda));
		var theta = radToDeg(Math.asin(sint));
		return theta;		// in degrees
	}

//***********************************************************************/
//* Name:    calcLocalHourAngle						*/
//* Type:    Function							*/
//* Purpose: calculate the local hour angle of the sun			*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//*   longitude : longitude of observer in degrees			*/
//* Return value:							*/
//*   local hour angle of the sun in degrees				*/
//***********************************************************************/

	function calcLocalHourAngle(t, longitude)
	{

		var epsilon = degToRad(calcObliquityCorrection(t));
		var alpha = calcSunRtAscensionGeo(t);
		var omega = 125.0445222 -   1934.1362608 * t + 0.0020708 * t * t + 0.0000022 * t * t * t;
		var L     = 280.4660694 +  36000.7697972 * t + 0.0003025 * t * t;
		var LA    = 218.3164325 + 481267.8812772 * t - 0.0016117 * t * t + 0.0000053 * t * t * t;

		var dPsi = -0.0047777 * Math.sin(degToRad(omega)) +
			   -0.0003663 * Math.sin(degToRad(2 * L)) +
			   -0.0000632 * Math.sin(degToRad(2 * LA)) +
			    0.0000573 * Math.sin(degToRad(2 * omega));

		var GMST = 280.46061838 + 13185000.770053609 * t + 0.000387934 * t * t - 0.0000000258 * t * t * t;
		var GAST = GMST + dPsi * Math.cos(epsilon);
		var localHourAngle = GAST - longitude - alpha;

		while(localHourAngle > 360.0)
		{
			localHourAngle -= 360.0;
		}
		while(localHourAngle < 0.0)
		{
			localHourAngle += 360.0;
		}

		return localHourAngle;		// in degrees
	}

//***********************************************************************/
//* Name:    calcSunRtAscension						*/
//* Type:    Function							*/
//* Purpose: calculate the apparent right ascension of the sun		*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//*   latitude : latitude of observer in degrees			*/
//*   longitude : longitude of observer in degrees			*/
//*   height : height of observer above the level of the local horizon	*/
//*            in meters                                                */
//* Return value:							*/
//*   sun's apparent right ascension in degrees				*/
//***********************************************************************/

	function calcSunRtAscension(t, latitude, longitude, height)
	{
		var alpha = calcSunRtAscensionGeo(t);
		var deltaRad = degToRad(calcSunDeclinationGeo(t));
		var latRad = degToRad(latitude);
		var R = calcSunRadVector(t);
		var localHourAngle = calcLocalHourAngle(t, longitude);

		var rho = 0.99832711 + 0.00167640 * Math.cos(2 * latRad)
				     - 0.00000352 * Math.cos(4 * latRad)
				     + 0.00000001 * Math.cos(6 * latRad);   //  GRS80 ellipsoid
		var rho = rho + height / (6378137 * rho);
		var PhiA = Math.atan(0.99330561997710 * Math.tan(latRad));
		var rhoCosPhiA = rho * Math.cos(PhiA);
		var sinPi = 0.000042635212 / R;

		var dAlpha1 = 0.000088889 * rhoCosPhiA * Math.cos(localHourAngle) / Math.cos(deltaRad);

		var tananum = -rhoCosPhiA * sinPi * Math.sin(localHourAngle);
		var tanadenom = Math.cos(deltaRad) - rhoCosPhiA * sinPi * Math.cos(localHourAngle);
		var dAlpha2 = radToDeg(Math.atan2(tananum, tanadenom));

		var alpha = alpha + dAlpha1 + dAlpha2;
		while(alpha > 360.0)
		{
			alpha -= 360.0;
		}
		while(alpha < 0.0)
		{
			alpha += 360.0;
		}

		return alpha;		// in degrees
	}

//***********************************************************************/
//* Name:    calcSunDeclination						*/
//* Type:    Function							*/
//* Purpose: calculate the apparent declination of the sun		*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//*   latitude : latitude of observer in degrees			*/
//*   longitude : longitude of observer in degrees			*/
//*   height : height of observer above the level of the local horizon	*/
//*            in meters                                                */
//* Return value:							*/
//*   sun's apparent declination in degrees				*/
//***********************************************************************/

	function calcSunDeclination(t, latitude, longitude, height)
	{
		var alpha = calcSunRtAscensionGeo(t);
		var delta = calcSunDeclinationGeo(t);
		var deltaRad = degToRad(delta);
		var latRad = degToRad(latitude);
		var R = calcSunRadVector(t);
		var localHourAngle = calcLocalHourAngle(t, longitude);

		var rho = 0.99832711 + 0.00167640 * Math.cos(2 * latRad)
				     - 0.00000352 * Math.cos(4 * latRad)
				     + 0.00000001 * Math.cos(6 * latRad);   //  GRS80 ellipsoid
		var rho = rho + height / (6378137 * rho);
		var PhiA = Math.atan(0.99330561997710 * Math.tan(latRad));
		var rhoCosPhiA = rho * Math.cos(PhiA);
		var rhoSinPhiA = rho * Math.sin(PhiA);
		var sinPi = 0.000042635212 / R;

		var dDelta1 = 0.000088889 * rhoCosPhiA * Math.sin(localHourAngle) * Math.sin(deltaRad);

		var tananum = -rhoCosPhiA * sinPi * Math.sin(localHourAngle);
		var tanadenom = Math.cos(deltaRad) - rhoCosPhiA * sinPi * Math.cos(localHourAngle);
		var dAlpha2Rad = Math.atan2(tananum, tanadenom);
		var tananum = (Math.sin(deltaRad) - rhoSinPhiA * sinPi) * Math.cos(dAlpha2Rad);
		var tanadenom = Math.cos(deltaRad) - rhoCosPhiA * sinPi * Math.cos(localHourAngle);
		var dDelta2 = radToDeg(Math.atan2(tananum, tanadenom) - deltaRad);

		var delta = delta + dDelta1 + dDelta2;

		return delta;		// in degrees
	}

//***********************************************************************/
//* Name:    calcEquationOfTime						*/
//* Type:    Function							*/
//* Purpose: calculate the difference between true solar time and mean	*/
//*		solar time						*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//* Return value:							*/
//*   equation of time in minutes of time				*/
//***********************************************************************/

	function calcEquationOfTime(t)
	{
		var epsilon = calcObliquityCorrection(t);
		var L0 = calcGeomMeanLongSun(t);
		var alpha = calcSunRtAscensionGeo(t);
		var R = calcSunRadVector(t);

		while((L0 - alpha) > 180.0)
		{
			L0 -= 360.0;
		}
		while((L0 - alpha) < -180.0)
		{
			alpha -= 360.0;
		}

		var omega = 125.0445222 -   1934.1362608 * t + 0.0020708 * t * t + 0.0000022 * t * t * t;
		var L     = 280.4660694 +  36000.7697972 * t + 0.0003025 * t * t;
		var LA    = 218.3164325 + 481267.8812772 * t - 0.0016117 * t * t + 0.0000053 * t * t * t;

		var deltaPsi = -0.0047777 * Math.sin(degToRad(omega)) +
			       -0.0003663 * Math.sin(degToRad(2 * L)) +
			       -0.0000632 * Math.sin(degToRad(2 * LA)) +
			        0.0000573 * Math.sin(degToRad(2 * omega));

		var Etime = L0 - (0.0056916 / R) - alpha + deltaPsi * Math.cos(degToRad(epsilon));

		return (Etime * 4.0);	// in minutes of time
	}

//***********************************************************************/
//* Name:    calcHourAngleSunrise					*/
//* Type:    Function							*/
//* Purpose: calculate the hour angle of the sun at sunrise for the	*/
//*			latitude					*/
//* Arguments:								*/
//*   lat : latitude of observer in degrees				*/
//*   solarDec : declination angle of sun in degrees			*/
//*   height : height of observer above the level of the local horizon	*/
//*            in meters                                                */
//* Return value:							*/
//*   hour angle of sunrise in radians					*/
//***********************************************************************/

	function calcHourAngleSunrise(lat, solarDec, height)
	{
		var latRad = degToRad(lat);
		var sdRad  = degToRad(solarDec);
		var heightkm = height / 1000;
		var rc = (2.12 + 0.00111 * heightkm - 0.00124 * Math.pow(heightkm, 2) + 0.000413 * Math.pow(heightkm, 3) - 0.0000420 * Math.pow(heightkm, 4)) / 60.0;
		var zdRad  = degToRad(90.83333333333333 + rc * Math.sqrt(height));

		var HAarg = (Math.cos(zdRad)/(Math.cos(latRad)*Math.cos(sdRad))-Math.tan(latRad) * Math.tan(sdRad));

		var HA = (Math.acos(Math.cos(zdRad)/(Math.cos(latRad)*Math.cos(sdRad))-Math.tan(latRad) * Math.tan(sdRad)));

		return HA;		// in radians
	}

//***********************************************************************/
//* Name:    calcHourAngleSunset					*/
//* Type:    Function							*/
//* Purpose: calculate the hour angle of the sun at sunset for the	*/
//*			latitude					*/
//* Arguments:								*/
//*   lat : latitude of observer in degrees				*/
//*   solarDec : declination angle of sun in degrees			*/
//*   height : height of observer above the level of the local horizon	*/
//*            in meters                                                */
//* Return value:							*/
//*   hour angle of sunset in radians					*/
//***********************************************************************/

	function calcHourAngleSunset(lat, solarDec, height)
	{
		var latRad = degToRad(lat);
		var sdRad  = degToRad(solarDec);
		var heightkm = height / 1000;
		var rc = (2.12 + 0.00111 * heightkm - 0.00124 * Math.pow(heightkm, 2) + 0.000413 * Math.pow(heightkm, 3) - 0.0000420 * Math.pow(heightkm, 4)) / 60.0;
		var zdRad  = degToRad(90.83333333333333 + rc * Math.sqrt(height));

		var HAarg = (Math.cos(zdRad)/(Math.cos(latRad)*Math.cos(sdRad))-Math.tan(latRad) * Math.tan(sdRad));

		var HA = (Math.acos(Math.cos(zdRad)/(Math.cos(latRad)*Math.cos(sdRad))-Math.tan(latRad) * Math.tan(sdRad)));

		return -HA;		// in radians
	}


//***********************************************************************/
//* Name:    calcSunriseUTC						*/
//* Type:    Function							*/
//* Purpose: calculate the Universal Coordinated Time (UTC) of sunrise	*/
//*			for the given day at the given location on earth*/
//* Arguments:								*/
//*   JD  : julian day							*/
//*   latitude : latitude of observer in degrees			*/
//*   longitude : longitude of observer in degrees			*/
//*   height : height of observer above the level of the local horizon	*/
//*            in meters                                                */
//* Return value:							*/
//*   time in minutes from zero Z					*/
//***********************************************************************/

	function calcSunriseUTC(JD, latitude, longitude, height)
	{
		var t = calcTimeJulianCent(JD);

		// *** First pass to approximate sunrise

		var eqTime = calcEquationOfTime(t);
		var solarDec = calcSunDeclination(t, latitude, longitude, height);
		var hourAngle = calcHourAngleSunrise(latitude, solarDec, height);

		var delta = longitude - radToDeg(hourAngle);
		var timeDiff = 4 * delta;	// in minutes of time
		var timeUTC = 720 + timeDiff - eqTime;	// in minutes

		// *** Second pass includes fractional jday in gamma calc

		var newt = calcTimeJulianCent(calcJDFromJulianCent(t) + timeUTC/1440.0); 
		eqTime = calcEquationOfTime(newt);
		solarDec = calcSunDeclination(newt, latitude, longitude, height);
		hourAngle = calcHourAngleSunrise(latitude, solarDec, height);
		delta = longitude - radToDeg(hourAngle);
		timeDiff = 4 * delta;
		timeUTC = 720 + timeDiff - eqTime; // in minutes

		return timeUTC;
	}

//***********************************************************************/
//* Name:    calcSolNoonUTC						*/
//* Type:    Function							*/
//* Purpose: calculate the Universal Coordinated Time (UTC) of solar	*/
//*		noon for the given day at the given location on earth	*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//*   longitude : longitude of observer in degrees			*/
//* Return value:							*/
//*   time in minutes from zero Z					*/
//***********************************************************************/

	function calcSolNoonUTC(t, longitude)
	{
		var newt = calcTimeJulianCent(calcJDFromJulianCent(t) + 0.5 + longitude / 360.0); 

		var eqTime = calcEquationOfTime(newt);
		var solNoonUTC = 720 + (longitude * 4) - eqTime; // min

		var newt = calcTimeJulianCent(calcJDFromJulianCent(t) + solNoonUTC / 1440.0); 

		var eqTime = calcEquationOfTime(newt);
		// var solarNoonDec = calcSunDeclination(newt, latitude, longitude, height);
		var solNoonUTC = 720 + (longitude * 4) - eqTime; // min

		return solNoonUTC;
	}

//***********************************************************************/
//* Name:    calcSolApparentAltitudeNoon				*/
//* Type:    Function							*/
//* Purpose: calculate the apparent altitude of the Sun at noon		*/
//* Arguments:								*/
//*   t : number of Julian centuries since J2000.0			*/
//*   longitude : longitude of observer in degrees			*/
//*   latitude : latitude of observer in degrees			*/
//*   height : height of observer above the level of the local horizon	*/
//*            in meters                                                */
//* Return value:							*/
//*   apparent altitude in degrees					*/
//***********************************************************************/

	function calcSolApparentAltitudeNoon(t, latitude, longitude, height)
	{
		var t = calcTimeJulianCent(calcJDFromJulianCent(t)); 
		var solNoonUTC = calcSolNoonUTC(t, longitude);
		var newt = calcTimeJulianCent(calcJDFromJulianCent(t) + solNoonUTC/1440.0); 

		var solarNoonDec = calcSunDeclination(newt, latitude, longitude, height);
		var solarNoonDecRad = degToRad(solarNoonDec);
		var latRad = degToRad(latitude);
		var R = calcSunRadVector(newt);
		var SolAltitudeNoonRad = Math.asin(Math.sin(latRad) * Math.sin(solarNoonDecRad) + Math.cos(latRad) * Math.cos(solarNoonDecRad));
		var SolAltitudeNoon = radToDeg(SolAltitudeNoonRad);

		var g0 = 978032.67715 + 5185.95970 * Math.sin(latRad) * Math.sin(latRad) - 5.73675 * Math.sin(2 * latRad) * Math.sin(2 * latRad);
		var g0 = g0 / 100000;
		var g =  g0 - 0.000003086 * height;
		var T = 288.15 - 0.0065 * height; // temperature in K
		var p = 1013.25 * Math.exp((-28.966 * 1.6605402 * Math.pow(10,-27) * ((g0 + g) / 2.0) * height) / (1.380658 * Math.pow(10,-23) * ((288.15 + T) / 2.0))); // pressure in hPa

		var X = SolAltitudeNoon + 10.3 / (SolAltitudeNoon + 5.11);
		var XRad = degToRad(X);
		var SolAppAltitudeNoon = SolAltitudeNoon + (1.02 / (60 * Math.tan(XRad))) * (p / 1010) * (283.15 / T);

		for(i=0; i<=10; i++)
		{
		var R = 1 / Math.tan(degToRad(SolAppAltitudeNoon + (7.31 / (SolAppAltitudeNoon + 4.4))));
		var R = R - 0.06 * Math.sin(degToRad(14.7 * R + 13.0)) + 0.0148283122959588 * Math.pow(Math.sin(SolAppAltitudeNoon),4);
		var R = R * (p / 1010) * (283.15 / T);
		var SolAppAltitudeNoon = SolAltitudeNoon + R / 60.0;
		}

		return SolAppAltitudeNoon;
	}

//***********************************************************************/
//* Name:    calcSunsetUTC						*/
//* Type:    Function							*/
//* Purpose: calculate the Universal Coordinated Time (UTC) of sunset	*/
//*			for the given day at the given location on earth*/
//* Arguments:								*/
//*   JD  : julian day							*/
//*   latitude : latitude of observer in degrees			*/
//*   longitude : longitude of observer in degrees			*/
//*   height : height of observer above the level of the local horizon	*/
//*            in meters                                                */
//* Return value:							*/
//*   time in minutes from zero Z					*/
//***********************************************************************/

	function calcSunsetUTC(JD, latitude, longitude, height)
	{
		var t = calcTimeJulianCent(JD);

		// First calculates sunrise and approx length of day

		var eqTime = calcEquationOfTime(t);
		var solarDec = calcSunDeclination(t, latitude, longitude, height);
		var hourAngle = calcHourAngleSunset(latitude, solarDec, height);

		var delta = longitude - radToDeg(hourAngle);
		var timeDiff = 4 * delta;
		var timeUTC = 720 + timeDiff - eqTime;

		// first pass used to include fractional day in gamma calc

		var newt = calcTimeJulianCent(calcJDFromJulianCent(t) + timeUTC/1440.0); 
		eqTime = calcEquationOfTime(newt);
		solarDec = calcSunDeclination(newt, latitude, longitude, height);
		hourAngle = calcHourAngleSunset(latitude, solarDec, height);

		delta = longitude - radToDeg(hourAngle);
		timeDiff = 4 * delta;
		timeUTC = 720 + timeDiff - eqTime; // in minutes

		return timeUTC;
	}


//*********************************************************************/

// Returns the decimal latitude from the degrees, minutes and seconds entered 
// into the form	

	function getLatitude(latLongForm)
	{
		var neg = 0;
		var degs = parseFloat(latLongForm["latDeg"].value);
		if (latLongForm["latDeg"].value.charAt(0) == '-') 
		{
			neg = 1;
		}

		if(latLongForm["latMin"].value == "")
		{
			latLongForm["latMin"].value = 0;
		}
		if(latLongForm["latSec"].value == "")
		{
			latLongForm["latSec"].value = 0;
		}

		var mins = parseFloat(latLongForm["latMin"].value);

		var secs = parseFloat(latLongForm["latSec"].value);

		if(neg != 1)
		{
			var decLat = degs + (mins / 60) + (secs / 3600);
		} else if(neg == 1)
		{
			var decLat = degs - (mins / 60) - (secs / 3600);
		} else 
		{
			return -9999;
		}
		return decLat;
	}	


//*********************************************************************/

// Returns the decimal longitude from the degrees, minutes and seconds entered 
// into the form	

	function getLongitude(latLongForm)
	{
		var neg = 0;
		var degs = parseFloat(latLongForm["lonDeg"].value);
		if (latLongForm["lonDeg"].value.charAt(0) == '-') 
		{
			neg = 1;
		}

		if(latLongForm["lonMin"].value == "")
		{
			latLongForm["lonMin"].value = 0;
		}
		if(latLongForm["lonSec"].value == "")
		{
			latLongForm["lonSec"].value = 0;
		}

		var mins = parseFloat(latLongForm["lonMin"].value);
		var secs = parseFloat(latLongForm["lonSec"].value);
		var decLon = -1*(degs + (mins / 60) + (secs / 3600));

		if(neg != 1)
		{
			var decLon = -1*(degs + (mins / 60) + (secs / 3600));
		} else if(neg == 1)
		{
			var decLon = -1*(degs - (mins / 60) - (secs / 3600));
		} else 
		{
			return -9999;
		}
		return decLon;
	}	

// Returns the height entered into the form	

	function getHeight(latLongForm)
	{

		var height = latLongForm["height"].value;

		return height;
	}

//***********************************************************************/
//* Name:    findRecentSunrise						*/
//* Type:    Function							*/
//* Purpose: calculate the julian day of the most recent sunrise	*/
//*          starting from the given day at the given location on earth	*/
//* Arguments:								*/
//*   JD  : julian day							*/
//*   latitude : latitude of observer in degrees			*/
//*   longitude : longitude of observer in degrees			*/
//*   height : height of observer above the level of the local horizon	*/
//*            in meters                                                */
//* Return value:							*/
//*   julian day of the most recent sunrise				*/
//***********************************************************************/

	function findRecentSunrise(jd, latitude, longitude, height)
	{
		var julianday = jd;

		var time = calcSunriseUTC(julianday, latitude, longitude, height);
		while(!isNumber(time)){
			julianday -= 1.0;
			time = calcSunriseUTC(julianday, latitude, longitude, height);
		}

		return julianday;
	}


//***********************************************************************/
//* Name:    findRecentSunset						*/
//* Type:    Function							*/
//* Purpose: calculate the julian day of the most recent sunset		*/
//*          starting from the given day at the given location on earth	*/
//* Arguments:								*/
//*   JD  : julian day							*/
//*   latitude : latitude of observer in degrees			*/
//*   longitude : longitude of observer in degrees			*/
//*   height : height of observer above the level of the local horizon	*/
//*            in meters                                                */
//* Return value:							*/
//*   julian day of the most recent sunset				*/
//***********************************************************************/

	function findRecentSunset(jd, latitude, longitude, height)
	{
		var julianday = jd;

		var time = calcSunsetUTC(julianday, latitude, longitude, height);
		while(!isNumber(time)){
			julianday -= 1.0;
			time = calcSunsetUTC(julianday, latitude, longitude, height);
		}

		return julianday;
	}


//***********************************************************************/
//* Name:    findNextSunrise						*/
//* Type:    Function							*/
//* Purpose: calculate the julian day of the next sunrise		*/
//*          starting from the given day at the given location on earth	*/
//* Arguments:								*/
//*   JD  : julian day							*/
//*   latitude : latitude of observer in degrees			*/
//*   longitude : longitude of observer in degrees			*/
//*   height : height of observer above the level of the local horizon	*/
//*            in meters                                                */
//* Return value:							*/
//*   julian day of the next sunrise					*/
//***********************************************************************/

	function findNextSunrise(jd, latitude, longitude, height)
	{
		var julianday = jd;

		var time = calcSunriseUTC(julianday, latitude, longitude, height);
		while(!isNumber(time)){
			julianday += 1.0;
			time = calcSunriseUTC(julianday, latitude, longitude, height);
		}

		return julianday;
	}


//***********************************************************************/
//* Name:    findNextSunset						*/
//* Type:    Function							*/
//* Purpose: calculate the julian day of the next sunset		*/
//*          starting from the given day at the given location on earth	*/
//* Arguments:								*/
//*   JD  : julian day							*/
//*   latitude : latitude of observer in degrees			*/
//*   longitude : longitude of observer in degrees			*/
//*   height : height of observer above the level of the local horizon	*/
//*            in meters                                                */
//* Return value:							*/
//*   julian day of the next sunset					*/
//***********************************************************************/

	function findNextSunset(jd, latitude, longitude, height)
	{
		var julianday = jd;

		var time = calcSunsetUTC(julianday, latitude, longitude, height);
		while(!isNumber(time)){
			julianday += 1.0;
			time = calcSunsetUTC(julianday, latitude, longitude, height);
		}

		return julianday;
	}

//***********************************************************************/
//* Name:    timeString							*/
//* Type:    Function							*/
//* Purpose: convert time of day in minutes to a zero-padded string	*/
//*          suitable for printing to the form text fields		*/
//* Arguments:								*/
//*   minutes : time of day in minutes					*/
//* Return value:							*/
//*   string of the format HH:MM:SS, minutes and seconds are zero padded*/
//***********************************************************************/

	function timeString(minutes)
	// timeString returns a zero-padded string (HH:MM:SS) given time in minutes
	{
		var floatHour = minutes / 60;
		var hour = Math.floor(floatHour);
		var floatMinute = 60 * (floatHour - Math.floor(floatHour));
		var minute = Math.floor(floatMinute);
		var floatSec = 60 * (floatMinute - Math.floor(floatMinute));
		var second = Math.floor(floatSec + 0.5);

		var timeStr = hour + ":";
		if (minute < 10)	//	i.e. only one digit
			timeStr += "0" + minute + ":";
		else
			timeStr += minute + ":";
		if (second < 10)	//	i.e. only one digit
			timeStr += "0" + second;
		else
			timeStr += second;

		return timeStr;
	}


//***********************************************************************/
//* Name:    timeStringShortAMPM					*/
//* Type:    Function							*/
//* Purpose: convert time of day in minutes to a zero-padded string	*/
//*		suitable for printing to the form text fields.  If time	*/
//*		crosses a day boundary, date is appended.		*/
//* Arguments:								*/
//*   minutes : time of day in minutes					*/
//*   JD  : julian day							*/
//* Return value:							*/
//*   string of the format HH:MM[AM/PM] (DDMon)				*/
//***********************************************************************/

// timeStringShortAMPM returns a zero-padded string (HH:MM *M) given time in 
// minutes and appends short date if time is > 24 or < 0, resp.

	function timeStringShortAMPM(minutes, JD)
	{
		var julianday = JD;
		var floatHour = minutes / 60;
		var hour = Math.floor(floatHour);
		var floatMinute = 60 * (floatHour - Math.floor(floatHour));
		var minute = Math.floor(floatMinute);
		var floatSec = 60 * (floatMinute - Math.floor(floatMinute));
		var second = Math.floor(floatSec + 0.5);
		var PM = false;

		minute += (second >= 30)? 1 : 0;

		if (minute >= 60) 
		{
			minute -= 60;
			hour ++;
		}

		var daychange = false;
		if (hour > 23) 
		{
			hour -= 24;
			daychange = true;
			julianday += 1.0;
		}

		if (hour < 0)
		{
			hour += 24;
			daychange = true;
			julianday -= 1.0;
		}

		if (hour > 11)
		{
			// hour -= 12;
			PM = true;
		}

		// if (hour == 0)
		// {
		// 	PM = false;
		// 	hour = 12;
		// }

		var timeStr = hour + ":";
		if (minute < 10)	//	i.e. only one digit
			timeStr += "0" + minute // + ((PM)?" PM":" AM");  //  i.e. AM/PM indicator (optional)
		else
			timeStr += "" + minute  // + ((PM)?" PM":" AM");  //  i.e. AM/PM indicator (optional)

		if (daychange) return timeStr + " " + calcDayFromJD(julianday);
		return timeStr;
	}


//***********************************************************************/
//* Name:    timeStringAMPMDate						*/
//* Type:    Function							*/
//* Purpose: convert time of day in minutes to a zero-padded string	*/
//*          suitable for printing to the form text fields, and appends	*/
//*          the date.							*/
//* Arguments:								*/
//*   minutes : time of day in minutes					*/
//*   JD  : julian day							*/
//* Return value:							*/
//*   string of the format HH:MM[AM/PM] DDMon				*/
//***********************************************************************/

// timeStringAMPMDate returns a zero-padded string (HH:MM[AM/PM]) given time 
// in minutes and julian day, and appends the short date

	function timeStringAMPMDate(minutes, JD)
	{
		var julianday = JD;
		var floatHour = minutes / 60;
		var hour = Math.floor(floatHour);
		var floatMinute = 60 * (floatHour - Math.floor(floatHour));
		var minute = Math.floor(floatMinute);
		var floatSec = 60 * (floatMinute - Math.floor(floatMinute));
		var second = Math.floor(floatSec + 0.5);

		minute += (second >= 30)? 1 : 0;

		if (minute >= 60) 
		{
			minute -= 60;
			hour ++;
		}

		if (hour > 23) 
		{
			hour -= 24;
			julianday += 1.0;
		}

		if (hour < 0)
		{
			hour += 24;
			julianday -= 1.0;
		}

		var PM = false;
		if (hour > 11)
		{
			// hour -= 12;
			PM = true;
		}

		// if (hour == 0)
		// {
		// 	PM = false;
		// 	hour = 12;
		// }

		var timeStr = hour + ":";
		if (minute < 10)	//	i.e. only one digit
			timeStr += "0" + minute // + ((PM)?" PM":" AM");  //  i.e. AM/PM indicator (optional)
		else
			timeStr += "" + minute  // + ((PM)?" PM":" AM");  //  i.e. AM/PM indicator (optional)

		return timeStr + " " + calcDayFromJD(julianday);
	}


//***********************************************************************/
//* Name:    timeStringDate						*/
//* Type:    Function							*/
//* Purpose: convert time of day in minutes to a zero-padded 24hr time	*/
//*		suitable for printing to the form text fields.  If time	*/
//*		crosses a day boundary, date is appended.		*/
//* Arguments:								*/
//*   minutes : time of day in minutes					*/
//*   JD  : julian day							*/
//* Return value:							*/
//*   string of the format HH:MM (DDMon)				*/
//***********************************************************************/

// timeStringDate returns a zero-padded string (HH:MM) given time in minutes
// and julian day, and appends the short date if time crosses a day boundary

	function timeStringDate(minutes, JD)
	{
		var julianday = JD;
		var floatHour = minutes / 60;
		var hour = Math.floor(floatHour);
		var floatMinute = 60 * (floatHour - Math.floor(floatHour));
		var minute = Math.floor(floatMinute);
		var floatSec = 60 * (floatMinute - Math.floor(floatMinute));
		var second = Math.floor(floatSec + 0.5);

		minute += (second >= 30)? 1 : 0;

		if (minute >= 60) 
		{
			minute -= 60;
			hour ++;
		}

		var daychange = false;
		if (hour > 23) 
		{
			hour -= 24;
			julianday += 1.0;
			daychange = true;
		}

		if (hour < 0)
		{
			hour += 24;
			julianday -= 1.0;
			daychange = true;
		}

		var timeStr = hour + ":";
		if (minute < 10)	//	i.e. only one digit
			timeStr += "0" + minute;
		else
			timeStr += minute;

		if (daychange) return timeStr + " " + calcDayFromJD(julianday);
		return timeStr;
	}

	
//***********************************************************************/
//* Name:    calcSun							*/
//* Type:    Main Function called by form controls			*/
//* Purpose: calculate time of sunrise and sunset for the entered date	*/
//*		and location.  In the special cases near earth's poles, */
//*		the date of nearest sunrise and set are reported.	*/
//* Arguments:								*/
//*   riseSetForm : for displaying results				*/
//*   latLongForm : for reading latitude and longitude data		*/
//*   index : daylight saving yes/no select				*/
//*   index2 : city select index					*/
//* Return value:							*/
//*   none								*/
//*	(fills riseSetForm text fields with results of calculations)	*/
//***********************************************************************/

	function calcSun(riseSetForm, latLongForm, index, index2) 
	{
		if(index2 != 0)
		{
			setLatLong(latLongForm, index2);
		}

		var latitude = getLatitude(latLongForm);
		var longitude = getLongitude(latLongForm);
		var height = getHeight(latLongForm);
		var indexRS = riseSetForm.mos.selectedIndex
		if (isValidInput(riseSetForm, indexRS, latLongForm)) 
		{
			if((latitude >= -90) && (latitude < -89.8))
			{
				alert("Alle breedtegraden tussen 89°.8 en 90° ZB \nzullen worden vervangen door 89°.8 ZB");
				latLongForm["latDeg"].value = -89.8;
				latLongForm["latMin"].value = 0;
				latLongForm["latSec"].value = 0;
				latitude = -89.8;
			}
			if ((latitude <= 90) && (latitude > 89.8))
			{
				alert("Alle breedtegraden tussen 89°.8 en 90° NB \nzullen worden vervangen door 89°.8 NB");
				latLongForm["latDeg"].value = 89.8;
				latLongForm["latMin"].value = 0;
				latLongForm["latSec"].value = 0;
				latitude = 89.8;
			}

			//*****	Calculate the time of sunrise			

//*********************************************************************/
//*********************************************************************/

			var JD = (calcJD(parseFloat(riseSetForm["year"].value), indexRS + 1, parseFloat(riseSetForm["day"].value)));
			var doy = calcDayOfYear(indexRS + 1, parseFloat(riseSetForm["day"].value), isLeapYear(riseSetForm["year"].value));
			var T = calcTimeJulianCent(JD);
			//var L0 = calcGeomMeanLongSun(T);
			//var O = calcSunTrueLong(T);
			//var R = calcSunRadVector(T);
			//var lambda = calcSunApparentLong(T);
			//var epsilon0 = calcMeanObliquityOfEcliptic(T);
			//var epsilon = calcObliquityCorrection(T);
			var alpha = calcSunRtAscension(T, latitude, longitude, height);
			var theta = calcSunDeclination(T, latitude, longitude, height);
			var Etime = calcEquationOfTime(T);
			
//*********************************************************************/

			var eqTime = Etime;
			var solarDec = theta;

			riseSetForm["eqTime"].value = (Math.floor(100*eqTime + 0.5))/100;
			// riseSetForm["solarDec"].value = (Math.floor(100*(solarDec + 0.5)))/100;

			// Calculate sunrise for this date
			// if no sunrise is found, set flag nosunrise

			var nosunrise = false;

			var riseTimeGMT = calcSunriseUTC(JD, latitude, longitude, height);
			if (!isNumber(riseTimeGMT))
			{
				nosunrise = true;
			}

			// Calculate sunset for this date
			// if no sunset is found, set flag nosunset

			var nosunset = false;
			var setTimeGMT = calcSunsetUTC(JD, latitude, longitude, height);
			if (!isNumber(setTimeGMT))
			{
				nosunset = true;
			}

			var daySavings = YesNo[index].value;  // = 0 (no) or 60 (yes)
			var zone = latLongForm["hrsToGMT"].value;
			var zone = -1 * zone;
			if(zone > 12 || zone < -12.5)
			{
				var zone = Math.floor((-longitude / 15) + 0.5);
				alert("De tijdzone moet liggen tussen -12.5 en 12. \nDe tijdzone zal worden veranderd in \"" + zone + "\".");
				latLongForm["hrsToGMT"].value = zone;
				var zone = -1 * zone;
			}
			
			riseSetForm["timezone"].value = -1 * zone;

			if (!nosunrise)		// Sunrise was found
			{
				var riseTimeLST = riseTimeGMT - (60 * zone) + daySavings;	
					//	in minutes
				var riseStr = timeStringShortAMPM(riseTimeLST, JD);
				var utcRiseStr = timeStringDate(riseTimeGMT, JD);

				riseSetForm["sunrise"].value = riseStr;
				riseSetForm["utcsunrise"].value = utcRiseStr;
			}

			if (!nosunset)		// Sunset was found
			{
				var setTimeLST = setTimeGMT - (60 * zone) + daySavings;
				var setStr = timeStringShortAMPM(setTimeLST, JD);
				var utcSetStr = timeStringDate(setTimeGMT, JD);

				riseSetForm["sunset"].value = setStr;
				riseSetForm["utcsunset"].value = utcSetStr;
			}

			// Calculate solar noon for this date

			var solNoonGMT = calcSolNoonUTC(T, longitude);
			var solNoonLST = solNoonGMT - (60 * zone) + daySavings;
			var ApparentAltitudeNoon = calcSolApparentAltitudeNoon(T, latitude, longitude, height);

			var solnStr = timeString(solNoonLST);
			var utcSolnStr = timeString(solNoonGMT);

			riseSetForm["solnoon"].value = solnStr;
			riseSetForm["utcsolnoon"].value = utcSolnStr;
			riseSetForm["MaxApparentAltitude"].value = (Math.floor(100*ApparentAltitudeNoon + 0.5))/100;
			
			//***********Convert lat and long to standard format
			convLatLong(latLongForm);

			// report special cases of no sunrise

			if(nosunrise)
			{ 
				riseSetForm["utcsunrise"].value = "";
				riseSetForm["sunrise"].value = "";
				// if Northern hemisphere and spring or summer, OR  
				// if Southern hemisphere and fall or winter, use 
				// previous sunrise and next sunset

				if ( ((latitude > 66.4) && (doy > 79) && (doy < 267)) ||
				   ((latitude < -66.4) && ((doy < 83) || (doy > 263))) )
				{
					newjd = findRecentSunrise(JD, latitude, longitude, height);
					newtime = calcSunriseUTC(newjd, latitude, longitude, height)
						 - (60 * zone) + daySavings;
					if (newtime > 1440)
					{
						newtime -= 1440;
						newjd += 1.0;
					}
					if (newtime < 0)
					{
						newtime += 1440;
						newjd -= 1.0;
					}
					riseSetForm["sunrise"].value = 
						timeStringAMPMDate(newtime, newjd);
					riseSetForm["utcsunrise"].value = "prior sunrise";
				}

				// if Northern hemisphere and fall or winter, OR 
				// if Southern hemisphere and spring or summer, use 
				// next sunrise and previous sunset

				else if ( ((latitude > 66.4) && ((doy < 83) || (doy > 263))) ||
					((latitude < -66.4) && (doy > 79) && (doy < 267)) )
				{
					newjd = findNextSunrise(JD, latitude, longitude, height);
					newtime = calcSunriseUTC(newjd, latitude, longitude, height)
						 - (60 * zone) + daySavings;
					if (newtime > 1440)
					{
						newtime -= 1440;
						newjd += 1.0;
					}
					if (newtime < 0)
					{
						newtime += 1440;
						newjd -= 1.0;
					}
					riseSetForm["sunrise"].value = 
						timeStringAMPMDate(newtime, newjd);
//					riseSetForm["sunrise"].value = calcDayFromJD(newjd)
//						+ " " + timeStringDate(newtime, newjd);
					riseSetForm["utcsunrise"].value = "next sunrise";
				}
				else 
				{
					alert("Op de opgegeven datum vindt geen zonsopkomst plaats!");
				}

				// alert("Last Sunrise was on day " + findRecentSunrise(JD, latitude, longitude, height));
				// alert("Next Sunrise will be on day " + findNextSunrise(JD, latitude, longitude, height));

			}

			if(nosunset)
			{ 
				riseSetForm["utcsunset"].value = "";
				riseSetForm["sunset"].value = "";
				// if Northern hemisphere and spring or summer, OR
				// if Southern hemisphere and fall or winter, use 
				// previous sunrise and next sunset

				if ( ((latitude > 66.4) && (doy > 79) && (doy < 267)) ||
				   ((latitude < -66.4) && ((doy < 83) || (doy > 263))) )
				{
					newjd = findNextSunset(JD, latitude, longitude, height);
					newtime = calcSunsetUTC(newjd, latitude, longitude, height)
						 - (60 * zone) + daySavings;
					if (newtime > 1440)
					{
						newtime -= 1440;
						newjd += 1.0;
					}
					if (newtime < 0)
					{
						newtime += 1440;
						newjd -= 1.0;
					}
					riseSetForm["sunset"].value = 
						timeStringAMPMDate(newtime, newjd);
					riseSetForm["utcsunset"].value = "next sunset";
					riseSetForm["utcsolnoon"].value = "";
				}

				// if Northern hemisphere and fall or winter, OR
				// if Southern hemisphere and spring or summer, use 
				// next sunrise and last sunset

				else if ( ((latitude > 66.4) && ((doy < 83) || (doy > 263))) ||
					((latitude < -66.4) && (doy > 79) && (doy < 267)) )
				{
					newjd = findRecentSunset(JD, latitude, longitude, height);
					newtime = calcSunsetUTC(newjd, latitude, longitude, height)
						 - (60 * zone) + daySavings;
					if (newtime > 1440)
					{
						newtime -= 1440;
						newjd += 1.0;
					}
					if (newtime < 0)
					{
						newtime += 1440;
						newjd -= 1.0;
					}
					riseSetForm["sunset"].value = 
						timeStringAMPMDate(newtime, newjd);
					riseSetForm["utcsunset"].value = "prior sunset";
					riseSetForm["solnoon"].value = "         N/A";
					riseSetForm["MaxApparentAltitude"].value = "      N/A";
					riseSetForm["utcsolnoon"].value = "";
				}

				else 
				{
					alert ("Op de opgegeven datum vindt geen zonsondergang plaats!");
				}
			}
		}
	}



//*********************************************************************/

