// Daily Quote Script
// Client-side JavaScript
// Copyright (c) 2005 Richard Van Hall, Jr. http://richhall.com
// Usage license: the GNU GPL http://www.gnu.org

// Also includes some basic ephermeral algorithms.

// Code status is early pre-alpha -- guaranteed to change.



/*
rvh_hillel.js -- Hebrew Calendar package
Copyright (c) 1999 Richard Van Hall, Jr. http://richhall.com
Usage licensed under GNU GPL http://www.gnu.org
Keep notices intact.

Uses the Gauss and Oudin algorithms.
*/

var Hillel={

month:[
	'Nisan', 'Iyyar', 'Sivan', 'Tammuz', 'Av', 'Elul',
	'Tishrei', 'Cheshvan', 'Kislev', 'Tevet', 'Shevat',
	'Adar', 'Adar I', 'Adar II'],

Gauss:function(y){
	var a,b,c,m,g; // "day in March" on which Pesach falls (return value)
	a=Math.floor((12*y + 17) % 19);
	b=Math.floor(y%4);
	m=32.044093161144 + 1.5542417966212 * a +  b / 4.0 - 0.0031777940220923 * y;
	if(m<0) m-=1;
	g=Math.floor(m);
	if(m<0) m++;
	m-=g;
	c=Math.floor((g + 3*y + 5*b + 5) % 7);
	if(c==0 && a>11 && m>=0.89772376543210 ) g++;
	else if(c==1 && a>6 && m>=0.63287037037037) g+=2;
	else if(c==2 || c==4 || c==6) g++;
	g+=Math.floor((y - 3760) / 100) - Math.floor((y - 3760) / 400) - 2;
	return g;
},

leap:function(y){
	return ((y % 400 == 0) || (y % 100 != 0 && y % 4 == 0));
},

isoDaysInMonth:function(y,m){
	if(m==2) return 28 + Hillel.leap(y);
	if(m == 4 || m == 6 || m == 9 || m == 11) return 30;
	return 31;
},

fromDate:function(o){
  return Hillel.fromISO(o.getFullYear(), o.getMonth()+1, o.getDate());
},

fromUTCDate:function(o){
  return Hillel.fromISO(o.getUTCFullYear(), o.getUTCMonth()+1, o.getUTCDate());
},

fromISO:function(y,m,d){
	var hy, pesach, anchor, adarType;

	m -= 2;
	if (m <= 0) { // Jan or Feb
		m += 12;
		y -= 1;
	}

	d += Math.floor(7 * m / 12 + 30 * (m - 1)); // day in March
	hy = y + 3760;	// get Hebrew year
	pesach = Hillel.Gauss(hy);
	if (d <= pesach - 15) { // before 1 Nisan
		anchor = pesach;
		d += 365;
		if(Hillel.leap(y))
			d++;
		y -= 1;
		hy -= 1;
		pesach = Hillel.Gauss(hy);
	}
	else
		anchor = Hillel.Gauss(hy + 1);

	d -= pesach - 15;
	anchor -= pesach - 12;
	y++;
	if(Hillel.leap(y))
		anchor++;

	for(m = 0; m < 11; m++) {
		var days;
		if(m == 7 && anchor % 30 == 2)
			days = 30; // Cheshvan
		else if(m == 8 && anchor % 30 == 0)
			days = 29; // Kislev
		else
			days = 30 - m % 2;
		if(d <= days)
			break;
		d -= days;
	}

	adarType = 0;			// plain old Adar
	if (m == 11 && anchor >= 30) {
		if (d > 30) {
			adarType = 2;	// Adar 2
			d -= 30;
		}
		else
			adarType = 1;	// Adar 1
	}

	if(m >= 6)		// Tishrei or after?
		hy++;		// then bump up year

	if(m == 11)			// Adar?
		m += adarType;	// adjust for Adars

	return {y:hy,d:d,m:m};
},


Easter:function(Y){
	var C = Math.floor(Y / 100);
	var N = Y - 19 * Math.floor(Y / 19);
	var K = Math.floor((C - 17) / 25);
	var I = C - Math.floor(C / 4) - Math.floor((C - K) / 3) + 19 * N + 15;
	I = I - 30*Math.floor((I / 30));
	I = I - Math.floor(I / 28) * (1 - Math.floor(I / 28) * Math.floor(29 / (I + 1)) * Math.floor((21 - N) / 11));
	var J = Y + Math.floor(Y / 4) + I + 2 - C + Math.floor(C / 4);
	J = J - 7 * Math.floor(J / 7);
	var L = I - J;
	var M = 3 + Math.floor((L + 40) / 44);
	var D = L + 28 - 31 * Math.floor(M / 4);

	return {m:M,d:D};
},

DOW:function(year,month,day){
	var a = Math.floor((14 - month)/12);
	var y = year - a;
	var m = month + 12*a - 2;
	var d = (day + y + Math.floor(y/4) - Math.floor(y/100) +
			Math.floor(y/400) + Math.floor((31*m)/12)) % 7;
	return d + 1;
},

NthDOW:function(n,w,m,y){
	if(n>0) return (n - 1) * 7 + 1 + (7 + w - Hillel.DOW(y,m,(n - 1) * 7 + 1)) % 7;
	var d=Hillel.isoDaysInMonth(y,m);
	return d-(Hillel.DOW(y,m,d) - w + 7) % 7;
},

/*
*/

holiDate:function(o){
  var y=o.getFullYear(), m=o.getMonth()+1, d=o.getDate(), w=o.getDay()+1, h=Hillel.fromISO(y,m,d), rc=Hillel.holiday(y,m,d), rh=Hillel.moadim(y,m,d, h.m, h.d, w);
  return ( (rc&&rh)? rc+' / '+rh : rh? rh: rc? rc: '' );
},

holiDateUTC:function(o){
  var y=o.getUTCFullYear(), m=o.getUTCMonth()+1, d=o.getUTCDate(), w=o.getUTCDay()+1, h=Hillel.fromISO(y,m,d), rc=Hillel.holiday(y,m,d), rh=Hillel.moadim(y,m,d, h.m, h.d, w);
  return ( (rc&&rh)? rc+' / '+rh : rh? rh: rc? rc: '' );
},


// American civil holidays and some major religious holiday
holiday:function(y,m,d){ switch(m){
	case 1:
		if(d==1) return "New Year's Day";
		break;
	case 2:
		if(d==12) return "Lincoln's Birthday";
		if(d==14) return "Valentine's Day";
		if(d==Hillel.NthDOW(3, 2, 2, y)) return "President's Day";
		break;
	case 3:
		if(d==17) return "St. Patrick's Day";
		//break;
	case 4:
		var e = Hillel.Easter(y);
		if(m==e.m && d==e.d) return "Easter";
		break;
	case 5:
		if(d==Hillel.NthDOW(2, 1, 5, y)) return "Mother's Day";
		if(d==Hillel.NthDOW(3, 7, 5, y)) return "Armed Forces Day";
		if(m==5 && d==Hillel.NthDOW(0, 2, 5, y)) return "Memorial Day";
		break;
	case 6:
		if(d==14) return "Flag Day";
		if(d==Hillel.NthDOW(3, 1, 6, y)) return "Father's Day";
		break;
	case 7:
		if(d==4) return "Independence Day";
		break;
	case 8:
		break;
	case 9:
	  if(d==11) return "Patriot Day";
		if(d==Hillel.NthDOW(1, 2, 9, y)) return "Labor Day";
		break;
	case 10:
		if(d==Hillel.NthDOW(2, 2, 10, y)) return "Columbus Day";
		if(d==31) return "Halloween";
		break;
	case 11:
		if(d==11) return "Veterans' Day";
		if(d==Hillel.NthDOW(4, 5, 11, y)) return "Thanksgiving";
		break;
	case 12:
		if(d==25) return "Christmas";
		break;
	default: 
  }
	return "";
},

moadim:function(cy, cm, cd, m, d, w){ switch (m) {
	case 0:
		if(d==12 && w==5) return "Taanit Bechorot";
	  if(d==14 && w!=7) return "Taanit Bechorot";
		if(d>=15 && d<=21) return "Pesach";
		if(d==22) return "Pesach (d)";
	  break;
	case 1:
		if(d==3 && w==5) return "Yom Ha'Atzmaut";
		if(d==4 && w==5) return "Yom Ha'Atzmaut";
		if(d==5 && w!=6 && w!=7) return "Yom Ha'Atzmaut";
		if(d==14) return "Pesah sheni";
		if(d==18) return "Lag B'Omer";
		if(d==28) return "Yom Yerushalayim";
	  break;
	case 2:
		if(d==6) return "Shavuot";
		if(d==7) return "Shavuot (d)";
	  break;
	case 3:
		if(d==17 && w!=7) return "Fast of Tammuz";
		if(d==18 && w==1) return "Fast of Tammuz";
	  break;
	case 4:
		if(d==9 && w!=7) return "Tisha B'Av";
		if(d==10 && w==1) return "Tisha B'Av";
		if(d==15) return "Tu B'Av";
	  break;
	case 6:
		if(d==1 || d==2) return "Rosh Hashana";
		if(d==3 && w!=7) return "Fast of Gedalia";
		if(d==4 && w==1) return "Fast of Gedalia";
		if(d==10) return "Yom Kippur";
		if(d>=15 && d<=22) return "Sukkot";
		if(d==23) return "Sukkot (d)";
	  break;
	case 8:
		if(d>=25) return "Chanukkah";
	  break;
	case 9:
		if(d<=2) return "Chanukkah";
		if(d==3) {
			// Kislev can be malei or chaser
			if(cd==1){ cd=29; cm=11; }else if(cd==2){ cd=30; cm=11; } else cd-=3;
			var hdate = Hillel.fromISO(cy, cm, cd);
			if(hdate.d==29) return "Chanukkah";
		}
		if(d==10) return "Fast of Tevet";
	  break;
	case 10:
		if(d==15) return "Tu b'Shvat";
	  break;
	case 11: case 13:
		if(d==11 && w==5) return "Taanit Esther";
		if(d==13 && w!=7) return "Taanit Esther";
		if(d==14) return "Purim";
		if(d==15) return "Shushan Purim";
	  break;
	default:
  }
	return "";
}

}; // end Hillel


