/////////////////////////////////////////////////////////////////////////////
//
// Gladiator Components
//
// http://www.gladiatorweb.com
//
// (c) 2006 by Edward H. Trager .  All Rights Reserved
//
// NAME: gladiatorCalendar.js
//
// DESCRIPTION: Implements a popup calendar
//
// AUTHOR: Edward H. Trager
//
// LAST UPDATE: 2006.03.08
//
// NOTE: SEE "LICENSE" FILE FOR LICENSING TERMS
//
//////////////////////////////////////////////////////////////////////////////

//
// DEPENDENCY CHECKING:
//
if(typeof GLADIATOR_CORE_INCLUDED == 'undefined') alert('NOTE BENE: "gladiatorCalendar.js" REQUIRES "gladiatorCore.js"!');
if(typeof GLADIATOR_DATE_INCLUDED == 'undefined') alert('NOTE BENE: "gladiatorCalendar.js" REQUIRES "gladiatorDate.js"!');
if(typeof GLADIATOR_REQUEST_INCLUDED == 'undefined') alert('NOTE BENE: "gladiatorCalendar.js" REQUIRES "gladiatorRequest.js"!');

var GLADIATOR_CALENDAR_INCLUDED=true;

///////////////////////////////////////////////////////
//
// Calendar Class
//
// id: ID of the calendar
// top: top (y) coordinate in pixels
// left: left (x) coordinate in pixels
// year:  year
// month: month
// day:   selected day of month to highlight
// show:  t/f; show or hide
// showHeaderImages: t/f; show header background images
// showCanvasImages: t/f; show canvas background images
// eventFeedURL:     URL for event feeds
// parentNode:       Uses "body" unless otherwise specified
//
///////////////////////////////////////////////////////
function gCalendar(id,top,left,year,month,day,show,showHeaderImages,showCanvasImages,eventFeedURL,targetNodeOrMethod,parentNode){
	
	/////////////////////////////////////////////
	//
	// _self variable: Needed because of an 
	// ECMAScript standards definition error
	// whereby inner methods do not have access to the
	// containing class' "this" reference:
	//
	/////////////////////////////////////////////
	var _self =this;
	
	var _id=id;
	
	var _top;
	if(top) _top=top;
	else    _top=DIMENSIONS.verticalCenter-100;
	
	var _left;
	if(left) _left=left;
	else     _left=DIMENSIONS.horizontalCenter-150;
	
	var _height; // set in setCalendar() ...
	
	//
	// Year, month, and day are the selected year, month and day,
	// any of which can evaluate to false, in which case defaults
	// are used (see the Date class);
	//
	var _thisDate = new gDate(year,month,day);
	
	var _show;
	if(show) _show=show;
	else     _show=false;
	
	//
	// By default, the calendar displays background images
	// in the calendar bar, but does not display background
	// images in the canvas.  See the public methods
	// showHeaderImages() and showCanvasImages() below.
	//
	var _showHeaderImages;
	if(!isUndefined(showHeaderImages)) _showHeaderImages=showHeaderImages;
	else                 _showHeaderImages=true;
	
	var _showCanvasImages;
	if(!isUndefined(showCanvasImages)) _showCanvasImages=showCanvasImages;
	else                 _showCanvasImages=false;
	
	var _url=eventFeedURL;
	
	//
	// If calendar was created in a rtl environment, remember
	// this:
	//
	var _rtl = false;
	if(typeof LC != "undefined"){
		_rtl = ( LC.getDirection() == "rtl" );
	}
	
	//
	// Note: this can either be a target node like a
	// text field on a form, or a method which will accept
	// a date in ISO form and process it further:
	//
	var _targetNodeOrMethod = targetNodeOrMethod;
	
	//////////////////////////////////////////////
	//
	// USE W3C DOM METHODS TO CREATE THE CALENDAR:
	//
	//////////////////////////////////////////////
	
	// CREATE THE WINDOW AND SET ITS ID AND CLASS:
	var _thisCalendar = document.createElementNS(NS.xhtml,"div");
	_thisCalendar.id=_id;
	_thisCalendar.setAttribute("class","calendar");
	_thisCalendar.style.left=_left+"px";
	_thisCalendar.style.top=_top+"px";
	// ADD SHADOW COMPONENTS:
	var shadowComponent = document.createElementNS(NS.xhtml,"div");
	shadowComponent.setAttribute("class","sRT");
	_thisCalendar.appendChild(shadowComponent);
	shadowComponent = document.createElementNS(NS.xhtml,"div");
	shadowComponent.setAttribute("class","sR");
	_thisCalendar.appendChild(shadowComponent);
	shadowComponent = document.createElementNS(NS.xhtml,"div");
	shadowComponent.setAttribute("class","sRB");
	_thisCalendar.appendChild(shadowComponent);
	shadowComponent = document.createElementNS(NS.xhtml,"div");
	shadowComponent.setAttribute("class","sB");
	_thisCalendar.appendChild(shadowComponent);
	shadowComponent = document.createElementNS(NS.xhtml,"div");
	shadowComponent.setAttribute("class","sLB");
	_thisCalendar.appendChild(shadowComponent);
	// CALENDAR BAR:
	var _calendarBar = document.createElementNS(NS.xhtml,"div");
	_calendarBar.setAttribute("class","calendarBar");
	_thisCalendar.appendChild(_calendarBar);
	
	// CALENDAR BAR ARROWS:
	var _leftYearArrow   = document.createElementNS(NS.xhtml,"div");_leftYearArrow.setAttribute("class","lsa");
	var _rightYearArrow  = document.createElementNS(NS.xhtml,"div");_rightYearArrow.setAttribute("class","rsa");
	var _leftMonthArrow  = document.createElementNS(NS.xhtml,"div");_leftMonthArrow.setAttribute("class","la");
	var _rightMonthArrow = document.createElementNS(NS.xhtml,"div");_rightMonthArrow.setAttribute("class","ra");
	var _leftDecadeArrow = document.createElementNS(NS.xhtml,"div");_leftDecadeArrow.setAttribute("class","lma");
	var _rightDecadeArrow= document.createElementNS(NS.xhtml,"div");_rightDecadeArrow.setAttribute("class","rma");
	
	// Assign event handlers on arrow buttons:
	if(LC && LC.getDirection()=="rtl"){
		_leftYearArrow.addEventListener("click",nextYear,false);
		_rightYearArrow.addEventListener("click",previousYear,false);
		_leftMonthArrow.addEventListener("click",nextMonth,false);
		_rightMonthArrow.addEventListener("click",previousMonth,false);
	}else{
		_leftYearArrow.addEventListener("click",previousYear,false);
		_rightYearArrow.addEventListener("click",nextYear,false);
		_leftMonthArrow.addEventListener("click",previousMonth,false);
		_rightMonthArrow.addEventListener("click",nextMonth,false);
		
	}
	//
	// rightDecades() and leftDecades() handle LC.direction by themselves:
	//
	_rightDecadeArrow.addEventListener("click",rightDecades,false);
	_leftDecadeArrow.addEventListener("click",leftDecades,false);
	
	_calendarBar.appendChild(_leftYearArrow   );
	_calendarBar.appendChild(_leftMonthArrow  );
	_calendarBar.appendChild(_rightYearArrow  );
	_calendarBar.appendChild(_rightMonthArrow );
	_calendarBar.appendChild(_leftDecadeArrow );
	_calendarBar.appendChild(_rightDecadeArrow);
	
	// CALENDAR BAR CLICK REGION:
	var _clickRegion = document.createElementNS(NS.xhtml,"div");
	_clickRegion.setAttribute("class","clickRegion");
	// CLICK REGION EVENT HANDLERS:
	_clickRegion.addEventListener("dblclick" ,shade     ,false);
	_clickRegion.addEventListener("mousedown",grabWindow,false);
	_calendarBar.appendChild(_clickRegion);

	
	// CALENDAR MONTH AND YEAR:
	var _titleMonth = document.createElementNS(NS.xhtml,"p");
	var _tmonth     = document.createTextNode(LC.month(_thisDate.getMonth()));
	var _titleYear  = document.createElementNS(NS.xhtml,"p");
	var _tyear      = document.createTextNode(LC.localizeDigits(_thisDate.getYear()));
	_titleMonth.appendChild(_tmonth);
	_titleYear.appendChild(_tyear );
	_clickRegion.appendChild(_titleMonth);
	_clickRegion.appendChild(_titleYear );
	
	// CALENDAR CANVAS:
	var _canvas       = document.createElementNS(NS.xhtml,"div");
	if(_rtl) _canvas.setAttribute("class","calendarCanvas_rtl");
	else     _canvas.setAttribute("class","calendarCanvas");
	_thisCalendar.appendChild(_canvas);
	
	// CALENDAR CANVAS DAY ABBREVIATIONS:
	var i;
	var _d;
	var _dt;
	for(i=1;i<=7;i++){
		_d  = document.createElementNS(NS.xhtml,"div");
		_d.setAttribute("class","dayAbbr");
		_dt = document.createTextNode(LC.dayAbbr(i));
		_d.appendChild(_dt);
		_canvas.appendChild(_d);
	}
	//
	// CALENDAR CANVAS "TABLE" DAYS BEFORE:
	// --> There can be up to six blanks before:
	//
	for(i=0;i<6;i++){
		_d  = document.createElementNS(NS.xhtml,"div");
		_d.setAttribute("class","day");
		_d.addEventListener("click",previousMonth,false);
		_dt = document.createTextNode("");
		_d.appendChild(_dt);
		_canvas.appendChild(_d);
	}
	// CALENDAR CANVAS "TABLE" DAYS OF THE MONTH:
	// --> There can be up to 31 days in a month:
	for(var i=1;i<=31;i++){
		_d  = document.createElementNS(NS.xhtml,"div");
		_d.setAttribute("class",(i==_thisDate.getDay())?"thisDay":"day");
		// We construct unique ids for each day in the month
		// by putting the number first so we can just use parseInt()
		// when retrieving the day of the month later:
		_d.id=i+"_"+_id;
		_d.addEventListener("click",setDateInTarget,false);
		_dt = document.createTextNode(LC.dayOfMonth(i));
		_d.appendChild(_dt);
		_canvas.appendChild(_d);
	}
	// CALENDAR CANVAS "TABLE" DAYS AFTER:
	// --> There can be up to six blanks after:
	for(i=0;i<6;i++){
		_d  = document.createElementNS(NS.xhtml,"div");
		_d.setAttribute("class","day");
		_d.addEventListener("click",nextMonth,false);
		_dt = document.createTextNode("");
		_d.appendChild(_dt);
		_canvas.appendChild(_d);
	}

	/// CALENDAR CLOSER:
	var _closer = document.createElementNS(NS.xhtml,"div");
	if(_rtl) _closer.setAttribute("class","calendarCloser_rtl");
	else     _closer.setAttribute("class","calendarCloser");
	_closer.addEventListener("click",close,false);
	_thisCalendar.appendChild(_closer);
	
	// Set the background image display states:
	if(!_showHeaderImages) _calendarBar.style.backgroundImage="none";

	if(!_showCanvasImages) _canvas.style.backgroundImage="none";
	
	
	////////////////////////////////////////////////
	//
	// Add the window node to the DOM tree:
	// By default, add window to the BODY element,
	// unless some other parentNode was supplied as
	// an argument to the constructor:
	//
	////////////////////////////////////////////////
	if(parentNode){
		parentNode.appendChild(_thisCalendar);
	}else{
		// Using document.body.appendChild is convenient, but 
		// apparently it is not in the W3C standard ...
		// ==> document.body.appendChild(_thisWindow);
		// The W3C standards way to get the document body:
		var bodyRef = document.getElementsByTagName("body").item(0);
		bodyRef.appendChild(_thisCalendar);
	}
	
	
	//
	// START XMLHTTPREQUEST SECTION
	// START XMLHTTPREQUEST SECTION
	// START XMLHTTPREQUEST SECTION
	//
	// XMLHttpRequest to obtain calendar event data on a yearly basis:
	//
	var _calendarEvents=false;
	
	////////////////////////////////////////////////////////////////////////
	//
	// populateCalendarEvents()
	//
	// -> Populates the EVENT and EVENTCLASS associative arrays (objects):
	//
	////////////////////////////////////////////////////////////////////////
	function populateCalendarEvents(request){
		
		// Reset array:
		//_calendarEvents = false;
		_calendarEvents = new Array();
		
		var entryCount = request.responseXML.getElementsByTagName("entry").length;
		var eventTitle;
		var eventDate;
		var classObject;
		var eventClass;
		for(var i=0;i<entryCount;i++){
			
			eventTitle  = request.responseXML.getElementsByTagName("entry")[i].getElementsByTagName('title')[0].firstChild.nodeValue;
			eventDate   = request.responseXML.getElementsByTagName("entry")[i].getElementsByTagName('date')[0].firstChild.nodeValue;
			classObject = request.responseXML.getElementsByTagName("entry")[i].getElementsByTagName('class');
			if( classObject && classObject.length ){
				eventClass = classObject[0].firstChild.nodeValue;
			}else{
				eventClass = "";
			}
			//
			// Put key value pairs into _calendarEvents[] array:
			//
			_calendarEvents[eventDate] = new gCalendarEvent(eventDate,eventTitle,eventClass);
			
		}
		//
		// This has to be called here to insure that events actually
		// get on the very first calendar shown when a page loads:
		//
		putEventsOnCalendar();
	}
	
	////////////////////////////
	//
	// gRequestObject
	//
	////////////////////////////
	var _gRequestObject = new gRequest(_url,populateCalendarEvents,"post");
	
	//
	// getCalendarEvents()
	//
	function getCalendarEvents(year){
		
		//alert("(1) in getCalendarEvents("+year+")");
		_gRequestObject.setQueryTermsString("year="+year);
		_gRequestObject.load();
		
	}
	//
	// END XMLHTTPREQUEST SECTION
	// END XMLHTTPREQUEST SECTION
	// END XMLHTTPREQUEST SECTION
	//
	
	////////////////////
	//
	// Calendar fader:
	//
	////////////////////
	var _fader = new gVisibilityManager(_thisCalendar);
	
	////////////////////
	//
	// "Set" the calendar:
	//
	////////////////////
	if(_url) getCalendarEvents(_thisDate.getYear());
	setCalendar();
	
	////////////////////////////////////////////////////////////////////
	//
	// Now set the calendar's display properties:
	//
	////////////////////////////////////////////////////////////////////
	if(_show){
		_thisCalendar.style.display="block";
	}else{
		_thisCalendar.style.display="none";
	}
	
	//////////////////////////////////
	//
	// Create the two DECADES objects
	//
	//////////////////////////////////
	//
	// POSITIONING IS STILL ROUGH: AND DEPENDENT ON CSS
	var x= _left -217; // 217 is width of decadeContainer plus borders
	var y= _top  - 73; // This is based on what "looks good"!
	var _leftDecades  = new gDecadeContainer("mLD",x,y,_thisDate.getYear(),false,setYear);
	// POSITIONING IS STILL ROUGH: AND DEPENDENT ON CSS
	x= _left + 286; // 286 is width of calendar plus borders minus shadow width
	var _rightDecades = new gDecadeContainer("mRD",x,y,_thisDate.getYear(),true,setYear);
	
	////////////////////////
	//
	// PRIVATE METHODS:
	//
	////////////////////////
	
	//
	// setDateInTarget() : set the date of the target object
	//             and close the calendar window
	//
	function setDateInTarget(e){
		
		e = gEvent(e);
		e.preventDefault();
		
		//
		// Set the date so the calendar remembers it:
		//
		var y = _thisDate.getYear();
		var m = _thisDate.getMonth();
		var d = parseInt(e.targetElement.id,10);
		
		// 
		// Make the calendar remember the selected date:
		//
		_thisDate.setDate(y,m,d);
		setCalendar();
		
		//
		// ISO Date string to set in target or via method:
		//
		var dateToSet = formatISODate(y,m,d);
		
		if(typeof _targetNodeOrMethod == "object" ){
			
			// Put date into target:
			_targetNodeOrMethod.value = dateToSet;
			
		}else if(typeof _targetNodeOrMethod == "function"){
			
			// call supplied method with dateToSet as the argument:
			_targetNodeOrMethod( dateToSet );
			
		}else{
			
			// Not sure what's going on : maybe just testing? 
			alert("You have chosen day "+parseInt(e.targetElement.id,10)+" but no target object or method was specified in the constructor!");
			
		}
		// Close the calendar:
		close(e);
		
	}
	
	//
	// getDateFromTarget()
	//
	function getDateFromTarget(){
		
		if(typeof _targetNodeOrMethod == "object" ){
			
			var s = _targetNodeOrMethod.value.trim();
			if( s != "" ){
				_thisDate.setDate( s );
				setCalendar();
				
			}
		}
	}
	
	//
	// previousMonth
	//
	function previousMonth(e){
		var y=_thisDate.getYear();
		var m=_thisDate.getMonth();
		var d=_thisDate.getDay();
		m--;
		if(m<1){
			m=12;
			y--;
		}
		_thisDate.setDate(y,m,d);
		setCalendar();
	}
	
	//
	// nextMonth
	//
	function nextMonth(e){
		var y=_thisDate.getYear();
		var m=_thisDate.getMonth();
		var d=_thisDate.getDay();
		m++;
		if(m>12){
			m=1;
			y++;
		}
		_thisDate.setDate(y,m,d);
		setCalendar();
	}
	
	//
	// previousYear
	//
	function previousYear(e){
		var y=_thisDate.getYear();
		var m=_thisDate.getMonth();
		var d=_thisDate.getDay();
		y--;
		_thisDate.setDate(y,m,d);
		
		if(_url) getCalendarEvents(y);
		
		setCalendar();
		
	}
	
	//
	// nextYear
	//
	function nextYear(e){
		var y=_thisDate.getYear();
		var m=_thisDate.getMonth();
		var d=_thisDate.getDay();
		y++;
		_thisDate.setDate(y,m,d);
		
		if(_url) getCalendarEvents(y);
		
		setCalendar();
		
	}
	
	//
	// setYear
	//
	function setYear(year){
		var m=_thisDate.getMonth();
		var d=_thisDate.getDay();
		_thisDate.setDate(year,m,d);
		
		if(_url) getCalendarEvents(year);
		
		setCalendar();
	}
	
	
	//
	// leftDecades()
	//
	function leftDecades(){
		// Don't allow _rightDecades to display at the same time:
		_rightDecades.show(false);
		
		// POSITIONING IS STILL ROUGH: AND DEPENDENT ON CSS
		var x= _left -217; // 217 is width of decadeContainer plus borders
		var y= _top  - 73; //
		// Check if off screen:
		if(x<DIMENSIONS.padding)    x=DIMENSIONS.padding;
		if(x>DIMENSIONS.width-217)  x=DIMENSIONS.width-217;
		if(y<DIMENSIONS.padding)    y=DIMENSIONS.padding;
		if(y>DIMENSIONS.height-256) y=DIMENSIONS.height-256;
		_leftDecades.setPosition(x,y); 
		_leftDecades.setStartYear( _thisDate.getYear() );
		_leftDecades.show(true); 
	}
	
	//
	// rightDecades()
	//
	function rightDecades(){
		// Don't allow _leftDecades to display at the same time:
		_leftDecades.show(false);
		
		// POSITIONING IS STILL ROUGH: AND DEPENDENT ON CSS
		var x= _left + 286; // 286 is width of calendar plus borders minus shadow width
		var y= _top  - 73; // 
		// Check if off screen:
		if(x<DIMENSIONS.padding)    x=DIMENSIONS.padding;
		if(x>DIMENSIONS.width-217)  x=DIMENSIONS.width-217;
		if(y<DIMENSIONS.padding)    y=DIMENSIONS.padding;
		if(y>DIMENSIONS.height-256) y=DIMENSIONS.height-256;
		_rightDecades.setPosition(x,y);
		_rightDecades.setStartYear( _thisDate.getYear() );
		_rightDecades.show(true); 
		
	}

	////////////////////
	//
	// setCalendar()
	//
	////////////////////
	function setCalendar(){
		
		// Set Calendar month and year titles:
		var month = _thisDate.getMonth();
		var year  = _thisDate.getYear();
		
		_tmonth.nodeValue=LC.month( month );
		_tyear.nodeValue=LC.localizeDigits( year );
		
		//
		// Set the header and canvas background images:
		//
		// --> Here we use a "filmstrip" of background images
		//     and then just change the offset.  This is *much*
		//     faster than image preloading:
		//
		var verticalOffset;
		var verticalAdvance;
		var thisMonthOffset;
		// Set header background images:
		if(_showHeaderImages){
			verticalOffset = 0;
			verticalAdvance= -50;
			thisMonthOffset = verticalOffset + (month-1)*verticalAdvance;
			_calendarBar.style.backgroundPosition= "80px " + thisMonthOffset + "px";
		}
		if(_showCanvasImages){
			verticalOffset = 25;
			verticalAdvance= -150;
			thisMonthOffset = verticalOffset + (month-1)*verticalAdvance;
			_canvas.style.backgroundPosition= "0px " + thisMonthOffset + "px";
		}
		
		// What is the first day of this month?
		var firstDay = new gDate(year,month,01);
		
		// The first seven nodes are the day of week abbreviations, which
		// we don't want to hide, so we "skip" those nodes by adding 7
		// to the dow, and start with the seventh node:
		var dow = firstDay.dayOfWeek();
		var dowPlusSeven=dow+7;
		var i;
		// Display the proper number of blank cells prior to the first day of
		// the month:
		for(i=7;i<13;i++){
			if(i<dowPlusSeven) _canvas.childNodes[i].style.display="block";
			else               _canvas.childNodes[i].style.display="none";
		}
		
		// Show the proper number of days in this month:
		var dim = daysInMonth(_thisDate.getMonth(),_thisDate.getYear());
		for(i=29;i<=31;i++){
			var dayNode=document.getElementById(i+"_"+_id);
			if(i<=dim) dayNode.style.display="block";
			else       dayNode.style.display="none";
		}
		
		// Special case of October, 1582: skipped days 5-14:
		var isOctober1582 = _thisDate.isInOctober1582();
		if( isOctober1582 ){
			for(i=5;i<=14;i++){
				document.getElementById(i+"_"+_id).style.display="none";
			}
		}else{
			// Have to check if previously displayed month was October, 1582:
			if(document.getElementById(5+"_"+_id).style.display=="none"){
				for(i=5;i<=14;i++){
					document.getElementById(i+"_"+_id).style.display="block";
				}
			}
		}
		
		//
		// Show the proper number of blank cells at the end of the month:
		// We have "dow" blank cells at the start, plus "dim" filled cells.  
		// In normal cases,
		// these need to fill up either 6 weeks (42 cells) or 5 weeks (35 cells).
		// Counting the day abbreviation heading, the _canvas has a total of 
		// 50 child nodes (some may be hidden, but in any case are still there).
		// We need to selectively show or hide some of the last six cells from
		// 44 to 50 which are blank cells.  "cts", meaning "(blank) cells to show
		// (at the end)" therefore starts as 42 minus "dow" minus "dim":
		//
		var cts = 42 - dow - dim;
		// Subtract off weeks: "wct" = "week count" :
		var wct = 6;
		while(cts > 6){
			cts -= 7;
			wct--;
		}
		if( isOctober1582 ) wct--; // Special case of October 1582 ...
		// Show "cts" blank cells at end:
		for(i=0;i<6;i++){
			if( i<cts || isOctober1582 ) _canvas.childNodes[i+44].style.display="block";
			else      _canvas.childNodes[i+44].style.display="none";
		}
		// Fix calendar height to exactly accomodate the number of weeks:
		// ROUGH SPOT : Changes if CSS changes!
		//
		// headerHeight includes calendarBar, day abbreviations, and shadow height:
		//
		var headerHeight  = 83;
		var cellHeight    = 22;
		//_thisCalendar.style.height = headerHeight+"px";
		
		_height = headerHeight + wct*cellHeight;
		_thisCalendar.style.height = _height + "px";
		
		//
		// If a URL was given for event feeds,
		// check to see if there are any events that
		// need to be put on the calendar:
		if(_url){
			putEventsOnCalendar();
		}
		
	}
	
	////////////////////////////////////
	//
	// function putEventsOnCalendar()
	//
	////////////////////////////////////
	function putEventsOnCalendar(){
		
		var year    = _thisDate.getYear();
		var month   = _thisDate.getMonth();
		var thisDay = _thisDate.getDay();
		
		//alert("(2) in putEventsOnCalendar for "+formatISODate(year,month,thisDay));
		
		for(var i=1;i<daysInMonth(month,year);i++){
			
			var calEvent = _calendarEvents[ formatISODate(year,month,i) ];
			var dayNode  = document.getElementById(i+"_"+_id);
			if(calEvent){
				
				//alert("(3) " + calEvent.date + " : " + calEvent.title );
				if( calEvent.cssClass ){
					dayNode.setAttribute("class",calEvent.cssClass);
				}else{
					dayNode.setAttribute("class","eventDay");
				}
				
				//
				// In any case, always show the event description:
				//
				dayNode.setAttribute("title",calEvent.title);
			}else{
				// Clear any previous class and titles:
				dayNode.setAttribute("class",i==thisDay ? "thisDay" : "day" );
				dayNode.setAttribute("title","");
			}
			
		}
	}

	
	//////////////////////////////////////////////////////////////////////
	//
	// shade() PRIVATE method:
	//
	//////////////////////////////////////////////////////////////////////
	
	var _isShaded = false;
	//
	// shade()
	//
	function shade(e){
		e = gEvent(e);
		e.preventDefault();
		
		if(_isShaded){
			
			_thisCalendar.style.height = _height + "px";
			_isShaded = false;
			
		}else{
			// ROUGH SPOT -- DEPENDS ON CSS:
			_thisCalendar.style.height = 56 + "px";
			_isShaded = true;
			
		}
		//
		// Theoretically this stops additional onclick handler action:
		//
		//e.stopPropogation(); // -- ROUGH SPOT : stopPropogation D.N.E. 
		return false;
	}
	
	///////////////////////////////////////
	//
	// close() : close the calendar window
	//
	///////////////////////////////////////
	function close(e){
		
		e = gEvent(e);
		e.preventDefault();
		
		_leftDecades.show(false);
		_rightDecades.show(false);
		
		_fader.fadeOut();
		
	}
	
	/////////////////////////////////////
	//
	// DRAG AND RESIZE METHODS:
	//
	/////////////////////////////////////
	var _mouseStartX;
	var _mouseStartY;
	var _isMoveable=false;
	
	//
	// grabWindow():
	//
	function grabWindow(e){
		
		//
		// Always prevent the default ...
		//
		e=gEvent(e);
		e.preventDefault();
		
		_mouseStartX = e.xPosition;
		_mouseStartY = e.yPosition;
		
		// Don't allow display of decades when trying to move
		// the calendar because currently the decades don't 
		// move with the calendar:
		_leftDecades.show(false);
		_rightDecades.show(false);
		
		
		_isMoveable  = true;
		//
		// Add an event listener to the whole document:
		// This is better than adding it just to title bar, because
		// the user can easily move the mouse so fast that it leaves the
		// titleBar's event area, leaving the window "frozen".  
		//
		document.addEventListener("mousemove",moveWindow   ,false);
		document.addEventListener("mouseup"  ,releaseWindow,false);
		return false;
	}
	
	//
	// moveWindow:
	//
	function moveWindow(e){
		e=gEvent(e);
		e.preventDefault();
		
		if(_isMoveable){
			
			var newX = _left - _mouseStartX + e.xPosition;
			var newY = _top  - _mouseStartY + e.yPosition;
			
			// Don't let window ever go completely off screen:
			//if(newX > DIMENSIONS.width -DIMENSIONS.padding) newX=DIMENSIONS.width-DIMENSIONS.padding;
			//if(newY > DIMENSIONS.height-DIMENSIONS.padding) newY=DIMENSIONS.height-DIMENSIONS.padding;
			//if(newX < DIMENSIONS.padding-_width ) newX = DIMENSIONS.padding-_width;
			//if(newY < DIMENSIONS.padding-_height) newY = DIMENSIONS.padding-_height;
			
			_thisCalendar.style.left=newX+"px";
			_thisCalendar.style.top=newY+"px";
		}
		return false;
	}
	
	//
	// releaseWindow()
	//
	function releaseWindow(e){
		e = gEvent(e);
		e.preventDefault();
		if(_isMoveable){
			// Set _left and _top to correspond to
			// where the window has moved:
			//
			_left = parseInt(_thisCalendar.style.left,10);
			_top  = parseInt(_thisCalendar.style.top ,10);
				
			// Stop moving the window:
			document.removeEventListener("mousemove",moveWindow   ,false);
			document.removeEventListener("mousup"   ,releaseWindow,false);
			_isMoveable = false;
			
		}
		return false;
	}
	
	
	///////////////////////////
	//
	// PUBLIC METHODS:
	//
	///////////////////////////
	
	//
	// show(): display / hide the calendar
	//
	this.show = function(showIt){
		
		_show=showIt;
		if(_show){
			// Try to get the current year, month, or full date
			// from the target node before displaying the calendar:
			getDateFromTarget();
			_fader.show();
		}else{
			_fader.hide();
		}
	}
	
	//
	// setPosition(): set calendar's x,y position:
	//
	this.setPosition = function(x,y){
		
		_left = x;
		_top  = y;
		_thisCalendar.style.left = x + "px";
		_thisCalendar.style.top  = y + "px";
		
	}
}

