/*****************************************************************************
Copyright (C) 2006  Nick Baicoianu

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


Massive Modifications 2006 by Malte John

	20.07.2006, mJ:	lots of hacks
		callback function ( called with start and end-date)
		multiple => start/end
	
		value assignments to year and month => parseInt
		CalHeading weg
		Footer jedesmal erzeugen
	
	30.07.2006, mJ:	startdate / enddate beide inclusive
					
	
	31.07.2006, mJ:	mindate, maxdate als argument in den Konstruktor (YYYY-MM-TT
					callback bekommt Art des reports als argument
	

	08.08.2006, mJ:	Epoch => SECalendar
					massive Änderungen


	18.08.2006, mJ: veröddelte Quarter-Berechnung korrigiert
	14.11.2006, mJ: noch einen Quarter-Bug gefixt
	
	18.02.2010, mJ:	month buttons have no text, style with background
					some new classes
	
BUGS:
	some parseInts for month and year seem to be missing!
	week-headers are not perfect...
	

*****************************************************************************/
//constructor for the main SECalendar class (ENGLISH VERSION)


function SECalendar( name, mode, targetelement, callback, mindate, maxdate, startdate, enddate )
{

	this.selectState = 0;	// 0: normal (select start date);  1: select end date

	this.state = 0;
	
	this.name = name;
	
	this.curDate = new Date();
	
	this.mindate = new Date( mindate );
	this.maxdate = new Date( maxdate );
	
	this.startdate = new Date( startdate );
	this.enddate = new Date( enddate );
	
//	alert( this.mindate+ "-/-"+this.maxdate);
	
	this.displayMode = mode;
	this.callback = callback;
	
	//the various calendar variables
	//this.selectedDate = this.curDate;
	this.selectedDates = new Array();
	this.calendar;
	this.calHeading;
	this.calCells;
	this.rows;
	this.cols;
	this.cells = new Array();
	
	//The controls
	this.monthSelect;
	this.yearSelect;
	
	//standard initializations
	this.mousein = false;
	this.calConfig();
	this.setDays();
	this.displayYear = this.displayYearInitial;
	this.displayMonth = this.displayMonthInitial;
	
	this.createCalendar(); //create the calendar DOM element and its children, and their related objects
	
	if(this.displayMode == 'popup' && targetelement && targetelement.type == 'text') //if the target element has been set to be an input text box
	{
		this.tgt = targetelement;
		this.calendar.style.position = 'absolute';
		this.topOffset = this.tgt.offsetHeight; // the vertical distance (in pixels) to display the calendar from the Top of its input element
		this.leftOffset = 0; 					// the horizontal distance (in pixels) to display the calendar from the Left of its input element
		this.calendar.style.top = this.getTop(targetelement) + this.topOffset + 'px';
		this.calendar.style.left = this.getLeft(targetelement) + this.leftOffset + 'px';
		document.body.appendChild(this.calendar);
		this.tgt.calendar = this;
		this.tgt.onfocus = function () {this.calendar.show();}; //the calendar will popup when the input element is focused
		this.tgt.onblur = function () {if(!this.calendar.mousein){this.calendar.hide();}}; //the calendar will popup when the input element is focused
	}
	else
	{
		this.container = targetelement;
		this.container.appendChild(this.calendar);
	}
	
	this.state = 2; //0: initializing, 1: redrawing, 2: finished!
	this.visible ? this.show() : this.hide();
}
//-----------------------------------------------------------------------------
SECalendar.prototype.calConfig = function () //PRIVATE: initialize calendar variables
{
	//this.displayMode = 'flat'; //can be 'flat' or 'popup'
/*
	this.displayYearInitial = this.curDate.getFullYear(); //the initial year to display on load
	this.displayMonthInitial = this.curDate.getMonth(); //the initial month to display on load (0-11)
	this.rangeYearLower = 2006;
	this.rangeYearUpper = 2008;
	
	this.mindate = new Date(2006,0,1);
	this.maxdate = new Date(2008,0,1);
*/
	this.rangeYearLower = this.mindate.getFullYear();
	this.rangeYearUpper = this.maxdate.getFullYear();


/*	this.displayYearInitial = this.maxdate.getFullYear(); //the initial year to display on load
	this.displayMonthInitial = this.maxdate.getMonth(); //the initial month to display on load (0-11)*/
	
	this.displayYearInitial = this.startdate.getFullYear(); //the initial year to display on load
	this.displayMonthInitial = this.startdate.getMonth(); //the initial month to display on load (0-11)

	
	this.startDay = 1; // the day the week will 'start' on: 0(Sun) to 6(Sat)
	this.showWeeks = false; //true; //whether the week numbers will be shown
	this.selCurMonthOnly = false; //allow user to only select dates in the currently displayed month
	this.clearSelectedOnChange = true; //whether to clear all selected dates when changing months
	
	//flat mode-only settings:
	//this.selectMultiple = true; //whether the user can select multiple dates (flat mode only)

	switch(this.displayMode) //set the variables based on the calendar mode
	{
		case 'popup': //popup options
			this.visible = false;
			break;
		case 'flat':
			this.visible = true;
			
			break;
	}
	this.setLang();
};
//-----------------------------------------------------------------------------
SECalendar.prototype.setLang = function()  //all language settings for SECalendar are made here.  Check Date.dateFormat() for the Date object's language settings
{
	this.daylist		= new Array('Su','Mo','Tu','We','Th','Fr','Sa','Su','Mo','Tu','We','Th','Fr','Sa'); /*<lang:en>*/
	this.months_sh 		= new Array('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
	
	this.monthup_title 	= 'Go to the next month';
	this.monthdn_title	= 'Go to the previous month';
	
	this.calcell_title	= new Array( 'Select start date', 'Select end date' );	// indexed by selectState
	this.week_title 	= 'Select week';
	this.month_title 	= 'Select month';
	this.quarter_title	= 'Select quarter';
	this.year_title 	= 'Select year';

	
	this.clearbtn_caption = 'Clear';
// 	this.clearbtn_title = 'Clears any dates selected on the calendar';
	this.maxrange_caption = 'This is the maximum range';
};
//-----------------------------------------------------------------------------
SECalendar.prototype.getTop = function (element) //PRIVATE: returns the absolute Top value of element, in pixels
{
    var oNode = element;
    var iTop = 0;
    
    while(oNode.tagName != 'BODY') {
        iTop += oNode.offsetTop;
        oNode = oNode.offsetParent;
    }
    
    return iTop;
};
//-----------------------------------------------------------------------------
SECalendar.prototype.getLeft = function (element) //PRIVATE: returns the absolute Left value of element, in pixels
{
    var oNode = element;
    var iLeft = 0;
    
    while(oNode.tagName != 'BODY') {
        iLeft += oNode.offsetLeft;
        oNode = oNode.offsetParent;        
    }
    
    return iLeft;
};
//-----------------------------------------------------------------------------
SECalendar.prototype.show = function () //PUBLIC: displays the calendar
{
	this.calendar.style.display = 'block';
	this.visible = true;
};
//-----------------------------------------------------------------------------
SECalendar.prototype.hide = function () //PUBLIC: Hides the calendar
{
	this.calendar.style.display = 'none';
	this.visible = false;
};
//-----------------------------------------------------------------------------
SECalendar.prototype.toggle = function () //PUBLIC: Toggles (shows/hides) the calendar depending on its current state
{
	if(this.visible) {
		this.hide();
	}
	else {
		this.show();
	}
};
//-----------------------------------------------------------------------------
SECalendar.prototype.setDays = function ()  //PRIVATE: initializes the standard Gregorian Calendar parameters
{
	this.daynames = new Array();
	var j=0;
	for(var i=this.startDay; i< this.startDay + 7;i++) {
		this.daynames[j++] = this.daylist[i];
	}
		
	this.monthDayCount = new Array(31,((this.curDate.getFullYear() - 2000) % 4 ? 28 : 29),31,30,31,30,31,31,30,31,30,31);
};
//-----------------------------------------------------------------------------
SECalendar.prototype.setClass = function (element,className) //PRIVATE: sets the CSS class of the element, W3C & IE
{
	element.setAttribute('class',className);
	element.setAttribute('className',className); //<iehack>
};
//-----------------------------------------------------------------------------
SECalendar.prototype.createCalendar = function ()  //PRIVATE: creates the full DOM implementation of the calendar
{
	var tbody, tr, td;
	this.calendar = document.createElement('table');
	
	this.calendar.setAttribute('id',this.name+'_calendar');
	this.setClass(this.calendar,'calendar');
	//to prevent IE from selecting text when clicking on the calendar
	this.calendar.onselectstart = function() {return false;};
	this.calendar.ondrag = function() {return false;};
	tbody = document.createElement('tbody');
	
	//create the Main Calendar Heading
	tr = document.createElement('tr');
	td = document.createElement('td');
	
	td.appendChild(this.createMainHeading());
	tr.appendChild(td);
	tbody.appendChild(tr);
	
	//create the calendar Day Heading
	tr = document.createElement('tr');
	td = document.createElement('td');
	td.appendChild(this.createDayHeading());
	tr.appendChild(td);
	tbody.appendChild(tr);

	//create the calendar Day Cells
	tr = document.createElement('tr');
	td = document.createElement('td');
	td.setAttribute('id',this.name+'_cell_td');
	this.calCellContainer = td;	//used as a handle for manipulating the calendar cells as a whole
	td.appendChild(this.createCalCells());
	tr.appendChild(td);
	tbody.appendChild(tr);
	
	
	// footer will be created along with the calendar cells
	
	//add the tbody element to the main calendar table
	this.calendar.appendChild(tbody);

	//and add the onmouseover events to the calendar table
	this.calendar.owner = this;
	this.calendar.onmouseover = function() {this.owner.mousein = true;};
	this.calendar.onmouseout = function() {this.owner.mousein = false;};
};
//-----------------------------------------------------------------------------
SECalendar.prototype.createMainHeading = function () //PRIVATE: Creates the primary calendar heading, with months & years
{
	//create the containing <div> element
	var container = document.createElement('div');
	container.setAttribute('id',this.name+'_mainheading');
	this.setClass(container,'mainheading');
	//create the child elements and other variables
	this.monthSelect = document.createElement('select');
	this.yearSelect = document.createElement('select');
	
	this.setClass( this.monthSelect, 'select-month' );
	this.setClass( this.yearSelect, 'select-year' );
	
	var monthDn = document.createElement('input'), 
		monthUp = document.createElement('input');
	
	var opt, i;
	//fill the month select box
	for(i=0;i<12;i++)
	{
		opt = document.createElement('option');
		opt.setAttribute('value',i);
		if(this.state == 0 && this.displayMonth == i) {
			opt.setAttribute('selected','selected');
		}
		opt.appendChild(document.createTextNode(this.months_sh[i]));
		this.monthSelect.appendChild(opt);
	}
	//and fill the year select box
	for(i=this.rangeYearLower;i<=this.rangeYearUpper;i++)
	{
		opt = document.createElement('option');
		opt.setAttribute('value',i);
		if(this.state == 0 && this.displayYear == i) {
			opt.setAttribute('selected','selected');
		}
		opt.appendChild(document.createTextNode(i));
		this.yearSelect.appendChild(opt);		
	}
	//add the appropriate children for the month buttons
	monthDn.setAttribute('type','button');
	monthDn.setAttribute('value', '');					
	monthDn.setAttribute('title',this.monthdn_title);
	this.setClass( monthDn, 'month-down' );
	monthUp.setAttribute('type','button');
	monthUp.setAttribute('value','');
	monthUp.setAttribute('title',this.monthup_title);
	this.setClass( monthUp, 'month-up' );
	
	
	this.monthSelect.owner = this.yearSelect.owner = monthUp.owner = monthDn.owner = this;  //hack to allow us to access this calendar in the events (<fix>??)
	
	//assign the event handlers for the controls
	monthUp.onmouseup = function () {this.owner.nextMonth();};
	monthDn.onmouseup = function () {this.owner.prevMonth();};
	this.monthSelect.onchange = function() {
		this.owner.displayMonth = parseInt(this.value);
		this.owner.displayYear = parseInt(this.owner.yearSelect.value); 
		this.owner.goToMonth(this.owner.displayYear,this.owner.displayMonth);
	};
	this.yearSelect.onchange = function() {
		this.owner.displayMonth = parseInt(this.owner.monthSelect.value);
		this.owner.displayYear = parseInt(this.value); 
		this.owner.goToMonth(this.owner.displayYear,this.owner.displayMonth);
	};
	
	//and finally add the elements to the containing div
	container.appendChild(monthDn);
	container.appendChild(this.monthSelect);
	container.appendChild(this.yearSelect);
	container.appendChild(monthUp);
	return container;
};
//-----------------------------------------------------------------------------
SECalendar.prototype.createFooter = function () //PRIVATE: creates the footer of the calendar - goes under the calendar cells
{
	var container = document.createElement('span');
	var monthlyButton = document.createElement('input');
	monthlyButton.setAttribute('type','button');
//	clearSelected.setAttribute('value', this.displayMonth /*this.clearbtn_caption*/);
	monthlyButton.setAttribute('value', this.months_sh[this.displayMonth]);

	monthlyButton.setAttribute('title', this.month_title);
	monthlyButton.owner = this;
	
	monthlyButton.startdate = new Date(this.displayYear,this.displayMonth,1);
	if( parseInt(this.displayMonth) < 11 ) {
		monthlyButton.enddate = new Date(this.displayYear,parseInt(this.displayMonth)+1,1);
	} else {
		monthlyButton.enddate = new Date(parseInt(this.displayYear)+1,0,1);
	}
	monthlyButton.enddate.setDate( monthlyButton.enddate.getDate() - 1 );

    if( monthlyButton.enddate > this.maxdate )
		monthlyButton.enddate = this.maxdate;
		
	if( monthlyButton.startdate < this.mindate )
		monthlyButton.startdate = this.mindate;
		
	monthlyButton.onclick = function()
	{
//		alert( this.startdate+ " .. " + this.enddate );
		this.owner.startdate = this.startdate;
		this.owner.enddate = this.enddate;
		this.owner.reDraw();

		if( this.owner.callback )
			this.owner.callback( this.startdate, this.enddate, 1 );
		
	};
	
	container.appendChild(monthlyButton);

		
	var quarterlyButton = document.createElement('input');

	var quarter0 = Math.floor( ( this.displayMonth) / 3 );
	
	quarterlyButton.setAttribute( 'type','button' );
	quarterlyButton.setAttribute( 'value', "Q "+ (quarter0 + 1) );

	quarterlyButton.setAttribute('title', this.quarter_title);
	quarterlyButton.owner = this;
	
	quarterlyButton.startdate = new Date(this.displayYear,quarter0*3,1);
	if( parseInt(this.displayMonth) < 9 ) {
		quarterlyButton.enddate = new Date(this.displayYear,quarter0*3+3,1);
	} else {
		quarterlyButton.enddate = new Date(parseInt(this.displayYear)+1,0,1);
	}
	quarterlyButton.enddate.setDate( quarterlyButton.enddate.getDate() - 1 );
    
	if( quarterlyButton.enddate > this.maxdate )
		quarterlyButton.enddate = this.maxdate;

	if( quarterlyButton.startdate < this.mindate )
		quarterlyButton.startdate = this.mindate;

	quarterlyButton.onclick = function()
	{
//		alert( this.startdate+ " .. " + this.enddate );
		this.owner.startdate = this.startdate;
		this.owner.enddate = this.enddate;
		this.owner.reDraw();
		
		if( this.owner.callback )
			this.owner.callback( this.startdate, this.enddate, 1 );
	};
	container.appendChild(quarterlyButton);

	
	var yearlyButton = document.createElement('input');
	yearlyButton.setAttribute('type','button');
	yearlyButton.setAttribute('value', this.displayYear );

	yearlyButton.setAttribute('title', this.year_title);
	yearlyButton.owner = this;
	
	yearlyButton.startdate = new Date(this.displayYear,0,1);
	yearlyButton.enddate = new Date(parseInt(this.displayYear)+1,0,1);
	yearlyButton.enddate.setDate( yearlyButton.enddate.getDate() - 1 );

    if( yearlyButton.enddate > this.maxdate ) 
		yearlyButton.enddate = this.maxdate;
		
	if( yearlyButton.startdate < this.mindate )
		yearlyButton.startdate = this.mindate;
	
	yearlyButton.onclick = function()
	{
//		alert( this.startdate+ " .. " + this.enddate );
		this.owner.startdate = this.startdate;
		this.owner.enddate = this.enddate;
		this.owner.reDraw();
		
		if( this.owner.callback )
			this.owner.callback( this.startdate, this.enddate, 1 );
	};
	container.appendChild(yearlyButton);
	
	return container;
};
//-----------------------------------------------------------------------------
SECalendar.prototype.resetSelections = function (returnToDefaultMonth)  //PRIVATE: reset the calendar's selection variables to defaults
{
	this.selectedDates = new Array();
	this.rows = new Array(false,false,false,false,false,false,false);
	this.cols = new Array(false,false,false,false,false,false,false);
	if(this.tgt)  //if there is a target element, clear it too
	{
		this.tgt.value = '';
		if(this.displayMode == 'popup') {//hide the calendar if in popup mode
			this.hide();
		}
	}
		
	if(returnToDefaultMonth == true) {
		this.goToMonth(this.displayYearInitial,this.displayMonthInitial);
	}
	else {
		this.reDraw();
	}
};
//-----------------------------------------------------------------------------
SECalendar.prototype.createDayHeading = function ()  //PRIVATE: creates the heading containing the day names
{
	//create the table element
	this.calHeading = document.createElement('table');
	this.calHeading.setAttribute('id',this.name+'_caldayheading');
	this.setClass(this.calHeading,'caldayheading');
	var tbody,tr,td;
	tbody = document.createElement('tbody');
	tr = document.createElement('tr');
	this.cols = new Array(false,false,false,false,false,false,false);
	
	//if we're showing the week headings, create an empty <td> for filler
	if(this.showWeeks)
	{
		td = document.createElement('td');
		td.setAttribute('class','wkhead');
		td.setAttribute('className','wkhead'); //<iehack>

		td.appendChild( document.createTextNode( "  " ) );	//	mJ
		tr.appendChild(td);
	}
	//populate the day titles
	for(var dow=0;dow<7;dow++)
	{
		td = document.createElement('td');
		td.appendChild(document.createTextNode(this.daynames[dow]));
		tr.appendChild(td);
	}
	tbody.appendChild(tr);
	this.calHeading.appendChild(tbody);
	return this.calHeading;	
};
//-----------------------------------------------------------------------------
SECalendar.prototype.createCalCells = function ()  //PRIVATE: creates the table containing the calendar day cells
{
	this.rows = new Array(false,false,false,false,false,false);
	this.cells = new Array();
	var row = -1, totalCells = (this.showWeeks ? 48 : 42);
	var beginDate = new Date(this.displayYear,this.displayMonth,1);
	var endDate = new Date(this.displayYear,this.displayMonth,this.monthDayCount[this.displayMonth]);
	var sdt = new Date(beginDate);
	sdt.setDate(sdt.getDate() + (this.startDay - beginDate.getDay()) - (this.startDay - beginDate.getDay() > 0 ? 7 : 0) );
	//create the table element
	this.calCells = document.createElement('table');
	this.calCells.setAttribute('id',this.name+'_calcells');
	this.setClass(this.calCells,'calcells');
	var tbody,tr,td;
	tbody = document.createElement('tbody');
	
	for(var i=0;i<totalCells;i++) {
		if(this.showWeeks) { //if we are showing the week headings
			if(i % 8 == 0) {
				row++;
				tr = document.createElement('tr');
				td = document.createElement('td');
				td.weekObj = new WeekHeading(this,td,sdt.getWeek(),row)
				td.appendChild(document.createTextNode(sdt.getWeek()));
				tr.appendChild(td);
				i++;
			}
		} else if(i % 7 == 0) { //otherwise, new row every 7 cells
			row++;
			tr = document.createElement('tr');
		}
		//create the day cells
		td = document.createElement('td');
		td.appendChild(document.createTextNode(sdt.getDate()));// +' ' +sdt.getUeDay()));
		var cell = new CalCell(this,td,sdt,row);
		this.cells.push(cell);
		td.cellObj = cell;
		sdt.setDate(sdt.getDate() + 1); //increment the date
		tr.appendChild(td);
		tbody.appendChild(tr);
	}
	
	// mJ create Footer here!
	tr = document.createElement('tr');
	this.setClass( tr, "footer" );
	td = document.createElement('td');
	td.setAttribute( "colSpan", "8" );
	td.appendChild( this.createFooter() );

	tr.appendChild(td);
	tbody.appendChild(tr);

	
	this.calCells.appendChild(tbody);
	this.reDraw();
	return this.calCells;
};
//-----------------------------------------------------------------------------
SECalendar.prototype.reDraw = function () //PRIVATE: reapplies all the CSS classes for the calendar cells, usually called after chaning their state
{
	this.state = 1;
	var i,j;
	for(i=0;i<this.cells.length;i++) {
		this.cells[i].selected = false;
	}
	for(i=0;i<this.cells.length;i++)
	{
		for(j=0;j<this.selectedDates.length;j++) { //if the cell's date is in the selectedDates array, set its selected property to true
			if(this.cells[i].date.getUeDay() == this.selectedDates[j].getUeDay() ) {
				this.cells[i].selected = true;
			}
		}

		this.cells[i].setClass();
	}
	//alert(this.selectedDates);
	this.state = 2;
};
//-----------------------------------------------------------------------------
SECalendar.prototype.deleteCells = function () //PRIVATE: removes the calendar cells from the DOM (does not delete the cell objects associated with them
{
	this.calCellContainer.removeChild(this.calCellContainer.firstChild); //get a handle on the cell table (optional - for less indirection)
	this.cells = new Array(); //reset the cells array
};
//-----------------------------------------------------------------------------
SECalendar.prototype.goToMonth = function (year,month) //PUBLIC: sets the calendar to display the requested month/year
{
	this.monthSelect.value = this.displayMonth = month;
	this.yearSelect.value = this.displayYear = year;
	this.deleteCells();
	this.calCellContainer.appendChild(this.createCalCells());
};
//-----------------------------------------------------------------------------
SECalendar.prototype.nextMonth = function () //PUBLIC: go to the next month.  if the month is december, go to january of the next year
{
	
	//increment the month/year values, provided they're within the min/max ranges
	if(this.monthSelect.value < 11) {
		this.monthSelect.value++;
	}
	else
	{
		if(this.yearSelect.value < this.rangeYearUpper)
		{
			this.monthSelect.value = 0;
			this.yearSelect.value++;
		}
		else {
			alert(this.maxrange_caption);
		}
	}
	//assign the currently displaying month/year values
	this.displayMonth = parseInt(this.monthSelect.value);
	this.displayYear = parseInt(this.yearSelect.value);
	
	//and refresh the calendar for the new month/year
	this.deleteCells();
	this.calCellContainer.appendChild(this.createCalCells());
};
//-----------------------------------------------------------------------------
SECalendar.prototype.prevMonth = function () //PUBLIC: go to the previous month.  if the month is january, go to december of the previous year
{
	//increment the month/year values, provided they're within the min/max ranges
	if(this.monthSelect.value > 0)
		this.monthSelect.value--;
	else
	{
		if(this.yearSelect.value > this.rangeYearLower)
		{
			this.monthSelect.value = 11;
			this.yearSelect.value--;
		}
		else {
			alert(this.maxrange_caption);
		}
	}
	
	//assign the currently displaying month/year values
	this.displayMonth = this.monthSelect.value;
	this.displayYear = this.yearSelect.value;
	
	//and refresh the calendar for the new month/year
	this.deleteCells();
	this.calCellContainer.appendChild(this.createCalCells());
};
//-----------------------------------------------------------------------------
SECalendar.prototype.addZero = function (vNumber) //PRIVATE: pads a 2 digit number with a leading zero
{
	return ((vNumber < 10) ? '0' : '') + vNumber;
};
//-----------------------------------------------------------------------------
SECalendar.prototype.addDates = function (dates,redraw)  //PUBLIC: adds the array "dates" to the calendars selectedDates array (no duplicate dates) and redraws the calendar
{
	var j,in_sd;
	for(var i=0;i<dates.length;i++)
	{	
		in_sd = false;
		for(j=0;j<this.selectedDates.length;j++)
		{
			if(dates[i].getUeDay() == this.selectedDates[j].getUeDay())
			{
				in_sd = true;
				break;
			}
		}
		if(!in_sd) { //if the date isn't already in the array, add it!
			this.selectedDates.push(dates[i]);
		}
	}
	if(redraw != false) {//redraw  the calendar if "redraw" is false or undefined
		this.reDraw();
	}
};
//-----------------------------------------------------------------------------
SECalendar.prototype.removeDates = function (dates,redraw)  //PUBLIC: adds the dates to the calendars selectedDates array and redraws the calendar
{
	var j;
	for(var i=0;i<dates.length;i++)
	{
		for(j=0;j<this.selectedDates.length;j++)
		{
			if(dates[i].getUeDay() == this.selectedDates[j].getUeDay()) { //search for the dates in the selectedDates array, removing them if the dates match
				this.selectedDates.splice(j,1);
			}
		}
	}
	if(redraw != false) { //redraw  the calendar if "redraw" is false or undefined
		this.reDraw();
	}
};
//-----------------------------------------------------------------------------
SECalendar.prototype.outputDate = function (vDate, vFormat) //PUBLIC: outputs a date in the appropriate format (DEPRECATED)
{
	var vDay			= this.addZero(vDate.getDate()); 
	var vMonth			= this.addZero(vDate.getMonth() + 1); 
	var vYearLong		= this.addZero(vDate.getFullYear()); 
	var vYearShort		= this.addZero(vDate.getFullYear().toString().substring(3,4)); 
	var vYear			= (vFormat.indexOf('yyyy') > -1 ? vYearLong : vYearShort);
	var vHour			= this.addZero(vDate.getHours()); 
	var vMinute			= this.addZero(vDate.getMinutes()); 
	var vSecond			= this.addZero(vDate.getSeconds()); 
	return vFormat.replace(/dd/g, vDay).replace(/mm/g, vMonth).replace(/y{1,4}/g, vYear).replace(/hh/g, vHour).replace(/nn/g, vMinute).replace(/ss/g, vSecond);
};
//-----------------------------------------------------------------------------
SECalendar.prototype.updatePos = function (target) //PUBLIC: moves the calendar's position to target's location (popup mode only)
{
	this.calendar.style.top = this.getTop(target) + this.topOffset + 'px'
	this.calendar.style.left = this.getLeft(target) + this.leftOffset + 'px'
}
//-----------------------------------------------------------------------------

/*****************************************************************************/
function WeekHeading(owner,tableCell,week,row)
{
	this.owner = owner;
	this.tableCell = tableCell;
	this.week = week;
	this.tableRow = row;
	this.tableCell.setAttribute('class','wkhead');
	this.tableCell.setAttribute('className','wkhead'); //<iehack>
	//the event handlers
	this.tableCell.title = this.owner.week_title;
	
// 	this.tableCell.onmouseover = this.onmouseover;
// 	this.tableCell.onmouseout = this.onmouseout;
	this.tableCell.onclick = this.onclick;
}
//-----------------------------------------------------------------------------
WeekHeading.prototype.onmouseover = function () //replicate CSS :hover effect for non-supporting browsers <iehack>
{
	this.setAttribute('class',this.cellClass + '_hover');
	this.setAttribute('className',this.cellClass + '_hover');
};
//-----------------------------------------------------------------------------
WeekHeading.prototype.onmouseout = function () //replicate CSS :hover effect for non-supporting browsers <iehack>
{
	this.setAttribute('class','wkhead');
	this.setAttribute('className','wkhead'); //<iehack>
};

WeekHeading.prototype.onclick = function ()
{
	//reduce indirection:
	var owner = this.weekObj.owner;
	var cells = owner.cells;
	var sdates = owner.selectedDates;
	var i,j;
    var	startdate = null, enddate;
	//mJ: Zelldatum direkt abgreifen.

	owner.startdate = cells[this.weekObj.tableRow*7].date;
	owner.enddate = cells[this.weekObj.tableRow*7+6].date;
    
	if( owner.enddate < owner.mindate )
		owner.enddate = owner.mindate;
	
    if( owner.enddate > owner.maxdate )
		owner.enddate = owner.maxdate;
	
	if( owner.startdate < owner.mindate )
		owner.startdate = owner.mindate;
	
	if( owner.startdate > owner.maxdate )
		owner.startdate = owner.maxdate;


	owner.reDraw();
	
	if( owner.callback )
		owner.callback( owner.startdate, owner.enddate, 1 );
	
};
/*****************************************************************************/
//-----------------------------------------------------------------------------
function CalCell( owner, tableCell, dateObj, row )
{
	this.owner = owner;						//used primarily for event handling
	this.tableCell = tableCell; 			//the link to this cell object's table cell in the DOM
	this.cellClass;							//the CSS class of the cell
	this.selected = false;					//whether the cell is selected (and is therefore stored in the owner's selectedDates array)
	this.date = new Date(dateObj);
	this.dayOfWeek = this.date.getDay();
	this.week = this.date.getWeek();
	this.tableRow = row;
	this.clickable = false;
	
	//assign the event handlers for the table cell element
	this.tableCell.onclick = this.onclick;
	
//	mJ
//	this.tableCell.onmouseover = this.onmouseover;
//	this.tableCell.onmouseout = this.onmouseout;
	
	//and set the CSS class of the table cell
	this.setClass();
}
//-----------------------------------------------------------------------------

CalCell.prototype.onmouseover = function () //replicate CSS :hover effect for non-supporting browsers <iehack>
{
	this.setAttribute('class',this.cellClass + ' hover');
	this.setAttribute('className',this.cellClass + ' hover');
};
//-----------------------------------------------------------------------------

CalCell.prototype.onmouseout = function () //replicate CSS :hover effect for non-supporting browsers <iehack>
{
	this.cellObj.setClass();
};

//-----------------------------------------------------------------------------
CalCell.prototype.onclick = function () 
{
	//reduce indirection:
	var cell = this.cellObj;
	var owner = cell.owner;
// 	if(!owner.selCurMonthOnly || cell.date.getMonth() == owner.displayMonth && cell.date.getFullYear() == owner.displayYear)
// 	{
// 		owner.selectedDates = new Array(cell.date);
// 		if(owner.tgt) { //if there is a target element to place the value in, do so
// 			owner.tgt.value = owner.selectedDates[0].dateFormat();
// 			if(owner.mode == 'popup') {
// 				owner.hide();
// 			}
// 		}
// //		owner.reDraw(); //redraw the calendar cell styles to reflect the changes
// 	}

	if( cell.clickable ) {

    	if( owner.selectState == 0 ) {	//	select startdate
			owner.selectState = 1;
			owner.startdate = cell.date;
			owner.enddate = cell.date;
		 	if( owner.callback )
		 		owner.callback( cell.date, cell.date, 0 );
		} else {						//	select enddate
			owner.selectState = 0;
			if( owner.startdate > cell.date ) {
				owner.enddate = owner.startdate;
				owner.startdate = cell.date;
			} else {
				owner.enddate = cell.date;
			}
		 	if( owner.callback )
		 		owner.callback( owner.startdate, owner.enddate, 1 );
		}

		owner.reDraw();
	}
};

CalCell.prototype.ondblclick = function () 
{
	//reduce indirection:
	var cell = this.cellObj;
	var owner = cell.owner;
	if(!owner.selCurMonthOnly || cell.date.getMonth() == owner.displayMonth && cell.date.getFullYear() == owner.displayYear)
	{
		owner.selectedDates = new Array(cell.date);
		if(owner.tgt) { //if there is a target element to place the value in, do so
			owner.tgt.value = owner.selectedDates[0].dateFormat();
			if(owner.mode == 'popup') {
				owner.hide();
			}
		}
//		owner.reDraw(); //redraw the calendar cell styles to reflect the changes
	}
	if( 1 || this.clickable ) {
		owner.startdate = cell.date;
		owner.enddate = cell.date;
		
		if( owner.callback )
			owner.callback( cell.date, cell.date, 1 );
	
		owner.reDraw();
	}
};
//-----------------------------------------------------------------------------
CalCell.prototype.setClass = function ()  //private: sets the CSS class of the cell based on the specified criteria
{
	//	 mJ
	if( this.date >= this.owner.mindate && this.date <= this.owner.maxdate  ) {
		this.clickable = true;
		this.cellClass = "possible";
		this.tableCell.title = this.owner.calcell_title[this.owner.selectState];
	} else {
		this.cellClass = "notmnth";
	}

	if( this.owner.selectState == 0 ) {	// normal / select start
		if( this.date >= this.owner.startdate && this.date <= this.owner.enddate  ) {
			this.cellClass = "cell_selected";
	//		this.tableCell.title = "daily report";
		}
	} else {	// primed / select end
		if( this.date == this.owner.startdate ) {
			this.cellClass = "cell_primed";
		}
	}

	this.tableCell.setAttribute( 'class', this.cellClass );
	this.tableCell.setAttribute( 'className', this.cellClass ); //<iehack>
};
/*****************************************************************************/
Date.prototype.getDayOfYear = function () //returns the day of the year for this date
{
	return parseInt((this.getTime() - new Date(this.getFullYear(),0,1).getTime())/86400000 + 1);
};
//-----------------------------------------------------------------------------
Date.prototype.getWeek = function () //returns the day of the year for this date
{
	return parseInt((this.getTime() - new Date(this.getFullYear(),0,1).getTime())/604800000 + 1);
};
/*function getISOWeek()
{
	var newYear = new Date(this.getFullYear(),0,1);
	var modDay = newYear.getDay();
	if (modDay == 0) modDay=6; else modDay--;
	
	var daynum = ((Date.UTC(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0) - Date.UTC(this.getFullYear()),0,1,0,0,0)) /1000/60/60/24) + 1;
	
	if (modDay < 4 ) {
	    var weeknum = Math.floor((daynum+modDay-1)/7)+1;
	}
	else {
	    var weeknum = Math.floor((daynum+modDay-1)/7);
	    if (weeknum == 0) {
	        year--;
	        var prevNewYear = new Date(this.getFullYear(),0,1);
	        var prevmodDay = prevNewYear.getDay();
	        if (prevmodDay == 0) prevmodDay = 6; else prevmodDay--;
	        if (prevmodDay < 4) weeknum = 53; else weeknum = 52;
	    }
	}
	
	return + weeknum;
}*/
//-----------------------------------------------------------------------------
Date.prototype.getUeDay = function () //returns the number of DAYS since the UNIX Epoch - good for comparing the date portion
{
	return parseInt(Math.floor((this.getTime() - this.getTimezoneOffset() * 60000)/86400000)); //must take into account the local timezone
};
//-----------------------------------------------------------------------------
Date.prototype.dateFormat = function(format)
{
	if(!format) { // the default date format to use - can be customized to the current locale
		format = 'm/d/Y';
	}
	LZ = function(x) {return(x < 0 || x > 9 ? '' : '0') + x};
	var MONTH_NAMES = new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
	var DAY_NAMES = new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat');
	format = format + "";
	var result="";
	var i_format=0;
	var c="";
	var token="";
	var y=this.getFullYear().toString();
	var M=this.getMonth()+1;
	var d=this.getDate();
	var E=this.getDay();
	var H=this.getHours();
	var m=this.getMinutes();
	var s=this.getSeconds();
	var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;
	// Convert real this parts into formatted versions
	var value = new Object();
	//if (y.length < 4) {y=''+(y-0+1900);}
	value['Y'] = y.toString();
	value['y'] = y.substring(2);
	value['n'] = M;
	value['m'] = LZ(M);
	value['F'] = MONTH_NAMES[M-1];
	value['M'] = MONTH_NAMES[M+11];
	value['j'] = d;
	value['d'] = LZ(d);
	value['D'] = DAY_NAMES[E+7];
	value['l'] = DAY_NAMES[E];
	value['G'] = H;
	value['H'] = LZ(H);
	if (H==0) {value['g']=12;}
	else if (H>12){value['g']=H-12;}
	else {value['g']=H;}
	value['h']=LZ(value['g']);
	if (H > 11) {value['a']='pm'; value['A'] = 'PM';}
	else { value['a']='am'; value['A'] = 'AM';}
	value['i']=LZ(m);
	value['s']=LZ(s);
	//construct the result string
	while (i_format < format.length) {
		c=format.charAt(i_format);
		token="";
		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
			token += format.charAt(i_format++);
			}
		if (value[token] != null) { result=result + value[token]; }
		else { result=result + token; }
		}
	return result;
};
/*****************************************************************************/
Array.prototype.arrayIndex = function(searchVal,startIndex) //similar to array.indexOf() - created to fix IE deficiencies
{
	startIndex = (startIndex != null ? startIndex : 0); //default startIndex to 0, if not set
	for(var i=startIndex;i<this.length;i++)
	{
		if(searchVal == this[i]) {
			return i;
		}
	}
	return -1;
};
/*****************************************************************************/


var MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
var DAY_NAMES=new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat');
function LZ(x) {return(x<0||x>9?"":"0")+x}


function formatDate(date,format)
{
	format=format+"";
	var result="";
	var i_format=0;
	var c="";
	var token="";
	var y=date.getYear()+"";
	var M=date.getMonth()+1;
	var d=date.getDate();
	var E=date.getDay();
	var H=date.getHours();
	var m=date.getMinutes();
	var s=date.getSeconds();
	var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;
	// Convert real date parts into formatted versions
	var value=new Object();
	if (y.length < 4) {y=""+(y-0+1900);}
	value["y"]=""+y;
	value["yyyy"]=y;
	value["yy"]=y.substring(2,4);
	value["M"]=M;
	value["MM"]=LZ(M);
	value["MMM"]=MONTH_NAMES[M-1];
	value["NNN"]=MONTH_NAMES[M+11];
	value["d"]=d;
	value["dd"]=LZ(d);
	value["E"]=DAY_NAMES[E+7];
	value["EE"]=DAY_NAMES[E];
	value["H"]=H;
	value["HH"]=LZ(H);
	if (H==0){value["h"]=12;}
	else if (H>12){value["h"]=H-12;}
	else {value["h"]=H;}
	value["hh"]=LZ(value["h"]);
	if (H>11){value["K"]=H-12;} else {value["K"]=H;}
	value["k"]=H+1;
	value["KK"]=LZ(value["K"]);
	value["kk"]=LZ(value["k"]);
	if (H > 11) { value["a"]="PM"; }
	else { value["a"]="AM"; }
	value["m"]=m;
	value["mm"]=LZ(m);
	value["s"]=s;
	value["ss"]=LZ(s);
	while (i_format < format.length) {
		c=format.charAt(i_format);
		token="";
		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
			token += format.charAt(i_format++);
			}
		if (value[token]!= null) { result=result + value[token]; }
		else { result=result + token; }
		}
	return result;
}



