function Zman (name, keyword, rows, order, type, deg, min, rize, shaa, hatzot_num, knissat_offset, offset, roundup, nereg, visible, pos, posInPercents) {
	this.name = name;
	this.keyword = keyword;
	this.rows = rows;
	this.order = order;
	this.type = type;
	this.deg = deg;
	this.min = min;
	this.rize = rize;
	this.shaa = shaa;
	this.hatzot_num = hatzot_num;
	this.knissat_offset = knissat_offset;
	this.offset = offset;
	this.roundup = roundup;
	this.time = null;
	this.nereg = nereg;
	this.visible = visible;
	this.pos = 0;
	this.pip = posInPercents;
	this.width = 0;
	this.exist = true;
}

function Zman_set_time (time) {
	this.time = time;
}
	
function Zman_get_time (time) {
	return this.time;
}

Zman.prototype.set_time = Zman_set_time;
Zman.prototype.get_time = Zman_get_time;

function Zmanim (longitude, latitude, timezone, day_of_year, day_of_year_prev, day_of_year_next) {
	this.longitude = longitude;
	this.latitude = latitude; 
	this.timezone = timezone;
	this.day_of_year = day_of_year;
	this.day_of_year_prev = day_of_year_prev;
	this.day_of_year_next = day_of_year_next;
	this.zmanim = new Array ();
	this.error = "";
	this.sunrise = 0;
	this.sunset = 0;
	this.next_sunrise = 0;
	this.prev_sunset = 0;
	this.knissat_offset = 18;
}

function Zmanim_set_knissat (offset_minutes) {
		this.knissat_offset = offset_minutes;
}
Zmanim.prototype.set_knissat = Zmanim_set_knissat;

function Zmanim_get_zmanim () {
		return this.zmanim;
}
Zmanim.prototype.get_zmanim = Zmanim_get_zmanim;

function Zmanim_get_zman_key_by_keyword (keyword) {
	for (order in this.zmanim) {
		if (this.zmanim[order].keyword == keyword) {
				return order;
			}
		}
	return -1;
}
Zmanim.prototype.get_zman_key_by_keyword = Zmanim_get_zman_key_by_keyword;

function Zmanim_set_zmanim () {
		
		this.set_sunrise (this.zmanim[3], true);
		this.set_sunrise (this.zmanim[3], false);
		this.set_sunset (this.zmanim[11], true);
		this.set_sunset (this.zmanim[11], false);
				
		for (order in this.zmanim) {
			switch (this.zmanim[order].type) {
				case 3:
					if (this.zmanim[order].hatzot_num == 1) {
						this.zmanim[order].set_time((this.sunrise + this.prev_sunset)/2);
					} else {
						this.zmanim[order].set_time((this.sunset + this.next_sunrise)/2);
					}
					break;
				case 1:
					if (this.zmanim[order].keyword == 'sunset') {
						this.zmanim[order].set_time(this.sunset);
					} else if (this.zmanim[order].keyword == 'sunrise') {
						this.zmanim[order].set_time(this.sunrise);
					} else {
						var time = this.suntime(this.zmanim[order].deg, this.zmanim[order].min, this.day_of_year);
						if (time.status == 0) {
							this.zmanim[order].exist = true;
							if (this.zmanim[order].rize)
								this.zmanim[order].set_time(time.rise);
							else 
								if (time.set < 12)
									this.zmanim[order].set_time(time.set + 24);
								else
									this.zmanim[order].set_time(time.set);
						} else {
							this.zmanim[order].set_time(null);
							this.zmanim[order].exist = false;
						}
					}
					break;
				case 2:
					var shaa_zmanit = (this.sunset - this.sunrise)/12;
					this.zmanim[order].set_time(this.sunrise + shaa_zmanit * this.zmanim[order].shaa);
					break;
				case 4:
					var time = this.suntime(this.zmanim[order].deg, this.zmanim[order].min, this.day_of_year);
					if (time.status == 0) {
						this.zmanim[order].knissat_offset = this.knissat_offset;
						if (this.zmanim[order].rize)
							this.zmanim[order].set_time(time.rise - this.zmanim[order].knissat_offset/60);
						else
							this.zmanim[order].set_time(time.set - this.zmanim[order].knissat_offset/60);
					} else {
							this.zmanim[order].set_time(null);
					}
					break;
				default:
					this.set_error("Неизвестный тип змана");
			}			
		}
}
Zmanim.prototype.set_zmanim = Zmanim_set_zmanim;

