//
// Enhanced JavaScript Event Calendar
//
// Author:  Rick Pike
// Website: http://calendar.pikesys.com
// Email: calendar@pikesys.com
//'Version 0.3 - (07-May-2004)
//
// based on an earlier script by Kevin Ilsen (http://calendar.ilsen.net or kevin@ilsen.net)

// Configurable values are set to defaults here; you can override them before calling Calendar( ) from your HTML page
// Calendar was modified by Lindsay C. All rights belong to the original creator. 
var SpecialDay=1;	// 1=Sunday, 2=Monday, . . . 7=Saturday
var ColorBackground="#FBF1EB";
var ColorSpecialDay = "black";
var ColorToday = "green";
var ColorEvent = "black";
var showImages = false; 	// enable event images (might disable for print version)
var showLinks = true; 		// enable event hyperlinks (might disable for print version)
var msgBoxColor = "#ffcc99";
var navColor = "#FBF1EB";
var imageAlign = "left"; 	// default event image alignment
var imageScale = 100;		// percent scale factor for images
var altAlign = true; 		// alternate left/right alignment of event images on same date for readability
var DefaultFormat = "custom"; // for compatibility; set to "layer" to simplify formatting
var ExportPage = ""; 		// name of html page for displaying event text for export
var PrintPage = ""; 		// name of html page for printer-friendly format

// all format codes must have "|" to separate before and after tags
var DateFontSize=4;
var AltDateFormat = "<font color=#999999 size=2>|</font>";
var MonthFormat = "<font size=" + DateFontSize + "><b>|</b></font>";
var AltMonthFormat = "<br><font color=#999999 size=" + DateFontSize/2 + ">|</font>";
var HolidayFormat = "<font color=#999999 size=2><b><center>|</center></b></font>";
var AltHolyFormat = "<font  size=2><b><center>|</center></b></font>";
var DefaultLayerFormat = "<font size=2>|</font>";

var defaultMsgBox = "Note: The information here may not be current; please confirm dates and times.<p>";
var course;
//Set the following to the first Monday of the semester
var fmonday = 2;
//Set the following to the first date of the class
var fday = 3;
//Set the following to the first month of the semester, i.e. September = 9
var fmonth = 2;
//Set the following to the last month and last day of the semester, i.e. December = 12
var lmonth = 5;
var lday = 14;
var	one_day = 1000*60*60*24;
// Initialize the range of the calendar to Jan - Dec of the current year.
// Event definitions will change this as needed; or it can be explicitly
// overridden before calling Calendar( ) from the HTML page.

var today = new Date();
var FirstMonth=GetFullYear(today) * 100 + 1;
var LastMonth=FirstMonth + 11;

// Layers[] is an array of layer names and default formats. Call DefineLayer( ) to add the
// layer name and corresponding format. New layer names in event definitions will
// be added automatically (watch case and spelling).

var Layers = new Array;
var LayerString = "|";
var layerCount = 0;
var layerChange = false;

// MsgBoxes[] is a SPARSE array; Call AddMsgBox( ) to populate it

var MsgBoxes = new Array;

// Events[] is a SPARSE array; Call AddEvent( ) to populate it or DefineEvent( ) for
// compatibility with existing calendar definition scripts.

var Events = new Array;

// Each event is defined by calling the AddEvent( ) routine with the following parameters:
//
//   AddEvent(Date, Description, Layer, Format, Link, Image, Width, Height, Align, Alt)
//        Date is a numeric value in the format YYYYMMDD
//        Description is a string (may include embedded HTML tags such as <BR>, <strong>, etc.)
//        Layer is an event category whose display on the calendar can be toggled on/off by the user
//        Format specifies the source of the format info for Description. Note that any embedded
//        formats in Description will still be applied; this argument is primarily to provide
//        a more convenient way to specify formats. 
//            "layer" = use default format from layer definition (plus any embedded tags!)
//            "custom" = only use format tags embedded in Description string
//            "<...>|</...>" = series of before and after tags separated by a "|" to wrap around
//               Description (plus any embedded tags!)  
//        Link is the URL of the target page if a hyperlink is desired from this event entry
//        Image is the URL of the image if you want to display an image with this event
//        Width is the width of the image in pixels
//        Height is the height of the image in pixels
//        Align is the alignment of the image such as "left", or "right"
//        Alt is the alternate text of the image (for mouseover or non-graphic browser)

