"use strict";

var CALENDAR_DAY_NAMES="Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday";
var CALENDAR_MONTH_NAMES="January,February,March,April,May,June,July,August,September,October,November,December";
var CALENDAR_SHORT_DAY_NAMES="S,M,T,W,T,F,S,S";
var CALENDAR_SHORT_MONTH_NAMES="Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec";
var CALENDAR_TODAY="Today";

function UTCCalendar(dateSelected, mxAnchor, mxInput, dateMinimum, dateMaximum, bNearFutureWeekDays)
{
	if(typeof bNearFutureWeekDays=="boolean")
		this.bNearFutureWeekDays=bNearFutureWeekDays;
	
	this.dateSelected=(typeof dateSelected=="object" && dateSelected!==null)?new Date(dateSelected.getTime()):new Date();
	this.dateInView=new Date(Date.UTC(this.dateSelected.getUTCFullYear(), this.dateSelected.getUTCMonth(), 1));
	
	this.dateMinimum=dateMinimum?new Date(dateMinimum.getTime()):new Date(Date.UTC(2002, 0, 1));
	this.dateMaximum=dateMaximum?new Date(dateMaximum.getTime()):new Date(Date.UTC(2037, 11, 31, 23, 59, 59, 999));
	this.dateMinimum=new Date(Date.UTC(this.dateMinimum.getUTCFullYear(), this.dateMinimum.getUTCMonth(), this.dateMinimum.getUTCDate()));
	this.dateMaximum=new Date(Date.UTC(this.dateMaximum.getUTCFullYear(), this.dateMaximum.getUTCMonth(), this.dateMaximum.getUTCDate(), 23, 59, 59, 999));

	this._elParentAnchor=(typeof mxAnchor=="string")?document.getElementById(mxAnchor):mxAnchor;
	this._elInputUnixTimestampSeconds=(typeof mxInput=="string")?document.getElementById(mxInput):mxInput;
	
	
	this._arrHandlerFunctions=[];
	
	
	this._elTableDays=document.createElement("table");
	this._elTableDays.className="UTCCalendar_tableDays";
	
	
	this._elDivHeaderContainer=document.createElement("div");
	this._elDivHeaderContainer.className="UTCCalendar_headerContainer";
	
	this._elDivButtonPrevious=document.createElement("div");
	this._elDivButtonNext=document.createElement("div");
	
	this._elDivArrowLeft=document.createElement("div");
	this._elDivArrowLeft.className="UTCCalendar_arrowLeft";
	this._elDivButtonPrevious.appendChild(this._elDivArrowLeft);

	this._elDivArrowRight=document.createElement("div");
	this._elDivArrowRight.className="UTCCalendar_arrowRight";
	this._elDivButtonNext.appendChild(this._elDivArrowRight);
	
	this._elSelectYear=document.createElement("select");
	for(var nYear=this.dateMinimum.getUTCFullYear(); nYear<=this.dateMaximum.getUTCFullYear(); nYear++)
		this._elSelectYear.options[this._elSelectYear.options.length]=new Option(nYear, nYear);
	
	this._elSelectMonth=document.createElement("select");
	this._arrShortMonthNames=CALENDAR_SHORT_MONTH_NAMES.split(",");
	
	this._elDivHeaderContainer.appendChild(this._elDivButtonPrevious);
	this._elDivHeaderContainer.appendChild(this._elSelectYear);
	this._elDivHeaderContainer.appendChild(this._elSelectMonth);
	this._elDivHeaderContainer.appendChild(this._elDivButtonNext);
	
	
	this._elDivCalendar=document.createElement("div");
	this._elDivCalendar.className="UTCCalendar";
	this._elDivCalendar.style.position="absolute";
	this._elDivCalendar.appendChild(this._elDivHeaderContainer);
	this._elDivCalendar.appendChild(this._elTableDays);
	
	
	var _self=this;
	
	
	if(["iPad", "iPhone", "iPod"].indexOf(navigator.platform)>-1)	
	{
		this._elNativeDatePicker=document.createElement("input");
		this._elNativeDatePicker.type="date";
		this._elNativeDatePicker.style.position="absolute";
		this._elNativeDatePicker.min=this.dateMinimum.toISOString().substr(0, 10);
		this._elNativeDatePicker.max=this.dateMaximum.toISOString().substr(0, 10);
		
		this._elNativeDatePicker.addEventListener(
			"blur",
			function(){
				_self.selectTime(Date.UTC(_self._elNativeDatePicker.value.substr(0, 4), parseInt(_self._elNativeDatePicker.value.substr(5, 2))-1, _self._elNativeDatePicker.value.substr(8, 2)));
			},
			false
		);
	}
	
	
	this._elSelectYear.addEventListener(
		"change", 
		function(){
			_self._onChangeYearSelector();
		},
		false
	);
	
	this._elSelectMonth.addEventListener(
		"change", 
		function(){
			_self._onChangeMonthSelector();
		},
		false
	);
	
	this._elSelectYear.addEventListener(
		"keyup", 
		function(){
			_self._onChangeYearSelector();
		},
		false
	);
	
	this._elSelectMonth.addEventListener(
		"keyup", 
		function(){
			_self._onChangeMonthSelector();
		},
		false
	);
	
	this._elDivButtonPrevious.addEventListener(
		"click", 
		function(){
			_self.previousMonth();
		},
		false
	);
	
	this._elDivButtonNext.addEventListener(
		"click", 
		function(){
			_self.nextMonth();
		},
		false
	);
	
	document.addEventListener(
		"click", 
		function(event){
			if(!_self._elDivCalendar.parentNode)
				return;
			
			for(var element=event.target; element; element=element.parentNode)
				if(element===_self._elDivCalendar)
					return;
			
			_self.hide();
		}
	);
	
	if(typeof dateSelected=="string")
		var savedInputValue=this._elInputUnixTimestampSeconds.value;
	
	this.selectTime(this.dateSelected.getTime());
	
	if(typeof dateSelected=="string")
	{
		this._elParentAnchor.innerHTML=dateSelected;
		this._elInputUnixTimestampSeconds.value=savedInputValue;
	}


	if(this._elParentAnchor)
	{
		this._elParentAnchor.addEventListener(
			"click",
			function(){
				_self.show();

				return false;
			},
			false
		);
	}
}