function Zmanim_set_sunrise (zman, current) {
		if (current == true) {
			var time = this.suntime(zman.deg, zman.min, this.day_of_year);
			if (time.status == 0)
				this.sunrise = time.rise;
			else
				this.set_error ("Ошибка при рассчете восхода текущего дня");
		} else {
			var time = this.suntime(zman.deg, zman.min, this.day_of_year_next);
			if (time.status == 0)
				this.next_sunrise = time.rise + 24;
			else
				this.set_error ("Ошибка при рассчете восхода следующего дня");
		}
}
Zmanim.prototype.set_sunrise = Zmanim_set_sunrise;

function Zmanim_set_sunset (zman, current) {
		if (current == true) {
			var time = this.suntime(zman.deg, zman.min, this.day_of_year);
			if (time.status == 0)
				this.sunset = time.set;
			else
				this.set_error ("Ошибка при рассчете захода текущего дня");
		} else {
			var time = this.suntime(zman.deg, zman.min, this.day_of_year_prev);
			if (time.status == 0)
				this.prev_sunset = time.set - 24;
			else
				this.set_error ("Ошибка при рассчете захода предыдущего дня");
		}
}
Zmanim.prototype.set_sunset = Zmanim_set_sunset;

function Zmanim_suntime(sundeg, sunmin, yday)
{
	var invalid = 0;	// start out as OK

	var pi2 = Math.acos(0);
	
	var A = pi2; 
	var B = pi2*2; 
	var C = pi2*3; 
	var D = pi2*4;      
	var E = (pi2/90) * this.latitude;
	var F = (pi2/90) * this.longitude; 
	var G = (pi2/6) * this.timezone;

	var R = Math.cos((pi2/90) * (sundeg + sunmin/60.0));

	var J;

	// two times through the loop
	//    i=0 is for sunrise
	//    i=1 is for sunset
	for (i = 0; i < 2; i++) { 

		if(!i)
			J =  A;	// sunrise 
		else
			J = C;	// sunset

		var K = yday + ((J - F) / D); 
		var L = (K * .017202) - .0574039;              // Solar Mean Anomoly 
		var M = L + .0334405 * Math.sin(L);            // Solar True Longitude 
		M += 4.93289 + (3.49066E-04) * Math.sin(2 * L); 
		
		// Quadrant Determination 
		if (D == 0) {
			alert("Trying to normalize with zero offset...");
			exit;
		} 

		while(M < 0)
			M = (M + D);

		while(M >= D)
			M = (M - D);

		if ((M / A) - Math.floor(M / A) == 0)
			M += 4.84814E-06;

		var P = Math.sin(M) / Math.cos(M);                   // Solar Right Ascension 
		P = Math.atan2(.91746 * P, 1); 

		// Quadrant Adjustment 
		if (M > C)
			P += D;
		else {
			if (M > A)
				P += B;
		} 

		var Q = .39782 * Math.sin(M);      // Solar Declination 
		Q = Q / Math.sqrt(-Q * Q + 1);     // This is how the original author wrote it! 
		Q = Math.atan2(Q, 1);

		var S = R - (Math.sin(Q) * Math.sin(E)); 
		S = S / (Math.cos(Q) * Math.cos(E)); 

		if(Math.abs(S) > 1)
			invalid = 1;	// uh oh! no sunrise/sunset

		S = S / Math.sqrt(-S * S + 1); 
		S = A - Math.atan2(S, 1); 

		if(!i)
			S = D - S;	// sunrise

		var T = S + P - 0.0172028 * K - 1.73364;  // Local apparent time 
		var U = T - F;                            // Universal timer 
		var V = U + G;                            // Wall clock time 
		
		// Quadrant Determination 
		if(D == 0) {
			alert("Trying to normalize with zero offset...");
			exit;
		} 
		
		while(V < 0)
			V = V + D;
		while(V >= D)
			V = V - D;
		V = V * 3.81972; 

		if(!i)
			sr = V;	// sunrise
		else
			ss = V;	// sunset
	} 

	var ret = new Object();
	ret.status = invalid;
	ret.rise = sr;
	ret.set = ss;
	return ret;
}
Zmanim.prototype.suntime = Zmanim_suntime;

function Zmanim_set_error (text) {
		this.error = text;
}

