/*
* writen by: aa.temkin@gmail.com
* Last mod: 20.03.2008
* Release: 4
*/
TSCalendar = Class.create();
TSCalendar.instances = [];

TSCalendar.prototype = Object.extend(TSCalendar,{
	initialize : function(target,options){
		this.selectedDays = []; 
		this.specialDays = [];
		this.run(target,options);
		TSCalendar.instances[TSCalendar.instances.length] = this;
	},
	load_config : function(){
	},
	run : function(target,options){
		if (typeof(target)=='object'){
			options = target; 
			if (options.control){
				if (typeof(options.control)=='object' && options.control.length>0){
					var k = $A(options.control).first();
					if (t = $(k)) target = $(k).up();
				}else if ($(options.control)) target = $($(options.control).up());
			}
		}
		if (!(this.targ = $(target))) { alert('Calendar: cannot initialize Calendar - [target Element not found]'); return false;}
		this.options = Object.extend({
			FirstDayOfWeek : 1, // первый день недели (0 - воскресенье (для америкосов и т.п.), 1 - понедельник, ..., 6 - пятница)
			date : new Date(), // дата для инициализации календаря
			days : [], // выделеные дни, если стоит аттрибут multiple
			special : [],
			config : false,
			debug : true,
			multiple : false, // мультивыбор
			input : false, // поле, если указано, то в него возвращается значение при клике
			control : false, // управляющая кпопка для календаря
			onClick : function(date_object,selected,special) {}, // вызывается при клике на дату
			onLoad : function(){}, // вызывается при загрузке объекта в память
			onShow : function(){}, // для всплывающего при его показе
			onClose : function(){}, // для всплывающего при закрытии
			onStartDrag : function(element){},
			onDrag : function(x,y,element){},
			onStopDrag : function(element){},
			onLoadDays : function(d){ return true },
			onNavClick : function(type,d){},
			isFlat : false, // задается тип календаря, если true - тогда обычный иначе - popup
			DragableCal : true,
			takeOtherDays : false, // если true тогда берёт числа с прошлого и следующих месяцев иначе - нет
			show_time : false,
			name : 'calendar',
			classes : {
				'calendar'		:	 'ts-calendar',
				'head'			:	 'cal-head',
				'navigation'		:	 'nav-bar',
				'main'				:	 'main-area',
				'status'			:	 'status-area'
			},
			format : '%d/%m/%Y', // формат даты
			display : {
				head : true,
				navigation : true,
				main : true,
					days_title: true,
					week_numbers : true,
					coloredDays : false,
				status : true // пока не реализовано
			}
		},options || {});
		this.IE6 = null;
		if (this.options.config){
			if ((/\.js$/i).test(this.options.config)){
				document.write('<script type="text/javascript" src="'+this.options.config+'"><\/script>');
			}
		}

		//if (this.options.show_time && !(/\%\h(.*?)\%\I/ig.test(this.options.format))) this.options.format+= ' %H:%I'; /// ПРОТЕСТИТЬ ЭТУ ХРЕНЬ!!!
		
		this.options.strings = Object.extend({
				'head_format':"#{month_name}, #{year}",
				'prev_year':'&laquo;&laquo;', 
				'next_year':'&raquo;&raquo;',
				'prev_month':'&laquo;',
				'next_month':'&raquo;',
				'today':"Сегодня",
				'close_button' : " X "
		},this.buttons || {});
		this.table = false;
		if (elm = $(this.options.input)){
			today = new Date();
			if (k=today.parseDate(elm.value,this.options.format)){ 
				this.options.date = k;
				this.selectedDays[this.options.multiple?this.selectedDays.length:0] = this.convert_date($H({'year':k.getFullYear(),'month':k.getMonth(),'day':k.getDate()}))
			}
		}

		if (this.options.control){
			if (typeof(this.options.control)=='object' && this.options.control.length>0){
				for(i=0;i<this.options.control.length;i++){
					if (cntrl = $(this.options.control[i])) cntrl.observe('click',function (event){this.open(Event.element(event));Event.stop(event);}.bind(this));
				}
			} else if (cntrl = $(this.options.control)) cntrl.observe('click',function (event){this.open(Event.element(event));Event.stop(event);}.bind(this));
		}

		if (this.options.multiple && this.options.days){for(i=0;i<this.options.days.length; i++){ this.selectedDays[this.selectedDays.length] = this.options.days[i];}}
		for(i=0;i<this.options.special.length; i++){ 
			var ff = new Date();
			var da = ff.parseDate(this.options.special[i],this.options.format);
			this.specialDays[this.specialDays.length] = this.convert_date($H({'year':da.getFullYear(),'month':da.getMonth(),'day':da.getDate()}));
		}
		if (!this.options.date){ alert('Calendar: invalid date'); return false;}
		this.classes = this.options.classes;
		if (this.options.isFlat){
			this.load_dates();
			this._built_cal();
		}else{
			this.visible = false;
			this.opened_once = false;
		}
		this.options.onLoad(this);
	},
	load_dates : function(){
		this.year = this.options.date.getFullYear();
		this.month = this.options.date.getMonth();
		this.month_name = this.MonthsNames[this.month];
		this.day = this.options.date.getDate(); 
		this.dow = this.options.date.getDay();
		this.hour = this.options.date.getHours();
		this.minute = this.options.date.getMinutes();
	},
	serialize : function(){
		this.selectedDays.uniq();
		if (this.selectedDays.length) return (this.options.name+'[]='+(this.selectedDays).join('&'+this.options.name+'[]='));
		return '';
	},
	_rebuilt : function(target,options){this._destroy();this.run(target,options);},
	_destroy : function(){
		var debug = this.options.debug;
		this.options = null;
		this.month_days = null;
	},
	_ie6 : function(){
		if (this.IE6==null){
			var userAgent = navigator.userAgent; var ieIndex = userAgent.indexOf("MSIE");
			if (ieIndex > 0) { var ieVer = userAgent.substring(ieIndex + 5, ieIndex + 8); if (ieVer<7){ this.IE6=true; return true;}}
			this.IE6=false;
			return false;
		} return this.IE6;
	},
	open  : function(elem){
		for(i=0,mx=TSCalendar.instances.length;i<mx;i++){ // fix to hide another instances
			if (TSCalendar.instances[i] != this && !TSCalendar.instances[i].options.isFlat) TSCalendar.instances[i].hide();
		}
		if (el = $(elem)){
			this.draging = false;
			this.elem = el;
			if (!this.visible){
				this.chide = this.hide.bindAsEventListener(this);
				this.close_hook = Event.observe(Prototype.Browser['IE']?document.body:window,'click', this.chide);
				if (elm = $(this.options.input)){
					today = this.options.date;
					k=today.parseDate(elm.value,this.options.format); if (k){ 
						this.options.date = k;
						this.selectedDays[this.options.multiple?this.selectedDays.length:0] = this.convert_date($H({'year':k.getFullYear(),'month':k.getMonth(),'day':k.getDate()}))
					}
				}
				if (this._ie6()){ // fix for IE 6
					if (!this.opened_once){
						this.iframe = new Element('iframe'); 
						$(this.targ).appendChild(this.iframe);
						$(this.iframe).setStyle({'width':'1px','height':'1px'});
						$(this.iframe).absolutize();
					} else $(this.iframe).show();
				}
				if (!this.opened_once){
					this.load_dates();
					this._built_cal();
					$(this.table).absolutize();
					this.opened_once = true;
					this.hide();
					this.open(el);
					return;
				} else $(this.table).show();
				var pos = el.positionedOffset();
				var ofs = el.getDimensions();
				$(this.table).setStyle({'top':pos[1]+ofs['height']-2,'left':pos[0]+ofs['width']-2});
				this.visible = true;
				this.opened_once = true;
				this.options.onShow();
				if (this._ie6()) { // fix for IE 6
					Position.clone(this.table,this.iframe);
					$(this.iframe).writeAttribute('frameborder',0);
				}

			} else this.hide();
		}
	},
	hide : function(){
		if (this.visible && (el = $(this.elem))){
			$(this.table).hide();
			this.close_hook = Event.stopObserving(Prototype.Browser['IE']?document.body:window,'click', this.chide);
			this.visible = false;
			this.draging = false;
			this.opera_fix(500);
			this.options.onClose();
			if (this._ie6() && this.iframe) { // fix for IE 6
				$(this.iframe).hide();
			}
		}
	},
	opera_fix : function(m){ // fix for fucking Opera
		if (Prototype.Browser['Opera']===true){
			var now_k = new Date(); var exitTime = now_k.getTime() + (m?m:10);
			while(true){now_k = new Date();if(now_k.getTime() > exitTime) return;}
		}
	},
	_built_cal : function(){
		this.table = new Element('table',{'border':0, 'cellspacing': 1, 'cellpadding' : 1}).addClassName(this.classes['calendar']);
		this.observer = $(this.table).observe('click',function(event){ Event.stop(event);});
		if (this.options.display.head) this._draw_head(this.table);
		if (this.options.display.navigation) this._draw_navigation(this.table);
		if (this.options.display.main) this._draw_main(this.table);
		if (this.options.show_time && this.options.display.status) this._draw_status(this.table);
		$(this.targ).appendChild(this.table);
	},
	_draw_head : function(target){
		var head = $($(target).insertRow(-1)).insertCell(-1);
		this.head = head;
		$(head).update(new Template(this.options.strings['head_format']).evaluate(this)).addClassName('calendar-head');
		if (!this.options.isFlat){
			var close_btn = new Element('a',{'href':'#'});
			$(close_btn).update(new Template(this.options.strings['close_button']).evaluate(this)).addClassName('');
			$(close_btn).onclick = function(event){ this.hide(); }.bind(this);
			$(head).appendChild(close_btn);
			var cls_btn = new Element('span');
			if (this.options.DragableCal){
				$(head).addClassName('cursor_move');
				$(head).observe('mousedown',function(event){
					var el = Event.element(event);
					t = el.cumulativeOffset();
					this.offset = {'x' :  Event.pointerX(event)-(t[0]+2), 'y' : (Event.pointerY(event)-(t[1]+2))+5};
					$(this.table).addClassName('ts-cal-move');
					this.draging = true;
					this._disableSelection(document.body); // для того чтобы не выделалась страница во время перетаскивания
					this.options.onStartDrag(this.table);
				}.bind(this));
				$(head).observe('mouseup',function(event){
					$(this.table).removeClassName('ts-cal-move'); this.draging = false;
					this._enableSelection(document.body); // возвращаем возможность выделения
					this.options.onStopDrag(this.table);
				}.bind(this));
				Event.observe(Prototype.Browser['IE']?document.body:window,'mousemove',function(event){
					if (this.draging){
						var Y = Event.pointerY(event)+(-this.offset['y']);
						var X = Event.pointerX(event)+(-this.offset['x']);
						var pos = {'top':Y,'left':X};
						$(this.table).setStyle(pos);
						if (this._ie6()) $(this.iframe).setStyle(pos); // fix for IE 6
						this.options.onDrag(Event.pointerX(event)-this.offset['x'],Event.pointerY(event)-this.offset['y'],this.table);
						Event.stop(event);
					}
				}.bind(this));
			}
		}
	},
	_disableSelection: function (elem) { 
		if (window.ActiveXObject) elem.onselectstart = function() {return false};
		else elem.style.MozUserSelect = "none";
	},
	_enableSelection: function (elem) { 
		if (window.ActiveXObject) elem.onselectstart = function() {return true};
		else elem.style.MozUserSelect = "";
	},

	_draw_navigation : function(target){
		targ = $($(target).insertRow(-1)).insertCell(-1);
		var nav = new Element('table').addClassName(this.options.classes['navigation']);
		var tr = nav.insertRow(-1);
		this.create_nav_element(tr,{name : 'prev_year', onClick : function(){
			var dD  = new Date(this.year-1,this.month,1,this.hour,this.minute);
			this._switch_cal(target,{date : dD});
			this.options.onNavClick('prev_year',dD);
		}.bind(this)});
		this.create_nav_element(tr,{name : 'prev_month', onClick : function(){
			var dD = new Date(this.year,this.month-1,1,this.hour,this.minute);
			this._switch_cal(target,{date : dD});
			this.options.onNavClick('prev_month',dD);
		}.bind(this)});
		this.create_nav_element(tr,{name : 'today', onClick : function(){
			var dD = new Date();
			var dD = new Date(dD.getFullYear(),dD.getMonth(),dD.getDate(),dD.getHours(),dD.getMinutes());
			if (dD.getMonth()!=this.month || dD.getFullYear()!=this.year){
				this._switch_cal(target,{date : dD});
			}else if(dD.getMonth()==this.month){
				var allow_hide = true;
				if (dD.getDate()!=this.day) allow_hide = false;
				this.selectedDays = [];
				$A($(this.targ).getElementsByClassName('active-cell')).invoke('removeClassName','active-cell');
				$A($(this.targ).getElementsByClassName('day'+dD.getDate())).each(function(elm){
					if ($(elm).hasClassName('clickable')){
						var dd = new Date($(elm).readAttribute('full_date'));
						val = this.convert_date($H({'year':dd.getFullYear(),'month':dd.getMonth(),'day':dd.getDate(),'hour':this.hour,'minute':this.minute}));
						if (!this.options.multiple) $A($(this.targ).getElementsByClassName('active-cell')).invoke('removeClassName','active-cell');
						if (!$($(elm).up()).hasClassName('calendar-other-month') || this.options.multiple) $(elm).toggleClassName('active-cell');
						if (this.options.multiple){
							if ($(elm).hasClassName('active-cell')){
								this.selectedDays = this.selectedDays.without(val);
								this.selectedDays[this.selectedDays.length] = val;
							}else this.selectedDays = this.selectedDays.without(val);
						}else this.selectedDays[0] = val;
						if (!this.options.multiple) if (t = $(this.options.input)){$(t).value = val;}
						this.options.onClick(new Date($(elm).readAttribute('full_date')), $(elm).hasClassName('active-cell'), $(elm).hasClassName('special_day'));
						if (allow_hide)
						if (!this.options.isFlat && !this.options.multiple && this.elem) this.hide();
					}
				}.bind(this));
			}

			this.options.onNavClick('today',dD);
		}.bind(this)});
		this.create_nav_element(tr,{name : 'next_month', onClick : function(){
			var dD = new Date(this.year,this.month+1,1,this.hour,this.minute);
			this._switch_cal(target,{date : dD});
			this.options.onNavClick('next_month',dD);
		}.bind(this)});
		this.create_nav_element(tr,{name : 'next_year', onClick : function(){
			var dD = new Date(this.year+1,this.month,1,this.hour,this.minute);
			this._switch_cal(target,{date : dD});
			this.options.onNavClick('next_year',dD);
		}.bind(this)});
		$(targ).appendChild(nav);

		this.muo = this.mouse_up_observer.bindAsEventListener(this);
		$(targ).observe('mousedown',function(event){
			trg_elm = Event.element(event);
			if ($(trg_elm).readAttribute('act')!='today'){
				Event.stop(event);
				Event.observe(Prototype.Browser['IE']?document.body:window,'mouseup',this.muo);
				var hlp = new Element('div');
				$(this.targ).appendChild(hlp);
				$(hlp).addClassName('calendar-popup-helper').absolutize();
				var al = Event.element(event);
				var k = $(al).cumulativeOffset();
				var inner = ''; 
				if (/month/.test($(trg_elm).readAttribute('act'))) for(i=0;i<this.MonthsNames.length;i++) { 
					inner+='<a class="month'+(this.month==i?' selected':'')+'" value="'+i+'" href="javascript:void(0);">'+this.MonthsNames[i]+'</a>';
				} else if (/year/.test($(trg_elm).readAttribute('act'))){
					for(i=this.year-5;i<this.year+5;i++){inner+='<a class="year'+(this.year==i?' selected':'')+'"  value="'+i+'" href="javascript:void(0);">'+i+'</a>';}
				}
				$(hlp).update(inner).hide();
				var gCallCount = 0;
				new PeriodicalExecuter(function(pe) {if (++gCallCount >= 1){pe.stop();try{$(hlp).show();}catch(e){};}}, 0.2);
				$(hlp).setStyle({'left':k[0]+20,'top':k[1]});
			}
		}.bind(this));
	},
	mouse_up_observer : function(event){
		var k = Event.element(event);
		if ($($(k).up()).hasClassName('calendar-popup-helper')){
			var dD = null;
			if ($(k).hasClassName('year')) dD = new Date($(k).readAttribute('value'),this.month,1,this.hour,this.minute);
			else dD = new Date(this.year, $(k).readAttribute('value'),1,this.hour,this.minute);
			this._switch_cal(this.table,{date : dD});
		}
		var elms = $(this.targ).getElementsByClassName('calendar-popup-helper');
		if (Prototype.Browser['Opera']){
			$A(elms).invoke('hide');
			this.opera_fix(500);
		}
		$A(elms).invoke('remove');
		this.my_handler = Event.stopObserving(Prototype.Browser['IE']?document.body:window,'mouseup', this.muo);
	},

	_switch_cal : function(target,op){
		this.options = Object.extend(this.options,op || {});this.load_dates();
		if (this.options.display.head){
			$(this.head).update(new Template(this.options.strings['head_format']).evaluate(this));
			if (!this.options.isFlat){
				var close_btn = new Element('a',{'href':'#'});
				$(close_btn).update(new Template(this.options.strings['close_button']).evaluate(this)).addClassName('');
				$(close_btn).onclick = function(event){ this.hide(); Event.stop(event);}.bind(this);
				$(this.head).appendChild(close_btn);
			}
		}
		this._draw_main(target);
		if (this.options.show_time && this.options.display.status) this._draw_status(target);
		if (this._ie6() && this.iframe) Position.clone(this.table,this.iframe);
	},
	create_nav_element : function(target,options){
		var el = target.insertCell(-1);
		var a = new Element('a',{'href':'javascript:void(0);'}).addClassName('nav-bar');
		$(a).update(new Template(this.options.strings[options.name]).evaluate(this));
		$(a).writeAttribute('act',options.name);
		$(a).observe('mouseup',options.onClick);
		$(el).appendChild(a);
	},
	_draw_main : function(target){
		$A($(target).getElementsByClassName('main_area')).each(function(el){$(el).up(1).remove();});
		targ = $(target.insertRow(-1)).insertCell(-1);
		var main = '<table class="main_area">';
		if (this.options.display.days_title){main+=this._draw_day_header();}
		this._get_days();
		var week = [];
		for(k=0,nk=this.month_days.length;k<nk;k++){ // рисуем...
			week = this.month_days[k];
			main+='<tr>';
			var wNo = this.getWeekNr(week);
			if (this.options.display.week_numbers) main+='<td class="calendar-days">'+wNo+'</td>';
			for(key=0,wk=week.length;key<wk;key++){
				day = week[key];
				var d = day.values();
				var cls = 'calendar-td';
				var now = this.options.date;
				var td = '<td class="'+cls+'">';
				var value = this.convert_date($H(day));
				var mD = d[2];
				var cls = 'calendar-cell';
				if (d[0]==now.getFullYear() && d[1]==now.getMonth() && d[2]==now.getDate()){cls+=' active-cell';}
				cls+= ' calendar-row-'+wNo;
				cls+= ' calendar-dof-'+($R(1,6).include(key+this.options.FirstDayOfWeek)?(key+this.options.FirstDayOfWeek):0);
				cls+= ' day'+mD;
				if (d[1]!=this.month && !this.options.takeOtherDays) mD = ' ';
				else if (this.options.onLoadDays(dD)) cls+= ' clickable';
				var dD = new Date(d[0],d[1],d[2],this.hour,this.minute);
				var cell = '<a href="#" onclick="return false;" class="'+cls+'" value="'+value+'" full_date="'+dD+'">'+mD+'</a>';
				main+=td+cell+'</td>';
			}
			main+='</tr>';
		}
		$(targ).update(main);
		var k = $A($(targ).getElementsByClassName('main_area')).first();
		if (this.options.multiple && !this.options.isFlat){$(k).observe('dblclick',function(){this.hide();}.bind(this));}
		$(k).observe('click',function(event){ // вешаем обработчик событий
			var elm = Event.element(event);
			if ($(elm).hasClassName('clickable')){
				var dC = new Date($(elm).readAttribute('full_date'));
				val = this.convert_date($H({'year':dC.getFullYear(),'month':dC.getMonth(),'day':dC.getDate(),'hour':this.hour,'minute':this.minute}));
				if (!this.options.multiple) $A($(this.targ).getElementsByClassName('active-cell')).invoke('removeClassName','active-cell');
				if (!$($(elm).up()).hasClassName('calendar-other-month') || this.options.multiple) $(elm).toggleClassName('active-cell');
				if (this.options.multiple){
					if ($(elm).hasClassName('active-cell')){
						this.selectedDays = this.selectedDays.without(val);
						this.selectedDays[this.selectedDays.length] = val;
					}else this.selectedDays = this.selectedDays.without(val);
				}else this.selectedDays[0] = val;

				var dC = new Date($(elm).readAttribute('full_date'));
				var dC = new Date(dC.getFullYear(),dC.getMonth(),dC.getDate(),this.hour,this.minute);
				if (!this.options.multiple){
					if (t = $(this.options.input)){$(t).value = val;}
					this.options.date = dC;
					this.load_dates();
				}
				this.options.onClick(dC, $(elm).hasClassName('active-cell'), $(elm).hasClassName('special_day'));
				if (!this.options.isFlat && !this.options.multiple && this.elem) this.hide();
			}
			return false;
		}.bind(this));
	},
	_draw_status : function(target){
		$A($(target).getElementsByClassName('status-time')).each(function(el){$(el).up(1).remove();});
		var targ = $(target.insertRow(-1)).insertCell(-1);
		var inner = '<table class="status-time" width="60px" cellspacing="0" border="0" celpadding="0" align="'+(Prototype.Browser['IE']?'left':'right')+'">';
		inner+='<tr><td class="cell s_hour" rowspan="2">'+(this.hour<10?'0'+this.hour:this.hour)+'</td><td align="left" class="time-select up hour">&#9650;</td>';
		inner+='<td class="cell s_minute" rowspan="2">'+(this.minute<10?'0'+this.minute:this.minute)+'</td><td align="left" class="time-select up minute">&#9650;</td></tr>';
		inner+='<tr><td align="right" class="time-select down hour">&#9660;</td><td align="right" class="time-select down minute">&#9660;</td></tr></table>';
		$(targ).update(inner);
		var elm = $($(this.table).getElementsByClassName('status-time').first());
		$(elm).observe('mousedown',function(event){
			this.time_sel = true;
			this._disableSelection(Event.element(event));
			this.time_event = event;
			this._time_change(event);
			new PeriodicalExecuter(function(pe){
				if (!this.time_sel) pe.stop();
				else this._time_change(this.time_event);
			}.bind(this), 0.1);
		}.bind(this));
		$(elm).observe('mouseup',function(event){
			this.time_sel = false;
			this._enableSelection(Event.element(event));
			this.time_event = null;
			Event.stop(event);
		}.bind(this));
		$(elm).observe('mouseover',function(event){
			var elm = Event.element(event);
			if ($(elm).hasClassName('time-select')) $(elm).addClassName('hilite');
		});
		$(elm).observe('mouseout',function(event){
			var elm = Event.element(event);
			if ($(elm).hasClassName('time-select')) $(elm).removeClassName('hilite');
			this.time_sel = false;
			this._enableSelection(elm);
			this.time_event = null;
			Event.stop(event);
		}.bind(this));
	},
	_time_change : function(event){
			var el = Event.element(event);
			if ($(el).hasClassName('time-select')){
				var dir = ($(el).hasClassName('up')?1:-1);
				if ($(el).hasClassName('hour')){
					this.hour+=dir;
					this.hour = parseInt(this.hour);
					if (this.hour>23) this.hour=0;else if(this.hour<0) this.hour=23;
					$($(el).up(2).getElementsByClassName('s_hour').first()).update((this.hour<10?'0'+this.hour:this.hour));
				}
				else{ 
					this.minute+=dir; 
					this.minute = parseInt(this.minute);
					if (this.minute>59) this.minute=0; else if(this.minute<0) this.minute=59;
					$($(el).up(2).getElementsByClassName('s_minute').first()).update((this.minute<10?'0'+this.minute:this.minute));
				}
				if (!this.options.multiple && (t = $(this.options.input))){
					$(t).value = this.convert_date($H({'year':this.year,'month':this.month,'day':this.day,'hour':this.hour,'minute':this.minute}));
				}
			}
	},
	convert_date : function(obj){
/* Форматы для парсинга
	%Y - год
	%y - год кратко
	%m - месяц (число)
	%M - месяц (название)
	%d - день
	%D - день недели (название)
	%I - минуты
	%H - часы
*/
		if (k = obj.values()){}
		else if(obj['year']) {
			var k = [];
			k[0] = obj['year'];
			k[1] = obj['month'];
			k[2] = obj['day'];
			k[3] = (obj['hour']<10?'0'+obj['hour']:obj['hour']);
			k[4] = (obj['minute']<10?'0'+obj['minute']:obj['minute'])+'kk';
		}
		try{
			var tmp = this.options.format;
			tmp = tmp.replace(/\%y/g,(k[0]).toString().substr(2));
			tmp = tmp.replace(/\%Y/gi,k[0]);
			var mm = parseInt(k[1]+1); tmp = tmp.replace(/\%m/g,(mm<10?'0'+mm:mm));
			tmp = tmp.replace(/\%d/g, (k[2]<10?'0'+k[2]:k[2]));
			tmp = tmp.replace(/\%H/g, (k[3]<10?'0'+k[3]:k[3]));
			tmp = tmp.replace(/\%I/g, (k[4]<10?'0'+k[4]:k[4]));
			tmp = tmp.replace(/\%D/g,this.DowsShort[this.dow]);
			tmp = tmp.replace(/\%M/g, this.MonthsNames[this.month]);
		}catch(e){};
		return tmp;
	},
	_cell_day : function(index){return '<td class="calendar-days">'+this.DowsShort[index]+'</td>';},
	_draw_day_header : function(){
		var tr = '<tr>'; 
		if (this.options.display.week_numbers){tr+='<td> </td>';}
		for(i=this.options.FirstDayOfWeek;i<7;i++) tr+=this._cell_day(i);
		if (this.options.FirstDayOfWeek!=0){
			for(i=6-this.options.FirstDayOfWeek;i<6-this.options.FirstDayOfWeek;i++){tr+=this._cell_day(i+1);}
			for(i=0;i<this.options.FirstDayOfWeek;i++) tr+=this._cell_day(i);
		}
		return tr;
	},
	_get_date : function(val){if (val){ val = $H(val).values(); return {'year':val[0],'month':val[1],'day':val[2]};} return false;},
	getWeekNr : function(opts){
		for(i=opts.length;i>0;i--){
			if (opts[i]) var kopts = this._get_date(opts[i]);
			if (kopts) if (kopts['day']!=null){
				var onejan = new Date(kopts['year'],0,1,0,0,0);
				var now = new Date(kopts['year'],kopts['month'],kopts['day']);
				return Math.ceil((((now - onejan) / 86400000) + onejan.getDay())/7);
			}
		}
	},


	_get_days : function(opts){
		opts = Object.extend({year : this.year,month : this.month, day : this.day},opts || {});
		var startDay	= new Date(opts.year,opts.month,1);
		var endDay		= new Date(opts.year,opts.month+1,0);
		var month = [];
		var sdd = startDay.getDate();
		var edd = endDay.getDate();
		for(i=sdd;i<edd+1;i++){ month[month.length] = $H({'year':opts.year,'month':opts.month,'day':i}); }
		var std_a = startDay.getDay();
		if (std_a!=this.options.FirstDayOfWeek){
			var tmp = [];
			var std = ((std_a-this.options.FirstDayOfWeek-1)>=0)?
					(-(std_a-this.options.FirstDayOfWeek-1)):
					(std_a-this.options.FirstDayOfWeek-1);
			if (std_a==0) std =-5;
			var start = new Date(opts.year,opts.month,std).getDate();
			var stop = new Date(opts.year,opts.month,0).getDate();
			for(day=start;day<stop+1;day++){
				tmp[tmp.length] = $H({'year':(opts.month-1<0)?opts.year-1:opts.year,'month':(opts.month-1<0)?11:opts.month-1,'day':day});
			}
			month = [tmp,$A(month)].flatten();
		}
		var edd = endDay.getDay();
		if (edd<7){var tmp = [];
			var mm = parseInt(6-(edd)+this.options.FirstDayOfWeek);
			if (mm<7 && mm>0){
				var ed = new Date(opts.year,opts.month+1,mm).getDate();
				for(day=1;day<ed+1;day++){
					tmp[tmp.length]  = $H({'year':(opts.month+1<12)?opts.year:opts.year+1,'month':(opts.month+1<12)?opts.month+1:0,'day':day});
				}
				month = [$A(month),tmp].flatten();
			}
		} 
		var m = [], k = [];
		for(i=1;i<month.length+1;i++){
			if (i%7==0 && i!=0){ k[k.length] = month[i-1]; m[m.length]=k; k = []; }
			else k[k.length] = month[i-1];
		}
		this.month_days = m;
		return this.month_days;
	}

});
/// for debug
var p = function(txt,app){
	if (el = $('debug')){
		var d = new Date();
		txt = '<span style="background:#BBBBBB;">['+d.getDate()+'/'+d.getMonth()+'/'+d.getFullYear()+' '+d.getHours()+':'+d.getMinutes()+':'+d.getSeconds()+'.'+d.getMilliseconds()+']</span> '+txt;
		if (!app) $(el).update(txt+'<br/>\n');else $(el).innerHTML=txt+'<br/>\n'+$(el).innerHTML;
	}
}
/// for debug