// The function DefineEvent(Date, Description, Link, Image, Width, Height) is used
// for compatibility with previous calendar definition scripts

// Event constructor
function Event(description, layer, format, link, image, width, height, align, alt) {

	this.description = description;
	if (link) this.link = link;
	if (image) {
	   this.image = image;
	   if (width) this.width = width;
	   if (height) this.height = height;
	   if (align) this.align = align;
	   if (alt) this.alt = alt;
	   //alert("Event: ("+this.width+","+this.height+")");
	}
	this.layer = (layer ? layer : "default");	 	// must have some associated layer
	
	if (format) {
	   if (CheckFormat(format)) this.format = format;
	}
}

// Compatibility function for existing calendar definition scripts
function DefineEvent(Date, Description, Link, Image, Width, Height) {
	AddEvent(Date, Description, "", "", Link, Image, Width, Height, "", "");
}

function AddEvent(Date, Description, Layer, Format, Link, Image, Width, Height, Align, Alt) {
	var i;

	if (!Events[Date]) Events[Date] = new Array;
	i = Events[Date].length;
	Events[Date][i] = new Event(Description, Layer, Format, Link, Image, Width, Height, Align, Alt);

	// Adjust the minimum and maximum month & year to include this date
	tmp = Math.floor(Date / 100);
	if (tmp < FirstMonth) FirstMonth = tmp;
	if (tmp > LastMonth) LastMonth = tmp;

	// if EventLayer not in Layers, add it
	// NOTE: watch case in DefineLayer and DefineEvent to avoid undesired "duplicates"

	//alert("AddEvent("+Date+","+Description+","+Width+","+Height+"...)");
	if (Layer && !Layers[Layer]) DefineLayer(Layer);
}

// Layer constructor
function Layer(format, show) {
	if (format) {	
	   if (format.indexOf("|")>0) {
	      this.format = format;
	   } else {
	      alert("Invalid Calendar Format String: " + format);
	   }
	} else {
	   this.format = DefaultLayerFormat;
	}
	this.show = (show=="false" ? "false" : "true");	// watch boolean vs. string arg
}

function DefineLayer(LayerName, LayerFormat, LayerShow) {
	Layers[LayerName] = new Layer(LayerFormat, LayerShow);
	layerCount++;
}

// Utility function to populate an array with values
function arr() {
	for (var n=0;n<arr.arguments.length;n++) {
		this[n+1] = arr.arguments[n];
	}
}

// Create the array of month names (used in various places)
var months = new arr("January","February","March","April","May","June","July","August","September","October","November","December");
var months2 = new arr("1\u6708","2\u6708","3\u6708","4\u6708","5\u6708","6\u6708","7\u6708","8\u6708","9\u6708","10\u6708","11\u6708","12\u6708");

// Calendar( ) is the only routine that needs to be called to display the calendar
var cookieName = "layerCalendar";
var pre, post, tmp, gloMo, gloYr, gloYearmonth;

