/*  2006-11-13  Calendar.js JavaScript functions

    Dr. Rolf Schröder
    Möörkenweg 37
    21029 Hamburg, Germany
    http://rschr.de

    The algorithm of the functions: date2julday(), julday2date(), easter()
    are taken from the book:
  
    Astronomical Algorithms, Jean Meeus, 1991, First English Edition

    Remark:
    1582-10-04, the last day of Julian calendar! (YYYY-MM-DD), the date one day later is
    1582-10-15, the first day of the Gregorian calendar.
*/
//------------------------------------------------------------------------------------------
//----------------------------------- Cookie declarations ----------------------------------
//------------------------------------------------------------------------------------------

var CT = 3600*24*400;  // Life time [s] for Cookies to keep 'LastYear' and individual dates. 
var LY = "LastYear";   // Cookie name for LastYear-value.

//------------------------------------------------------------------------------------------
//----------------------------------- Calendar functions -----------------------------------
//------------------------------------------------------------------------------------------

var FF = [ 1, 1,"Neujahrstag",  // Fixed Holidays (MM,DD,Text)
           5, 1,"Maifeiertag",
           1, 6,"Hl. Drei Könige",
           8,15,"Mariä Himmelfahrt",
          10, 3,"Tag der dt. Einheit",
          10,31,"Reformationstag",
          11, 1,"Allerheiligen"];
    FF = oneArray2twoArray( FF, 3 ); // Transform FF into a 3-dim array; FF[i] =(MM,DD,Text) 
var ED = [-2,"Karfreitag",       // Hollidays related to Easter Sunday
           0,"Ostersonntag",
           1,"Ostermontag",
          39,"Himmelfahrt",
          49,"Pfingstsonntag",
          50,"Pfingstmontag",
          60,"Fronleichnam"];  
    ED = oneArray2twoArray( ED, 2 );            // Transform ED into a 2-dim array; ED[i] =(DayDiff,Text) 
var MD = [31,28,31,30,31,30,31,31,30,31,30,31]; // MonthLength, no leap year, not 1582.
var WD = ["Mo","Di","Mi","Do","Fr","Sa","So"];  
var MO = ["Januar","Februar","März","April",  "Mai",     "Juni",
          "Juli",  "August", "September","Oktober","November","Dezember"];
var GREGSTART = 15821015;                       // YYYYMMDD (first day of Gregorian calendar)
//------------------------------------------------------------------------------------------

function oneArray2twoArray( oneArray, n) // Transforms 1-dim-array into array of arrays,
//       #################                  n: length of subarrays.
{
  var i;
  var twoArray = new Array(oneArray.length/n);
  for( i = 0; i < twoArray.length; i++ )
  { twoArray[i] = new Array(n);
    for( j = 0; j < n; j++ ) twoArray[i][j] = oneArray[n*i+j];
  }
  return twoArray;
}

function julday2dArray(jd)     // Convert Julian Day to dateArray.
//       #############            Return: Array (yyyy,mm,dd.dddd)
{
  var dArray = new Array(3);
  var A, B, C, D, E, Z, f, g;
  g = jd + 0.5;
  Z = Math.floor(g);
  f = g - Z;
  if( Z < 2299161 ) A = Z;
  else
  { C = Math.floor((Z - 1867216.25)/36524.25);
    A = Z + 1 + C - Math.floor(C/4);
  }
  B = A + 1524;
  C = Math.floor((B - 122.1)/365.25);
  D = Math.floor(365.25*C);
  E = Math.floor((B - D)/30.6001);
  dArray[2] = B - D - Math.floor(30.6001*E) + f;
  dArray[1] = ( E < 14 ) ? E - 1: E - 13;
  dArray[0] = ( dArray[1] >  2 ) ?  C - 4716:  C - 4715;
  return dArray;
}

