/**
 * This date selector comes from the PRADO framework, links: http://www.pradosoft.com/license/
 * Modified by Wang Yi to remove the use of PRADO framework and only need prototype and scriptaculous to work.
 * Also made it's keyboard event works correctly in Opera.
 * @Wang Yi
 */

var Akyla_Prototype_Widget_DateChoosers = Class.create(Akyla_Prototype_Observer,
{
	/**
	 * Name of the module 
	 */
	moduleName : "Akyla.Widget.DateChoosers",
	/**
	 * Load default preferences for the JSON  Table
	 */
	loadDefaultPreferences : function()
	{
	},
	/**
	 * Processes a JSON component.
	 * @param object component JSON component to process
	 */
	processComponent : function (component)
	{
	},
	domLoaded : function()
	{
		this.processElement(document.body);
		$("pageWrapper").observe("container:change", function (event)
		{
			this.processElement(event.target);
		}.bind(this));
	},
	processElement : function(htmlElement)
	{
		$(htmlElement).select("input.date").each( function(element) 
		{
			var preferences = {
				control : element
			};
			if (!element.hasClassName("dateChooser"))
			{
				new DateChooser(preferences).create();
				element.addClassName("dateChooser");
			}
		});
	}
});

/**
 * Instantiate the DateChooser Widget
 */
Akyla.Widget.DateChoosers = new Akyla_Prototype_Widget_DateChoosers();

