### Setting up agenda events & holidays

We've already known that the 3rd parameter in the name & id of the calendar tag denotes a file named `agenda.js`. This parameter is optional. The calendar can work without it, only that there will be no holidays or events showing. Once this parameter is defined, you'll have to have a `agenda.js` file in place, which provides all agenda events as well as the holidays for the calendar engine. If this file is missing and the parameter is defined, you will get errors and the calendar may not work properly.

Before we go further with the agenda, let's take another look at the calendar tag, as following.

> `<iframe name="gToday:normal:agenda.js" id="gToday:normal:agenda.js" src="ipopeng.htm" scrolling="no" frameborder="0"
> style="visibility:visible; z-index:999; position:absolute; top:-500px; left:-500px;">
> <LAYER name="gToday:normal:agenda.js" src="npopeng.htm" `background="npopeng.htm"`> </LAYER>
> </iframe>`

One thing need to be pointed out earlier is that the file doesn't have to be named as `agenda.js`. You may actually use any cgi program to generate this file on-the-fly, e.g. ASP, JSP or PHP, so that you can take advantage of it and retrieve whatever events from your backend server or database. We'll discuss about it later in this tutorial.

Now let's go through the `agenda.js` file in the HelloWorld demo directory and explain parts of it in detail.

#### What is an agenda event?

An agenda event is just a special day that you want to render it differently in the calendar, like appointments, meetings and/or other daily events. You may give them specific font colors, background colors, text tooltips, or even images and customized html code. All you need to do is to append a line in the `agenda.js` for each daily event you got. e.g.

> `fAddEvent(year, month, day, message, action, bgcolor, fgcolor, bgimg, boxit, html, etc);`

The parameters are detailed as following:

- year,month,day indicates the date of the agenda.
- message is the text that will be shown as a tooltip, as well as in the statusbar.
- action is the script to be executed when the agenda date gets selected. Specifically, if you set the action to `null` or "null", the agenda date will be visually disabled(line-crossed). Of course you may in addition use other properties to create a customized disabled-looking. If you set it to "", i.e. empty string, there will be no action performed when the date is selected.
- bgcolor is the background color of the agenda date.
- fgcolor is the font color of the agenda date. Setting it to ""(empty string) will make the fonts invisible and the date unselectable. This actually gives you another way to disable a day.
- bgimg is the URL pointing to the image tiled in the cell background of the agenda date.
- boxit if boxit is set other than `false` or `null` value, the date will be drawn in a box using boxit value as the color, or bgcolor if boxit is `true`.
- html is the HTML code to be injected into the cell of the agenda date. It's very useful if you want to pack more stuff in a cell, e.g. more events in the same day.
- etc is an additional param that can be used to store any value, object or even array with the specific date. Note that you have to be cautious to use object or array here because of the object reference side-effect.

Note: if you want to use an anchor <a> link inside the `html` param, you should set the `target` property to either _top or _blank. Because the calendar is contained in an iframe, and if you omit the `target`, the link will be just targeted into the calendar panel.

> ``fAddEvent(2004,1,18," Hello World! ", "", "#87ceeb", "dodgerblue", null, "gold",
> "<a href='``http://www.calendarxp.net``' target='_top'>...</a>");``

Following please find some sample usages, and note the `popup()` is a pre-defined JavaScript utility function located in the `plugins.js`.

> `fAddEvent(2004,1,18," Hello World! ", "`popup('/anypath/mypage.html', '_top')`", "#87ceeb", "dodgerblue", null, "gold");
> `fAddEvent(2004,1,19," Hello World! ", "", "#87ceeb", "dodgerblue", null, false, "<div align=left class='MsgBoard' onmousedown='popup(\"mailto:who@anyemail.address?subject=test\", \"_blank\")'>My Meeting</div>");
> `
> ```var anyURL="mailto:who@anyemail.address?subject=it's a sample for using quote";
> fAddEvent(2004,1,20," Hello World! ", "", "#87ceeb", "dodgerblue", null, false, "<div align=left class='MsgBoard' onmousedown='popup(anyURL, \"_blank\")'>My Meeting</div>````<div align=left class='MsgBoard' onmousedown='alert(\"Take a break\")'>Lunch Time</div>````");````