function Calendar( ) {
	var curdy, curmo, yr, mo, dy, firstday, yearmonth, bgn, lastdate, jump;
	var curmo2, curyr, allday, curti, curwdy, fdc, fdct;
	var weekdays = new arr("Mon","Tue","Wed","Thu","Fri");
	var weekdays2 = new arr("\u6708","\u706b","\u6c34","\u6728","\u91d1");
	var thispage = window.location.pathname;
	var myMsgBox = "Put this FREE calendar script on your own site; see <a href=http://calendar.pikesys.com>calendar.pikesys.com</a>.<p>";

	// define default MsgBox messages
	
	// Save current day and month for comparison
	curdy = today.getDate();
	curmo = today.getMonth()+1;
	curmo2 = today.getMonth();
	curyr = today.getFullYear();
	// Create a new date to not cause issues with today
	allday = new Date(curyr, curmo2, curdy,0,0,0,0);
	curti = allday.getTime();
	curwdy = allday.getDay();
//	document.write("current week day "+curwdy+"<br>");
	// If it is before the first day of class, enables the showing of the first week on Sep. 3
	// Note that the month is the known value - 1 i.e. 8 corresponds to September
	// To upkeep in following years, update the value for fmon in the setting at the top of the file
	fdc = new Date(curyr, fmonth-1, fmonday,0,0,0,0);
	fdct = fdc.getTime();
	if (allday < fdc) {
		// Reset everything so it starts at the begining of the semester if it hasn't started
		allday = fdc;
		curti = allday.getTime();
		curwdy = allday.getDay();
		curmo2 = 8;
		curmo = 9;
		// If it is a Saturday, the next week will be displayed
		} else if (curwdy == 6) {
			curti = curti + one_day*2;
			allday = new Date(curti);
			curwdy = allday.getDay();
		} else if (curwdy == 0) {
			curti = curti + one_day * 3;
			allday = new Date(curti);
			curwdy = allday.getDay();
		}  else if (curwdy == 5) {
			curti = curti - one_day*2;
			allday = new Date(curti);
			curwdy = allday.getDay();
//			document.write("Allday changed to "+allday+"<br>");
		}
	
	// Determine Monday's date
	fidyt = curti - one_day*(curwdy-1);
	fid = new Date(fidyt);
//	document.write("Start date "+fid+"<br>");
	fiday = fid.getDay();
	fidate = fid.getDate();
	// Determine Friday's date
	endd = new Date(one_day*4 + fidyt);
	endday = endd.getDay();
	enddate = endd.getDate();
	endt = endd.getTime();
	
	initCal(curmo);
	mo = gloMo; yr = gloYr; yearmonth = gloYearmonth;
	getLayersVisible(); 		// get layer visibility settings from cookie
	
	// Create a date object for the first day of the desired month
	bgn = new Date(months[mo] + fidate + yr);
	// Get the day-of-week of the first day, and the # days in the month
	firstday = 1;
	lastdate = enddate;
	lastday = 5;
	prev = next = "&nbsp;";
	
	parseFormat(MonthFormat);
	tmp = "<TABLE BORDER=2 BGCOLOR="+ColorBackground+" width=\"500\" cellpadding=\"3\" cellspacing=\"3\">";

	document.write(tmp);
	document.write("<TR BGCOLOR="+navColor+">");
	
	for (var i=1;i<=5;i++){
		document.write("<TD ALIGN=CENTER WIDTH=14% class=\"cellelem\"><FONT SIZE=3>"+weekdays2[i]+"</FONT></TD>");
	}
	dy = fidate;
	document.write("</TR><TR>");
	
	dyc = fid;
	dyct = dyc.getTime();
	dmo = dyc.getMonth()+1;
	
	while (dyc < endd) {
		for (var i=1; i<=5; i++) {
			// Create element by element filling with date and event
//				document.write("today is "+ dyc+ "<br>"+"month"+dmo+"day"+ dy + "<br>");
				document.write("<TD VALIGN=TOP height=\"200\" class=\"cellelem\">");
				ShowDate(yr,dmo,dy,i,curmo,curdy);
				document.write("</TD>");				
				dyct = dyct + one_day;
				dyc = new Date(dyct);
				dy = dyc.getDate();
				dmo = dyc.getMonth()+1;
				

			
		}
		document.write("</TR>");
	}

	
	document.write("</TABLE>");
}

// Display a date in the appropriate color, with events (if there are any)

