// SpryCalendar.js - version 1.0 (Based on Spry Pre-Release 1.6)
//
// Copyright (c) 2007. Ronald Ferguson.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of Ronald Ferguson or Adobe Systems Incorporated nor the names
//     of its contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

var Spry;
if (!Spry) Spry = {};
if (!Spry.Widget) Spry.Widget = {};

Spry.Widget.Calendar = function(element, opts)
{
	var defaultDate = new Date();
	
	this.element = this.getElement(element);
	this.month = defaultDate.getMonth();
	this.year = defaultDate.getFullYear();
	this.calendarMonths = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
	this.enableKeyboardNavigation = true;
	this.hasFocus = null;
	this.numDays = null;
	this.headerRowId = null;
	this.selectedDate = null;
	this.selectedElement = null;
	this.focusedCell = null;
	this.callbackFunction = null;
	
	// Navigation key codes
	this.previousDayKeyCode = Spry.Widget.Calendar.KEY_LEFT;
	this.nextDayKeyCode = Spry.Widget.Calendar.KEY_RIGHT;
	this.spaceKeyCode = Spry.Widget.Calendar.KEY_SPACE;
	
	// CSS
	this.calendarClass = "Calendar";
	this.foreignDayClass = "CalendarForeignDay";
	this.monthHeaderClass = "CalendarMonthHeader";
	this.navOverClass = "CalendarNavOver";
	this.dayHeaderClass = "CalendarDayHeader";
	this.dayClass = "CalendarDayCell";
	this.dayOverClass = "CalendarDayCellOver";
	this.todayClass = "CalendarTodayCell";
	this.selectedClass = "CalendarSelectedCell";
	this.focusedClass = "CalendarFocused";
	this.focusedCellClass = "CalendarFocusedDay";
	this.bookedDateClass = "CalendarBookedDay";
	// My edit to show and hide stuff
	this.showDates = true;
	this.showMonths = true;
	this.showNav = true;
	this.showWeekOnly = false;
	this.bookedDatesObj = null;
	this.setOptions(this, opts, true);
	
	this.init();
}

Spry.Widget.Calendar.KEY_LEFT = 37;
Spry.Widget.Calendar.KEY_RIGHT = 39;
Spry.Widget.Calendar.KEY_SPACE = 32;

Spry.Widget.Calendar.prototype.getElement = function(ele)
{
	if (ele && typeof ele == "string")
		return document.getElementById(ele);
	return ele;
};

Spry.Widget.Calendar.prototype.addClassName = function(ele, className)
{
	if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) != -1))
		return;
	ele.className += (ele.className ? " " : "") + className;
};

Spry.Widget.Calendar.prototype.removeClassName = function(ele, className)
{
	if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) == -1))
		return;
	ele.className = ele.className.replace(new RegExp("\\s*\\b" + className + "\\b", "g"), "");
};

Spry.Widget.Calendar.prototype.hasClassName = function(ele, className)
{
	if (!ele || !className || !ele.className || ele.className.search(new RegExp("\\b" + className + "\\b")) == -1)
		return false;
	return true;
};

Spry.Widget.Calendar.prototype.setOptions = function(obj, optionsObj, ignoreUndefinedProps)
{
	if (!optionsObj)
		return;
	for (var optionName in optionsObj)
	{
		if(optionName == "hasFocus" || optionName == "numDays" || optionName == "selectedElement" || optionName == "element" || optionName == "focusedCell")
			continue;
		if (ignoreUndefinedProps && optionsObj[optionName] == undefined)
			continue;
		obj[optionName] = optionsObj[optionName];
	}
};

Spry.Widget.Calendar.prototype.addEventListener = function(element, eventType, handler, capture)
{
	try
	{
		if (element.addEventListener)
			element.addEventListener(eventType, handler, capture);
		else if (element.attachEvent)
			element.attachEvent("on" + eventType, handler);
	}
	catch (err) {}
};

Spry.Widget.Calendar.prototype.removeEventListener = function(element, eventType, handler, capture)
{
	try
	{
		if (element.removeEventListener)
			element.removeEventListener(eventType, handler, capture);
		else if (element.detachEvent)
			element.detachEvent("on" + eventType, handler);
	}
	catch (err) {}
};

