// Produces a calendar for 1 year, including ISO weeknumbers and 4 moving holidays.
// Gregorian or Julian calendar. Does also work for all negative years,
// but the validity of the calendar and the holidays is nil.
// Should work for all type 4+ browsers, although window management is not optimal.
// Author & copyright: Oscar van Vlijmen, ovv (at) hetnet.nl
// Version date: 2003-12-17
// The only original web page is:  http://home.hetnet.nl/~vanadovv/Kalender.html
// Use at your own risk!
// You are allowed to reuse or redistribute this JavaScript for
// non-commercial exploitation purposes only,
// and only if all comment lines in this block are copied.

function init() {
// Fills the text input area with the current year on loading the page.
// Probably the only JavaScript 1.2 dependency in this script.
thans = new Date();
document.dform.jaarin.value=thans.getFullYear();
};


function jaarkalender(y) {
// global jan1weekday,dayspermonth,weeknumber
// There is no sanity check on the input!!
ysave=y;

// In order to be able to localize this application, here are all language
// dependent string variables.
kopg = " (Gregoriaans)"; kopj = " (Juliaans)"; // Calendar type for window title.
// Names of the days of the week abbreviated:
day1 = "Ma"; day2 = "Di"; day3 = "Wo"; day4 = "Do"; day5 = "Vr"; day6 = "Za";
day7 = "<font color='red'>Zo</font>"; // Sunday displayed in red.
wkpar = "(wk)"; // The word for "week" abbreviated and between ().
// The names of the months plus a space:
mnd01 = "januari "; mnd02 = "februari "; mnd03 = "maart "; mnd04 = "april ";
mnd05 = "mei "; mnd06 = "juni "; mnd07 = "juli "; mnd08 = "augustus ";
mnd09 = "september "; mnd10 = "oktober "; mnd11 = "november "; mnd12 = "december ";
calname = "Kalender "; // The word for "calendar" plus a space.
cset = "iso-8859-1"; // Character set designation for meta-tag.
// Another useful character set: utf-8. Note that the html page from which
// this script is invoked, must have the same character set meta-tag,
// especially if it's not the browser's default one.
// This script doesn't work properly in Mac Netscape 4.x in a utf-8 page
// due to idiotic behavior of NN4 (page is auto reloaded, but js is forgotten).
// And now some special Easter related holidays:
asw = "Aswoensdag"; // Ash Wednesday
pas = "Pasen"; // Easter
hmv = "Hemelvaart"; // Ascension day
pnk = "Pinksteren"; // Pentecost (Whitsunday)

// Localization explample with Russian.
// kopg = " (Грегорианский)"; kopj = " (Юлианский)";
// day1 = "пн"; day2 = "вт"; day3 = "ср";
// day4 = "чт"; day5 = "пт"; day6 = "сб";
// day7 = "<font color='red'>вс</font>";
// wkpar = "(нд)";
// mnd01 = "Январь "; mnd02 = "Февраль "; mnd03 = "Март ";
// mnd04 = "Апрель "; mnd05 = "Май "; mnd06 = "Июнь ";
// mnd07 = "Июль "; mnd08 = "Август "; mnd09 = "Сентябрь ";
// mnd10 = "Октябрь "; mnd11 = "Ноябрь "; mnd12 = "Декабрь ";
// calname = "Календарь ";
// cset = "utf-8"; // don't forget to set a utf-8 meta in the invoking page!
// asw = "Пепельная Среда";
// pas = "Пасха";
// hmv = "Вознесение";
// pnk = "Троицын день";


if (document.dform.gj[0].checked==true) { // Gregorian year
  kop = kopg;
  // In order to cope with years <=0,
  // we'll transpose a negative year to an equivalent positive year; there is a year 0.
  if (y<=0) {ystar = 2000-(-y % 400)} else {ystar=y};
  // Compute day of the week for Jan 1 (Monday=1, Sunday=7).
  y=ystar-1;
  jan1weekday = (y+Math.floor(y/4)-Math.floor(y/100)+Math.floor(y/400)+29) % 7;
  if (jan1weekday==0) jan1weekday = 7;
  // Is year leap?
  if ( ((ystar % 4 == 0) && (ystar % 100 != 0)) || (ystar % 400 == 0) ) {
    yleap=1
  } else {
    yleap=0
  };
  // Is year-1 leap?
  if ( (((ystar-1) % 4 == 0) && ((ystar-1) % 100 != 0)) || ((ystar-1) % 400 == 0) ) {
    yprevleap=1
  } else {
    yprevleap=0
  };
} else { // Julian year
  kop = kopj;
  // Transpose a negative year to an equivalent positive year.
  // There is no year 0 but it is correctly treated as the year -1.
  if (y<0) {ystar = 701-(-y % 28)} else {ystar=y};
  // Compute day of the week for Jan 1 (Monday=1, Sunday=7).
  y=ystar-1;
  jan1weekday = (y+Math.floor(y/4)+34) % 7;
  if (jan1weekday==0) jan1weekday = 7;
  // Is year leap?
  if (ystar % 4 == 0) {
    yleap=1
  } else {
    yleap=0
  };
  // Is year-1 leap?
  if ((ystar-1) % 4 == 0) {
    yprevleap=1
  } else {
    yprevleap=0
  };
}; // end if Gregorian/Julian year

// Is Jan 1 in week 52 or 53 of the previous year, or in week 1?
if ( 1<=(8-jan1weekday) && jan1weekday>4 ) {
  yearnumber=ystar-1;
  if ( jan1weekday==5 || (jan1weekday==6 && yprevleap==1) ) {
    weeknumber=53
  } else {
    weeknumber=52
  }
} else {
  yearnumber=ystar; weeknumber=1
};

dayspermonth = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
if (yleap==1) {dayspermonth[1]=29}; // Feb is leap; arrays start with position 0!
et = "</td><td>";
thedays = "<tr bgcolor='#b1ffff' align='center'><td>" + wkpar + et + day1 + et + day2 + et + day3 + et + day4 + et + day5 + et + day6 + et + day7 + "</td></tr>";

// Ready to produce a calendar for each month of the input year ysave.
nl = "\n";
tblm = "<table border=0 style='font-family:times; font-size:10px;'>"; etbl = "</table>"; etd = "</td>";
tdm = "<tr align='center'><td colspan=8>"; etdr = "</td></tr>";

// Yes, this could be coded more efficiently with arrays...
m01 = tblm + tdm + mnd01 + ysave + etdr + nl + thedays + nl;
m01 += fillmonth(1) + etbl + nl;
m02 = tblm + tdm + mnd02 + ysave + etdr + nl + thedays + nl;
m02 += fillmonth(2) + etbl + nl;
m03 = tblm + tdm + mnd03 + ysave + etdr + nl + thedays + nl;
m03 += fillmonth(3) + etbl + nl;
m04 = tblm + tdm + mnd04 + ysave + etdr + nl + thedays + nl;
m04 += fillmonth(4) + etbl + nl;
m05 = tblm + tdm + mnd05 + ysave + etdr + nl + thedays + nl;
m05 += fillmonth(5) + etbl + nl;
m06 = tblm + tdm + mnd06 + ysave + etdr + nl + thedays + nl;
m06 += fillmonth(6) + etbl + nl;
m07 = tblm + tdm + mnd07 + ysave + etdr + nl + thedays + nl;
m07 += fillmonth(7) + etbl + nl;
m08 = tblm + tdm + mnd08 + ysave + etdr + nl + thedays + nl;
m08 += fillmonth(8) + etbl + nl;
m09 = tblm + tdm + mnd09 + ysave + etdr + nl + thedays + nl;
m09 += fillmonth(9) + etbl + nl;
m10 = tblm + tdm + mnd10 + ysave + etdr + nl + thedays + nl;
m10 += fillmonth(10) + etbl + nl;
m11 = tblm + tdm + mnd11 + ysave + etdr + nl + thedays + nl;
m11 += fillmonth(11) + etbl + nl;
m12 = tblm + tdm + mnd12 + ysave + etdr + nl + thedays + nl;
m12 += fillmonth(12) + etbl + nl;


nlbr = "<br>" + nl;
td = "<td>";
validEasterdate = false;
// Compute some Easter related holidays, here for Gregorian years.
// Works very good, even for the difficult cases of
// 1954, 1981, 2049, 2076, 2106, 2133, 3165, 4000, 4200 etc.
// You could make it working for negative years by using the fact that the Julian
// canon of Easter dates has a periodicity of 532 years and the Gregorian a
// periodicity of exactly 5700000 years.
// First Easter is calculated: n = the number of the day in the year for Easter Sunday
if (ysave>0 && document.dform.gj[0].checked==true) { // Gregorian year
  validEasterdate = true;
  gz = (ysave % 19)+1;
  jhd = floor(ysave/100)+1;
  ksj = floor(3*jhd/4)-12;
  corr = floor((8*jhd+5)/25)-5;
  so = floor(5*ysave/4)-ksj-10;
  epa = (11*gz+20+corr-ksj) % 30;
  if ((epa==25 && gz >11) || epa==24) epa++;
  n = 44-epa;
  if (n<21) n += 30;
  n = n+7-((so+n) % 7);
  n += yleap;
  n += 59;
} else {
if (ysave>0 && document.dform.gj[1].checked==true) { // Julian year
  validEasterdate = true;
  a = ysave % 4; b = ysave % 7; c = ysave % 19;
  d = (19*c+15) % 30;
  e = (2*a+4*b-d+34) % 7;
  r = d+e+114;
  mn = floor(r/31); pd = (r % 31) + 1; // month & day; now convert to day of year
  aa = floor((mn+10)/13);
  bb = floor(pd+(611*(mn+2))/20-2*aa-91);
  n = bb + yleap*aa;
}};

if (validEasterdate==true) {
  // Make the date strings
  Dasw = ysave.toString() +"-"+ cdt(n-46,yleap);
  Dpas = ysave.toString() +"-"+ cdt(n,yleap);
  Dhmv = ysave.toString() +"-"+ cdt(n+39,yleap);
  Dpnk = ysave.toString() +"-"+ cdt(n+49,yleap);
  trf = nl + "<tr><td width=112>&nbsp;" + td;
  feestdgn = "<center>";
  feestdgn += asw + " " + Dasw + " | " +  pas +" " + Dpas + " | ";
  feestdgn += hmv + " " + Dhmv + " | " +  pnk +" " + Dpnk;
//  feestdgn += trf + pas + td + Dpas;
//  feestdgn += trf + hmv + td + Dhmv;
//  feestdgn += trf + pnk + td + Dpnk;
  feestdgn += nl + "</center>" + nlbr;
} else {
  feestdgn = nlbr
};


// Put the production into a new window.
// 12 table cells will be produced, each with a <table> formatted calendar for 1 month.

detekst = "<html><head><title>" + calname  + ysave + kop +"</title>" + nl;
detekst += "<meta http-equiv='Content-Type' content='text/html; charset=" + cset + "'>" + nl;
detekst += "</head><body style='font-family:verdana; font-size:10px;'>" + nl;
detekst += "<center><h5>Kalender " + ysave + "</h5><table border=1 cellspacing=18 style='font-family:verdana; font-size:10px;'>" + nl;
trd = "<tr valign='top'><td>";
tede="<td>"
ttd="</td><td>"
detekst += trd + m01 + ttd + m02 + ttd +m03 + etdr + nl ;
detekst += trd + m04 + ttd + m05 + ttd +m06 + etdr + nl ;
detekst += trd + m07 + ttd + m08 + ttd +m09 + etdr + nl ;
detekst += trd + m10 + ttd + m11 + ttd +m12 + etdr + nl ;
detekst += "</table></center>";
detekst += feestdgn + nlbr + "</body></html>";

win1=window.open('','Kalender','width=700,height=660,scrollbars=1,menubar=1,resizable=1,scrollbars=1');
// Netscape 4 has a window resize problem - not dealt with here.
win1.focus(); win1.document.write(detekst);

}; // end function jaarkalender


