Here are all kinds of tips and tricks that will help you greatly in mastering and enjoying FlatCalendarXP. Please come to visit our web site http://www.calendarxp.net from time to time and you will find more interesting stuff.
Simply insert the calendar tag to anywhere in your webpage, like a common HTML tag(NN4 needs more care). It'll then turn into a beautiful calendar in your browser. Magic?!
Create an empty directory, put the engine files in. Then go inside the themes
directory and pick up a theme you like, copy all files of the theme to the
directory just created. Finally, if you want to add agendas, you may start
with the agenda.js
in the HelloWorld demo, using it as a template
to create your own.
It's quite straightforward, please check out the Tutorials for more details and samples.
If you are upgrading from within the same major version, e.g. from 6.0 to 6.1, all you need is to copy the new engine files to overwrite the old ones. That's all, there is no need to change your production page or any theme options. It's quite easy and painless.
If you want to upgrade across major versions, e.g. from 5.2 to 6.0, then in addition to replacing the engine files you may also need to migrate the old theme options to the new theme if you want to keep your old theme working. Most of the time you don't need to change anything in your web page unless it is explicitly required by the new version in its release notes.
FlatCalendarXP comes with great themes optimized for English environment. But it's quite simple to localize them since all text strings are stored externally in either the theme files or the agenda file. All you need is to open them with a text editor like notepad and replace the English strings with your own language strings, then modify the css file to use corresponding font families.
If your page was generated dynamically by ASP server and you saw rectangles
instead of normal fonts in Netscape 4.x, please set the http charset header
to "" by using Response.Charset=""
in your
asp page. This header has to be set before anything else.
If your page aims to support multiple languages(i18n), please follow the instructions in the i18n tutorials to create a simple i18n framework.
Depending on the cgi service you are using the solution might be a little different. The basic idea is to make your cgi service produce the agenda.js dynamically, e.g. from a JSP or ASP. We had it detailed in the "Setting up agendas & holidays" tutorial.
When first loaded, the calendar will show up with the month set by the default-month
parameter in the name & id properties of the calendar tag. For example,
you may set the name&id of the calendar tag to "[2002,10]:normal:agenda.js"
and the calendar will then start on Oct 2002. Or you may choose "[gToday[0],gToday[1]+1]:normal:agenda.js"
so that it will always show the next month at the beginning.
There are 2 files involved in a theme that control the look of the calendar
- theme-name.js
& theme-name.css
. They are all
self-documented and you may use any text editor to fulfill the change. Please
refer to "Working with themes" tutorial for
details.
Go to the theme-name.css
file and find the .CalCell
class, change the value of text-align
to left
, center
or right
. To align it vertically, you'll have to turn it into
a table cell first by adding a CSS2 display
rule and then a vertical-align
rule as following:
.CalCell {...; display:table-cell; vertical-align:middle;
}
For example, you want to perform the alert() function.
fOnChange()
callback plugin in
the plugins.js
file, as following:
function fOnChange(y,m,d) {
if (d>0) alert([y,m,d]+' will be selected.');
return false; // return true to cancel the change.
}
fAfterSelected()
callback
plugin in the plugins.js
file, as following: function fAfterSelected(y,m,d) {
alert([y,m,d]+' has been selected.');
}
gsAction
option
in the theme-name.js
file, as following:
var gsAction="alert([y,m,d]+' has been
selected.');";
Simply change the #outerTable
CSS style in the theme-name.css
file. Since version 7.0 the style of iframe calendar tag no longer contains
any border style properties.
Note: NN4 doesn't support outside border, it can only have the table
border set by gsOuterTable
in theme-name.js
file.
The agenda event is set per day. If you have 2 more events in the same day,
you need to code your own functions to merge them into 1 agenda. And you may
take advantage of the html
parameter of the agenda to stuff in
more messages and even images into the calendar cell.
For example, add the following function to the top of agenda.js and replace all fAddEvent
calls with fAppendEvent
.
function fAppendEvent(y,m,d,message,action,bgcolor,fgcolor,bgimg,boxit,html) {
var ag=fGetEvent(y,m,d);
if (ag==null) fAddEvent(y,m,d,message,action,bgcolor,fgcolor,bgimg,boxit,html);
else fAddEvent(y,m,d,message?ag[0]+"\n"+message:ag[0],action
?action
:ag[1],
bgcolor
?bgcolor
:ag[2],fgcolor
?fgcolor
:ag[3],bgimg
?bgimg
:ag[4],boxit
?boxit
:ag[5],ag[6]+html);
}
In the above sample, the message and html will be appended while the action, colors and background images will be substituted by the subsequently added sub-events. But this is only our choice and you may modify it to implement your own solution. Note that do not use onclick event in the html for sub-events, use onmousedown instead.
Please check out the agenda.js of the msn-alike demo for a good example.
The calendar panel size is set initially by the width
and height
properties of <iframe> tag. If these 2 values are not matching the actual
size, the calendar engine will try to detect the real size after a short delay
specified by giResizeDelay
option. The incorrect initial size
problem will happen when the browser is not rendering DHTML faster enough
so that the document is not ready upon the size checking time.
If this is happening when using the calendar within tabstrip/multipage controls
of asp.net, you may fix it by either setting the AutoPostBack
property of the tabstrip control to true
, or appending an onclick="if(gfFlat)gfFlat.fResize()"
handler for the tabstrip to resize the calendar. e.g.
<ie:TabStrip runat="server" TargetID="mpage"
onclick="if(gfFlat)gfFlat.fResize()" ...>
...
Of course you should name your calendar context to gfFlat
for
the latter solution.
If this is happening in other situations, try increasing giResizeDelay
a bit. But don't set it too large because it will bring in visible delay.
Another solution is to adjust the width
and height
of the calendar tag to be the correct initial sizes.
Make sure the giCellWidth
is set larger enough. Try increasing
it 10 pixels a time until you see everything back normal. It's a NN4 bug triggered
when extra stuff stretching the top or bottom section too much wider than
the total width of date cells. Making the top and/or bottom sections shorter
could be another solution.
Please check the gAgendaMask
option in your theme settings.
Make sure the mask value is properly set. The agenda property will be masked
out if the relevant bit value is not set to -1
.
Quite simple, all you need is to put 2 lines of code to the top of fHoliday() function in agenda.js file so that it looks like:
function fHoliday(y,m,d) {
var dayOfWeek=new Date(y,m-1,d).getDay();
if (dayOfWeek==0||dayOfWeek==6) return ["Weekend is not selectable!",null];
...
It creates a "null" agenda template, null
value in
the action property, for the weekends so that they can't be selected. You
may also disable other dates via the same way. A tip is to have other properties
of agenda defined in the template in order to make the disabled dates in different
colors, backgrounds or even images.
No, you don't need. Duplicate events can be treated as holidays. Instead
of repeating the fAddEvent()
calls, using fHoliday()
to return a single agenda template is much more faster and efficient. The
above FAQ for disabling weekends is a good example. Also please check the
setup agenda tutorial for more detailed info.
The build-in gContainer
reference, which points to the enclosing
window object, is made for such purpose. Suppose you have a form named "testForm"
with a <input> tag named "dateInput" laying in the same page
as the calendar. And you want to put the selected date from the calendar into
the "dateInput" field with the "y/m/d" date format. To
achieve such purpose, you should use the following code in plugins.js
file:
function fAfterSelected(y,m,d) {
gContainer.document.testForm.dateInput.value=y+"/"+m+"/"+d;
}
function fAfterSelected(y,m,d) {
gContainer.opener.document.testForm.dateInput.value=y+"/"+m+"/"+d;
}
The calendar engine has 2 system objects "gd
" and
"gToday
" which can be overriden in agenda.js
file. You may exploit this feature when you want the calendar to mark a server-end
"today" instead of a client-side "today".
1st, you need to prepend the following lines of code to the top of agenda.js
file.
gd=new Date(2003,3-1,11); // You may use any date you want.
gToday=[gd.getFullYear(),gd.getMonth()+1,gd.getDate()];
gCurMonth=eval(gTheme[0]); // re-init the gCurMonth in case it using gToday.
2nd, you do a search in the theme-name.js file that your theme is using,
e.g. normal.js
, for any references to gToday
. And
copy those options definition to agenda.js file and put them just below the
above lines. e.g.
gd=new Date(2003,3-1,11); // You may use any date you want.
gToday=[gd.getFullYear(),gd.getMonth()+1,gd.getDate()];
gCurMonth=eval(gTheme[0]); // re-init the gCurMonth in case it using gToday.
gsBottom="..."; // re-init gsBottom if it contains reference to
gToday. (optional)
gdSelect=gToday; // re-init gdSelect if it references to gToday. (optional)
And that's it. Of course, you may use ASP/JSP to generate the agenda.js
so that you can specify a server-end date, like:
gd=new Date(<%= year %>,<%= month %>-1,<%= day %>);
// year, month, day are server-end variables
...
Also note that all options in the theme-name.js can be reset dynamically in this manner.
Please check whether the upper Navigation Section or lower Today Section is wider than the total width of date cells. The calendar panel will automatically get stretched if the outter table width becomes larger, and the streched part might not be evenly distributed among all columns.
You may either enlarge the giCellWidth
or cut down the stuff
in top/bottom sections to fix this problem.
Simply set the gBegin
option to gToday
in theme-name.js
file, as following:
...
var gBegin=gToday; // calendar date range begin from [Year,Month,Date]
var gEnd=[2030,12,31];
...
It's a bug of IE6 when setting the border style of <iframe> tag in
standard mode. To workaround, you may remove the border style from the <iframe>
tag and put it into the #outerTable
class in theme-name.css file
instead. All themes in version 7.0 and above have already been using this
solution to create calendar borders.
#outerTable {border:2px ridge #808080;}
Yes, easy. There is an option in the theme-name.js
file called
gsDays
which will be evaluated upon each calendar cell. Simply
name your image files as day1.gif, day2.gif, ... and create a function, named
fGetDateCell(), in the plugins.js as following:
function fGetDateCell(dateStr) {
var d=dateStr.split(",")[2];
return "<IMG src='/images/day"+d+".gif' height=...
width=... >";
}
and set the gsDays
to:
var gsDays="fGetDateCell(sCellDate);";
Note the sCellDate
above is an internal contextual variable
that stores the current cell date in format "y,m,d".
Well, there are tons of Easter algorithms on the internet. Here we just randomly
pick one to implement. What we need is to create a function in the plugins.js
to return the Easter date and then use it in fHoliday()
to verify whether
the day is Easter or not.
function getEaster(year) {
var a=year%19, b=Math.floor(year/100), c=year%100, d=Math.floor(b/4), e=b%4,
f=Math.floor((b+8)/25), g=Math.floor((b-f+1)/3), h=(19*a+b-d-g+15)%30, i=Math.floor(c/4),
k=c%4, l=(32+2*e+2*i-h-k)%7, m=Math.floor((a+11*h+22*l)/451), n=h+l-7*m+114;
return [Math.floor(n/31),n%31+1];
}
then in agenda.js:
function fHoliday(y,m,d) {
...
var easter=getEaster(y);
if (m==easter[0]&&d==easter[1]) r=[gMonths[m-1].substring(0,3)+"
"+d+", "+y+" Easter Day", gsAction,"#87ceeb","red"];
return r; // if r is null, the engine will just render it as a normal day.
}
Easy. There is an option in the theme-name.js
file called gbEuroCal
.
Set it to true will make Monday the first day of week, false will make Sunday
the first day of week.
Easy. Just need to add 1 line of code in fHoliday()
to return
a default agenda instead of null
value for every normal day.
e.g.
function fHoliday(y,m,d) {
...
if (r==null) return ["A message for everyday", gsAction];
return r; // if r is null, the engine will just render it as a normal day.
}
In the calendar tag, you need to change the value of name & id properties to include the absolute/relative path to the theme directory. e.g.
<iframe name="gToday:/theme1/normal:agenda.js" id="gToday:/theme1/normal:agenda.js" src="/engine/iflateng.htm" ... ></iframe>
The above tag will load the normal theme from /theme1/ virtual directory on your website instead of the /engine/ directory.
Note that you can't specify the protocol name and domain name in the name
& id, because they are fixed to the one used by the src
property.
It's a bug of the Thawte seal script and can be easily fixed by appending a line of script right behind it. i.e.
<script src="https://siteseal.thawte.com/cgi/server/thawte_seal_generator.exe"></script>
<script>if (document.layers) document.releaseEvents(Event.MOUSEDOWN);</script>
The 1st <script>
is the Thawte seal script, and the
2nd one is the fix.
Please try the following one. Note the form id must be set to the same
as the one passed in to gfPop.fPopCalendar()
, otherwise the
calendar will not be able to find the date input field.
TabStripDemo.aspx
<%@ Register TagPrefix="ie" Namespace="Microsoft.Web.UI.WebControls" Assembly="Microsoft.Web.UI.WebControls" %>
<html>
<body>
<form id="demoform" runat="server"><ie:TabStrip runat="server" TargetID="mpage" AutoPostBack="true"
TabDefaultStyle="background-color:#000000;font-family:verdana;font-weight:bold;font-size:8pt;color:#ffffff;width:100px;height:21px;text-align:center"
TabHoverStyle="background-color:#777777"
TabSelectedStyle="background-color:#ffffff;color:#000000">
<ie:Tab Text="PopCalendar" />
<ie:TabSeparator/>
<ie:Tab Text="FlatCalendar" />
<ie:TabSeparator/>
<ie:Tab Text="About"/>
</ie:TabStrip><ie:MultiPage id="mpage" SelectedIndex="1" runat="server">
<ie:PageView>
Date Field: <input name="dc" value="" size="11"><a href="javascript:void(0)" onclick="gfPop.fPopCalendar(document.demoform.dc);return false;" HIDEFOCUS><img name="popcal" align="absbottom" src="calbtn.gif" width="34" height="22" border="0" alt=""></a>
</ie:PageView><ie:PageView>
<iframe name="gToday:normal:agenda.js:gfFlat" id="gToday:normal:agenda.js:gfFlat" src="iflateng.htm" scrolling="no" frameborder="0">
</iframe>
</ie:PageView><ie:PageView>
Copyright (C)2003 Idemfactor Solutions, Inc.
</ie:PageView>
</ie:MultiPage>
</form>
<iframe name="gToday:supermini:agenda.js" id="gToday:supermini:agenda.js" src="ipopeng.htm" scrolling="no" frameborder="0" style="visibility:visible; z-index:999; position:absolute; left:-500px; top:0px;">
</iframe>
</body>
</html>
Put the above TabStripDemo.aspx into a virtual directory on your IIS with the supermini theme for PopCalendarXP and normal theme for FlatCalendarXP.
Note: If you don't want to set the AutoPostBack
of tabstrip
control, you should append the following onclick code to workaround a bug
in the tab/multipage control. (A bug that will mess up the calendar, but
only seems affecting IE.)
<ie:TabStrip runat="server" TargetID="mpage" onclick="if(gfPop)gfPop.fHideCal();if(gfFlat)gfFlat.fResize()" ...>
Sometimes we don't want to see month/year values that are out of the valid date range we specified in the month/year selectors. This logic has already been implemented in most themes, but for certain themes that use html dropdown lists, e.g. normal theme, we still need to build a small plugin as following in the plugins.js so as to keep only valid months/years in list (note this plugin doesn't support NN4).
// Append the following to the bottom of plugins.js file
giDCStyle=1;
gsCalTitle="fBuildCalTitle()";
function fBuildCalTitle(){
if (!gdBegin||!gdEnd) return "";
var a=[];
var mstr=["<SELECT id='MonSelect' class='CalTitle' onchange='if(!fSetCal(fGetById(document,\"YearSelect\").value, this.value,0,true,event))fRepaint()'>"];
for (var i=0; i<12; i++) {
if (gCurMonth[0]==gdBegin.getFullYear()&&i<gdBegin.getMonth()) continue;
if (gCurMonth[0]==gdEnd.getFullYear()&&i>gdEnd.getMonth()) break;
mstr.push("<OPTION value='",i+1,"' "+(i==gCurMonth[1]-1?"selected":"")+">",gMonths[i],"</OPTION>");
}
mstr.push("</SELECT>"); mstr=mstr.join('');
var ystr=["<SELECT id='YearSelect' class='CalTitle' onchange='if(!fSetCal(this.value, fGetById(document,\"MonSelect\").value,0,true,event))fRepaint()'>"];
for(var i=gdBegin.getFullYear();i<=gdEnd.getFullYear();i++)
ystr.push("<OPTION value='",i,"' "+(i==gCurMonth[0]?"selected":"")+">",i,"</OPTION>");
ystr.push("</SELECT>");
ystr=ystr.join('');
if (gbDCSeq) a.push(mstr,ystr);
else a.push(ystr,mstr);
return a.join("");
}
It's because the javascripts are by default cached by the browser. If you don't want the browser to do the caching, you should give the agenda url a random parameter to make it different everytime the browser loading the page. e.g. a JSP example would be
<iframe name="gToday:normal:agenda.js?dummy=<%=System.currentMillis()%>" id="gToday:normal:agenda.js?dummy=<%=System.currentMillis()%>"... >
The dummy param works as an unique id to prevent the agenda.js from being cached by the browser.
If you want to create a calendar that belongs to a user, you may need to pass the user id to the scripts that generate the agenda file so that the calendar only shows up the events related to that specific user.
CalendarXP has already taken it into account and all you need is set the url in the name & id of the calendar tag, as following:
<iframe name="gToday:normal:agenda.jsp?userId=<%=obj.getUserID()%>" id="gToday:normal:agenda.jsp?userId=user1"
...></iframe>
The above example is using JSP to generate the agenda file, of course you may use Perl, ASP, PHP or whatever else instead.
Note: you must escape any non-alphabetical chars in the URL, especially the colon char. Running the parameter string through an URL encoding filter and using the transformed string in the name & id of the calendar tag is always a safe way to go.
Yes, any actions can be associated with the week numbers, e.g. popup windows, change url or even update calendar events. Simply append the following to the plugins.js file:
gsWeeks="fFormatWeekNo(weekNo)";
function fFormatWeekNo(weekNo) {
var y=gCurMonth[0], m=gCurMonth[1];
var year=m==1&&weekNo>50?y-1:m==12&&weekNo<5?y+1:y;
return "<a class='WeekAnchor' href='javascript:void(0)' onclick='fOnWeekNo(" + year + ","
+ weekNo + ");return false;'>" + weekNo + "</a>";
}
function fOnWeekNo(year, weekNo) {
...
}
Now you may do whatever you like in the fOnWeekNo() function. It'll be called every time you click on the week number on the calendar. Please note that Lite Edition doesn't support week numbers.
The calendar size is determined by the calendar cell size and the padding and spacing size of the panel table. They can be adjusted easily by editing the following options in the theme-name.js file: giCellWidth, giCellHeight, gsInnerTable, gsOuterTable.
It's because sometimes the Konqueror has difficulty in expanding the calendar panel to the correct size. Currently it can be worked around by giving the iframe tag a larger-than-usual width value since it appears to have no problem in shrinking automatically. e.g. <iframe width=600 ...>
It's a bug of certain Konqueror version. This bug exists outside the calendar engine and you may test it with any simply link on a blank page - the onclick event will not be fired after your first click unless you move the mouse out of the link and back in again. Currently it can be worked around by using onmousedown
event handler instead of onclick
. (Modify gsNavPrev/gsNavNext options accordingly)
It looks like this bug only occurs on anchor links, it's not affecting buttons.
You need to put 4 lines of code into the fOnChange() plugin function. e.g.
function fOnChange(y,m,d,e) {
...
if (m!=gCurMonth[1]&&[y,m,d]+''!=gToday+'') { // cancel the auto month switching
fRepaint();
return true;
}
return false; // return true to cancel the change.
}
So afterwards, clicking the dimmed days that belongs to prev/next months will no longer automatically switch the calendar.