var DateChooser = Class.create(
{
	//set the overlay panel preferences
	setpreferences: function(preferences) 
	{
		this.preferences = Object.extend(
		{
			MonthNames : [	"January",	"February",	"March","April", "May",	"June",	"July",	"August", "September", "October", "November", "December"],		
			AbbreviatedMonthNames : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],		
			ShortWeekDayNames : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],		
			Format : "yyyy-MM-dd",		
			FirstDayOfWeek : 1, // 0 for sunday		
			ClassName : "",		
			CalendarStyle : "default",		
			FromYear : 1900, 			
			UpToYear: 2030
		}, preferences);
	},
	initialize : function(preferences)
	{
		this.setpreferences(preferences);
		this.preferences.Format = Akyla.preferences.localization.dateFormat;
		this.control = $(this.preferences.control);
		this.dateSlot = new Array(42);
		this.weekSlot = new Array(6);
		this.minimalDaysInFirstWeek	= 4;
		this.selectedDate = this.newDate();

		//which element to trigger to show the calendar
		if(this.preferences.Trigger)
		{
			this.trigger = $(this.preferences.Trigger) ;
			var triggerEvent = this.preferences.TriggerEvent || "click";
		}
		else
		{
			this.trigger  = this.control;
			var triggerEvent = this.preferences.TriggerEvent || "focus";
		}

		Event.observe(this.trigger, triggerEvent, this.show.bindAsEventListener(this));
		
		// Listen to change event if needed
		if (typeof(this.preferences.OnDateChanged) == "function")
		{
			if(this.control.type == "text")
			{
				Event.observe(this.control, "change", this.onDateChanged.bindAsEventListener(this));
			} 
			else
			{
				var day = DateChooser.getDayListControl(this.control);
				var month = DateChooser.getMonthListControl(this.control);
				var year = DateChooser.getYearListControl(this.control);
				Event.observe (day, "change", this.onDateChanged.bindAsEventListener(this));
				Event.observe (month, "change", this.onDateChanged.bindAsEventListener(this));
				Event.observe (year, "change", this.onDateChanged.bindAsEventListener(this));
			}
		}
	},

	create : function()
	{
		if(typeof(this._calDiv) != "undefined")
			return;

		var div;
		var table;
		var tbody;
		var tr;
		var td;

		// Create the top-level div element
		this._calDiv = new Element("div", { className : "DateChooser_"+this.preferences.CalendarStyle+" "+this.preferences.ClassName}).setStyle({ display : "none", position : "absolute"});

		// header div
		div = new Element("div", {className : "calendarHeader"});
		this._calDiv.insert({bottom : div});

		tbody = new Element("tbody");

		table = new Element("table").setStyle({ cellSpacing : 0}).insert({bottom : tbody});
		div.insert({bottom : table});


		tr = new Element("tr");
		tbody.insert({bottom : tr});

		// Previous Year Button
		var previousYear = new Element("button", {type : "button", className : "previousYearButton button image"}).update(new Element("img", {src : Akyla.preferences.baseUrl+"/image/icons/original/resultset_first.png"}));
		tr.insert({bottom : new Element("td").insert({bottom : previousYear})});

		// Previous Month Button
		var previousMonth = new Element("button", {type : "button", className : "previousMonthButton button image"}).update(new Element("img", {src : Akyla.preferences.baseUrl+"/image/icons/original/resultset_previous.png"}));
		tr.insert({bottom : new Element("td").insert({bottom : previousMonth})});

		//
		// Create the month drop down
		//
		td = new Element("td");
		tr.insert({bottom : td});
		this._monthSelect = new Element("select", {className : "months"});
	    for (var i = 0 ; i < this.preferences.MonthNames.length ; i++) {
			var opt = new Element("option", {value : i}).update(this.preferences.MonthNames[i]);
	        if (i == this.selectedDate.getMonth()) {
	            opt.selected = true;
	        }
	        this._monthSelect.insert({bottom : opt});
	    }
		td.insert({bottom : this._monthSelect});


		//
		// Create the year drop down
		//
		td = new Element("td", {className : "labelContainer"});
		tr.insert({bottom : td});
		this._yearSelect = new Element("select", {className : "years"});
		for(var i=this.preferences.FromYear; i <= this.preferences.UpToYear; ++i) {
			var opt = new Element("option", {value : i}).update(i);
			if (i == this.selectedDate.getFullYear()) {
				opt.selected = false;
			}
			this._yearSelect.insert({bottom : opt});
		}
		td.insert({bottom : this._yearSelect});

		//next month button
		var nextMonth = new Element("button", {type : "button", className : "nextMonthButton button image"}).update(new Element("img", {src : Akyla.preferences.baseUrl+"/image/icons/original/resultset_next.png"}));
		tr.insert({bottom : new Element("td").insert({bottom : nextMonth})});
		
		//next year button
		var nextYear = new Element("button", {type : "button", className : "nextYearButton button image"}).update(new Element("img", {src : Akyla.preferences.baseUrl+"/image/icons/original/resultset_last.png"}));
		tr.insert({bottom : new Element("td").insert({bottom : nextYear})});


		// Calendar body
		div = new Element("div", {className : "calendarBody"});
		this._calDiv.insert({bottom : div});
		var calendarBody = div;

		// Create the inside of calendar body

		var text;
		table = new Element("table", {align : "center", className : "grid"});

	    div.insert({bottom : table});
		tr = new Element("tr");
		var thead = new Element("thead").insert({bottom : tr});
		table.insert({bottom : thead});

		for(i=0; i < 7; ++i) {
			tr.insert({bottom : new Element("th", {className : "weeDayHead"}).update(this.preferences.ShortWeekDayNames[(i+this.preferences.FirstDayOfWeek)%7])});
		}

		// Date grid
		tbody = new Element("tbody");
		table.insert({bottom : tbody});

		for(week=0; week<6; ++week) {
			tr = new Element("tr");
			tbody.insert({bottom : tr});

			for(day=0; day<7; ++day) 
			{
				td = new Element("td", {className : "calendarDate"});
				text = document.createTextNode(String.fromCharCode(160));
				td.appendChild(text);

				tr.appendChild(td);
				var tmp = new Object();
				tmp.tag = "DATE";
				tmp.value = -1;
				tmp.data = text;
				this.dateSlot[(week*7)+day] = tmp;

				Event.observe(td, "mouseover", this.hover.bindAsEventListener(this));
				Event.observe(td, "mouseout", this.hover.bindAsEventListener(this));

			}
		}

		// Calendar Footer
		div = new Element("div", {className : "calendarFooter"});
		this._calDiv.appendChild(div);

		
		var todayButton = new Element("button", {type : "button"}).update(Akyla.translate("Today"));
		div.insert({bottom : todayButton});
		
		if(navigator.appName=="Microsoft Internet Explorer")
		{
			this.iePopUp = document.createElement('div');
			this.iePopUp.src = DateChooser.spacer;
			this.iePopUp.style.position = "absolute"
			this.iePopUp.scrolling="no"
			this.iePopUp.frameBorder="0"
			this.control.parentNode.appendChild(this.iePopUp);
		}

		//this.control.parentNode.appendChild(this._calDiv);
		$('pageWrapper').appendChild(this._calDiv);

		this.updateDate();
		this.updateHeader();

		this.ieHack(true);

		// IE55+ extension
		previousMonth.hideFocus = true;
		nextMonth.hideFocus = true;
		todayButton.hideFocus = true;
		// end IE55+ extension

		// hook up events
		Event.observe(previousMonth, "click", this.prevMonth.bindAsEventListener(this));
		Event.observe(nextMonth, "click", this.nextMonth.bindAsEventListener(this));
		Event.observe(previousYear, "click", this.prevYear.bindAsEventListener(this));
		Event.observe(nextYear, "click", this.nextYear.bindAsEventListener(this));
		Event.observe(todayButton, "click", this.selectToday.bindAsEventListener(this));
		//Event.observe(clearButton, "click", this.clearSelection.bindAsEventListener(this));
		Event.observe(this._monthSelect, "change", this.monthSelect.bindAsEventListener(this));
		Event.observe(this._yearSelect, "change", this.yearSelect.bindAsEventListener(this));

		Event.observe(calendarBody, "mousedown", this.selectDate.bindAsEventListener(this));
	},

	ieHack : function(cleanup)
	{
		// IE hack
		if(this.iePopUp)
		{
			this.iePopUp.style.display = "block";
			this.iePopUp.style.top = (this._calDiv.offsetTop -1 ) + "px";
			this.iePopUp.style.left = (this._calDiv.offsetLeft -1)+ "px";
			this.iePopUp.style.width = Math.abs(this._calDiv.offsetWidth -2)+ "px";
			this.iePopUp.style.height = (this._calDiv.offsetHeight + 1)+ "px";
			if(cleanup)
			{
				this.iePopUp.style.display = "none";
			}
		}
	},

	keyPressed : function(ev)
	{
		if(!this.showing) return;
		if (!ev) ev = document.parentWindow.event;
		var kc = ev.keyCode != null ? ev.keyCode : ev.charCode;

		if(kc == Event.KEY_RETURN || kc == Event.KEY_SPACEBAR)
		{
			this.setSelectedDate(this.selectedDate);
			Event.stop(ev);
			this.hide();
		}
		if(kc == Event.KEY_TAB)
		{
			//this.setSelectedDate(this.selectedDate);
			//if (navigator.appName == "Opera") //some hack to fix the tab key in opera
			//{
			//	ev.target.up().next().down(1).focus();
		//		Event.stop(ev);
		//	}
			this.hide();
		}
		if(kc == Event.KEY_ESC)
		{
			Event.stop(ev);
			this.hide();
		}

		var getDaysPerMonth = function (nMonth, nYear)
		{
			nMonth = (nMonth + 12) % 12;
	        var days= [31,28,31,30,31,30,31,31,30,31,30,31];
			var res = days[nMonth];
			if (nMonth == 1) //feburary, leap years has 29
                res += nYear % 4 == 0 && !(nYear % 400 == 0) ? 1 : 0;
	        return res;
		}

		if(kc < 37 || kc > 40) return true;

		var current = this.selectedDate;
		var d = current.valueOf();
		if(kc == Event.KEY_LEFT)
		{
			if(ev.ctrlKey || ev.shiftKey) // -1 month
			{
                current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth() - 1,current.getFullYear())) ); // no need to catch dec -> jan for the year
                d = current.setMonth( current.getMonth() - 1 );
			}
			else
			{
				d -= 86400000; //-1 day
			}
			Event.stop(ev);
		}
		else if (kc == Event.KEY_RIGHT)
		{
			if(ev.ctrlKey || ev.shiftKey) // +1 month
			{
				current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth() + 1,current.getFullYear())) ); // no need to catch dec -> jan for the year
				d = current.setMonth( current.getMonth() + 1 );
			}
			else
			{
				d += 86400000; //+1 day
			}
			Event.stop(ev);
		}
		else if (kc == Event.KEY_UP)
		{
			if(ev.ctrlKey || ev.shiftKey) //-1 year
			{
				current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth(),current.getFullYear() - 1)) ); // no need to catch dec -> jan for the year
				d = current.setFullYear( current.getFullYear() - 1 );
			}
			else
			{
				d -= 604800000; // -7 days
			}
			Event.stop(ev);
		}
		else if (kc == Event.KEY_DOWN)
		{
			if(ev.ctrlKey || ev.shiftKey) // +1 year
			{
				current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth(),current.getFullYear() + 1)) ); // no need to catch dec -> jan for the year
				d = current.setFullYear( current.getFullYear() + 1 );
			}
			else
			{
				d += 7 * 24 * 61 * 60 * 1000; // +7 days
			}
			Event.stop(ev);
		}
		this.setSelectedDate(d);
		Event.stop(ev);
	},

	selectDate : function(ev)
	{
		var el = Event.element(ev);
		while (el.nodeType != 1)
			el = el.parentNode;

		while (el != null && el.tagName && el.tagName.toLowerCase() != "td")
			el = el.parentNode;

		// if no td found, return
		if (el == null || el.tagName == null || el.tagName.toLowerCase() != "td")
			return;

		var d = this.newDate(this.selectedDate);
		var n = Number(el.firstChild.data);
		if (isNaN(n) || n <= 0 || n == null)
			return;

		d.setDate(n);
		this.setSelectedDate(d);
		this.hide();
		Event.stop(ev);
	},

	selectToday : function()
	{
		if(this.selectedDate.toISODate() == this.newDate().toISODate())
			this.hide();

		this.setSelectedDate(this.newDate());
	},

	clearSelection : function()
	{
		this.setSelectedDate(this.newDate());
		this.hide();
	},

	monthSelect : function(ev)
	{
		this.setMonth(Form.Element.getValue(Event.element(ev)));
	},

	yearSelect : function(ev)
	{
		this.setYear(Form.Element.getValue(Event.element(ev)));
	},

	// Respond to change event on the textbox or dropdown list
	// This method raises OnDateChanged event on client side if it has been defined
	onDateChanged : function ()
	{
		if (this.preferences.OnDateChanged)
		{
		 	var date;
		 	if (this.control.type == "text")
		 	{
		 		date=this.control.value;
		 	} 
		 	else
		 	{
		 		var day = DateChooser.getDayListControl(this.control).selectedIndex+1;
				var month = DateChooser.getMonthListControl(this.control).selectedIndex;
				var year = DateChooser.getYearListControl(this.control).value;
				date=new Date(year, month, day, 0,0,0).SimpleFormat(this.preferences.Format, this);
			}
			this.preferences.OnDateChanged(this, date);
		}
	},
	
	onChange : function()
	{
		if(this.control.type == "text")
		{
			this.control.value = this.formatDate();
			Event.fireEvent(this.control, "change");
		}
		else
		{
			var day = DateChooser.getDayListControl(this.control);
			var month = DateChooser.getMonthListControl(this.control);
			var year = DateChooser.getYearListControl(this.control);
			var date = this.selectedDate;
			if(day)
			{
				day.selectedIndex = date.getDate()-1;
			}
			if(month)
			{
				month.selectedIndex = date.getMonth();
			}
			if(year)
			{
				var years = year.preferences;
				var currentYear = date.getFullYear();
				for(var i = 0; i < years.length; i++)
					years[i].selected = years[i].value.toInteger() == currentYear;
			}
			Event.fireEvent(day || month || year, "change");
		}
	},

	formatDate : function()
	{
		return this.selectedDate ? this.selectedDate.SimpleFormat(this.preferences.Format,this) : '';
	},

	newDate : function(date)
	{
		if(!date)
			date = new Date();
		if(typeof(date) == "string" || typeof(date) == "number")
			date = new Date(date);
		return new Date(Math.min(Math.max(date.getFullYear(),this.preferences.FromYear),this.preferences.UpToYear), date.getMonth(), date.getDate(), 0,0,0);
	},

	setSelectedDate : function(date)
	{
		if (date == null)
			return;
		var old=this.selectedDate;
		this.selectedDate = this.newDate(date);
		this.updateHeader();
		this.updateDate();
		if (typeof(this.onChange) == "function") //old - this.selectedDate !=0 && 
		{
			this.onChange(this, date);
		}
	},

	getElement : function()
	{
		return this._calDiv;
	},

	getSelectedDate : function ()
	{
		return this.selectedDate == null ? null : this.newDate(this.selectedDate);
	},

	setYear : function(year)
	{
		var d = this.newDate(this.selectedDate);
		d.setFullYear(year);
		this.setSelectedDate(d);
	},

	setMonth : function (month)
	{
		var d = this.newDate(this.selectedDate);
		d.setDate(Math.min(d.getDate(), this.getDaysPerMonth(month,d.getFullYear())));
		d.setMonth(month);
		this.setSelectedDate(d);
	},

	nextMonth : function ()
	{
		this.setMonth(this.selectedDate.getMonth()+1);
	},

	prevMonth : function ()
	{
		this.setMonth(this.selectedDate.getMonth()-1);
	},
	
	nextYear : function ()
	{
		this.setYear(this.selectedDate.getFullYear()+1);
	},

	prevYear : function ()
	{
		this.setYear(this.selectedDate.getFullYear()-1);
	},

	getDaysPerMonth : function (month, year)
	{
		month = (Number(month)+12) % 12;
        var days = [31,28,31,30,31,30,31,31,30,31,30,31];
		var res = days[month];
		if (month == 1 && ((!(year % 4) && (year % 100)) || !(year % 400))) //feburary, leap years has 29
            res++;
        return res;
	},

	getDateChooserOffsetHeight : function()
	{
		if(this.control.type == "text")
			return this.control.offsetHeight;

		var control = DateChooser.getDayListControl(this.control);
		if(control) return control.offsetHeight;

		var control = DateChooser.getMonthListControl(this.control);
		if(control) return control.offsetHeight;

		var control = DateChooser.getYearListControl(this.control);
		if(control) return control.offsetHeight;
		return 0;
	},

	show : function()
	{
		this.create();
		if(!this.showing)
		{
			// IE select hack, fix the problem that in IE, select always have the highest z-index
			if(navigator.appName=="Microsoft Internet Explorer")
			{
				$$("select").invoke('setStyle', {'visibility': "hidden"});
				$$("div.calendarHeader select").invoke('setStyle', {'visibility': ""});
			}
			var pos = this.control.cumulativeOffset();

			pos[1] += this.getDateChooserOffsetHeight();

			this._calDiv.style.display = "block";
			this._calDiv.style.top = (pos[1]-1) + "px";
			this._calDiv.style.left = pos[0] + "px";

			this.ieHack(false);
			this.documentClickEvent = this.hideOnClick.bindAsEventListener(this);
			this.documentKeyDownEvent = this.keyPressed.bindAsEventListener(this);
			Event.observe(document.body, "click", this.documentClickEvent);
			var date = this.getDateFromInput();
			if(date)
			{
				this.selectedDate = date;
				this.setSelectedDate(date);
			}
			if (navigator.appName == "Netscape" || navigator.appName == "Microsoft Internet Explorer")
			{
				
				Event.observe(document,"keydown", this.documentKeyDownEvent);
			}
			else if (navigator.appName == "Opera")
			{
				Event.observe(document,"keypress", this.documentKeyDownEvent);
			}
			this.showing = true;
		}
	},

	getDateFromInput : function()
	{
		if(this.control.type == "text")
			return Date.SimpleParse($F(this.control), this.preferences.Format);
		else
			return DateChooser.getDropDownDate(this.control);
	},

	//hide the calendar when clicked outside any calendar
	hideOnClick : function(ev)
	{
		if(!this.showing) return;
		var el = Event.element(ev);
		var within = false;
		do
		{
			within = within || (el.className && Element.hasClassName(el, "DateChooser_"+this.preferences.CalendarStyle));
			within = within || el == this.trigger;
			within = within || el == this.control;
			if(within) break;
			el = el.parentNode;
		}
		while(el);
		if(!within) this.hide();
	},


	hide : function()
	{
		if(this.showing)
		{
			this._calDiv.style.display = "none";
			if(this.iePopUp)
				this.iePopUp.style.display = "none";
			this.showing = false;
			Event.stopObserving(document.body, "click", this.documentClickEvent);
			if (navigator.appName == "Netscape" || navigator.appName == "Microsoft Internet Explorer")
			{
				if(navigator.appName=="Microsoft Internet Explorer")
				{
					// remove IE select hack
					$$("select").invoke('setStyle', {'visibility': ""});
				}
				Event.stopObserving(document,"keydown", this.documentKeyDownEvent);//function(event){this.keyPressed(event).bind(this);}.bind(this)
			}
			else if (navigator.appName == "Opera")
			{
				Event.stopObserving(document,"keypress", this.documentKeyDownEvent);
			}
		}
	},

	updateDate : function()
	{
		// Calculate the number of days in the month for the selected date
		var date = this.selectedDate;
		var today = (this.newDate()).toISODate();

		var selected = date.toISODate();
		var d1 = new Date(date.getFullYear(), date.getMonth(), 1);
		var d2 = new Date(date.getFullYear(), date.getMonth()+1, 1);
		var monthLength = Math.round((d2 - d1) / (24 * 60 * 60 * 1000));

		// Find out the weekDay index for the first of this month
		var firstIndex = (d1.getDay() - this.preferences.FirstDayOfWeek) % 7 ;
	    if (firstIndex < 0)
	    	firstIndex += 7;

		var index = 0;
		while (index < firstIndex) {
			this.dateSlot[index].value = -1;
			this.dateSlot[index].data.data = String.fromCharCode(160);
			this.dateSlot[index].data.parentNode.className = "empty";
			index++;
		}

	    for (i = 1; i <= monthLength; i++, index++) {
			var slot = this.dateSlot[index];
			var slotNode = slot.data.parentNode;
			slot.value = i;
			slot.data.data = i;
			slotNode.className = "date";
			//slotNode.style.color = "";
			if (d1.toISODate() == today) {
				slotNode.className += " today";
			}
			if (d1.toISODate() == selected) {
			//	slotNode.style.color = "blue";
				slotNode.className += " selected";
			}
			d1 = new Date(d1.getFullYear(), d1.getMonth(), d1.getDate()+1);
		}

		var lastDateIndex = index;

	    while(index < 42) {
			this.dateSlot[index].value = -1;
			this.dateSlot[index].data.data = String.fromCharCode(160);
			this.dateSlot[index].data.parentNode.className = "empty";
			++index;
		}

	},

	hover : function(ev)
	{
		if(Event.element(ev).tagName)
		{
			if(ev.type == "mouseover")
				Event.element(ev).addClassName("hover");
				else
				Event.element(ev).removeClassName("hover");
		}
	},

	updateHeader : function () 
	{

		var preferences = this._monthSelect.options;
		var m = this.selectedDate.getMonth();
		for(var i=0; i < preferences.length; ++i) {
			preferences[i].selected = false;
			if (preferences[i].value == m) {
				preferences[i].selected = true;
			}
		}

		preferences = this._yearSelect.options;
		var year = this.selectedDate.getFullYear();
		for(var i=0; i < preferences.length; ++i) {
			preferences[i].selected = false;
			if (preferences[i].value == year) {
				preferences[i].selected = true;
			}
		}

	}
});