// API should be separate from data.
var RVH_quotes={
  add:function(n,i){ // puts a new list in the contents array.
    if(!n)return false; if(!i){ if(n.i)i=n.i; else return false; }
    var a=(this._[i]?this._[i]:false), c=((a&&a._)?a._:false), j;
    if(n.split){
      if(a && c){
        c[c.length]=n;
      }else if(a){
        c=[n];
        a._=c;
      }
      n=a;
    } else if(n.join) {
      if(a && c){
        for(j=0;j<n.length;j++)this.add(i,n[j]);
      }else if(a){
        a._=n;
      }else{
        a={_:n};
      }
			n=a;
    } else {
      if(a && c){
        if(n._)for(j=0;j<n._.length;j++)this.add(i,n._[j]);
      }
    }
    if(!n.$)n.$=i;
    if(!n.$$)n.$$='quotelist';
    if(!this._[i])this._[i]=n; // a must be at least { _:['quote1','quote2'] }
    return n;
  },
  addList:function(n){
    if(n.$)this._[n.$]=n; else return false;
    return n;
  },
  get:function(s,j){ if(!this._[s])return false;
    var p=this._[s], a=( (p && p._)? p._ : ['']), l=a.length, d=false; 
    if(!l){a=[''];l=1;}
    if(!j){
      d=new Date();
      j=this.dateToJulian(d);
    }else if(j.getTime){
      d=j;
      j=this.dateToJulian(d);
    }
    var k=Math.floor(j), i=k%l, q=(a[i]?a[i]:'');
    if(q){
      if(q.split){
        q={$$:'quote', __:p, _:q}; 
      }else{
        if(!q.__)q.__=p;
        if(!q.$$)q.$$='quote';
      }
      if(q._&&q._.indexOf('--><p>')==0)q._=q._.substring(6,q._.length-8);
      if(d)q.d=d;
      if(j)q.j=j;
      q.n=i;
      q.toString=this.quote_toString;
    }
    return q;
  },
  quote_toString:function(){
    var q=this, r='', s=(q.__?q.__:false); //<div class="quote">
    if(q._){
      r+='<span class="quote_body">&ldquo;'+q._+'&rdquo; </span>';
    }
    if(s){
      r+='<span class="quote_source">&mdash;'
      if(s.cite)r+=s.cite;
      if(q.n)r+=', quote '+q.n;
      r+='</span>';
    }
    if(q.d && q.d.getUTCFullYear){
      var h=Hillel.fromUTCDate(q.d), g=Hillel.holiDateUTC(q.d);
      r+='<span class="quote_date">, for '+RVH_quotes.Day[q.d.getUTCDay()]+ ' '+ q.d.getUTCFullYear()+ '&nbsp;'+RVH_quotes.Mon[q.d.getUTCMonth()]+ '&nbsp;'+q.d.getUTCDate()+', '+h.y+'&nbsp;'+Hillel.month[h.m]+'&nbsp;'+h.d;
      if(g)r+=', '+g;
      if(q.j){
        var b=RVH_quotes.getVED(q.j);
        r+=' (JD&nbsp;'+Math.floor(q.j);
        var m=RVH_quotes.getCycle(q.j), v=RVH_quotes.getCycle(q.j,RVH_quotes.tropicalYear);
        r+=', SM&nbsp;'+m.t+':'+m.e+'+'+m.r+'='+m.l+', VE&nbsp;'+v.t+':'+v.e+'+'+v.r+'='+v.l;
        r+=', BD&nbsp;'+b.y+'-'+b.m+'-'+b.d+'/'+b.doy;
        r+=')';
      }
      r+='</span>';
    }
    return (r?'<div class="quote">'+r+'</div>':r); //+
  },
  Mon:['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
  Day:['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
  dateToJulian:function(o){ //convert a date object to Julian Days
    if(!o)o=new Date();
    var y=o.getUTCFullYear(), m=o.getUTCMonth()+1, d=o.getUTCDate(), h=o.getUTCHours(), min=o.getUTCMinutes(), s=o.getUTCSeconds(), ms=0;//o.getUTCMilliseconds();
		h+=(min+(s+ms/1000)/60)/60;
		var g = (y<=1585?0:1), n=m-9, k=(n<0?-1:1), a=Math.abs(n), b=Math.floor(y+k*Math.floor(a/7));
		return -1*Math.floor(7*(Math.floor((m+9)/12) + y)/4) + Math.floor(275*m/9) + d + (g* -1 * Math.floor((Math.floor(b/100) + 1)*3/4)) + 1721027 + 2*g + 367*y - 0.5 + (h/24);
  },
  julianToDate:function(jd){ //convert Julian Days to a date object
		var	j1, j2, j3, j4, j5;			//scratch
	
		//
		// get the date from the Julian day number
		//
    var intgr   = Math.floor(jd);
    var frac    = jd - intgr;
    var gregjd  = 2299160.5;
		if( jd >= gregjd ) {				//Gregorian calendar correction
			var tmp = Math.floor( ( (intgr - 1867216.0) - 0.25 ) / 36524.25 );
			j1 = intgr + 1 + tmp - Math.floor(0.25*tmp);
		} else j1 = intgr;
	
		//correction for half day offset
		var df = frac + 0.5;
		if( df >= 1.0 ) {
			df -= 1.0;
			++j1;
		}
	
		j2 = j1 + 1524.0;
		j3 = Math.floor( 6680.0 + ( (j2 - 2439870.0) - 122.1 )/365.25 );
		j4 = Math.floor(j3*365.25);
		j5 = Math.floor( (j2 - j4)/30.6001 );
	
		var d = Math.floor(j2 - j4 - Math.floor(j5*30.6001));
		var m = Math.floor(j5 - 1.0);
		if( m > 12 ) m -= 12;
		var y = Math.floor(j3 - 4715.0);
		if( m > 2 )   --y;
		if( y <= 0 )  --y;
	
		//
		// get time of day from day fraction
		//
		var hr  = Math.floor(df * 24.0);
		var mn  = Math.floor((df*24.0 - hr)*60.0);
			 f  = ((df*24.0 - hr)*60.0 - mn)*60.0;
		var sc  = Math.floor(f);
		 f -= sc;
    if( f > 0.5 ) ++sc;
    if( sc == 60 ) {
        sc = 0;
        ++mn;
    }
    if( mn == 60 )  {
        mn = 0;
        ++hr;
    }
    if( hr == 24 )  {
        hr = 0;
        ++d;            //this could cause a bug, but probably will never happen in practice
    }
    
    var o=new Date(Date.UTC(y,m-1,d,hr,mn,sc,0));
    return o;
  },
  oldMonth:{d:29.53058773,f:1732096.35694},//2450931.1875
  oldYear:{d:365.24219878,f:1732096.35694},//1732096.41667
  tropicalYear:{d:365.24219878,f:2450893.32986},
  synodicMonth:{d:29.53058773,f:2450931.1875},
  
  getCycle:function(j,c){
    if(!c)c=this.synodicMonth;
    j=Math.floor(j);
    var d=c.d, f=c.f, s=Math.floor((j-f)/d), n=s+1, p=Math.floor(s*d+f); n=Math.floor(n*d+f);
    return {e:j-p, r:n-j, l:n-p, t:s, p:p, n:n, c:c}; //elapsed, remaining, length, total, prev, next
  },
  getVED:function(j){
    var t=this, m=t.getCycle(j), y=t.getCycle(j,t.tropicalYear), v=t.getCycle(y.p);
    var f=Math.floor((y.e+v.e)/t.synodicMonth.d);
    return {y:y.t, m:f, d:m.e, j:j, doy:y.e };
  },
  test:function(){
    var o=new Date(), j=this.dateToJulian(o), j2=2453489.5346875, d=this.julianToDate(j2);
    document.write('<p>'+o+' is '+j+' JD, which is '+ d+'</p>');
    document.write('<p>'+this.get('PRA')+'</p>');
  },
  _:[] // this will be filled later-- holds lists of quotes, not quotes.
};
/*
RVH_quotes.addList({ $:'PRA', cite:"Benjamin Franklin, <i>Poor Richard's Almanack</i>", _:[
"An old young man will be a young old man.",
"Youth is pert and positive, Age modest and doubting: So Ears of Corn when young and light, stand bold upright, but hang their Heads when weighty, full and ripe.",
"All would live long, but none would be old.",
"At 20 years of age the Will reigns; at 30 the Wit; at 40 the Judgment.",
"If <i>Passion</i> drives, Let <i>Reason</i> hold the Reins.",
"Take heed of the Vinegar of sweet Wine, and the Anger of Good-nature."
]});
*/