function dArray2julday(dArray)  // Convert date to Julian Day.
//       #############
{
  var dA = new Array(dArray.length);
  var y, m, a, b, d;
  dA = dArray;
  dA[1] = Math.abs(dA[1]);
  dA[2] = Math.abs(dA[2]);
  y = dA[0];
  m = dA[1];
  d = dA[2];
  if( m < 3 )
  { y -= 1;
    m += 12;
  }
  b = 0;
  if( dA[0]*10000 + dA[1]*100 + d >= GREGSTART )
  { a = Math.floor(y/100);
    b = 2 - a + Math.floor(a/4);
  }
  return  Math.floor(365.25*(y+4716)) + Math.floor(30.6001*(m+1)) + d + b - 1524.5;
}

function dArray2ISOdate(dArray)
//       ##############
{
  var dA = new Array(dArray.length);
  dA = dArray;
  dA[2] = ( dA[2] % 1 < 0.000005 ) ? dA[2].toFixed(0): dA[2].toFixed(5);
  if( dA[1] < 10 ) dA[1] = "0" + dA[1];
  if( dA[2] < 10 ) dA[2] = "0" + dA[2];
  return "" + dA[0] + "-" + dA[1] + "-" + dA[2];
}

function ISOdate2dArray(isoDate) // YYYY-MM-DD.dddd to dArray
//       ##############
{
  var flag = false;
  var dArray = new Array(3);
  if( isoDate[0] == "-" )
  { flag = true;
    var parts = isoDate.substring(1).split("-");
  }
  else
  { var parts = isoDate.split("-");
  }
  dArray[0] = parseInt(parts[0]);
  dArray[1] = parseInt(parts[1]);
  dArray[2] = parseFloat(parts[2]);
  if( flag ) dArray[0] = -dArray[0];
  return dArray;
}

function dArray2weekDayNum(dArray)   // Conv. dArray to weekday number
//       #################              Monday = 0, ..., Sunday = 6.
{
  return Math.floor(dArray2julday(dArray) +0.5) % 7;
}

function leapYear(year)    // Returns 1 if year is a leapyear, else 0.
//       ########
{
  if( year > 1582 ) return ( ((year%4 == 0) && (year%100 != 0)) || year%400 == 0 ) ? 1: 0;
  else              return (year%4 == 0) ? 1: 0;
}

function EasterHolidays(year)  // Return array of Easter related holidays (see def. ED-Array).
//       ##############
{
  var i;
  var dTempArray = new Array();
  var FArray = new Array(ED.length);
  var Easter_Julday = dArray2julday( easter_dArray(year) );
  for( i = 0; i < ED.length; i++ )
  { dTempArray = julday2dArray( Easter_Julday + parseInt(ED[i][0])); 
    FArray[i]  = new Array(dTempArray[1],dTempArray[2],ED[i][1]); 
  }
  return FArray;	
}

function ChristmasHolidays(year)  // 1st to 4th Advent Sundays, 1st, 2nd Christmasday,
//       #################           'Buß- und Bettag' in Germany.

{
  var Christmas_dArray = new Array(year,12,25);
  var i, s;
  var dTempArray = new Array();
  var FArray = new Array(7);
  var Christmas_Julday = dArray2julday(Christmas_dArray);
  var ChristmasWeekDayNum = dArray2weekDayNum(Christmas_dArray); // 0: Mo, ..., 6: So.  
  for( i = 0; i < 4; i++ )             
  { dTempArray = julday2dArray( Christmas_Julday - (ChristmasWeekDayNum+1) +7*i - 21 );
    FArray[i]  = new Array(dTempArray[1],dTempArray[2],(i+1).toString()+". Advent");
  }
  dTempArray = julday2dArray( Christmas_Julday - (ChristmasWeekDayNum+1) -32 );
  FArray[4]  = new Array(dTempArray[1],dTempArray[2],"Buß- und Bettag");
  dTempArray = julday2dArray( Christmas_Julday );
  FArray[5]  = new Array(dTempArray[1],dTempArray[2],"1. Weihnachtstag");
  dTempArray = julday2dArray( Christmas_Julday + 1 );
  FArray[6]  = new Array(dTempArray[1],dTempArray[2],"2. Weihnachtstag");
  return FArray;
}