Spry.Widget.Calendar.prototype.attachBehaviors = function()
{
	this.addClassName(this.element, this.calendarClass)
	var navigators = this.getNavigators();
	var cells = this.getDayCells();
	// Add the event listeners for the monthly navigation.
	for(var i = 0; i < navigators.length; i++)
	{
		this.addNavigationListener(navigators[i]);
	}
	// Add the event listeners for the day cells.
	for(var i = 0; i < cells.length; i++)
	{
		this.addDateListener(cells[i]);
	}
	if (this.enableKeyboardNavigation) {
		var self = this;
		this.addEventListener(this.element, "focus", function(e) { return self.onFocus(e); }, false);
		this.addEventListener(this.element, "blur", function(e) { return self.onBlur(e); }, false);
		this.addEventListener(this.element, "keydown", function(e) { return self.onKeyDown(e); }, false);
	}
	
};

Spry.Widget.Calendar.prototype.focus = function()
{
	if (this.element && this.element.focus)
		this.element.focus();
};

Spry.Widget.Calendar.prototype.blur = function()
{
	if (this.element && this.element.blur)
		this.element.blur();
};

Spry.Widget.Calendar.prototype.onFocus = function(e)
{
	this.hasFocus = true;
	this.focus();
	this.addClassName(this.element, this.focusedClass);
	return false;
};

Spry.Widget.Calendar.prototype.onBlur = function(e)
{
	this.hasFocus = false;
	this.blur();
	this.removeClassName(this.element, this.focusedClass);
	return false;
	
};

Spry.Widget.Calendar.prototype.init = function()
{
	this.setCalendarDate(this.year, this.month);
	if(this.callbackFunction && typeof(this.callbackFunction) != 'function')
		this.callbackFunction = null;
	this.attachBehaviors();	
	
}

Spry.Widget.Calendar.prototype.getDayCells = function()
{
	var tds = this.element.getElementsByTagName("td");
	var cells = new Array();
	for(var i = 0; i < tds.length; i++)
	{
		if (!this.hasClassName(tds[i], this.dayClass)) continue;
		cells.push(tds[i]);
	}
	return cells;
}

Spry.Widget.Calendar.prototype.setCalendarDate = function(yr, mo)
{	
	var tmpDate = new Date();
	
	if(yr == undefined)
		yr = tmpDate.getFullYear();
	if(mo == undefined || mo < 0 || mo > 11)
		mo = tmpDate.getMonth();
	
		
	tmpDate.setDate(1);
	tmpDate.setUTCMonth(mo);
	tmpDate.setUTCFullYear(yr);
	
	this.numDays = tmpDate.getDaysInMonth();
	this.month = tmpDate.getUTCMonth();
	this.year = tmpDate.getUTCFullYear();
	this.populate();
	tmpDate = null;
	this.removeClassName(this.getFocusedElement(), this.focusedCellClass);
	this.focusedCell = null;
}

