
/*===============================================================================
	DatePicker.js
	John Larson
	9/26/08
	
	Add date picking input+icon+widget to a span!
	
	Example:
	<span id="billingWidgetDateFrom"></span>
	
	myDatePicker = new DatePicker('billingWidgetDateFrom',
		{initialValue:		'9/20/08',
		 dateFormat:		'%m/%D/%y',
		 direction:			-1,
		 inputClass:		'DatePicker',
		 showOnInputFocus:	false });

===============================================================================*/


var DatePicker = new Class({
	
	Implements: [Options, Events],
	
	options: {
		initialValue:			'',
		dateFormat:				'%m/%D/%Y',
		inputClass:				'DatePicker-input',
		inputID:				null,
		direction:  			0,
		minDate:				null,
		maxDate:				null,
		showPickerIcon:			true,
		showOnInputFocus:		true,
		hideOnBlur:				true,
		pickerPositionOptions:	{offset: {x:0,y:-50}},
		exemptDateSet:			[],
		className:				'DatePicker',
		headerLabelSet:			['S', 'M', 'T', 'W', 'T', 'F', 'S'],
		weekRowCount:			0 // zero implies use on as many as needed
	},
	
	initialize: function(container, inputName, options) {
		
		if(!$(container))
			throw('Unknown container passed to DatePicker contructor (' + container + ')');
		this.container = $(container);
		this.setOptions(options);
		
		// Create the input:
		var theInput = new Element('input');
		theInput.type = 'text';
		theInput.name = inputName;
		if(this.options.inputID)  theInput.ID = this.options.inputID;
		theInput.addClass(this.options.inputClass);
		theInput.maxLength	= 10;
		
		// What's our initially chosen date?
		this.chosenDate = Date.parse(this.options.initialValue);
	//	dbug.log(this.options.initialValue + ' / ' + this.chosenDate);
		this.viewingDate = $pick(this.chosenDate, new Date());
		if(this.chosenDate)
			theInput.value = this.convertDateToPreferredFormat(this.options.initialValue);
		
		this.theInput = theInput;
		this.isVisible = false;
		
		var me = this;
		
		this.createPickerWidget();
		
		if(this.options.showOnInputFocus  ||  !this.options.showPickerIcon)
			theInput.addEvent('focus', me.showPickerWidget.bind(me));
		
		if(this.options.hideOnBlur)
			this.widgetDiv.addEvent('outerClick', function() {
				if(me.isVisible)
					me.hidePickerWidget.bind(me)();
			});
		
		this.container.adopt(theInput);
		
		
		// Now the icon, if called for:
		if(this.options.showPickerIcon) {
			var theIcon = new Element('span');
			theIcon.addClass(this.options.className + '-Icon'); // MUST be defined in CSS
			theIcon.addEvent('click', me.showPickerWidget.bind(me));
			theIcon.setStyle('vertical-align', 'middle');
			this.container.adopt(theIcon);
		}
		
		
		// Now let's establish our date range limits, if any:
		this.maxDate = this.options.maxDate ||  new Date(2999, 11, 31);
		this.minDate = this.options.minDate ||  new Date(1000,  1,  1);
		var direction = this.options.direction;
		if(direction > 0)
			this.minDate = Date.max(this.minDate, (new Date()).clearTime().increment('day', Math.floor(direction)))
		else if(this.options.direction < 0)
			this.maxDate = Date.min(this.maxDate, (new Date()).decrement('day', Math.floor(-direction)))
		
		
	},
	
	
	showPickerWidget: function() {
		
		// What date should we show as chosen on our calendar?
		if(Date.isValidDate(this.theInput.value)) {
			dbug.log('input value is a valid date!');
			this.chosenDate = Date.parse(this.theInput.value);
			this.viewingDate = this.chosenDate;
		}
		else
			this.viewingDate = new Date();
		dbug.log(this.viewingDate);
		this.renderViewingMonth();
		
		
		this.resetWidgetPosition();
		this.widgetDiv.appear();
		setTimeout(function() {this.isVisible = true;}.bind(this),  500);

	},
	
	hidePickerWidget: function(theTD) {
		this.widgetDiv.fade();
		this.isVisible = false;
	},
	
	
	goBackOneMonth: function() {
		this.viewingDate.decrement('month', 1);
		this.renderViewingMonth();
	},
	
	
	goForwardOneMonth: function() {
		this.viewingDate.increment('month', 1);
		this.renderViewingMonth();
	},
	
	
	
	renderViewingMonth: function() {
		
		this.monthLabel.setText(this.viewingDate.format('%B %Y'));
		
		var firstOfThisMonth = this.viewingDate.clone().set('date', 1);
	//	dbug.log(firstOfThisMonth)
		
		currentDateIntRange = this.gridMinMaxDateInts(this.viewingDate, this.options.weekRowCount);
		
		var minDateInt  = currentDateIntRange[0];
		var pointerDate = DatePicker.calendarIntToDate(minDateInt).increment('hour', 12);
	//	dbug.log('pointerDate=' + pointerDate);
	//	dbug.log('chosenDate=' + this.chosenDate);
		
		var today = (new Date()).format('%m/%d/%y');
		
		// Now place the date numbers on each square:
		var dayCellSet = $ES('td', this.canvasGrid);
		var dayCellCount = dayCellSet.length;
		for (var d = 0; d < dayCellCount; d++) {
			var dayCell = dayCellSet[d];
			dayCell.setText(pointerDate.getDate());
			
			var cellClass = '';
			if(this.dateIsValid(pointerDate)) {
				if(pointerDate.getMonth() != this.viewingDate.getMonth())
					cellClass = this.options.className + '-DayOtherMonth';
				
				if(pointerDate.format(this.options.dateFormat) == this.chosenDate)
					cellClass += ' chosenDate';
			}
			else
				cellClass = this.options.className + '-invalidDay';
			
			
			if(pointerDate.format('%m/%d/%y') == today)
				cellClass += ' dayToday';
			
			dayCell.className = cellClass;
			
			
			dayCell.theDate = pointerDate.format(this.options.dateFormat);
		//	dbug.log(dayCell.theDate);
			
			pointerDate.increment('day', 1);
		}
		
		// Show or hide forward/back buttons based on our range:
	//	if(DatePicker.calendarIntToDate(currentDateIntRange[0]) <= this.minDate)
		if(firstOfThisMonth <= this.minDate)
			this.backButton.setStyle('display', 'none');
		else
			this.backButton.setStyle('display', '');
		
		if(DatePicker.calendarIntToDate(currentDateIntRange[1]) >= this.maxDate)
			this.forwardButton.setStyle('display', 'none');
		else
			this.forwardButton.setStyle('display', '');
		
	},
	
	pickDate: function(theTD) {
	//	dbug.log(theTD.theDate + '!!');
		if(!this.dateIsValid(theTD.theDate))
			return;
	//	dbug.log('ok!');
		
		this.theInput.value = theTD.theDate;
		this.hidePickerWidget();
	},
	
	
	convertDateToPreferredFormat: function(value) {
		if(Date.isValidDate(Date.parse(value)))
			return Date.format(Date.parse(value), this.options.dateFormat);
		else
			return null;
	},
	
	
	dateIsValid: function(theDate) {
		theDate = $D(theDate);
		if(theDate > this.maxDate)
			return false;
		
		if(theDate < this.minDate)
			return false;
		
		
		return true;
	},
	
	
	createPickerWidget: function() {
		var widgetDiv = new Element('div', {
			'styles':{'visibility': 'hidden',
				'position': 'absolute',
				'width': '150px',
				'z-index': 2000},
			'class': this.options.className + '-WidgetDiv'
		});
		
		
		widgetDiv.injectInside(document.body);
		
		// Set the position of our picker widget.  By default it will
		// be centered on our container, but pickerPositionOptions
		// can override any aspect:
		
		widgetDiv.setPosition($merge({
			relativeTo: this.container,
			position: {
				x: 'center', //left, center, right
				y: 'center' //top, center, bottom
			},
			edge: false,
			returnPos: false,
			relFixedPosition: false,
			ignoreMargins: false
		}, this.options.pickerPositionOptions));
		
		
		// Build the widget.  Start with top controls:
		var backButton		= new Element('span', {'class': this.options.className + '-BackButton' });
		var forwardButton	= new Element('span', {'class': this.options.className + '-ForwardButton' });
		var monthLabel		= new Element('span', {'class': this.options.className + '-MonthLabel' });
		
		backButton.addEvent('click', this.goBackOneMonth.bind(this));
		forwardButton.addEvent('click', this.goForwardOneMonth.bind(this));
		
		this.backButton		= backButton;
		this.forwardButton	= forwardButton;
		
		var controlsDiv = new Element('div', {'class': this.options.className + '-ControlDiv' });
		controlsDiv.adopt(backButton);
		controlsDiv.adopt(forwardButton);
		controlsDiv.adopt(monthLabel);
		this.monthLabel = monthLabel;
		
		var canvasDiv = new Element('div', {'class': this.options.className + '-CanvasDiv' });
		
		var canvasGrid = new Element('table').addClass(this.options.className + '-CanvasGrid');
		canvasGrid.cellPadding = 1;
		canvasGrid.cellSpacing = 0;
		
		var headerRow = new Element('tr');
		for (var d = 0; d < 7; d++)
			headerRow.adopt(new Element('th').setText(this.options.headerLabelSet[d]));
		
		var me = this;
		
		canvasGrid.adopt(new Element('thead').adopt(headerRow));
		var tbody = new Element('tbody');
		var standardRowCount = 6;
		for(var w = 0; w < standardRowCount; w++) {
			var weekRow = new Element('tr');
			for(var d = 0; d < 7; d++) {
				
				var dayCell = new Element('td');
			//	dayCell.id = 'SummaryCalendar' + this.ID + '_' + w + '_' + d;
				dayCell.addEvent('click', me.pickDate.pass(dayCell, me));

			//	if(d==0  ||  d==6)
			//		dayCell.addClass(this.options.className + '-DayWeekend');
				
				weekRow.adopt(dayCell);
			}
			tbody.adopt(weekRow);
		}
		
		canvasGrid.adopt(tbody);
		canvasDiv.adopt(canvasGrid);
		this.canvasGrid = canvasGrid;
		
		
		widgetDiv.adopt(controlsDiv);
		widgetDiv.adopt(canvasDiv);
		
		this.widgetDiv = widgetDiv;
		
	},
	
	gridMinMaxDateInts: function(theDate, rowCount) {
		theDate = Date.parse(theDate);
		
		var firstOfThisMonth = new Date(theDate.setDate(1));
		var firstOfNextMonth = (new Date(theDate)).increment('month', 1).setDate(1);
		var currentMonthDay = 1 - (firstOfThisMonth.getDay());  // negative implies last month
		var thisMonthDayCount = (new Date(firstOfNextMonth)).decrement('day', 1).getDate();
		
		this.rowCount = rowCount  ||  Math.ceil((thisMonthDayCount - currentMonthDay) / 7);
		
		var minGridCalendarInt = currentMonthDay-1 + DatePicker.calendarDateToInt(firstOfThisMonth);
		var maxGridCalendarInt = minGridCalendarInt + (7*this.rowCount) - 1;
		
		return [minGridCalendarInt, maxGridCalendarInt];
	},
	
	
	resetWidgetPosition: function() {
		this.widgetDiv.setPosition($merge({
			relativeTo: this.container
		}, this.options.pickerPositionOptions));
	}
	
});

// A few utility static items:
$extend(DatePicker, {
	
	ZERO_DATE: new Date(Date.parse('1/1/2000 12:00 am')),
	
	calendarDateToInt: function(theDate) {
		return DatePicker.ZERO_DATE.diff(new Date(new Date(theDate).setHours(12))); 
	},

	calendarIntToDate: function(theInt) {
		return DatePicker.ZERO_DATE.clone().increment('day', theInt).clearTime(); 
	}
});