function easter_dArray(year)
//       #############
//
//  Date of the Christian easter sunday, for the Gregorian calendar after
//  Spencer Jones (JBAA 88, p.91, 1977), and for the Julian calendar after
//  Jean Meeus, Astronomical Algorithms (1991). The algorithm for the
//  Gregorian Calender (year > 1582) is defined in Butcher's Ecclesiastical
//  Calendar (1876). This algorithm defines the ecclesiastical easter sunday,
//  it is not always identical with the astronomical easter definition.
{
  var dArray = new Array(3);
  var y,a,b,c,d,e,f,g,h,i,k,l,m,n,p;
  y = year;
  if( y > 1582)     // Gregorian calendar
  { a = y % 19;
    b = Math.floor(y/100);
    c = y % 100;
    d = Math.floor(b/4);
    e = b % 4;
    f = Math.floor((b + 8)/25);
    g = Math.floor((b - f + 1)/3);
    h = (19*a + b - d - g + 15) % 30;
    i = Math.floor(c/4);
    k = c % 4;
    l = (32 + 2*e + 2*i - h - k) % 7;
    m = Math.floor((a + 11*h + 22*l)/451);
    p = h + l - 7*m + 114;
    n = Math.floor(p/31);
    d = p % 31 + 1;
  }
  else               // Julian calendar
  { f = ( y <= 0 ) ? (Math.floor(-y/532)+1)*532: 0;
    y += f;
    a = y % 4;
    b = y % 7;
    c = y % 19;
    d = (19*c + 15) % 30;
    e = (2*a + 4*b - d + 34) % 7;
    g = d + e + 114;
    n = Math.floor(g/31);
    g = g % 31;
    d = g + 1;
  }
  dArray[0] = y;
  dArray[1] = n;
  dArray[2] = d;
  return dArray;
}

function monthLength(year,month)    // Januar = 1, ... December = 12. 
//       ###########
{
  if(year == 1582 && month == 10) return 21;	// Due to Grogorian Calendar-reform! 
  return ( month == 2 ) ? MD[1] + leapYear(year) : MD[month-1];
}

function monthArray( year, month)
//       ##########
{
  var i, j, spalte, first;
  var dTempF = new Array();
  var mArray = new Array(7);
  switch(month)
  { case  3:
    case  4:
    case  5:
    case  6:
      dTempF = FF.concat( EasterHolidays(year) );
      break;
    case 11:
    case 12:
      dTempF = FF.concat( ChristmasHolidays(year) );
      break;
    default:
      dTempF = FF;	
  }
//  dTempF = FF.concat( moonPhaseDates(year) );
  for( i = 0; i < mArray.length; i++ )
  { mArray[i] = new Array(6);
    for( j = 0; j < mArray[0].length; j++ ) mArray[i][j] = "&nbsp;&nbsp;";
  }
  first = dArray2weekDayNum([year,month,1]);
  spalte = 0;
  n = 0;
  for( i = first; i < first + monthLength(year,month); i++ )
  { mArray[i%7][spalte] = (year == 1582 && month == 10 && (i-first+1) > 4) ?
      mArray[i%7][spalte] = (i-first+11):
      mArray[i%7][spalte] = (i-first+1 < 10) ?
        "&nbsp;" + (i-first+ 1):
        (i-first+1);
    if( i%7 == 6 )
    { mArray[6][spalte] = "<font color=\"#FF0000\"><b>" + mArray[6][spalte] + "<\/b><\/font>";
      spalte++;
    }  
    else
    { for( j = 0; j < dTempF.length; j++ )
      { if( parseInt(dTempF[j][0]) == month && parseInt(dTempF[j][1]) == (i-first+1) )
            mArray[i%7][spalte] = "<font color=\"#FF0000\"><b>" + mArray[i%7][spalte] + "<\/b><\/font>";
      }
    }
  }
  return mArray;
}

