﻿/*
- mind the scope. use qualified path on html attribute
window[name].printCal() to open an instance of DatePicker
- yes crashes MACIE but who cares anymore
- tested in saf, ff, opera, netscape of mac and winie 6, ff on win

- currently alows only one instance onscreen. uses getElement to find the
current open object. u could also use a static prop to store the div in



to do:

-cursor in saf on hover doesn respond very well
-modify position methods

to add:
-master slave. slave opens with date after master
slave is not alleow to choose a date before date of master (perhaps
by adding a argument in the constructor. if set it indixates the master
others a slaves. keep track to static properties....

*/
function DatePicker (name) {

// instance properties
this.name = name;
this.input;
this.currentDate = new Date();
//***** end of Constructor
}
// class properties
DatePicker.version = '0.9';
DatePicker.body;
DatePicker.div;


// more class properties - default settings
DatePicker.months = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
DatePicker.weekDays = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
DatePicker.weekStart = 1;
DatePicker.styles = [];
DatePicker.styles['datepicker'] = new Array ( 
	{ key : 'position' , value : 'absolute' }, 
	{ key : 'z-index' , value : '100' }, 
	{ key : 'border' , value : '4px double silver' } , 
	{ key : 'padding' , value : '6px' }, 
	{ key : 'backgroundColor' , value : 'white' }, 
	{ key : 'color' , value : 'orange' }, 
	{ key : 'fontFamily' , value : 'Verdana, sans-serif' }, 
	{ key : 'fontSize' , value : '10px' },
	{ key : 'marginLeft' , value : '30px' }	);

DatePicker.styles['tbody'] = new Array (
	{ key : 'fontSize' , value : '9px' }, 
	{ key : 'textAlign' , value : 'right' }, 
	{ key : 'padding' , value : '0' }  ); 

DatePicker.usestyles = true;

// **** PRINT CAL METHOD
DatePicker.prototype.printCal = function  () {

// remove existing div
this.remove();

// Boxes
DatePicker.body = document.getElementsByTagName('body')[0];
// create new box div
DatePicker.div = DatePicker.body.appendChild ( document.createElement ('div') );
DatePicker.div.setAttribute('id', 'datepicker');
// get ref to input field
this.input = (document.getElementById(this.name) ) ? document.getElementById(this.name) : alert ('can not reference input field: ' + this.name);

// get date and set first day of month
this.firstDay = new Date(this.currentDate)
this.firstDay.setDate(1);
// currentDay ref to actual date // cellCounter counts generated td's
var currentDay;
var cellCounter = 0;

// HEADER H4
var h4 = document.createElement('h4');
h4.innerHTML = DatePicker.months[this.firstDay.getMonth()] + ' - ' + this.currentDate.getFullYear()  ;
DatePicker.div.appendChild(h4);

// TABLE
var table, tbody, tr, td;
table = document.createElement('table');
DatePicker.div.appendChild(table);
// use tbody for winie!!
tbody = table.appendChild ( document.createElement('tbody') );

// TABLE header weekdays
tr = document.createElement('tr');
for (var i = 0; i < 7 ; i++) {
td = tr.appendChild ( document.createElement('td') );
td.innerHTML = DatePicker.weekDays[(DatePicker.weekStart+i)%7] ;
}
tbody.appendChild(tr);


// prepend last month days - calculate offset - set: current day - offset
tr = document.createElement('tr');
var dayOffset = (this.firstDay.getDay() == 0) ? 6 : this.firstDay.getDay() - DatePicker.weekStart;
if (dayOffset > 0) {
currentDay = new Date(this.firstDay);
currentDay.setDate(-dayOffset+1); //  allow for first day
for (var i = 0 ; i < dayOffset ; i++ ) {
tr.appendChild ( this.setCellValue ( currentDay ) ); //......................
currentDay.setDate(currentDay.getDate()+1 );
cellCounter++;
}
}

// fill CURRENT month - reset current day;
currentDay = new Date(this.firstDay);

while (currentDay.getMonth() == this.currentDate.getMonth() ) {
tr.appendChild ( this.setCellValue ( currentDay ) ); //......................
currentDay.setDate(currentDay.getDate()+1);
cellCounter++;

// adjust dayOffset print tr if end of row	
dayOffset++;
if (dayOffset == 7) {
dayOffset = 0;
tbody.appendChild(tr);
tr = document.createElement('tr');
}
}

// append next month days - recaluculate offset for last cells and print
if (dayOffset > 0) { dayOffset = 7 - dayOffset; }
if (dayOffset > 0) {
for (var i = 0 ; i < dayOffset ; i++ ) {
tr.appendChild ( this.setCellValue ( currentDay ) ); //......................

currentDay.setDate(currentDay.getDate()+1);
cellCounter++;
}
}

tbody.appendChild(tr);

// do we need to append extra row for consistent table size
if (cellCounter <= 35 ) {
 	tr = document.createElement('tr');	
for (var i = 0 ; i < 7 ; i++ ) {
td = tr.appendChild ( document.createElement('td') );
td.appendChild (this.setCellValue ( currentDay ) ) ; //......................

currentDay.setDate(currentDay.getDate()+1);
cellCounter++;

}

tbody.appendChild(tr);

}


var p = DatePicker.div.appendChild(document.createElement('p'));

// use innerHTML winie returns error when using setAttribute to set onclick
// or use object and reference this on the a object and use function to handle click
// with inside call to instance. see set cellValue
var str = '';
str += '<a href="javascript:void;" onclick="'+this.name+'.movePrevious(); return false;">prev</a>';
str += ' | ';
str += '<a href="javascript:void;" onclick="'+this.name+'.today(); return false;">today</a>';
str += ' | ';
str += '<a href="javascript:void;" onclick="'+this.name+'.moveNext(); return false;">next</a>';

str += ' | ';
str += '<a href="javascript:void;" onclick="'+this.name+'.remove(); return false;">X</a>';

p.innerHTML = str;


// calculate POSITION and add to styles
DatePicker.div.style.position = 'absolute';
DatePicker.div.style.top = this.getOffset(this.input, 'offsetTop') + this.input.offsetHeight +'px';
DatePicker.div.style.left = this.getOffset(this.input, 'offsetLeft')+ this.input.offsetWidth +'px';
// finaly add some default STYLES (or not)
if (DatePicker.usestyles) {
// set each attribute seperately for winie. string doesn't work
// 'style', 'xx:yy;zz:aa;' works in safare e.o. but not in winie
// winie simply silently igonores the keyword 'style'!
this.setAttributes (DatePicker.div, 'datepicker');
this.setAttributes (tbody, 'tbody');

} //


/// END PRINT METHOD
}
/// END PRINT METHOD