function fillmonth(thismonth) {
// Make a calendar for thismonth, including weeknumbers.
// 1 html table contents is produced; no table start-end tags!
// global jan1weekday,dayspermonth,weeknumber
firstofmonth = jan1weekday;
for (j=1;j<=thismonth-1;j++) {
  firstofmonth += dayspermonth[j-1]
};
firstofmonth = firstofmonth % 7;
if (firstofmonth==0) { firstofmonth=7 };
// firstofmonth = the weekday of the 1st of month thismonth.
tl = "<tr align='center'><td bgcolor='#b1ffff'>"; etl = "</tr>";
tc = "<td>"; etc = "</td>"; tcsp = tc + "&nbsp;" + etc;
tcc = "<td bgcolor='#fffbb1'>";

amonth = tl + "(" + twodigit(weeknumber) + ")" + etc;
// First, shift the beginning of the month to the correct position
// under the day abbrevs.
for (i=1;i<=firstofmonth-1;i++) {
  amonth += tcsp
};

// Then fill the month up; new line on monday; start with weeknumber.
// The date numbers will be displayed on yellow background.
for (i=1;i<=dayspermonth[thismonth-1];i++) {
  if ( ((i+firstofmonth-2) % 7 == 0) && i>1 ) {
    amonth += etl + "\n";
    if ( (weeknumber==52 || weeknumber==53) && thismonth==1 ) {
      weeknumber=1
    } else {
      weeknumber += 1
    };
    if (weeknumber==53 && thismonth==12 && i>=29) {
      weeknumber=1
    };
    amonth += tl + "(" + twodigit(weeknumber) + ")" + etc;
    amonth += tcc + twodigit(i) + etc
  } else {
    amonth += tcc + twodigit(i) + etc
  }
};

// Sometimes a month ends on sunday; next month starts with next weeknumber.
if ( (((dayspermonth[thismonth-1])+firstofmonth-1) % 7 == 0) ) {
  weeknumber += 1
};
amonth += etl;
// Syntactically there is still something wrong with the table: most months will have
// too few cells at the end. Most browsers seem not to care.
return amonth
}; // end fillmonth


function twodigit(dit) {
// Input: integer; if length=1, then put 0 in front.
if (String(dit).length==1) { return "0" + dit.toString();} else { return dit};
};

function floor(x) {
return Math.floor(x)
};

function cdt(dn,leap) {
// Converts day number within a year to day & month numbers
// leap = 1 for a leap year, 0 for a regular year
if (dn>59+leap) dn += 2-leap;
dn += 91;
mn = floor((20*dn)/611)-2;
dg = dn-floor(((mn+2)*611)/20);
return twodigit(mn) +"-"+ twodigit(dg)
};