/*
* (c) 2005-2009 Richard Cowin (http://openrico.org)
* (c) 2005-2009 Matt Brown (http://dowdybrown.com)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
// Inspired by code originally written by Tan Ling Wee on 2 Dec 2001
Rico.CalendarControl = function(id,options) {
this.initialize(id,options);
};
Rico.CalendarControl.prototype = {
/**
* @class Implements a pop-up Gregorian calendar.
* Dates of adoption of the Gregorian calendar vary by country - accurate as a US & British calendar from 14 Sept 1752 to present.
* Mark special dates with calls to addHoliday()
* @extends Rico.Popup
* @constructs
* @param id unique identifier
* @param options object may contain any of the following:
* - startAt
- week starts with 0=sunday, 1=monday? default=0
* - showWeekNumber
- show week number in first column? default=0
* - showToday
- show "Today is..." in footer? default=1
* - dateFmt
- date format for return value (one of values accepted by {@link Date#formatDate}), default=ISO8601
* - minDate
- earliest selectable date? default=today-50 years
* - maxDate
- last selectable date? default=today+50 years
*
*/
initialize: function(id,options) {
this.id=id;
var today=new Date();
Rico.extend(this, new Rico.Popup());
Rico.extend(this.options, {
ignoreClicks:true,
startAt : 0,
showWeekNumber : 0,
showToday : 1,
dateFmt : 'ISO8601',
minDate : new Date(today.getFullYear()-50,0,1),
maxDate : new Date(today.getFullYear()+50,11,31)
});
Rico.extend(this.options, options || {});
/**
* alias for closePopup
* @function
*/
this.close=this.closePopup;
this.bPageLoaded=false;
this.img=[];
this.Holidays={};
this.weekString=Rico.getPhraseById("calWeekHdg");
this.re=/^\s*(\w+)(\W)(\w+)(\W)(\w+)/i;
this.setDateFmt(this.options.dateFmt);
},
setDateFmt: function(fmt) {
this.dateFmt=(fmt=='rico') ? Rico.dateFmt : fmt;
Rico.log(this.id+' date format set to '+this.dateFmt);
this.dateParts={};
if (this.re.exec(this.dateFmt)) {
this.dateParts[RegExp.$1]=0;
this.dateParts[RegExp.$3]=1;
this.dateParts[RegExp.$5]=2;
}
},
/**
* Call before displaying calendar to highlight special days
* @param d day (1-31)
* @param m month (1-12)
* @param y year (0 implies a repeating holiday)
* @param desc description
* @param bgColor background color for cell displaying this day (CSS value, defaults to '#DDF')
* @param txtColor text color for cell displaying this day (CSS value), if not specified it is displayed with the same color as other days
*/
addHoliday : function(d, m, y, desc, bgColor, txtColor) {
this.Holidays[this.holidayKey(y,m-1,d)]={desc:desc, txtColor:txtColor, bgColor:bgColor || '#DDF'};
},
/** @private */
holidayKey : function(y,m,d) {
return 'h'+Rico.zFill(y,4)+Rico.zFill(m,2)+Rico.zFill(d,2);
},
atLoad : function() {
Rico.log('Calendar#atLoad: '+this.id);
var div=Rico.$(this.id);
if (div) {
this.setDiv(div);
} else {
this.createContainer();
this.container.id=this.id;
}
Rico.addClass(this.content, Rico.theme.calendar || 'ricoCalContainer');
this.content.style.display='block'; // override jquery ui
// Navigation controls
this.heading=this.content.appendChild(document.createElement("div"));
this.heading.className='RicoCalHeading';
if (Rico.theme.calendarHeading) Rico.addClass(this.heading,Rico.theme.calendarHeading)
this.heading.appendChild(this._createTitleSection('Month'));
this.heading.appendChild(this._createTitleSection('Year'));
new Rico.HoverSet(this.heading.getElementsByTagName('a'));
if (this.position == 'absolute') this.heading.appendChild(Rico.closeButton(Rico.eventHandle(this,'close')));
this.maintab=document.createElement("table");
this.maintab.cellSpacing=2;
this.maintab.cellPadding=0;
this.maintab.border=0;
this.maintab.style.borderCollapse='separate';
this.maintab.className='ricoCalTab';
if (Rico.theme.calendarTable) Rico.addClass(this.maintab,Rico.theme.calendarTable)
this.tbody=Rico.getTBody(this.maintab);
var r,c,i,j,img,dow,a,s,tab;
this.colStart=this.options.showWeekNumber ? 1 : 0;
for (i=0; i<7; i++) {
r=this.tbody.insertRow(-1);
r.className='row'+i;
for (c=0; c<7+this.colStart; c++) {
r.insertCell(-1);
}
}
r=this.tbody.rows[0];
r.className='ricoCalDayNames';
if (this.options.showWeekNumber) {
r.cells[0].innerHTML=this.weekString;
for (i=0; i<7; i++) {
this.tbody.rows[i].cells[0].className='ricoCalWeekNum';
}
}
this.styles=[];
for (i=0; i<7; i++) {
dow=(i+this.options.startAt) % 7;
r.cells[i+this.colStart].innerHTML=Rico.dayAbbr(dow);
this.styles[i]='ricoCal'+dow;
}
// table footer (today)
if (this.options.showToday) {
this.tfoot=this.maintab.createTFoot();
r=this.tfoot.insertRow(-1);
this.todayCell=r.insertCell(-1);
this.todayCell.colSpan=7+this.colStart;
if (Rico.theme.calendarFooter) Rico.addClass(this.todayCell,Rico.theme.calendarFooter);
Rico.eventBind(this.todayCell,"click", Rico.eventHandle(this,'selectNow'), false);
}
this.content.appendChild(this.maintab);
new Rico.HoverSet(this.tbody.getElementsByTagName('td'),{ hoverNodes: function(e) { return e.innerHTML.match(/^\d+$/) ? [e] : []; } });
// month selector
this.monthPopup=new Rico.Popup(document.createElement("div"));
this.monthPopup.closePopup();
tab=document.createElement("table");
tab.className='ricoCalMenu';
if (Rico.theme.calendarPopdown) Rico.addClass(tab,Rico.theme.calendarPopdown);
tab.cellPadding=2;
tab.cellSpacing=0;
tab.border=0;
tab.style.borderCollapse='separate';
tab.style.margin='0px';
for (i=0; i<4; i++) {
r=tab.insertRow(-1);
for (j=0; j<3; j++) {
c=r.insertCell(-1);
a=document.createElement("a");
a.innerHTML=Rico.monthAbbr(i*3+j);
a.name=i*3+j;
if (Rico.theme.calendarDay) Rico.addClass(a,Rico.theme.calendarDay);
c.appendChild(a);
Rico.eventBind(a,"click", Rico.eventHandle(this,'selectMonth'), false);
}
}
new Rico.HoverSet(tab.getElementsByTagName('a'));
this.monthPopup.content.appendChild(tab);
this.container.appendChild(this.monthPopup.container);
// year selector
this.yearPopup=new Rico.Popup(document.createElement("div"));
this.yearPopup.closePopup();
this.yearPopup.content.className='ricoCalYearPrompt';
if (Rico.theme.calendarPopdown) Rico.addClass(this.yearPopup.content,Rico.theme.calendarPopdown);
var p1=document.createElement("p");
p1.innerHTML=Rico.getPhraseById("calYearRange",this.options.minDate.getFullYear(),this.options.maxDate.getFullYear());
var p2=document.createElement("p");
this.yearInput=p2.appendChild(document.createElement("input"));
this.yearInput.maxlength=4;
this.yearInput.size=4;
Rico.eventBind(this.yearInput,"keyup", Rico.eventHandle(this,'yearKey'), false);
a=Rico.floatButton('Checkmark', Rico.eventHandle(this,'processPopUpYear'));
p2.appendChild(a);
a=Rico.floatButton('Cancel', Rico.eventHandle(this,'popDownYear'));
p2.appendChild(a);
this.yearPopup.content.appendChild(p1);
this.yearPopup.content.appendChild(p2);
this.container.appendChild(this.yearPopup.container);
this.yearPopup.container.style.left='';
this.yearPopup.container.style.right='5px';
this.yearPopup.container.style.zIndex=10;
// fix anchors so they work in IE6
a=this.content.getElementsByTagName('a');
for (i=0; i this.options.maxDate.getFullYear()) return false;
if (yr == this.options.maxDate.getFullYear() && mo > this.options.maxDate.getMonth()) return false;
return true;
},
incMonth : function() {
var newMonth=this.monthSelected+1;
var newYear=this.yearSelected;
if (newMonth>11) {
newMonth=0;
newYear++;
}
if (!this.isValidMonth(newYear,newMonth)) return;
this.monthSelected=newMonth;
this.yearSelected=newYear;
this.constructCalendar();
},
decMonth : function() {
var newMonth=this.monthSelected-1;
var newYear=this.yearSelected;
if (newMonth<0) {
newMonth=11;
newYear--;
}
if (!this.isValidMonth(newYear,newMonth)) return;
this.monthSelected=newMonth;
this.yearSelected=newYear;
this.constructCalendar();
},
/** @private */
selectMonth : function(e) {
var el=Rico.eventElement(e);
this.monthSelected=parseInt(el.name,10);
this.constructCalendar();
Rico.eventStop(e);
},
popUpMonth : function(e) {
Rico.eventStop(e);
if (this.monthPopup.visible()) {
this.popDownMonth();
return;
}
this.popDownYear();
if (Rico.isIE && Rico.ieVersion < 7) {
// fix position absolute inside container without hasLayout
this.monthPopup.openPopup(null, this.heading.offsetHeight+2);
this.monthPopup.container.style.left='';
} else {
this.monthPopup.openPopup(3, this.heading.offsetHeight+2);
}
return false;
},
popDownMonth : function() {
this.monthPopup.closePopup();
},
popDownYear : function() {
this.yearPopup.closePopup();
this.yearInput.disabled=true; // make sure this does not get submitted
},
/**
* Prompt for year
*/
popUpYear : function(e) {
Rico.eventStop(e);
if (this.yearPopup.visible()) {
this.popDownYear();
return;
}
this.popDownMonth();
this.yearInput.disabled=false;
this.yearInput.value=''; // this.yearSelected
this.yearPopup.openPopup(null, this.heading.offsetHeight+2);
var self=this;
setTimeout(function() { self.yearInput.focus(); }, 10); // ie8 has issues without this delay
return false;
},
yearKey : function(e) {
switch (Rico.eventKey(e)) {
case 27: this.popDownYear(); Rico.eventStop(e); return false;
case 13: this.processPopUpYear(); Rico.eventStop(e); return false;
}
return true;
},
processPopUpYear : function() {
var newYear=this.yearInput.value;
newYear=parseInt(newYear,10);
if (isNaN(newYear) || newYearthis.options.maxDate.getFullYear()) {
alert(Rico.getPhraseById("calInvalidYear"));
} else {
this.yearSelected=newYear;
this.popDownYear();
this.constructCalendar();
}
},
incYear : function() {
if (this.yearSelected>=this.options.maxDate.getFullYear()) return;
this.yearSelected++;
this.constructCalendar();
},
decYear : function() {
if (this.yearSelected<=this.options.minDate.getFullYear()) return;
this.yearSelected--;
this.constructCalendar();
},
// tried a number of different week number functions posted on the net
// this is the only one that produced consistent results when comparing week numbers for December and the following January
WeekNbr : function(year,month,day) {
var when = new Date(year,month,day);
var newYear = new Date(year,0,1);
var offset = 7 + 1 - newYear.getDay();
if (offset == 8) offset = 1;
var daynum = ((Date.UTC(year,when.getMonth(),when.getDate(),0,0,0) - Date.UTC(year,0,1,0,0,0)) /1000/60/60/24) + 1;
var weeknum = Math.floor((daynum-offset+7)/7);
if (weeknum == 0) {
year--;
var prevNewYear = new Date(year,0,1);
var prevOffset = 7 + 1 - prevNewYear.getDay();
weeknum = (prevOffset == 2 || prevOffset == 8) ? 53 : 52;
}
return weeknum;
},
constructCalendar : function() {
var aNumDays = [31,0,31,30,31,30,31,31,30,31,30,31];
var startDate = new Date (this.yearSelected,this.monthSelected,1);
var endDate,numDaysInMonth,i,colnum;
if (typeof this.monthSelected!='number' || this.monthSelected>=12 || this.monthSelected<0) {
alert('ERROR in calendar: monthSelected='+this.monthSelected);
return;
}
if (this.monthSelected==1) {
endDate = new Date (this.yearSelected,this.monthSelected+1,1);
endDate = new Date (endDate - (24*60*60*1000));
numDaysInMonth = endDate.getDate();
} else {
numDaysInMonth = aNumDays[this.monthSelected];
}
var dayPointer = startDate.getDay() - this.options.startAt;
if (dayPointer<0) dayPointer+=7;
this.popDownMonth();
this.popDownYear();
//this.bgcolor=Rico.getStyle(this.tbody,'background-color');
//this.bgcolor=this.bgcolor.replace(/\"/g,'');
if (this.options.showWeekNumber) {
for (i=1; i<7; i++) {
this.tbody.rows[i].cells[0].innerHTML=' ';
}
}
for ( i=0; i maxyr) this.oyearSelected-=100;
}
} else {
if (curval) {
alert('ERROR: invalid date passed to calendar ('+curval+')');
}
}
if (this.oyearSelected > 0) {
this.dateSelected=this.odateSelected;
this.monthSelected=this.omonthSelected;
this.yearSelected=this.oyearSelected;
} else {
this.dateSelected=this.dateNow;
this.monthSelected=this.monthNow;
this.yearSelected=this.yearNow;
}
this.constructCalendar();
this.openPopup();
}
};