Spry.Widget.Calendar.prototype.populate = function()
{
	// Set the month/year text in the title row
	var titleRow = this.getHeaderRow();
	var titleCell = titleRow[1].innerHTML = (this.calendarMonths[this.month] + " " + this.year);

	
	// Get the cells used to hold the dates
	var cells = this.getDayCells();
	var filled = 0;
	var tmpDate = new Date(this.year,this.month,1);
	var today = new Date();
	var datesBooked = this.bookedDatesObj == null? false : true; // if dates booked we need to highlight them
	// If the first day of the month does not fall on Sunday, we need to add the foreign day class to the first cells.
	if(tmpDate.getUTCDay() != 0)
	{
		//var lastMonthDays = today.getDaysInMonth(tmpDate.getUTCFullYear(),tmpDate.getUTCMonth()-1)+ 1; // populate cell with last dates so get number of days
		for(var i = 0; i < tmpDate.getUTCDay(); i++)
		{
			var ele = this.getElement(cells[i].id);
			this.addClassName(ele, this.foreignDayClass);
			// Remove all class names (except the default).
			this.removeClassName(ele, this.todayClass);
			this.removeClassName(ele, this.selectedClass);
			this.removeClassName(ele, this.focusedCellClass);
			this.removeClassName(ele, this.bookedDateClass);
			ele.innerHTML = '&nbsp;' ;// lastMonthDays- tmpDate.getUTCDay() + i; // populate cells with last months dates
			filled++;
		}
	}
	// Set the date text for each cell in the month.
	for(var i = 0; i < this.numDays; i++)
	{
		var idx = i+1;
		var el = cells[filled];
		if(el.parentNode.style.display == 'none') el.parentNode.style.display = '';
		el.innerHTML = idx;
		// Remove the foreign day class if it exists for the cell.
		this.removeClassName(el, this.foreignDayClass);
		
		// if there are days booked highlight them
		if(datesBooked){
			if(this.isDateBooked(this.year,(this.month+1),idx))
				this.addClassName(el, this.bookedDateClass);
			else this.removeClassName(el, this.bookedDateClass);
		}
		// Highlight today's date
		if(today.getFullYear() == tmpDate.getUTCFullYear() && today.getMonth() == tmpDate.getUTCMonth() && today.getDate() == idx) {
			this.addClassName(el, this.todayClass);
		} else {
			this.removeClassName(el, this.todayClass);
		}
		if(this.selectedDate && (this.selectedDate.getUTCFullYear() == tmpDate.getUTCFullYear() && this.selectedDate.getUTCMonth() == tmpDate.getUTCMonth() && idx == this.selectedDate.getUTCDate())){
			this.addClassName(el, this.selectedClass);
			// Since it's possible to pre-populate the selected date, we may not have a selected element so we'll need to
			// set it so the class can be cleared if need be.
			if(!this.selectedElement)
				this.selectedElement = el;
		} else {
			this.removeClassName(el, this.selectedClass);
		}
		tmpDate.setUTCDate(idx);
		filled++;
	}
	var lastDayCell = (filled-1);
	
	// Reset our tmpDate to the last day of the currently loaded month/year.
	tmpDate.setUTCMonth(this.month);
	tmpDate.setUTCDate(this.numDays);
	tmpDate.setUTCFullYear(this.year);
	
	// Do we need to add foreign day class to any remaining cells?
	if(filled < cells.length){
		var nextMonth = 0
		while(filled < cells.length){
			nextMonth++;
			var ele = this.getElement(cells[filled].id);
			this.addClassName(ele, this.foreignDayClass);
			// Remove all class names (except the default).
			this.removeClassName(ele, this.todayClass);
			this.removeClassName(ele, this.selectedClass);
			this.removeClassName(ele, this.focusedCellClass);
			ele.innerHTML = '&nbsp;'; //nextMonth;
			filled++;
		}
	}
	
	// Get the tr tag of the last table cell tag that we filled.
	if(u = cells[lastDayCell]){
		if(u.parentNode && u.parentNode.nextSibling)
			var e = cells[lastDayCell].parentNode.nextSibling.nextSibling;
	}
	// Do we need to hide the last table row(s)? We want to do this only if no day cells are being used.
	// Use DOM functions to get the last table row tag.
	var tRows = this.element.getElementsByTagName("tr");
	idx = null;
	for(var i = 0; i < tRows.length; i++)
	{
		if(!e || e && e.id != tRows[i].id && idx == null) continue;
		tRows[i].style.display = 'none';
		idx = i;
	}
	
	// set date display
	this.displayDates(this.showDates);
	// set title nav display
	this.displayTitleNavs(this.showNav);
	this.displayWeekOnly(this.showWeekOnly);
}

Spry.Widget.Calendar.prototype.getNavigators = function()
{
	// Set the month/year text in the title row
	var titleRow = this.getHeaderRow();
	var navigators = new Array();
	for(var i = 0; i < titleRow.length; i++)
	{
		// The navigators should be the first and last cells of the header row, so skip the one with the month/year text.
		if(i == 1) continue;
		navigators.push(titleRow[i]);
	}
	return navigators;
}

Spry.Widget.Calendar.prototype.addDateListener = function(ele)
{
	var self = this;
	this.addEventListener(ele, 'mouseover', function(e) { return self.onDateMouseOver(e, ele); }, false);
	this.addEventListener(ele, 'mouseout', function(e) { return self.onDateMouseOut(e, ele); }, false);
	this.addEventListener(ele, 'click', function(e) { return self.onDateClick(e, ele); }, false);
}

Spry.Widget.Calendar.prototype.onDateMouseOver = function(evt, ele)
{
	if(this.hasClassName(ele, this.foreignDayClass))
		return true;
	this.addClassName(ele, this.dayOverClass);
	return false;
}

Spry.Widget.Calendar.prototype.onDateMouseOut = function(evt, ele)
{
	if(this.hasClassName(ele, this.foreignDayClass))
		return true;
	this.removeClassName(ele, this.dayOverClass);
	return false;
}