function ShowDate(yr, mo, dy, dayofweek, currentmonth, currentday) {

	var ind, DayHighlighted, tmp, event;
	var fdc, fdct, dyt, now, nowt, weeknum, ldc;
	var fmonwe = fmonth-1;
	var lmonwe = lmonth-1;
	var mowe = mo-1;
	//Find current week number as of first week of class
	var fdy = new Date(yr, fmonwe, fday,0,0,0,0);
	fdc = new Date(yr, fmonwe, fmonday,0,0,0,0);
	ldc = new Date(yr, lmonwe, lday,0,0,0,0);
	now = new Date(yr, mowe, dy,0,0,0,0);
	var compensation = now.getDay();
	if (compensation > 4) compensation -= 4;
	else compensation += 3;
	weeknum = Math.round((((now-fdc)/86400000)+compensation)/7);
//		Do not insert a link if the week is 0 or >15
		if (now >= fdy && now <= ldc && dayofweek >= 1 && dayofweek < 7) {
			var insert1 = "<a href=" + course + "/week" + weeknum + ".html class=\"date\">";
			var insert2 = "</a>";
		} else {
			var insert1 = "";
			var insert2 = "";
		}
		
	tmp = "<TABLE><TR VALIGN=TOP><TD ALIGN=LEFT WIDTH=10%>";
	tmp += "<FONT SIZE=" + DateFontSize;	// Note open tag!	
	DayHighlighted = false;

	if ((mo == currentmonth) && (dy == currentday)) {
		tmp += " COLOR=" + ColorToday;
		DayHighlighted = true;
	} else if (dayofweek == SpecialDay) {
		tmp += " COLOR=" + ColorSpecialDay;
		DayHighlighted = true;
	}

	ind = (((yr * 100) + mo) * 100) + dy;
	if (Events[ind]) {
		//event = Events[ind];
		if (!DayHighlighted) tmp += " COLOR=" + ColorEvent;
//	} else {
//		event = "&nbsp;<BR>&nbsp;";
	}
	
	tmp += "><B>" + insert1 + dy + (dy<10 ? "&nbsp;" : "") + insert2 + "&nbsp;" + "</B></FONT></TD>";	 	// close tag
	document.write(tmp);
	

	document.write("</TR></TABLE>");

		
//	document.write("<P><FONT SIZE=1>" + event + "</FONT>");

	images=0;
	for (j in Events[ind]) {
		ev = Events[ind][j];
		// with(Events[ind][j]) { // greatly improves readability of following
		//for (k in ev) { alert("ev["+k+"]="+ev[k]) }
		//  document.write("Events[" + ind + "][" + j + "][" + k + "] = " + EscapeLtGt(Events[ind][j][k]), "<br>");
		//alert("-> Layers[Events["+ind+"]["+j+"]."+ev.layer+"].show="+Layers[ev.layer].show);
		tmp = "";		
		if (Layers[ev.layer].show == "true") {
			//alert("(+)showing event for "+ind+" in layer "+ev.layer);
			// Build the HTML string for this event
			tmp += (j>0 ? "<br>" : "");
			if (ev.image && showImages) {
				if (ev.align) {
					align = ev.align;
				} else if (altAlign && images>0) {
					align = (lastAlign=="left" ? "right" : "left");
				} else {
					align = imageAlign;
				}
				tmp += '<img src="' + ev.image + 
					   (ev.width ? '"  width="' + ev.width*imageScale/100 : '') +
					   (ev.height ? '" height="' + ev.height*imageScale/100 : '') +
					   (ev.alt ? '" alt="' + ev.alt : '') +					   
					   '" align="' + align + '" valign="top">';
				lastAlign = align;
				images++;
			}
			format = "";
			if (ev.format == "layer") { // use format from layer (and embedded tags)
				format = Layers[ev.layer].format;
			} else if (ev.format == "custom") { // use only embedded tags
				format = "";
			} else if (ev.format) { 	// format string?
				if (CheckFormat(ev.format)) format = ev.format;
			} else if (DefaultFormat == "layer") { // use format from layer if no custom format
				format = Layers[ev.layer].format;
			}
			parseFormat(format);
			//tmp += pre + (ev.link && showLinks ? ev.description.link(ev.link) : ev.description) + post + "<br>";
			tmp += pre + (ev.link && showLinks ? "<a class="+ev.layer+" href="+ev.link+">"+ev.description+"</a>" : ev.description) + post + "<br>";
			
		} else {
			//alert("(-)skipping event for "+ind+" in layer "+ev.layer);
		}
	document.write("<FONT SIZE=1>" + tmp + "</FONT>");
	}
}