// **** OTHER METHODS

// setCellValue returns alinks for day in current Month or just a text value
// winie does not let u set onlclick with setAttribute
DatePicker.prototype.setCellValue = function (currentDay) {
td = document.createElement ('td');
var dayColor = this.getDayColor (currentDay);
var a = document.createElement('a');
a.o = this;
a.style.color = dayColor;
a.backgroundColor = 'white';
a.onclick = function () { this.o.click(this) };
a.onmouseover = function () {this.style.textDecoration = 'underline'; this.style.cursor = 'pointer'; };
a.onmouseout = function () { this.style.textDecoration = 'none'; this.style.cursor = 'auto'; };
a.innerHTML =  currentDay.getDate();
if (currentDay.getMonth() == this.currentDate.getMonth() ) { td.appendChild(a) } else { ( td.innerHTML = currentDay.getDate() ) } ;
return td;
}

// Click inserts date in input and remove instance
DatePicker.prototype.click = function (o) {
var d = this.currentDate.setDate(o.innerHTML); // day number from input sets currentDate object
d = new Date (d); // make it dateobject
this.input.value = DatePicker.months[d.getMonth()] + ' ' + d.getDate() + ' ' + d.getFullYear();

this.remove();

}
// getDayColor validates cuurentDay and returns a color
DatePicker.prototype.getDayColor = function (currentDay) {
var now = new Date();

if (currentDay.getDate() == now.getDate() && currentDay.getMonth() == now.getMonth() ) {
return 'blue';
} 	else if ( currentDay.getDay() == 0 || currentDay.getDay() == 6  ) {
return 'red';
} 	else if ( currentDay.getMonth() == this.currentDate.getMonth() ) {
return '#333333';
} 	else {
return 'silver';
} 
}



// **** NAVIGATION
DatePicker.prototype.today = function  () {
this.currentDate = new Date() ;
this.printCal();
}
DatePicker.prototype.movePrevious  = function () {
// to do: do not allow to go before current date
// subtract 1 or jump to dec when we are january
var m = (this.currentDate.getMonth() == 0 ) ? 11 : this.currentDate.getMonth()-1;
this.currentDate.setMonth( m );
this.printCal();
}
DatePicker.prototype.moveNext = function  () {
this.currentDate.setMonth( this.currentDate.getMonth()+1);
this.printCal();
}
// remove _any_ instance of datepicker currently onscreen
DatePicker.prototype.remove = function () {
if (document.getElementById('datepicker')) {
var div = document.getElementById('datepicker');
 div.parentNode.removeChild(div);
}

}



// **** UTILITIES

// offSet to what?
// find postion for winie 
// (in saf u get the offsetTop directly from the element)
// traverse through offsetParent to calculate offset
// for each containing block
DatePicker.prototype.getOffset = function (id, offset) {
var obj = id;
	var curleft = 0;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curleft += obj[offset];
			obj = obj.offsetParent;
		}
	}
	else if (obj.x)
		curleft += obj.x;
	return curleft;
}

DatePicker.prototype.setAttributes = function (obj, array ) {
for (var i = 0 ; i <DatePicker.styles[array].length ; i++) {
	obj.style[DatePicker.styles[array][i].key] = DatePicker.styles[array][i].value;
	}
}


// name of instance
DatePicker.prototype.toString = function () {
return this.name;
}