Spry.Widget.Calendar.prototype.onDateClick = function(evt, ele)
{	
	
	if (evt.preventDefault) evt.preventDefault();
	else evt.returnValue = false;
	if (evt.stopPropagation) evt.stopPropagation();
	else evt.cancelBubble = true;
	
	var day = ele.innerHTML; // get the day
	var m = this.month;
	var today = (new Date()).getDate();
	// once its a foreign class you know we are dealing with a month before or after.
	if(this.hasClassName(ele, this.foreignDayClass)){
		return true;
	/*	var cells = this.getDayCells(); // get cells
		if(day < 8) // cant be more than 7 days
			m = this.month + 1;
		else if(day > 21) // cant be less than 21 days
			m = this.month - 1;	*/
	}
	var tmpDate = new Date(this.year, parseInt(m), parseInt(day));
	// If this date is already selected, do nothing.
	if(this.selectedDate && (this.selectedDate.valueOf() == tmpDate.valueOf()))
		return true;
	this.removeClassName(this.focusedCell, this.focusedCellClass);
	this.removeClassName(this.selectedElement, this.selectedClass);
	
	this.focusedCell = ele;
	
	this.addClassName(ele, this.focusedCellClass);
	this.addClassName(ele, this.selectedClass);
	
	this.setSelectedDate(tmpDate);
	this.selectedElement = ele;
	
	if(this.callbackFunction){
		this.callbackFunction(this.selectedDate.valueOf());
	}
	
	return false;
}

Spry.Widget.Calendar.prototype.addNavigationListener = function(ele)
{
	var self = this;
	this.addEventListener(ele, "mouseover", function(e) { return self.onNavMouseOver(e, ele); }, false);
	this.addEventListener(ele, "mouseout", function(e) { return self.onNavMouseOut(e, ele); }, false);
	this.addEventListener(ele, "click", function(e) { return self.onNavClick(e, ele); }, false);
	if (this.enablekeyboardnavigation) {
		
	}
}

Spry.Widget.Calendar.prototype.onNavMouseOver = function(evt, ele)
{
	this.addClassName(ele, this.navOverClass);
	return false;
}

Spry.Widget.Calendar.prototype.onNavMouseOut = function(evt, ele)
{
	this.removeClassName(ele, this.navOverClass);
	return false;
}

Spry.Widget.Calendar.prototype.onNavClick = function(evt, ele)
{
	if (evt.preventDefault) evt.preventDefault();
	else evt.returnValue = false;
	if (evt.stopPropagation) evt.stopPropagation();
	else evt.cancelBubble = true;
	
	var navigators = this.getNavigators();
	var tmpDate = new Date(this.year, this.month, 1);
	for(var i = 0; i < navigators.length; i++)
	{
		if(ele == navigators[i]){
			switch(i)
			{
				case 1:
					tmpDate.setUTCMonth((this.month+1));
					break;
				default:
					tmpDate.setUTCMonth((this.month-1));
					break;
			}
			break;
		}
	}
	
	this.removeClassName(this.focusedCell, this.focusedCellClass);
	this.setCalendarDate(tmpDate.getUTCFullYear(), tmpDate.getUTCMonth());

	return false;
}

Spry.Widget.Calendar.prototype.onKeyDown = function(e)
{
	if(!this.hasFocus || !this.enableKeyboardNavigation)
		return true;
		
	var key = e.keyCode;
	var cells = this.getDayCells();
	
	if(key == this.previousDayKeyCode){
		var keyString = "Previous day";
		if (!this.focusedCell) {
			for(var i = 0; i < cells.length; i++)
			{
				if(this.hasClassName(cells[i], this.foreignDayClass)) {
					continue;
				}
				this.focusedCell = cells[i];
				break;
			}
		} else {
			this.removeClassName(this.focusedCell, this.focusedCellClass);
			if(parseInt(this.focusedCell.innerHTML) == 1) {
				var mo = this.month == 0 ? 11 : (this.month-1);
				var yr = mo == 11 ? (this.year-1) : this.year;
				this.setCalendarDate(yr, mo);
				for(var i = (cells.length-1); i > 0; i--)
				{
					if(cells[i].innerHTML != this.numDays.toString()){
						continue;
					}
					this.focusedCell = cells[i];
					break;
				}
			} else if(parseInt(this.focusedCell.innerHTML) == this.numDays) {
				for(var i = (cells.length-1); i > 0; i-- ) {
					if(this.hasClassName(cells[i], this.foreignDayClass)){
						continue;
					}
					if(this.focusedCell == cells[i]){
						this.focusedCell = cells[(i-1)];
						break;
					}
				}
			}else{
				for(var i = (cells.length-1); i > 0; i--)
				{
					if (this.hasClassName(cells[i], this.foreignDayClass)) {
						continue;
					}
					if (this.focusedCell == cells[i]) {
						this.focusedCell = cells[(i-1)];
						break;
					}
				}
			}
			
		
		}
	}else if(key == this.nextDayKeyCode){
		var keyString = "Next day";
		if (!this.focusedCell) {
			for(var i = 0; i < cells.length; i++)
			{
				if(this.hasClassName(cells[i], this.foreignDayClass)) {
					continue;
				}
				this.focusedCell = cells[i];
				break;
			}
		} else {
			this.removeClassName(this.focusedCell, this.focusedCellClass);
			for(var i = 0; i < cells.length; i++)
			{
				if (cells[(i-1)] == this.focusedCell) {
					if(parseInt(this.focusedCell.innerHTML) == this.numDays) {
						var mo = this.month == 11 ? 0 : (this.month+1);
						var yr = mo == 0 ? (this.year+1) : this.year;
						this.setCalendarDate(yr, mo);
						i = 0;
						while(this.hasClassName(cells[i], this.foreignDayClass))
						{
							i++;
						}
						this.focusedCell = cells[i];
						break;
					}
					this.focusedCell = cells[i];
					break;
				}
			}
		}
	}else if(key == this.spaceKeyCode){
		if(this.focusedCell) {
			var tmpDate = new Date(this.year, this.month, parseInt(this.focusedCell.innerHTML));
			if(this.selectedElement){
				this.clearSelection();
			}
			var ele = this.getElement(this.focusedCell);
			this.addClassName(ele, this.selectedClass);
			this.setSelectedDate(tmpDate);
			this.selectedElement = ele;
			if(this.callbackFunction){
				this.callbackFunction(this.selectedDate);
			}
			return false;
		} else {
			return true;
		}
	}else{
		return true;
	}
	
	this.addClassName(this.focusedCell, this.focusedCellClass);
	return false;
}