function getLayersVisible() {
	// get layer visibility settings from cookie
	prefix = cookieName + "|";
	cookie = unescape(document.cookie);
	pos = cookie.indexOf(prefix);
	if (pos>=0) {
	    pos += prefix.length;
	    sep = cookie.indexOf(";");
	    if (sep == -1) sep = cookie.length;
	    var layerCookies = cookie.substring(pos, sep).split("|");
	    for (i=0; i<layerCookies.length; i++) {
	        sep = layerCookies[i].indexOf("=");
		name = layerCookies[i].substring(0, sep);
		value = layerCookies[i].substring(sep+1, layerCookies[i].length);
		if (Layers[name]) Layers[name].show = value;
		//alert("Read cookie: "+name+"="+value+" Layers["+name+"]="+Layers[name].show);
	    }
	}
}
function initCal(curmo) {

	// create default layer
	if (!Layers["default"]) DefineLayer("default", DefaultLayerFormat);
	
	// Default to current month and year
	mo = curmo;
	yr = GetFullYear(today);
	yearmonth = (yr * 100) + mo;

	// If querystring parameter is present, get the month/year ("calendar.htm?YYYYMM")
	if (location.search.length > 1) {
		yearmonth = parseInt(location.search.substring(1,location.search.length));
		if ((""+yearmonth).length == 6) {
			mo = yearmonth % 100;
			yr = (yearmonth - mo) / 100;
		}
	}

	// Constrain to the range of months with events
	if (yearmonth < FirstMonth) {
		mo = FirstMonth % 100;
		yr = (FirstMonth - mo) / 100;
		yearmonth = FirstMonth;
	}
	if (yearmonth > LastMonth) {
		mo = LastMonth % 100;
		yr = (LastMonth - mo) / 100;
		yearmonth = LastMonth;
	}
	gloMo = mo; gloYr = yr; gloYearmonth = yearmonth;
}

function qw(string) {
	return "&quot;" + string + "&quot;";
}




// Remaining routines are utilities used above




// fixes a Netscape 2 and 3 bug
function GetFullYear(d) { // d is a date object
	var yr = d.getYear();
	return ( yr < 1000 ? yr + 1900 : yr );
}



// Create a message box

function DoMsgBox(yearmonth, msgSpan) {
	if (MsgBoxes[yearmonth]) {
		for (j in MsgBoxes[yearmonth]) { 	// find first unshown message for month that fits
			monthlyMsg =MsgBoxes[yearmonth][j];
			if (!monthlyMsg.shown) {
				//document.write("monthlyMsg.minspan = " + monthlyMsg.minspan + "<br>");
				if (!monthlyMsg.minspan || monthlyMsg.minspan <= msgSpan) {
					tmp = "<table border=2 bgcolor=" + msgBoxColor + " align=center>";
					tmp += "<TR><TD>"+ monthlyMsg.message + "</TD></TR></table>";
					document.write(tmp);
					monthlyMsg.shown = true;
					return;
				}
			}
		}
	}
	if (MsgBoxes[0]) { 	// show default MsgBox messages if they exists
		for (j in MsgBoxes[0]) { 	// find first unshown message for month that fits
			monthlyMsg =MsgBoxes[0][j];
			if (!monthlyMsg.shown) {
				//document.write("monthlyMsg.minspan = " + monthlyMsg.minspan + "<br>");
				if (!monthlyMsg.minspan || monthlyMsg.minspan <= msgSpan) {
					tmp = "<table border=2 bgcolor=" + msgBoxColor + " align=center>";
					tmp += "<TR><TD>"+ monthlyMsg.message + "</TD></TR></table>";
					document.write(tmp);
					monthlyMsg.shown = true;
					return;
				}
			}
		}
	}
}

// Utility routines

function escramVal(j,k){var a,b,c,d,e;a='<a href=\"mai';c='\">';a+='lto:';b=j+'@';e='</a>';b+=k;d=b;return(a+b+c+d+e);}

function parseFormat(format) {
	// pre and post are globals
	pre = post = "";
	if (format) {		
		var sep = format.indexOf("|");
		if (sep > 0) {  // split format into pre and post strings
			pre = format.substring(0, sep);
			post= format.substring(1+sep, format.length);
		}
	}
}

function CheckFormat(String) {
	var okay;
	okay =(String == "layer" || String == "custom" || String.indexOf("|")>0);
	if (!okay) alert("Invalid Calendar Format String: " + String);
	return okay;
}
	
// MsgBox constructor
function MsgBox(message,minspan,maxspan) {
	//alert("MsgBox("+message+","+minspan+","+maxspan+")");
	this.message = message;
	this.minspan = (minspan ? minspan : 1);
        if (maxspan) this.maxspan = maxspan;
	//this.shown = false;
}

function AddMsgBox(yearmonth,message,minspan,maxspan) {
	var i;

	if (!MsgBoxes[yearmonth]) MsgBoxes[yearmonth] = new Array;
	i = MsgBoxes[yearmonth].length;
	MsgBoxes[yearmonth][i] = new MsgBox(message,minspan,maxspan);
}// JavaScript Document