UTCCalendar.prototype={
	
	/**
	* This property affects a relative date (Tomorrow, Today, etc.) 
	* and as such it does not correspond to UTC but to the browsers timezone.
	*
	* Do not use if you need UTC accuracy for relative dates.
	*
	* If true, the anchor is updated with Sunday, Monday, Today, etc. 
	* if the selected date falls withing the next 6 days.
	*/
	bNearFutureWeekDays: false,
	
	
	/**
	* 
	*/
	dateSelected: null,
	
	
	/**
	* 
	*/
	dateInView: null,
	
	
	/**
	* 
	*/
	dateMinimum: null,
	
	
	/**
	*
	*/
	dateMaximum: null,
	
	
	/**
	* @protected
	*/
	_elParentAnchor: null,
	
	
	/**
	* @protected
	*/
	_elInputUnixTimestampSeconds: null,
	
	
	/**
	* @protected
	*/
	_elDivCalendar: null,
	
	
	/**
	* @protected
	*/
	_elNativeDatePicker: null,
	
	
	/**
	* @protected
	*/
	_arrHandlerFunctions: null,
	
	
	/**
	* @protected
	*/
	_elTableDays: null,
	
	
	/**
	* @protected
	*/
	_elDivHeaderContainer: null,
	
	
	/**
	* @protected
	*/
	_elDivButtonPrevious: null,
	
	
	/**
	* @protected
	*/
	_elDivButtonNext: null,
	
	
	/**
	* @protected
	*/
	_elDivArrowRight: null,
	
	
	/**
	* @protected
	*/
	_elSelectYear: null,
	
	
	/**
	* @protected
	*/
	_elSelectMonth: null,
	
	
	/**
	* @protected
	*/
	_arrShortMonthNames: null,
	
	
	/**
	* @public
	*/
	selectTime: function(nUnixtimeMilliseconds)
	{
		nUnixtimeMilliseconds=Math.min(nUnixtimeMilliseconds, this.dateMaximum.getTime());
		nUnixtimeMilliseconds=Math.max(nUnixtimeMilliseconds, this.dateMinimum.getTime());
		
		this.dateSelected=new Date(nUnixtimeMilliseconds);
		this.dateInView=new Date(Date.UTC(this.dateSelected.getUTCFullYear(), this.dateSelected.getUTCMonth(), 1));
		
		this._tableDaysPopulate();
		this._updateMonthYearSelectors();
		this._refreshButtonStates();
		
		this.hide();
		
		for(var i=this._arrHandlerFunctions.length-1; i>=0; i--)
			this._arrHandlerFunctions[i](nUnixtimeMilliseconds);
		
		if(this._elParentAnchor)
		{
			var dateNow=new Date();
			
			if(this.bNearFutureWeekDays)
				var nDaysDifference=Math.floor(nUnixtimeMilliseconds/(86400*1000)) - Math.floor(new Date(Date.UTC(dateNow.getFullYear(), dateNow.getMonth(), dateNow.getDate())).getTime()/(86400*1000));
			
			if(this.bNearFutureWeekDays && nDaysDifference>=0 && nDaysDifference<=6)
			{
				if(nDaysDifference===0)
					this._elParentAnchor.innerHTML=CALENDAR_TODAY;
				else if(nDaysDifference===1)
					this._elParentAnchor.innerHTML=CALENDAR_TOMORROW;
				else
				{
					var arrCalendarDays=CALENDAR_DAY_NAMES.split(",");
					this._elParentAnchor.innerHTML=arrCalendarDays[new Date(nUnixtimeMilliseconds).getDay()];
				}
			}
			else
				this._elParentAnchor.innerHTML=new Date(nUnixtimeMilliseconds).toISOString().substr(0, 10);
		}
		if(this._elInputUnixTimestampSeconds)
			this._elInputUnixTimestampSeconds.value=parseInt(nUnixtimeMilliseconds/1000);
	},
	
	
	/**
	* @public
	*/
	addSelectTimeHandler: function(fnHandler)
	{
		this._arrHandlerFunctions.push(fnHandler);
	},
	
	
	/**
	* @public
	*/
	removeSelectTimeHandler: function(fnHandler)
	{
		for(var i=this._arrHandlerFunctions.length-1; i>=0; i--)
			if(this._arrHandlerFunctions[i]===fnHandler)
				this._arrHandlerFunctions.splice(i, 1);
	},
	
	
	/**
	* @public
	*/
	show: function()
	{
		if((this._elNativeDatePicker && this._elNativeDatePicker.parentNode) || this._elDivCalendar.parentNode)
			return;
		
		var elParent=this._elParentAnchor;
		
		var offsetLeft=elParent.offsetLeft;
		var offsetTop=elParent.offsetTop;
		while(elParent.offsetParent)
		{
			elParent=elParent.offsetParent;
			offsetLeft+=elParent.offsetLeft;
			offsetTop+=elParent.offsetTop;
		}
		
		if(this._elNativeDatePicker)
		{
			this._elNativeDatePicker.value=this.dateSelected.toISOString().substr(0, 10);
			
			this._elNativeDatePicker.style.left=offsetLeft+"px";
			this._elNativeDatePicker.style.top=offsetTop+"px";
			
			document.body.appendChild(this._elNativeDatePicker);
			this._elNativeDatePicker.focus();
			
			this._elParentAnchor.style.visibility="hidden";
		}
		else
		{
			this._elDivCalendar.style.left=offsetLeft+"px";
			this._elDivCalendar.style.top=offsetTop+"px";
			
			setTimeout(
				(function(_self){
					return function(){
						document.body.appendChild(_self._elDivCalendar);
					};
				})(this), 
				20
			);
		}
	},
	
	
	/**
	* @public
	*/
	hide: function()
	{
		if(this._elNativeDatePicker && this._elNativeDatePicker.parentNode)
		{
			this._elNativeDatePicker.parentNode.removeChild(this._elNativeDatePicker);
			
			this._elParentAnchor.style.visibility="visible";
		}
		
		if(this._elDivCalendar.parentNode)
			this._elDivCalendar.parentNode.removeChild(this._elDivCalendar);
			
		this.dateInView=new Date(Date.UTC(this.dateSelected.getUTCFullYear(), this.dateSelected.getUTCMonth(), 1));
		
		this._tableDaysPopulate();
		this._updateMonthYearSelectors();
	},
	
	
	/**
	* @public
	*/
	nextMonth: function()
	{
		if(this.dateInView.getUTCFullYear()*12+this.dateInView.getUTCMonth() >= this.dateMaximum.getUTCFullYear()*12+this.dateMaximum.getUTCMonth())
			return;
		
		this.dateInView.setUTCMonth(this.dateInView.getUTCMonth()+1);
		this._tableDaysPopulate();
		this._updateMonthYearSelectors();
	},
	
	
	/**
	* @public
	*/
	previousMonth: function()
	{
		if(this.dateInView.getUTCFullYear()*12+this.dateInView.getUTCMonth() <= this.dateMinimum.getUTCFullYear()*12+this.dateMinimum.getUTCMonth())
			return;
		
		this.dateInView.setUTCMonth(this.dateInView.getUTCMonth()-1);
		this._tableDaysPopulate();
		this._updateMonthYearSelectors();
	},
	
	
	/**
	* @protected
	*/
	_refreshButtonStates: function()
	{
		this._elDivButtonPrevious.className="UTCCalendar_buttonArrow UTCCalendar_buttonArrowPrevious";
		this._elDivButtonNext.className="UTCCalendar_buttonArrow UTCCalendar_buttonArrowNext";
		
		if(this.dateInView.getUTCFullYear()*12+this.dateInView.getUTCMonth() <= this.dateMinimum.getUTCFullYear()*12+this.dateMinimum.getUTCMonth())
			this._elDivButtonPrevious.className="UTCCalendar_buttonArrow_disabled UTCCalendar_buttonArrowPrevious";
		
		if(this.dateInView.getUTCFullYear()*12+this.dateInView.getUTCMonth() >= this.dateMaximum.getUTCFullYear()*12+this.dateMaximum.getUTCMonth())
			this._elDivButtonNext.className="UTCCalendar_buttonArrow_disabled UTCCalendar_buttonArrowNext";
	},
	
	
	/**
	* @protected
	*/
	_tableDaysPopulate: function()
	{
		while(this._elTableDays.rows.length)
			this._elTableDays.deleteRow(-1);
		
		var objDateDisplayMonth=new Date(Date.UTC(this.dateInView.getUTCFullYear(), this.dateInView.getUTCMonth(), 1));
		var objDateIncrementable=new Date(Date.UTC(this.dateInView.getUTCFullYear(), this.dateInView.getUTCMonth(), 1));
		
		var objDateToday=new Date();
		var nUnixtimeToday=Date.UTC(objDateToday.getUTCFullYear(), objDateToday.getUTCMonth(), objDateToday.getUTCDate());
		var nUnixtimeSelected=Date.UTC(this.dateSelected.getUTCFullYear(), this.dateSelected.getUTCMonth(), this.dateSelected.getUTCDate());
		
		//ISO 8601 says Monday is the first day of the week, Sunday the last. JavaScript says 0=Sunday. UTCCalendar follows the ISO standard (1=Monday, 7=Sunday).
		var nDayOfWeek=objDateDisplayMonth.getUTCDay() || 7;
		objDateIncrementable.setTime(objDateIncrementable.getTime()-86400*1000*(nDayOfWeek-1));

		var arrCellClasses=["UTCCalendar_previousMonth", "UTCCalendar_currentMonth", "UTCCalendar_nextMonth"];
		var strCurrentCellStyle=arrCellClasses.shift();
		
		
		var elCell, elDiv;
		var arrCalendarShortDays=CALENDAR_SHORT_DAY_NAMES.split(",");
		var arrCalendarDays=CALENDAR_DAY_NAMES.split(",");
		var elRow=this._elTableDays.insertRow(-1);
		for(nDayOfWeek=1; nDayOfWeek<=7; nDayOfWeek++)
		{
			elCell=elRow.insertCell(-1);
			elCell.className="UTCCalendar_th_cell";
			
			elDiv=document.createElement("div");
			elDiv.innerHTML=arrCalendarShortDays[nDayOfWeek];
			elDiv.setAttribute("title", arrCalendarDays[nDayOfWeek]);
			
			elCell.appendChild(elDiv);
		}
		
		
		while(
			objDateIncrementable.getUTCFullYear()*12+objDateIncrementable.getUTCMonth()
			<=objDateDisplayMonth.getUTCFullYear()*12+objDateDisplayMonth.getUTCMonth()
		)
		{
			elRow=this._elTableDays.insertRow(-1);
			for(nDayOfWeek=1; nDayOfWeek<=7; nDayOfWeek++)
			{
				if(objDateIncrementable.getUTCDate()==1)
					strCurrentCellStyle=arrCellClasses.shift();
				
				elCell=elRow.insertCell(-1);
				
				var bEnabled=
					objDateIncrementable.getTime()>=this.dateMinimum.getTime()
					&& objDateIncrementable.getTime()<=this.dateMaximum.getTime()
				;
				if(bEnabled)
				{
					elCell.className=strCurrentCellStyle+" UTCCalendar_date_cell";
					if(nUnixtimeToday==objDateIncrementable.getTime())
						elCell.className+=" UTCCalendar_today";
					if(nUnixtimeSelected==objDateIncrementable.getTime())
						elCell.className+=" UTCCalendar_selected";
				}
				else
					elCell.className=strCurrentCellStyle+" UTCCalendar_date_cell_disabled";
				
				elDiv=document.createElement("div");
				elDiv.innerHTML=objDateIncrementable.getUTCDate();
				
				elCell.appendChild(elDiv);
				
				if(bEnabled)
				{
					elDiv.addEventListener(
						"click", 
						(function(nUnixtimeMilliseconds, _self){
							return function(){
								_self.selectTime(nUnixtimeMilliseconds);
							};
						})(objDateIncrementable.getTime(), this),
						false
					);
				}
				
				objDateIncrementable.setTime(objDateIncrementable.getTime()+86400*1000);
			}
		}
		
		this._refreshButtonStates();
	},
	
	
	/**
	* @protected
	*/
	_refreshMonthsSelect: function()
	{
		while(this._elSelectMonth.length)
			this._elSelectMonth.remove(0);
		
		for(var nMonthIndex=0; nMonthIndex<=11; nMonthIndex++)
		{
			if(
				new Date(Date.UTC(this._elSelectYear.value, nMonthIndex+1, 0))>=this.dateMinimum.getTime()
				&& this._elSelectYear.value*12+nMonthIndex <= this.dateMaximum.getUTCFullYear()*12+this.dateMaximum.getUTCMonth()
			)
			{
				this._elSelectMonth.options[this._elSelectMonth.options.length]=new Option(this._arrShortMonthNames[nMonthIndex], nMonthIndex);
				if(this.dateInView.getUTCMonth()==nMonthIndex)
					this._elSelectMonth.selectedIndex=this._elSelectMonth.options.length-1;
			}
		}

		this._onChangeMonthSelector();
	},
	
	
	/**
	* @protected
	*/
	_onChangeYearSelector: function()
	{
		this.dateInView.setUTCFullYear(this._elSelectYear.value);
		this._refreshMonthsSelect();
		this._tableDaysPopulate();
	},
	
	
	/**
	* @protected
	*/
	_onChangeMonthSelector: function()
	{
		this.dateInView.setUTCMonth(this._elSelectMonth.value);
		
		this._tableDaysPopulate();
	},
	
	
	/**
	* @protected
	*/
	_updateMonthYearSelectors: function()
	{
		var nYearInView=this.dateInView.getUTCFullYear();
		for(var nYearIndex=0;  nYearIndex<this._elSelectYear.options.length; nYearIndex++)
		{
			if(this._elSelectYear.options[nYearIndex].value==nYearInView)
			{
				this._elSelectYear.selectedIndex=nYearIndex;
				this._refreshMonthsSelect();
				
				break;
			}
		}
	}
};