function monthTable( year, month, align) //  'align':"h" or "v", arangement of week days. 
//       ##########
{
  var i, j;
  var MA = new Array();
  MA = monthArray( year, month);
  document.write("<table class=\"m\" rules=\"all\">\n");
  document.write("<tr>\n");
  if( align == "v" ||  align == "V" )
  {
    document.write("<th class=\"m\" colspan=\"" + (1+MA[0].length) +"\">" + MO[month-1] + "<\/th>\n");  
    document.write("<\/tr>\n");
    for( i = 0; i < MA.length; i++ )
    { document.write("<tr>\n");
      if( i != MA.length-1) document.write("<td class=\"m\">" + WD[i] + "<\/td>\n");
      else  document.write("<td class=\"m\"><font color=\"#FF0000\"><b>" + WD[i] + "<\/b><\/font><\/td>\n");          
      for( j = 0; j < MA[0].length; j++ )
      { document.write("<td class=\"m\">" + MA[i][j] + "<\/td>\n");
      }
      document.write("<\/tr>\n");
    }
  }
  else
  {
    document.write("<th class=\"m\" colspan=\"" + (WD.length) +"\">" + MO[month-1] + "<\/th>\n");
    document.write("<\/tr>\n");
    for( i=0; i < WD.length-1; i++ ) document.write("<td class=\"m\">" + WD[i] + "<\/td>\n");
    document.write("<td class=\"m\"><font color=\"#FF0000\"><b>" + WD[WD.length-1] + "<\/b><\/font><\/td>\n");
    for( i = 0; i < MA[0].length; i++ )
    { document.write("<tr>\n");
      for( j = 0; j <  MA.length; j++ )
      { document.write("<td class=\"m\">" + MA[j][i] + "<\/td>\n");
      }
      document.write("<\/tr>\n");
    }
  }
  document.write("<\/table>\n");
}

function concat_dArray_Array(dArray_Array)  // Concatenate text strings of multiple
//       ###################                   equal dates under one date. 
{
  var i, j, k, n, s;
  var new_dA_A = new Array();
  i = 0;
  n = 0;
  while( i < dArray_Array.length )
  { 
    k = 1;
    s = dArray_Array[i][2];    
    while( i+k < dArray_Array.length &&
           dArray_Array[i][0] == dArray_Array[i+k][0] &&
           dArray_Array[i][1] == dArray_Array[i+k][1] )
    { s += " & " + dArray_Array[i+k][2];
      k++;
    }
    new_dA_A[n] = new Array(dArray_Array[i][0],dArray_Array[i][1],s)
    new_dA_A[n] = new Array(dArray_Array[i][0],dArray_Array[i][1],s)
    i += k; 
    n++;
  }
  return new_dA_A;
}

function sort_dArray_Array(dArray_Array)
//       #################
{ 
  var i, j, tmp;
  var tA = new Array(dArray_Array.length);
  for( i = 0; i < dArray_Array.length; i++ )  // building string array tA[i] for sorting
  { tA[i] = "";
    for( j = 0; j < dArray_Array[i].length-1; j++ )
    { tA[i] += ( dArray_Array[i][j] < 10 ) ?
        "0" + dArray_Array[i][j]:
        dArray_Array[i][j];
    }
    tA[i] += dArray_Array[i][dArray_Array[i].length-1]
  }
  for( i = 0; i < tA.length-1; i++ )	     // here sorting...
  { for( j = i+1; j < tA.length; j++ )
    { if( tA[i] > tA[j] )
      {
      	tmp = tA[i];
      	tA[i] = tA[j];
      	tA[j] = tmp;
     	tmp = dArray_Array[i];
      	dArray_Array[i] = dArray_Array[j];
      	dArray_Array[j] = tmp;
      }
    }
  } 
  return dArray_Array;
}

function allHolidays2String(year)
//       ##################
{
  var i, s;
  var AllF = FF.concat( EasterHolidays(year) );
  AllF = AllF.concat( ChristmasHolidays(year) );
  AllF = AllF.concat( moonPhaseDates(year) );
  AllF = sort_dArray_Array(AllF);
  AllF = concat_dArray_Array(AllF);
  s = "";
  for( i = 0; i < AllF.length; i++ )
  { s = s + 
    (AllF[i][1] + ". " + MO[parseInt(AllF[i][0])-1] + ": " + AllF[i][2]).replace(/\s/g,"&nbsp;");
    if( i < AllF.length-1 ) s = s + ",&nbsp; ";
  }
  return s;
}

