var Calendar=Class.create()
Calendar.VERSION='1.0'
Calendar.DAY_NAMES=new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday')
Calendar.SHORT_DAY_NAMES=new Array('S','M','T','W','T','F','S','S')
Calendar.MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December')
Calendar.SHORT_MONTH_NAMES=new Array('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec')
Calendar.NAV_PREVIOUS_YEAR=-2
Calendar.NAV_PREVIOUS_MONTH=-1
Calendar.NAV_TODAY=0
Calendar.NAV_NEXT_MONTH=1
Calendar.NAV_NEXT_YEAR=2
Calendar._checkCalendar=function(event){if(!window._popupCalendar)
return false
if(Element.descendantOf(Event.element(event),window._popupCalendar.container))
return
window._popupCalendar.callCloseHandler()
return Event.stop(event)}
Calendar.handleMouseDownEvent=function(event)
{Event.observe(document,'mouseup',Calendar.handleMouseUpEvent)
Event.stop(event)}
Calendar.handleMouseUpEvent=function(event)
{var el=Event.element(event)
var calendar=el.calendar
var isNewDate=false
if(!calendar)return false
if(typeof el.navAction=='undefined')
{if(calendar.currentDateElement){Element.removeClassName(calendar.currentDateElement,'selected')
Element.addClassName(el,'selected')
calendar.shouldClose=(calendar.currentDateElement==el)
if(!calendar.shouldClose)calendar.currentDateElement=el}
calendar.date.setDateOnly(el.date)
isNewDate=true
calendar.shouldClose=!el.hasClassName('otherDay')
var isOtherMonth=!calendar.shouldClose
if(isOtherMonth)calendar.update(calendar.date)}
else
{var date=new Date(calendar.date)
if(el.navAction==Calendar.NAV_TODAY)
date.setDateOnly(new Date())
var year=date.getFullYear()
var mon=date.getMonth()
function setMonth(m){var day=date.getDate()
var max=date.getMonthDays(m)
if(day>max)date.setDate(max)
date.setMonth(m)}
switch(el.navAction){case Calendar.NAV_PREVIOUS_YEAR:if(year>calendar.minYear)
date.setFullYear(year-1)
break
case Calendar.NAV_PREVIOUS_MONTH:if(mon>0){setMonth(mon-1)}
else if(year-->calendar.minYear){date.setFullYear(year)
setMonth(11)}
break
case Calendar.NAV_TODAY:break
case Calendar.NAV_NEXT_MONTH:if(mon<11){setMonth(mon+1)}
else if(year<calendar.maxYear){date.setFullYear(year+1)
setMonth(0)}
break
case Calendar.NAV_NEXT_YEAR:if(year<calendar.maxYear)
date.setFullYear(year+1)
break}
if(!date.equalsTo(calendar.date)){calendar.setDate(date)
isNewDate=true}else if(el.navAction==0){isNewDate=(calendar.shouldClose=true)}}
if(isNewDate)event&&calendar.callSelectHandler()
if(calendar.shouldClose)event&&calendar.callCloseHandler()
Event.stopObserving(document,'mouseup',Calendar.handleMouseUpEvent)
return Event.stop(event)}
Calendar.defaultSelectHandler=function(calendar)
{if(!calendar.dateField)return false
if(calendar.dateField.tagName=='DIV')
Element.update(calendar.dateField,calendar.date.print(calendar.dateFormat))
else if(calendar.dateField.tagName=='INPUT'){calendar.dateField.value=calendar.date.print(calendar.dateFormat)}
if(typeof calendar.dateField.onchange=='function')
calendar.dateField.onchange()
if(calendar.shouldClose)calendar.callCloseHandler()}
Calendar.defaultCloseHandler=function(calendar)
{calendar.hide()}
Calendar.setup=function(params)
{function param_default(name,def){if(!params[name])params[name]=def}
param_default('dateField',null)
param_default('triggerElement',null)
param_default('parentElement',null)
param_default('selectHandler',null)
param_default('closeHandler',null)
if(params.parentElement)
{var calendar=new Calendar(params.parentElement)
calendar.setSelectHandler(params.selectHandler||Calendar.defaultSelectHandler)
if(params.dateFormat)
calendar.setDateFormat(params.dateFormat)
if(params.dateField){calendar.setDateField(params.dateField)
calendar.parseDate(calendar.dateField.innerHTML||calendar.dateField.value)}
calendar.show()
return calendar}
else
{var triggerElement=$(params.triggerElement||params.dateField)
triggerElement.onclick=function(){var calendar=new Calendar()
calendar.setSelectHandler(params.selectHandler||Calendar.defaultSelectHandler)
calendar.setCloseHandler(params.closeHandler||Calendar.defaultCloseHandler)
if(params.dateFormat)
calendar.setDateFormat(params.dateFormat)
if(params.dateField){calendar.setDateField(params.dateField)
calendar.parseDate(calendar.dateField.innerHTML||calendar.dateField.value)}
if(params.dateField)
Date.parseDate(calendar.dateField.value||calendar.dateField.innerHTML,calendar.dateFormat)
calendar.showAtElement(triggerElement)
return calendar}}}
Calendar.prototype={container:null,selectHandler:null,closeHandler:null,minYear:1900,maxYear:2100,dateFormat:'%Y-%m-%d',date:new Date(),currentDateElement:null,shouldClose:false,isPopup:true,dateField:null,initialize:function(parent)
{if(parent)
this.create($(parent))
else
this.create()},update:function(date)
{var calendar=this
var today=new Date()
var thisYear=today.getFullYear()
var thisMonth=today.getMonth()
var thisDay=today.getDate()
var month=date.getMonth();var dayOfMonth=date.getDate();if(date.getFullYear()<this.minYear)
date.setFullYear(this.minYear)
else if(date.getFullYear()>this.maxYear)
date.setFullYear(this.maxYear)
this.date=new Date(date)
date.setDate(1)
date.setDate(-(date.getDay())+1)
Element.getElementsBySelector(this.container,'tbody tr').each(function(row,i){var rowHasDays=false
row.immediateDescendants().each(function(cell,j){var day=date.getDate()
var dayOfWeek=date.getDay()
var isCurrentMonth=(date.getMonth()==month)
cell.className=''
cell.date=new Date(date)
Element.update(cell,day)
if(!isCurrentMonth)
Element.addClassName(cell,'otherDay')
else
rowHasDays=true
if(isCurrentMonth&&day==dayOfMonth){Element.addClassName(cell,'selected')
calendar.currentDateElement=cell}
if(date.getFullYear()==thisYear&&date.getMonth()==thisMonth&&day==thisDay)
Element.addClassName(cell,'today')
if([0,6].indexOf(dayOfWeek)!=-1)
Element.addClassName(cell,'weekend')
date.setDate(day+1)})
!rowHasDays?Element.hide(row):Element.show(row)})
this.container.getElementsBySelector('td.title')[0].update(Calendar.MONTH_NAMES[month]+' '+this.date.getFullYear())},create:function(parent)
{if(!parent){parent=document.getElementsByTagName('body')[0]
this.isPopup=true}else{this.isPopup=false}
var table=Builder.node('table')
var thead=Builder.node('thead')
table.appendChild(thead)
var row=Builder.node('tr',[Builder.node('td',{colSpan:7,className:'title'})])
thead.appendChild(row)
row=Builder.node('tr')
this._drawButtonCell(row,'&#x00ab;',1,Calendar.NAV_PREVIOUS_YEAR)
this._drawButtonCell(row,'&#x2039;',1,Calendar.NAV_PREVIOUS_MONTH)
this._drawButtonCell(row,'Today',3,Calendar.NAV_TODAY)
this._drawButtonCell(row,'&#x203a;',1,Calendar.NAV_NEXT_MONTH)
this._drawButtonCell(row,'&#x00bb;',1,Calendar.NAV_NEXT_YEAR)
thead.appendChild(row)
row=Builder.node('tr')
for(var i=0;i<7;++i){cell=Builder.node('th',Calendar.SHORT_DAY_NAMES[i])
if(i==0||i==6)
Element.addClassName(cell,'weekend')
row.appendChild(cell)}
thead.appendChild(row)
var tbody=table.appendChild(Builder.node('tbody'))
for(i=6;i>0;--i){row=tbody.appendChild(Builder.node('tr',{className:'days'}))
for(var j=7;j>0;--j){cell=row.appendChild(Builder.node('td'))
cell.calendar=this}}
this.container=Builder.node('div',{className:'calendar'})
if(this.isPopup){Element.setStyle(this.container,{position:'absolute',display:'none'})
Element.addClassName(this.container,'popup')}
this.container.appendChild(table)
this.update(this.date)
Event.observe(this.container,'mousedown',Calendar.handleMouseDownEvent)
parent.appendChild(this.container)},_drawButtonCell:function(parent,text,colSpan,navAction)
{var cell=Builder.node('td')
if(colSpan>1)cell.colSpan=colSpan
cell.className='button'
cell.calendar=this
cell.navAction=navAction
cell.innerHTML=text
cell.unselectable='on'
parent.appendChild(cell)
return cell},callSelectHandler:function()
{if(this.selectHandler)
this.selectHandler(this,this.date.print(this.dateFormat))},callCloseHandler:function()
{if(this.closeHandler)
this.closeHandler(this)},show:function()
{Element.show(this.container)
if(this.isPopup){window._popupCalendar=this
Event.observe(document,'mousedown',Calendar._checkCalendar)}},showAt:function(x,y)
{Element.setStyle(this.container,{left:x+'px',top:y+'px'})
this.show()},showAtElement:function(element)
{var pos=Position.cumulativeOffset(element)
this.showAt(pos[0],pos[1])},hide:function()
{if(this.isPopup)
Event.stopObserving(document,'mousedown',Calendar._checkCalendar)
Element.hide(this.container)},parseDate:function(str,format)
{if(!format)
format=this.dateFormat
this.setDate(Date.parseDate(str,format))},setSelectHandler:function(selectHandler)
{this.selectHandler=selectHandler},setCloseHandler:function(closeHandler)
{this.closeHandler=closeHandler},setDate:function(date)
{if(!date.equalsTo(this.date))
this.update(date)},setDateFormat:function(format)
{this.dateFormat=format},setDateField:function(field)
{this.dateField=$(field)},setRange:function(minYear,maxYear)
{this.minYear=minYear
this.maxYear=maxYear}}
window._popupCalendar=null
Date.DAYS_IN_MONTH=new Array(31,28,31,30,31,30,31,31,30,31,30,31)
Date.SECOND=1000
Date.MINUTE=60*Date.SECOND
Date.HOUR=60*Date.MINUTE
Date.DAY=24*Date.HOUR
Date.WEEK=7*Date.DAY
Date.parseDate=function(str,fmt){var today=new Date();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"%b":case"%B":for(j=0;j<12;++j){if(Calendar.MONTH_NAMES[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){m=j;break;}}
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"%M":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(a[i].search(/[a-zA-Z]+/)!=-1){var t=-1;for(j=0;j<12;++j){if(Calendar.MONTH_NAMES[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){t=j;break;}}
if(t!=-1){if(m!=-1){d=m+1;}
m=t;}}else 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;};Date.prototype.getMonthDays=function(month){var year=this.getFullYear()
if(typeof month=="undefined")
month=this.getMonth()
if(((0==(year%4))&&((0!=(year%100))||(0==(year%400))))&&month==1)
return 29
else
return Date.DAYS_IN_MONTH[month]};Date.prototype.getDayOfYear=function(){var now=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var then=new Date(this.getFullYear(),0,0,0,0,0);var time=now-then;return Math.floor(time/Date.DAY);};Date.prototype.getWeekNumber=function(){var d=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var DoW=d.getDay();d.setDate(d.getDate()-(DoW+6)%7+3);var ms=d.valueOf();d.setMonth(0);d.setDate(4);return Math.round((ms-d.valueOf())/(7*864e5))+1;};Date.prototype.equalsTo=function(date){return((this.getFullYear()==date.getFullYear())&&(this.getMonth()==date.getMonth())&&(this.getDate()==date.getDate())&&(this.getHours()==date.getHours())&&(this.getMinutes()==date.getMinutes()));};Date.prototype.setDateOnly=function(date){var tmp=new Date(date);this.setDate(1);this.setFullYear(tmp.getFullYear());this.setMonth(tmp.getMonth());this.setDate(tmp.getDate());};Date.prototype.print=function(str){var m=this.getMonth();var d=this.getDate();var y=this.getFullYear();var wn=this.getWeekNumber();var w=this.getDay();var s={};var hr=this.getHours();var pm=(hr>=12);var ir=(pm)?(hr-12):hr;var dy=this.getDayOfYear();if(ir==0)
ir=12;var min=this.getMinutes();var sec=this.getSeconds();s["%a"]=Calendar.SHORT_DAY_NAMES[w];s["%A"]=Calendar.DAY_NAMES[w];s["%b"]=Calendar.SHORT_MONTH_NAMES[m];s["%B"]=Calendar.MONTH_NAMES[m];s["%C"]=1+Math.floor(y/100);s["%d"]=(d<10)?("0"+d):d;s["%e"]=d;s["%H"]=(hr<10)?("0"+hr):hr;s["%I"]=(ir<10)?("0"+ir):ir;s["%j"]=(dy<100)?((dy<10)?("00"+dy):("0"+dy)):dy;s["%k"]=hr;s["%l"]=ir;s["%m"]=(m<9)?("0"+(1+m)):(1+m);s["%M"]=(min<10)?("0"+min):min;s["%n"]="\n";s["%p"]=pm?"PM":"AM";s["%P"]=pm?"pm":"am";s["%s"]=Math.floor(this.getTime()/1000);s["%S"]=(sec<10)?("0"+sec):sec;s["%t"]="\t";s["%U"]=s["%W"]=s["%V"]=(wn<10)?("0"+wn):wn;s["%u"]=w+1;s["%w"]=w;s["%y"]=(''+y).substr(2,2);s["%Y"]=y;s["%%"]="%";return str.gsub(/%./,function(match){return s[match]||match});};Date.prototype.__msh_oldSetFullYear=Date.prototype.setFullYear;Date.prototype.setFullYear=function(y){var d=new Date(this);d.__msh_oldSetFullYear(y);if(d.getMonth()!=this.getMonth())
this.setDate(28);this.__msh_oldSetFullYear(y);}