Object.extend(DateChooser,
{
	/**
	 * @return Date the date from drop down list preferences.
	 */
	getDropDownDate : function(control)
	{
		var now=new Date();
		var year=now.getFullYear();
		var month=now.getMonth();
		var day=1;

		var month_list = this.getMonthListControl(control);
	 	var day_list = this.getDayListControl(control);
	 	var year_list = this.getYearListControl(control);

		var day = day_list ? $F(day_list) : 1;
		var month = month_list ? $F(month_list) : now.getMonth();
		var year = year_list ? $F(year_list) : now.getFullYear();

		return new Date(year,month,day, 0, 0, 0);
	},

	getYearListControl : function(control)
	{
		return $(control.id+"_year");
	},

	getMonthListControl : function(control)
	{
		return $(control.id+"_month");
	},

	getDayListControl : function(control)
	{
		return $(control.id+"_day");
	}
});

//comes from PRADO framework, needed for date selector
Object.extend(Date.prototype,
{
	SimpleFormat: function(format, data)
	{
		data = data || {};
		var bits = new Array();
		bits['d'] = this.getDate();
		bits['dd'] = this.zerofill(String(this.getDate()), 2);

		bits['M'] = this.getMonth()+1;
		bits['MM'] = this.zerofill(String(this.getMonth()+1), 2);
		if(data.preferences.AbbreviatedMonthNames)
			bits['MMM'] = data.preferences.AbbreviatedMonthNames[this.getMonth()];
		if(data.preferences.MonthNames)
			bits['MMMM'] = data.preferences.MonthNames[this.getMonth()];
		var yearStr = "" + this.getFullYear();
		yearStr = (yearStr.length == 2) ? '19' + yearStr: yearStr;
		bits['yyyy'] = yearStr;
		bits['yy'] = bits['yyyy'].toString().substr(2,2);

		// do some funky regexs to replace the format string
		// with the real values
		var frm = new String(format);
		for (var sect in bits)
		{
			var reg = new RegExp("\\b"+sect+"\\b" ,"g");
			frm = frm.replace(reg, bits[sect]);
		}
		return frm;
	},

	toISODate : function()
	{
		var y = this.getFullYear();
		var m = this.zerofill(String(this.getMonth() + 1), 2);
		var d = this.zerofill(String(this.getDate()), 2);
		return String(y) + String(m) + String(d);
	},
	zerofill : function(data, num)
	{
		while (data.length < num)
		{
			data = '0'+data;
		}
		return data;
	}
});
//comes from PRADO framework, needed for date selector
Object.extend(Date,
{
	SimpleParse: function(value, format)
	{
		val=String(value);
		format=String(format);

		if(val.length <= 0) return null;

		if(format.length <= 0) return new Date(value);

		var isInteger = function (val)
		{
			var digits="1234567890";
			for (var i=0; i < val.length; i++)
			{
				if (digits.indexOf(val.charAt(i))==-1) { return false; }
			}
			return true;
		};

		var getInt = function(str,i,minlength,maxlength)
		{
			for (var x=maxlength; x>=minlength; x--)
			{
				var token=str.substring(i,i+x);
				if (token.length < minlength) { return null; }
				if (isInteger(token)) { return token; }
			}
			return null;
		};

		var i_val=0;
		var i_format=0;
		var c="";
		var token="";
		var token2="";
		var x,y;
		var now=new Date();
		var year=now.getFullYear();
		var month=now.getMonth()+1;
		var date=1;

		while (i_format < format.length)
		{
			// Get next token from format string
			c=format.charAt(i_format);
			token="";
			while ((format.charAt(i_format)==c) && (i_format < format.length))
			{
				token += format.charAt(i_format++);
			}

			// Extract contents of value based on format token
			if (token=="yyyy" || token=="yy" || token=="y")
			{
				if (token=="yyyy") { x=4;y=4; }
				if (token=="yy")   { x=2;y=2; }
				if (token=="y")    { x=2;y=4; }
				year=getInt(val,i_val,x,y);
				if (year==null) { return null; }
				i_val += year.length;
				if (year.length==2)
				{
					if (year > 70) { year=1900+(year-0); }
					else { year=2000+(year-0); }
				}
			}

			else if (token=="MM"||token=="M")
			{
				month=getInt(val,i_val,token.length,2);
				if(month==null||(month<1)||(month>12)){return null;}
				i_val+=month.length;
			}
			else if (token=="dd"||token=="d")
			{
				date=getInt(val,i_val,token.length,2);
				if(date==null||(date<1)||(date>31)){return null;}
				i_val+=date.length;
			}
			else
			{
				if (val.substring(i_val,i_val+token.length)!=token) {return null;}
				else {i_val+=token.length;}
			}
		}

		// If there are any trailing characters left in the value, it doesn't match
		if (i_val != val.length) { return null; }

		// Is date valid for month?
		if (month==2)
		{
			// Check for leap year
			if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year
				if (date > 29){ return null; }
			}
			else { if (date > 28) { return null; } }
		}

		if ((month==4)||(month==6)||(month==9)||(month==11))
		{
			if (date > 30) { return null; }
		}

		var newdate=new Date(year,month-1,date, 0, 0, 0);
		return newdate;
	}
});