Note: There is an important option called `gAgendaMask` in the `theme-name.js` file. It determines which parameter will be masked out - which one in use which one not. So be sure to check it before adapting your agendas and holidays, or just set all bit values to -1 to disable the mask.

Later, you read it out by calling the following and it will return any array in format `[message, action, bgcolor, fgcolor, bgimg, boxit, html, etc]` or `null` if nothing found :

> `fGetEvent(year, month, date);`

Or if you want to get rid of an event, you may simply call:

> `fRemoveEvent(year, month, date);`

#### How can I have 2 or more events within the same day?

The agenda event is set per day by `fAddEvent()` call, which means if you call it for the same day twice only the latter call will be used to set the effects. If you want to show more than one events in the same day, you need to code your own functions to merge them into one.

For example, you may simply add the following function to plugins.js and replace all `fAddEvent` calls with `fAppendEvent` in agenda.js.

> `function fAppendEvent(y,m,d,message,action,bgcolor,fgcolor,bgimg,boxit,html,etc) {
> var ag=fGetEvent(y,m,d);
> if (ag==null) fAddEvent(y,m,d,message,action,bgcolor,fgcolor,bgimg,boxit,html,etc);
> 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, etc?etc:ag[7]);
> }`

In the above sample, the message and html params will be appended, while the action, bgcolor, fgcolor, boxit and bgimg params will be substituted by the subsequently added sub-events. But this is just our choice and you may modify it to implement your own solution.

#### What is a holiday?