//
// class gDecadeContainer():
//
function gDecadeContainer(id,x,y,year,displayOnRight,setYearMethod,parentNode){
	
	var _id = id;
	
	var _startYear=year-year%10;
	
	var _displayOnRight=displayOnRight;
	
	var _setYearMethod = setYearMethod;
	
	var _rtl = false;
	if(typeof LC != "undefined"){
		_rtl = ( LC.getDirection() == "rtl" );
	}
	
	var _decadeContainer = document.createElementNS(NS.xhtml,"div");
	_decadeContainer.setAttribute("class","decadeContainer");
	//
	// Set position:
	//
	_decadeContainer.style.left = x + "px";
	_decadeContainer.style.top  = y + "px";
	
	var _decadeArray = new Array();
	
	var _radius=320.0;
	var _angle =7.9;
	var _xOrigin=0;
	var _yOrigin=0;
	
	if(_displayOnRight){
		
		// Fan decades to the right:
		_angle   *= -1;
		// This is the width of the calendar itself:
		_xOrigin =0;
		
	}else{
		
		// Fan decades to the left:
		// ROUGH SPOT: This is very dependent on the CSS:
		// Calculate as follows: decadeContainer.width - 40 -2*decade.borderWidth;
		_xOrigin +=168;
		
	}
	
	var yy=_startYear;
	var i;
	for(i=0;i<5;i++){
		
		_decadeArray[i] = document.createElementNS(NS.xhtml,"div");
		_decadeArray[i].setAttribute("class","decade");
		for(var j=0;j<10;j++){
			var decadeItem = document.createElementNS(NS.xhtml,"div");
			decadeItem.setAttribute("class",j==0 ? "itemBold" : "item" );
			decadeItem.id=( (yy+j) + "_" + _id );
			var itemText = document.createTextNode(yy+j);
			decadeItem.appendChild(itemText);
			// Add event listener:
			decadeItem.addEventListener("click",setYear,false);
			_decadeArray[i].appendChild( decadeItem );
			
		}
		if(_rtl){
			// Right-to-left case:
			if(_displayOnRight){
				yy-=10;
			}else{
				yy+=10;
			}
		}else{
			// Left-to-right case:
			if(_displayOnRight){
				yy+=10;
			}else{
				yy-=10;
			}
		}
		
		// Set the top and left positions:
		var n_angle=i*_angle*Math.PI/180.0;
		var dY=Math.ceil(_radius*(1-Math.cos(n_angle)));
		var dX=Math.ceil(_radius*Math.sin(n_angle));
		_decadeArray[i].style.top =_yOrigin+dY+"px";
		_decadeArray[i].style.left=_xOrigin-dX+"px";
		
	}
	// Insert into decade container in reverse order:
	for(i=4;i>=0;i--){
		_decadeContainer.appendChild( _decadeArray[i] );
	}

	////////////////////////////////////////////////
	//
	// Add the decadeContainer node to the DOM tree:
	// By default, add window to the BODY element,
	// unless some other parentNode was supplied as
	// an argument to the constructor:
	//
	////////////////////////////////////////////////
	if(parentNode){
		parentNode.appendChild(_decadeContainer);
	}else{
		// Using document.body.appendChild is convenient, but 
		// apparently it is not in the W3C standard ...
		// ==> document.body.appendChild(_thisWindow);
		// The W3C standards way to get the document body:
		var bodyRef = document.getElementsByTagName("body").item(0);
		bodyRef.appendChild(_decadeContainer);
	}
	
	/////////////////////
	//
	// PRIVATE METHODS
	//
	/////////////////////
	
	function setYear(e){
		
		// call the method passed to the constructor:
		e=gEvent(e);
		e.preventDefault();
		var year = parseInt( e.targetElement.id ,10);
		setYearMethod(year);
		_decadeContainer.style.display="none";
	}
	
	////////////////////
	//
	// PUBLIC METHODS:
	//
	////////////////////
	
	////////////////////////////////////////////////
	//
	// setStartYear(): Sets a new start year on 
	//                 the existing gDecades object
	////////////////////////////////////////////////
	this.setStartYear = function(year){
		
		_startYear=year-year%10;
		var y=_startYear;
		
		for(var i=0;i<5;i++){
			
			for(var j=0;j<10;j++){
				var decadeItem = _decadeArray[i].childNodes[j];
				decadeItem.id=( (y+j) + "_" + _id );
				decadeItem.firstChild.nodeValue=y+j;
			}
			if(_rtl){
				// Right-to-left case:
				if(_displayOnRight){
					y-=10;
				}else{
					y+=10;
				}
			}else{
				// Left-to-right case:
				if(_displayOnRight){
					y+=10;
				}else{
					y-=10;
				}
			}
		}
	}
	
	//
	// setPosition(): pass x / y coordinates
	//
	this.setPosition = function(x,y){
		
		_decadeContainer.style.left = x + "px";
		_decadeContainer.style.top  = y + "px";
		
	}
	
	//
	// show() : true / false
	//
	this.show = function(showIt){
		
		if(showIt) _decadeContainer.style.display="block";
		else       _decadeContainer.style.display="none";
		
	}
	
}

//
// Calendar Events:
//
function gCalendarEvent(date,title,cssClass){
	
	this.date =date;
	this.title=title;
	this.cssClass=cssClass;
	
}