/**
 * @class Event extensions. From PRADO, needed for DateChooser
 */
Object.extend(Event,
{
	/**
	 * @param {String} event type or event name.
	 * @return {Boolean} true if event type is of HTMLEvent, false
	 * otherwise
	 */
	isHTMLEvent : function(type)
	{
		var events = ['abort', 'blur', 'change', 'error', 'focus',
					'load', 'reset', 'resize', 'scroll', 'select',
					'submit', 'unload'];
		return events.include(type);
	},

	/**
	 * Dispatch the DOM event of a given <tt>type</tt> on a DOM
	 * <tt>element</tt>. Only HTMLEvent and MouseEvent can be
	 * dispatched, keyboard events or UIEvent can not be dispatch
	 * via javascript consistently.
	 * For the "submit" event the submit() method is called.
	 * @param {Object} element id string or a DOM element.
	 * @param {String} event type to dispatch.
	 */
	fireEvent : function(element,type)
	{
		element = $(element);
		if(type == "submit")
			return element.submit();
		if(document.createEvent)
        {
			if(Event.isHTMLEvent(type))
			{
				var event = document.createEvent('HTMLEvents');
	            event.initEvent(type, true, true);
			}
			else if(Event.isMouseEvent(type))
			{
				var event = document.createEvent('MouseEvents');
				if (event.initMouseEvent)
		        {
					event.initMouseEvent(type,true,true,
						document.defaultView, 1, 0, 0, 0, 0, false,
								false, false, false, 0, null);
		        }
		        else
		        {
		            // Safari
		            // TODO we should be initialising other mouse-event related attributes here
		            event.initEvent(type, true, true);
		        }
			}
            element.dispatchEvent(event);
        }
        else if(document.createEventObject)
        {
        	var evObj = document.createEventObject();
            element.fireEvent('on'+type, evObj);
        }
        else if(typeof(element['on'+type]) == "function")
            element['on'+type]();
	}
});