Date.prototype.parseDate = function(str, fmt) {
	var today = this; 
	var y = 0; var m = -1; var d = 0;
	var a = str.split(/\W+/);
	var b = fmt.match(/%./g);
	var i = 0, j = 0; var hr = 0; var min = 0;
	for (i = 0; i < a.length; ++i) {
		if (!a[i]) continue;
		switch (b[i]) {
			case "%d": case "%e": d = parseInt(a[i], 10); break;
			case "%m": m = parseInt(a[i], 10) - 1; break;
			case "%Y": case "%y": y = parseInt(a[i], 10); (y < 100) && (y += (y > 29) ? 1900 : 2000); break;
			case "%H": case "%I": case "%k": case "%l": hr = parseInt(a[i], 10); break;
			case "%P":
			case "%p": if (/pm/i.test(a[i]) && hr < 12) hr += 12; else if (/am/i.test(a[i]) && hr >= 12) hr -= 12; break;
			case "%I": min = parseInt(a[i], 10); break;
		}
	}
	if (isNaN(y)) y = today.getFullYear();
	if (isNaN(m)) m = today.getMonth();
	if (isNaN(d)) d = today.getDate();
	if (isNaN(hr)) hr = today.getHours();
	if (isNaN(min)) min = today.getMinutes();
	if (y != 0 && m != -1 && d != 0) return new Date(y, m, d, hr, min, 0);
	y = 0; m = -1; d = 0;
	for (i = 0; i < a.length; ++i) {
		if (parseInt(a[i], 10) <= 12 && m == -1) { m = a[i]-1;}
		else if (parseInt(a[i], 10) > 31 && y == 0) { y = parseInt(a[i], 10); (y < 100) && (y += (y > 29) ? 1900 : 2000);}
		else if (d == 0) { d = a[i];}
	}
	if (y == 0) y = today.getFullYear();
	if (m != -1 && d != 0) return new Date(y, m, d, hr, min, 0);
	return today;
}