function calendar(monthPerRow,align)  // Creates html-table of entire year (year from cookie or Date())
//       ########                        with 'monthPerRow' months per row.
{                                     // 'align':"h" or "v" arangement of week days.  
  var i, j, year;
  year = lastYearCookie();
  document.write("<table class=\"y\" id=\"Calendar\">\n");
  document.write("<tr>\n");
  document.write("<th class=\"y1\" colspan=\"" + monthPerRow + "\">" + year + "<\/th>\n");  
  document.write("<\/tr>\n");
  rows = 12 / monthPerRow;
  for( i = 0; i < rows; i++ )
  {
    document.write("<tr>\n");
    for( j = i*monthPerRow; j < (i+1)*monthPerRow; j++ )
    {
      document.write("<td class=\"y\">");
      monthTable(year,j+1,align);
      document.write("<\/td>\n");
    }
    document.write("<\/tr>\n");
  }
  document.write("<tr>\n");
  document.write("<th class=\"y2\" colspan=\"" + monthPerRow + "\">" + allHolidays2String(year) + "<\/td>\n");  
  document.write("<\/tr>\n");
  document.write("<\/table>\n");
  document.write("<br>\n");
  ctrlForm(year);
  document.inpform.inptext.focus();
}

function Year()   // returns actual year (integer)
//       ####
{
  var y = new Date();
  y = y.getYear();
  if( y < 1900 ) return y + 1900;
  return y;
}

function read_all_Cookie()  // Returns array[i][j], i = index of cookie: 0, 1, ...
//       ###############                            j = 0: name, j = 1: value of cookie.
{
  var i, j;
  var s = document.cookie;
  var x = new Array();
  var y = new Array();
  if( s )
  { x = s.split(";");
    for( i = 0; i < x.length; i++ )
    { y[i] = new Array();
      y[i] = x[i].split("=");
      for( j = 0; j < y[i].length; j++ ) y[i][j] = decodeURIComponent(y[i][j]);
    }
  }
  return y;
}

function new_Cookie(name,value,maxSeconds)
//       ##########
{
  document.cookie = name + "=" + encodeURIComponent(value) + "; max-age=" + maxSeconds;
}

function del_Cookie(name)
//       ##########
{
  document.cookie = name + "=\"\"; max-age=0;";
}

function lastYearCookie()  // Returns last visited Year from Cookie
//       ##############
{
  var lastYear = new Array();
  lastYear = read_all_Cookie();
  if( lastYear.length )
  { for( var i = 0; i < lastYear.length; i++ )
    { if( lastYear[i][0] == LY ) return Number(lastYear[i][1]);
    }
  }
  return Year();
}

function event_evaluation()
//       ################
{
  var year = parseInt(document.inpform.inptext.value);    // value of year-input
  if( typeof year != "number" || isNaN(year) )
  { year = Year();
    alert("Bitte eine Jahreszahl eingeben!");
  }
  if( year < -4712 )                            // Julian Day formula fails
  { year = -4712;   			
    alert("Bitte keine Jahre vor -4712 eingeben!");
  }
  new_Cookie(LY,String(year),CT);       // Keep last year in cookie
  if( year != lastYearCookie() ) alert("Bitte Cookies einschalten!");
  location.reload();
}

function ctrlForm(val)
//       #########
{
  document.write("<form name=\"inpform\" action=\"\">\n");
  document.write("<b>Anderes Jahr:<\/b> <input name=\"inptext\" type=\"text\" size=\"5\" maxlength=\"5\" value=\"" +
   val + "\" onKeyPress=\"javascript:return checkEnter(event)\">&nbsp;&nbsp;\n");
  document.write("<input name=\"Knopf\" type=\"button\" value=\"Los!\" onclick=\"javascript:event_evaluation()\">\n");
  document.write("<\/form>\n");
}

function checkEnter(event) // event is the event-object passed from function invocation
//       ##########
{
  var e = event || window.event;       // Key event object
  var code = e.charCode || e.keyCode;  // What key was pressed
  if(code == "13") event_evaluation();
  else  return true;
}