A holiday, a.k.a. recurring/batch event, is nothing but a special agenda that will recur from time to time. Therefore, we made a function to define them more efficiently, as following: (Note it's optional.)

> `function fHoliday(y,m,d) {
> var rE=fGetEvent(y,m,d), r=null;
> ` `if (m==1&&d==1)
> r=[" Jan 1, "+y+" \n Happy New Year! ", "", "steelblue"];
> }
> return rE?rE:r; // if returns null, the engine will just render it as a normal day.
> }`

Once defined, the calendar engine will call it each time when rendering a date cell. So if you have a lot of similar events or want to show the same agenda across a period of time, i.e. weeks or months, using the `fHoliday()` to return an agenda template is much viable than making tons of `fAddEvent()` calls. The plugin in range picker demo is a good example in reality.

This function should return an array as agenda template in the following format or a `null` value if nothing special.

`[message, action, bgcolor, fgcolor, bgimg, boxit, html, etc]`

In the above sample, we take the following logic -- first look up for the event of date [y,m,d]. If we found one, simply return it to the engine because we think the event created by the fAddEvent is more important.

Of course, we can easily make the code continue to check holidays and merge the result of both together if we like. Or we may favor holidays over events by checking the holiday first. Anyway, it's all up to you since this function is totally under your control.

Here is a sample function that enables most of USA holidays, except Easter which we'll discuss later. Note that here we use a pre-defined function `fGetDateByDOW()`. This function returns the date number of the 1st, 2nd ... or last - Monday, Tuesday ... or Sunday of any month. Using it will make certain holiday calculation much easier.

> `

> function fHoliday(y,m,d) {
> var rE=fGetEvent(y,m,d), r=null;

> // you may have sophisticated holiday calculation set here, following are only simple examples.
> if (m==1&&d==1)
> r=[" Jan 1, "+y+" \n Happy New Year! ",gsAction;,"skyblue","red"];
> else if (m==12&&d==25)
> r=[" Dec 25, "+y+" \n Merry X'mas! ",gsAction,"skyblue","red"];
> else if (m==7&&d==1)
> r=[" Jul 1, "+y+" \n Canada Day ",gsAction,"skyblue","red"];
> else if (m==7&&d==4)
> r=[" Jul 4, "+y+" \n Independence Day ",gsAction,"skyblue","red"];
> else if (m==11&&d==11)
> r=[" Nov 11, "+y+" \n Veteran's Day ",gsAction,"skyblue","red"];
> else if (m==1&&d<25) {
> var date=fGetDateByDOW(y,1,3,1); // Martin Luther King, Jr. Day is the 3rd Monday of Jan
> if (d==date) r=[" Jan "+d+", "+y+" \n Martin Luther King, Jr. Day ",gsAction,"skyblue","red"];
> }
> else if (m==2&&d<20) {
> var date=fGetDateByDOW(y,2,3,1); // President's Day is the 3rd Monday of Feb
> if (d==date) r=[" Feb "+d+", "+y+" \n President's Day ",gsAction,"skyblue","red"];
> }
> else if (m==9&&d<15) {
> var date=fGetDateByDOW(y,9,1,1); // Labor Day is the 1st Monday of Sep
> if (d==date) r=[" Sep "+d+", "+y+" \n Labor Day ",gsAction,"skyblue","red"];
> }
> else if (m==10&&d<15) {
> var date=fGetDateByDOW(y,10,2,1); // Thanksgiving is the 2nd Monday of October
> if (d==date) r=[" Oct "+d+", "+y+" \n Thanksgiving Day (Canada) ",gsAction,"skyblue","red"];
> }
> else if (m==11&&d>15) {
> var date=fGetDateByDOW(y,11,4,4); // Thanksgiving is the 4th Thursday of November
> if (d==date) r=[" Nov "+d+", "+y+" \n Thanksgiving Day (U.S.) ",gsAction,"skyblue","red"];
> }
> else if (m==5&&d>20) {
> var date=fGetDateByDOW(y,5,5,1); // Memorial day is the last Monday of May
> if (d==date) r=[" May "+d+", "+y+" \n Memorial Day ",gsAction,"skyblue","red"];
> }

> return rE?rE:r; // favor events over holidays

> }

> `

#### How to resolve conflicts between events and holidays?

By default, the `fHoliday()` in `agenda.js` will favor events over holidays, which means if you use `fAddEvent()` against a holiday day, all the holiday effects will be totally replaced by the event ones. But what if you only want to replace partial effects and keep other effects unchanged? Let's show you how to do it with an example.

Suppose we want to keep the font color of the date number as well as the background image from the holiday and all other effects from the conflicted event. To make it work, we need to modify the `fHoliday()` a little, as following:

> `function fHoliday(y,m,d) {
> var rE=fGetEvent(y,m,d), r=null;
> if (m==1&&d==1)
> r=[" Jan 1, "+y+" \n Happy New Year! ",gsAction,"skyblue","red"];
> ...
> if (r&&rE) { rE[3]=r[3]; rE[4]=r[4]; return rE;}
> else return rE?rE:r;
> }
> `

The above modification interprets as "get the agenda for event of day (y,m,d) and store it in `rE`; also get agenda into `r` if (y,m,d) is a holiday; if event conflicts with holiday, then replace the 3rd and 4th element (fgcolor and bgimg) of the event agenda with the values from holiday; or return the existing agenda if nothing conflicts."

#### Additional date calculations

In addition to do the simple comparison of year and month, you could use much more sophisticated formulas to check out the non-regular holidays like Easter, Thanksgiving or whatever holiday in your culture. There are so many standard calculations on the internet that can be used directly here.

You may just create some helper functions in plugins.js and then call them from within the fHoliday function to verify against the date passed in. For example, we have a function that calculates the Easter Day and want to use it in the `fHoliday()` call. We first add the following code to the `plugins.js`:

> `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 append the following code to the `fHoliday()` 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,"skyblue","red"];
> return rE?rE:r; // favor events over holidays`

> `}`

#### Retrieving agenda events from backend data source

Ever thought about making your agenda events manageable by using a backend server/database? No kidding, it's not only possible but always simple with CalendarXP.

Here we'll show you an example of how to generate a JavaScript file via JSP, which is capable of retrieve data from backend database. It's almost the same if you work with ASP, PHP or other cgi techniques, you may easily figure it out provided you know how to code in that language.

1. Suppose that you have got a table named "MY_AGENDAS" in your database, and you have already defined a datasource named "jdbc/myDataSource" (you should have set up db user/password in the datasource).
2. Suppose there are 10 columns in table "MY_AGENDAS" in accordance with the agenda format, as following: `ag_year,ag_month,ag_day,ag_message,ag_action,ag_bgcolor,ag_fgcolor,ag_bgimg,ag_boxit,ag_html`.
3. Build a JSP file named `agenda.jsp` as following:

   `<%@ page contentType="text/javascript" %>
   <% // The above JSP code set the generated page to be recognized as a javascript source. %>
   <% java.sql.Connection conn=null;
   try {
   // Establish database connection
   javax.naming.Context ctx = new javax.naming.InitialContext();
   javax.sql.DataSource ds = (javax.sql.DataSource)ctx.lookup("jdbc/myDataSource");
   conn = ds.getConnection();
   // Execute SQL and get a result set
   java.sql.Statement stmt = conn.createStatement();
   java.sql.ResultSet rs = stmt.executeQuery("SELECT * FROM MY_AGENDAS");
   // Loop through the result set to generate multiple fAddEvent() functions
   while (rs.next()) {
   %>
   fAddEvent(<%=rs.getString("ag_year")%>,<%=rs.getString("ag_month")%>,<%=rs.getString("ag_day")%>,
   '<%=rs.getString("ag_message")%>','<%=rs.getString("ag_action")%>','<%=rs.getString("ag_bgcolor")%>',
   '<%=rs.getString("ag_fgcolor")%>','<%=rs.getString("ag_bgimg")%>',<%=rs.getString("ag_boxit")%>,
   '<%=rs.getString("ag_html")%>');
   <% }
   // Close db connection and error handling
   rs.close();
   stmt.close();
   } catch (Exception e) {
   System.out.pringln("Service Error: "+e);
   } finally {
   if (conn!=null)
   try { conn.close() } catch (Exception ignore) {};
   }
   %>
   function fHoliday(y,m,d) {
   ... the javascript code for fHoliday ...
   }
   `

4. Place `agenda.jsp` in your JSP server, suppose the absolute URL path is `/myweb/agenda.jsp`. Change the 3rd parameter in the name&id of the calendar tag, as following:
   `<iframe width=174 height=189 name="gToday:normal:/myweb/agenda.jsp" id="gToday:normal:/myweb/agenda.jsp" src="ipopeng.htm" scrolling="no" frameborder="0">
   <layer name="gToday:normal:/myweb/agenda.jsp" src="npopeng.htm" `background="npopeng.htm"`> </layer>
   </iframe>`

That's all. Now all the agenda events should be loaded directly from your backend database table MY_AGENDAS. If you come across any problem, be sure to point your browser to /myweb/agenda.jsp first and retrieve the generated page source. Open it with a notepad and check carefully to see if anything were wrong in the JavaScript syntax.

Note: You should pay attention to using the quote char in ag_html, be sure to escape it if needed.
Note: The `<%@ page contentType="text/javascript" %>` is JSP coding. If you are using other CGI programming language, like php or asp, you should reference to its own way for how to set the content-type in the http header.
Note: The JSP code will print out "null" when the column value is NULL in DB, but other cgi like ASP and PHP might not work the same way and you should pay attention to dealing with the null value. The calendar engine will convert a "null" string into a null value automatically, also please remember that the boxit param is required to be a boolean value thus shouldn't be surrounded by the quotes.

#### Another data source example using PHP and MySQL

1st, create a database table in MySQL with the following scripts:

Code:
CREATE TABLE `my_agenda` ( `ag_year` smallint NOT NULL, `ag_month` smallint NOT NULL, `ag_day` smallint NOT NULL, `ag_message` varchar(255) default '', `ag_action` varchar(255) default '', `ag_bgcolor` varchar(20) default 'null', `ag_fgcolor` varchar(20) default 'null', `ag_bgimg` varchar(255) default 'null', `ag_boxit` varchar(20) default 'null', `ag_html` varchar(255) default 'null', PRIMARY KEY (`ag_year`,`ag_month`,`ag_day`) )

2nd, create a file named agenda.php with the following contents:

Code:
<? header("Content-Type: text/javascript"); $lnk=mysql_connect("<db_hostname>", "<db_user>", "<db_password>"); mysql_select_db("<db_name>",$lnk) or die( "Unable to select database"); $query="SELECT * FROM my_agenda"; $result=mysql_query($query); $num=mysql_numrows($result); mysql_close(); for ($i=0; $i < $num; $i++) { $year=mysql_result($result,$i,"ag_year"); $month=mysql_result($result,$i,"ag_month"); $day=mysql_result($result,$i,"ag_day"); $msg=mysql_result($result,$i,"ag_message"); $action=mysql_result($result,$i,"ag_action"); $bgcolor=mysql_result($result,$i,"ag_bgcolor"); $fgcolor=mysql_result($result,$i,"ag_fgcolor"); $bgimg=mysql_result($result,$i,"ag_bgimg"); $boxit=mysql_result($result,$i,"ag_boxit"); $html=mysql_result($result,$i,"ag_html"); ?> fAddEvent(<?=$year?>,<?=$month?>,<?=$day?>,"<?=$msg?>","<?=$action?>", "<?=$bgcolor?>","<?=$fgcolor?>","<?=$bgimg?>","<?=$boxit?>","<?=$html?>"); <? } ?> // -- you may append additional javascript here, e.g. fHoliday() --

Please remember to use the real db name, user id and password to replace the placeholders in above code.
Finally, upload the agenda.php to your web server (suppose it's placed at /php-cgi/agenda.php) and replace the agenda.js in the name and id of the <iframe> calendar tag with the full path to agenda.php:

Code:
<iframe name="gToday:normal:/php-cgi/agenda.php" name="gToday:normal:/php-cgi/agenda.php" ...>

Now put in some records in the db table and load up your page - you should see the agenda events showing up nicely in the calendar.
Note that you can still append the fHoliday() function, as in agenda.js, to the end of the agenda.php so as to retain the holidays. And in the agenda tutorial you may also find an example written in JSP code.
Last but not least, please be careful if you would like to use double-quote char (") in the strings - you should escape them wisely so as not to mess up the string boundary of javascript. If you were encountering any error, please point your browser directly to the URL of agenda.php, then either "view page source" or view the downloaded file - it should reveal the generated content. Make sure the content is pure valid javascript (no <script> tags allowed), as if it were in an agenda.js file.

#### How to pass parameters to the script that generates the agenda file?

If you want to create a calendar that belongs to a user, you may need to pass the user id to the script that generates 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=<%=obj.getUserID()%>"
> ...></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.

#### Share agendas among multiple calendars

Sometimes when you have more than 1 calendar on the same web page, especially when PopCalendar and FlatCalendar are working together, you may need to share the agenda events among them. Since version 7.0 this can be done by setting the agenda parameter of other-than-the-first calendar to "`share[<context-name>]`" instead of "`agenda.js`". In addition, the `gbShareAgenda` theme option of all shared calendars must be set to `true`. The following is a good example of agenda sharing between PopCalendarXP and FlatCalendarXP.

> `...`

> `<iframe name="gToday:normal:share[gfPop]:gfFlat" id="gToday:normal:share[gfPop]:gfFlat"
> src="iflateng.htm" scrolling="no" frameborder="0">
> <a name="gfFlat_spacer"><img width=172 height=180></a></iframe>`

> `...`

> `
> <iframe name="gToday:supermini:agenda.js:gfPop" id="gToday:supermini:agenda.js:gfPop" src="ipopeng.htm" scrolling="no" frameborder="0" style="visibility:visible; z-index:999; position:absolute; top:-500px; left:-500px; ">
> <LAYER name="gToday:supermini:agenda.js:gfPop" src="npopeng.htm" background="npopeng.htm"> </LAYER>
> </iframe>
> <LAYER name="gToday:normal:share[gfPop]:gfFlat" src="nflateng.htm" background="nflateng.htm"> </LAYER>
> </BODY>
> </HTML>`

Note that the 3rd parameter of the second calendar tag is `share[gfPop]`, which means to share the agenda loaded by `gfPop`. Should the agenda get changed by any run-time scripts both calendars would always get the same update automatically, and all you need to do is to call the `fRepaint()` afterwards to reveal the change in the calendar panel.

Another advantage of sharing is that the agenda.js will be loaded only once, no matter how many calendar tags are embedded on the same page. It could save a lot if you had a huge agenda database.

Note: When sharing the agenda.js file, it's important to know that any function definition, except fHoliday(), will not be shared, which means you should NOT declare any other functions in the agenda.js. If you really need an utility function, define it in the plugins.js instead.

#### Load agenda events per month instead of altogether

In certain situation, you may have intensive events in a calendar which may results in a huge agenda.js file. e.g. You have a table in DB that stores everyday events for each user for years, and you only want the calendar to load the events in current month so as not to generate too much network load. How to do it then?

Since version 8.2.202, PopCalendarXP has introduced an easy solution for it, as following:

1. Create a JSP/ASP/PHP page, here we assume using JSP solution to create one as `/jsp/agenda.jsp`, that generates the javascript source of the `agenda.js` by retrieving the events detail from the backend DB. Please refer to the above DB related section for how-to.
2. Move the fHoliday() function from the `agenda.jsp` to the `plugins.js` , and append the following code:

       function fOnChange(y,m,d,e) {
         if (gCurMonth[1]!=m||gCurMonth[0]!=y) fLoadEvents(y,m);          // MUST be the first line of fOnChange
         ...
       }

       function fLoadEvents(y,m) {
         if (fGetEvent(y,m,0)==null) {
           fAddEvent(y,m,0,"Loaded"); // mark the month as loaded
           fLoadScript("/jsp/agenda.jsp?year="+y+"&month="+m);
         }
       }

       fHoliday(y,m,d) {...you need to keep everything inside as is...}

       ...
       // The 2 lines below MUST sit at page bottom and be the last 2 lines of the plugins.js !!!
       if (gbShareAgenda&&!eval(gsAgShared)) eval(gsAgShared+"=[]");
       fLoadEvents(gCurMonth[0],gCurMonth[1]); // load data for the initial month

3. In the `/jsp/agenda.jsp` you may have your own code to read the `year` and `month` parameters from the http request, and use them to constraint the result set loaded from the backend DB so that only events of the specific month will get generated.
4. Append "`if(fRepaint)fRepaint();`" to `/jsp/agenda.jsp` file and make sure it gets generated as the last line of the javascript source. i.e. the generated source (you may test by pointing your browser directly to `/jsp/agenda.jsp` and select "view page source" ) should look like the following:

`fAddEvent(...);
fAddEvent(...);
...
if (fRepaint) fRepaint(); // must be the last line of the generated agenda source`

5. Modify the calendar tag to remove the "agenda.js" from the name & id, because after the above steps the agenda is loaded per request and we don't need it any more.

`<iframe name="gToday:normal::gfPop" id="gToday:normal::gfPop" ... ></iframe>`

That's it! Now every time you try to switch a month, the Server Page `/jsp/agenda.jsp` will be visited with 2 http parameters - `year` and `month`. And the value of the `year` and `month` represent the month that the calendar will be switching to. All you need is to parse the month value and then load corresponding events from DB table and generate corresponding `fAddEvent()` calls.

This should be easy, isn't it? However, the `fLoadScript()` call approach currently is only supported by IE5+(except Mac), NS6+, Mozilla and Opera7+. For those unsupported browsers you may consider using server-side browser detection to set the agenda param in the name & id (hint: `name='gToday:normal:<%=isIE4||isNN4?"agendaFull.jsp":""%>:gfFlat'` ) so as to load everything at one time.

#### Another way to load agenda events per month

As `XMLHttpRequest` object becomes more and more popular, a new option emerges to load scripts into the calendar engine and it can be used to complement the `fLoadScript()` to load events on demand.

All you need is to replace the fLoadEvent() function above with the following code:

    var xmlReq = null;
    function fLoadEvents(y,m) {
      if (fGetEvent(y,m,0)==null)
        fAddEvent(y,m,0,"Loaded"); // mark the month as loaded
      else
        return;
      var url  = "/jsp/agenda.jsp?year="+y+"&month="+m;
      try {
        xmlReq = self.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject("Microsoft.XMLHTTP");
        xmlReq.onreadystatechange=function() {
          if (xmlReq.readyState == 4 && xmlReq.status == 200) {
            eval(xmlReq.responseText);
          }
        }
        xmlReq.open("GET", url, true);
        if (self.XMLHttpRequest)
          xmlReq.send(null);
        else
          xmlReq.send();
      } catch (e) {
        alert("XMLHttpRequest is not supported.");
      }
    }

XMLHttpRequest is supported by IE5+, Mozilla and Safari 1.2+, and is not accessible when the page is on local file system - which means you have to put it into a web server to test.

#### Load agenda from external XML file

In additional to load script file, we can also code a plugin to load agenda events from xml file.
Suppose we have the following agenda.xml file located in the web root:

    <?xml version="1.0" encoding="ISO-8859-1" ?>
    <agenda>
    <event year="2005" month="4" date="1" bgcolor="#00ff00" fgcolor="black" bgimg="" border="gold">
    	<tooltip>this is a test</tooltip>
    	<action>alert(123)</action>
    </event>
    <event year="2005" month="1" date="1" bgcolor="green" border="null">
    	<tooltip>this is a new test</tooltip>
    	<action/>
    	<html><![CDATA[
    <br>
    <b>this is a new test</b>
    	]]></html>
    </event>
    </agenda>

To load it, we just need to append the following code in plugins.js :

    var _xmlReq;
    function loadXMLDoc(url) {
        if (window.XMLHttpRequest) {
            _xmlReq = new XMLHttpRequest();
            _xmlReq.onreadystatechange = processXML;
            _xmlReq.open("GET", url, true);
            _xmlReq.send(null);
        } else if (window.ActiveXObject) {
            _xmlReq = new ActiveXObject("Microsoft.XMLHTTP");
            if (_xmlReq) {
                _xmlReq.onreadystatechange = processXML;
                _xmlReq.open("GET", url, true);
                _xmlReq.send();
            }
        }
    }

    function processXML() {
          if (_xmlReq.readyState == 4) {
            if (_xmlReq.status == 200) {
                var e=_xmlReq.responseXML.getElementsByTagName("event");
              for (var i=0; i<e.length; i++) {
                 fAddEvent(parseAttrib(e[i],"year"), parseAttrib(e[i],"month"), parseAttrib(e[i],"date"),
                 parseText(e[i], "tooltip"), parseText(e[i], "action"), parseAttrib(e[i],"bgcolor"),
                 parseAttrib(e[i],"fgcolor"), parseAttrib(e[i],"bgimg"), parseAttrib(e[i],"border"),
                 parseText(e[i], "html"));
              }
              fRepaint();
            } else {
                alert("Please make sure loading agenda.xml from the same web domain, or it won't work due to browser security concern.\n" +
                    _xmlReq.statusText);
            }
        }
    }

    function parseText(root, tag) {
        var ns=root.getElementsByTagName(tag);
        if (ns.length==0) return null;
        ns=ns[0].childNodes;
        if (ns.length==0) return null;
        else if (ns.length==1) return ns[0].data;
        else return ns[1].data;
    }

    function parseAttrib(root, attr) {
        var n=root.getAttributeNode(attr);
        return n?n.value:null;
    }

Then, we can either use fOnload plugin to load the xml: (check __isAS to prevent redundant loading if you have multiple calendars sharing the same agenda)

    ////////////////// Calendar fOnload Handler ///////////////////////
    // It's triggered when the calendar engine is fully loaded by the browser.
    // NOTE: DO NOT define this handler unless you really need to use it.
    ////////////////////////////////////////////////////////////////////
    function fOnload() {
        if (!__isAS) loadXMLDoc("/agenda.xml");
    }

Or load it externally from the onload event in the body of the containing page: (suppose the calendar context name is gfPop)

`<body onload="gfPop.loadXMLDoc('/agenda.xml')">`

Note: you may still need the agenda.js file, as the only thing left inside is the fHoliday function. All fAddEvent calls can now be represented in XML.
NOTE: If you would like to test the XML loading, please do it on a real web server and place the xml in the same web domain. Trying to load agenda from local file system or another domain will fail because nowadays modern browsers do not allow loading resources from outside the current web domain.

Copyright© 2003-2007 Idemfactor Solutions, Inc. All rights reserved.