Spry.Widget.Calendar.prototype.clearSelection = function()
{
	this.removeClassName(this.selectedElement, this.selectedClass);
	this.selectedDate = null;
	this.selectedElement = null;
	
}

Spry.Widget.Calendar.prototype.setSelectedDate = function(val)
{
	if(this.selectedDate && this.selectedDate == val)
		return;
		
	if(typeof(val) == 'string'){
		this.selectedDate = new Date(val);
	} else {
		this.selectedDate = val;
	}
}

Spry.Widget.Calendar.prototype.getSelectedDate = function()
{
	return this.selectedDate;
}

Spry.Widget.Calendar.prototype.getSelectedElement = function()
{
	return this.selectedElement;
}

Spry.Widget.Calendar.prototype.getFocusedElement = function()
{
	return this.focusedCell;
}

Spry.Widget.Calendar.prototype.getHeaderRow = function()
{
	if(!this.headerRowId){
		var headerRow = this.element.getElementsByTagName("tr")[0].getElementsByTagName("td");
	} else {
		var headerRow = document.getElementById(this.headerRowId).getElementsByTagName("td");
	}
	return headerRow;
}

// my toggle show the dates row
Spry.Widget.Calendar.prototype.displayDates = function(s){
	var dayRow = document.getElementById('dayHeader');
	if(this.hasClassName(dayRow, 'hide') && !s)
		return;
	if(s)
		this.removeClassName(dayRow, 'hide');
	else this.addClassName(dayRow, 'hide');
}

// toggle show navigation
Spry.Widget.Calendar.prototype.displayTitleNavs = function(s){
	var titleRow = document.getElementById('titleRow');
	if(this.hasClassName(titleRow, 'hide') && !s)
		return;
	if(s)
		this.removeClassName(titleRow, 'hide');
	else this.addClassName(titleRow, 'hide');
}

// toggle show week only
Spry.Widget.Calendar.prototype.displayWeekOnly = function(s){
	var week = (new Date()).getWeekOfMonth();
	
	for(i = 1; i<6;i++){
		var weekRow = document.getElementById('c1_Week'+i);
		if(s && (week != i))
			this.addClassName(weekRow, 'hide');
	}
}

// this is to set the bookedDatesObj with and object of a date column and array of dates
Spry.Widget.Calendar.prototype.setHighlightedDates = function (n,dataObj){
	//alert('hi');
	this.bookedDatesObj = dataObj;
	this.populate();
}
// search for booked date
Spry.Widget.Calendar.prototype.isDateBooked = function(y,m,d){
	var date = y+'-'+(m < 10? '0' : '')+m+'-'+(d < 10? '0' : '')+d ;
	//alert(date);
	var rows = this.bookedDatesObj.bookedDates;
	var column = this.bookedDatesObj.dateColumn;
	for (var i = 0; i < rows.length; i++)
	{
	  if ((rows[i][column]).indexOf(date) >= 0) 
		return true;
	}
}
