Show and Tell – Dynamic Web Calendar

In this post in the developerWorks forum, Andy Herlihy is asking about how to create a small calendar that can be embedded into an existing intranet website. As Carl Tyler points out, Andy could simply use Dojo.

However, this is a good example to show some jQuery combined with the power of IBM Lotus Domino.  Lets’ break it down into a few smaller steps.

The first one is to create a Domino web agent that will take the current date and build a calendar for that month. There will be no links or javascript code in the HTML generated, just plain HTML code, with some id attributes to make it easy to address different cells later.

The next step is to create a very simple webpage, with some Javascript to load jQuery and set up the calendar to detect clicks. The clicks will cause another call to the Domino server, this time to a view using RestrictToCategories to only get the entries for the specified date. The entries are returned as HTML and through jQuery they are displayed in a div on the webpage. We also want to add a little bit of CSS to make the calendar pretty. The CSS also lives on the HTML page.

Finally we create the view, it’s associated view template form and a form to create some entries that we can use for testing.

 

Let’s look at the code. First the Domino web agent. It should be pretty self explanatory:

%REM
    Agent WebCalendar
    Created Nov 26, 2012 by Karl-Henry Martinsson/Deep-South
    Description: Returns HTML for a calendar for current month
%END REM
Option Public
Option Declare

Sub Initialize
    '*** Used to get URL params
    Dim session As New NotesSession
    Dim webform As NotesDocument
    Dim urlstring As String
    Dim urlarr As Variant
    Dim urlvaluename As Variant
    Dim urldata List As String
    Dim webservername As String
    '*** Agent specific
    Dim startDate As NotesDateTime
    Dim endDate As NotesDateTime
    Dim i As Integer
    Dim j As Integer
    Dim startweekday As Integer
    Dim thisMonth As Integer
    Dim currDate As Double
    Dim cellclass As String

    Set webform = session.DocumentContext
    '*** Calculate path for this database, for image/icon file references later
    webservername = webform.GetItemValue("Server_Name")(0)
    '*** Remove leading "OpenAgent"
    urlstring = StrRight(webform.Query_String_Decoded(0),"&")

    If urlstring <> "" Then
        '*** Get all params passed to agent
        urlarr = Split(urlstring,"&")
        For i = LBound(urlarr) To UBound(urlarr)
            urlvaluename = Split(urlarr(i),"=")
            urldata(urlvaluename(0)) = urlvaluename(1)
        Next

        If IsElement(urldata("date")) = False Then
            urldata("date") = Format$(Now(),"mm/dd/yyyy")
        End If
    Else
        urldata("date") = Format$(Now(),"mm/dd/yyyy")
    End If

    '*** Get first and last date of current month
    Set startDate = New NotesDateTime(Format$(urldata("date"),"mm/01/yyyy"))
    Set endDate = New NotesDateTime(startdate.DateOnly)
    Call endDate.AdjustMonth(1)
    Call endDate.AdjustDay(-1)

    currDate = CDbl(CDat(startDate.DateOnly))
    startweekday = Weekday(Cdat(startDate.Dateonly))

    '*** HTML header
    Print "Content-type: text/html" ' & CRLF
    Print |<table class="calendar">|
    '*** Create calendar header with weekdays
    Print |<tr><td colspan=7 class="labelMonthYear">| + Format$(CDat(startDate.DateOnly),"mmmm yyyy") + |</td></tr>|
    Print |<tr class="calendarHeader">|
    Print |<td>S</td><td>M</td><td>T</td><td>W</td><td>T</td><td>F</td><td>S</td>|
    Print |</tr>|

    '*** Build rows for the weeks
    For i = 1 To 6
        Print |<!-- Start row | & i & | -->|
        Print |<tr class="calendarRow" id="calendarRow| & i & |">|
        For j = 1 To 7
            If j < startweekday And i = 1 Then
                Print |<td class="emptyCell"></td>|            ' Blank cell before dates
            ElseIf currDate >= CDbl(CDat(startDate.DateOnly)) And currDate <= CDbl(CDat(endDate.DateOnly)) Then
                cellclass = "calendarCell"
                If j = 1 Then
                    cellclass = cellclass + " sundayCell"
                End If
                If j = 7 Then
                    cellclass = cellclass + " saturdayCell"
                End If
                Print |<td class="| + cellclass + |" id="| + Format$(CDat(currDate),"yyyy-mm-dd") + |">|
                Print Day(CDat(currDate))    ' Day number
                Print |</td>|
                currDate = currDate + 1
            Else
                Print |<td class="emptyCell"></td>|            ' Blank cell after dates
            End If
        Next
        Print |</tr>|
        Print |<!-- End row | & i & | -->|
        Print ""
        If currDate >= CDbl(CDat(endDate.DateOnly)) Then
            Exit For
        End If
    Next
    Print |</table>|
End Sub

I am setting the id attribute on the cells to the date in yyyy-mm-dd format, and I will also use that in the categorized view that will later use. Using ISO 8601 for dates have several advantages, in our case particular because we don’t have to worry about the / character that will mess up the URL to the Domino view we will call later.
I also use class attributes on the cells, so CSS can be used to easily format the calendar.
Next we create the web page where we will insert the calendar and also display the entries for any selected day. We load jQuery from Googles CDN, and after the DOM is fully loaded by the browser, we make an ajax call to the Domino server and get the HTML of the calendar back.

After the calendar is retrieved, we use jQuery to trigger a Javascript function on click on any date cell. The code will detect the id of the cell, which just happens to be the date, and then performs another ajax call to teh Domino server, this time to the view to return all entries for the selected date. The data returned is then inserted into a div.

<!doctype html> 
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Calendar Test</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
</head>
<script>
    // The following function is executed after page is fully loaded
    $(document).ready(function () {
        var remoteURL = "http://www.texasswede.com/test/calendar.nsf/WebCalendar?OpenAgent";
        var viewURL = "http://www.texasswede.com/test/calendar.nsf/CalendarView?OpenView";
        // Load the calendar HTML from our Domino server using ajax
        $.get(remoteURL,
        function(data) {
                // Update div with id 'ajaxCalendar' with HTML calendar from server
                $("#ajaxCalendar").html(data);
                // Set all calendar cells to trigger on click
                $(".calendarCell").each( function(i) { 
                        // Get the id (i.e. selected day) of the cell clicked
                        var id = $(this).attr("id");               
                        $(this).click( function() {
                        // Call view on Domino server and return entries for selected day
                        $.get(viewURL, { RestrictToCategory: id },
                                function(data) {
                                        // Put list of entries in viewData div
                                        $("#viewData").html(data);
                                 });
                        });
                } );
        });
});
</script>
<style>
    td.calendarCell {
        border: 1px dotted black;
        height: 30px;
        width: 30px;
        text-align: center;
        vertical-align: middle;
    }
    td.emptyCell {
        background-color: #EFEFEF;
        height: 30px;
        width: 30px;
    }
    td.sundayCell {
        background-color: #DDDDDD;
        color: #DD0000;
    }
    td.saturdayCell {
        background-color: #DDDDDD;
    }
    .labelMonthYear {
        font-family: Arial;
        font-size: 12pt;
        text-align: center;
    }
  </style>
  <body>
    <div id="ajaxCalendar"></div>
    <br>
    <div id="viewData"></div>
  </body>
</html>

What’s left to do to get this all to work is to create the following Domino design elements:

  1. A form to use for entering calendar data. I called it “Calendar Entry”, and just put two fields on it, ‘CreatedDate’ (computed for display, using @Now) and ‘Title’ (text field).
  2. A view called ‘CalendarView’, with two columns. The first one is categorized and is displaying the created date in yyyy-mm-dd format, while the second one is displaying the title field and adding an HTML line break after. The view is set to treat content as HTML.
  3. A form called ‘$$ViewTemplate for CalendarView’, set to treat form as HTML. The form only contains an embedded view element, pointing to ‘CalendarView’, and it is set to return data as HTML as well.

This is the formula I used in the first column of the ‘CalendarView’ view:

y:= @Year(CreatedDate);
m:= @Month(CreatedDate);
d:= @Day(CreatedDate);
yyyy:=@Text(y);
mm:=@If(m<10;"0";"") + @Text(m);
dd:=@If(d<10;"0";"") + @Text(d);
yyyy + "-" + mm + "-" + dd

And this is what it looks like when finished:

This is just one example of how you can combine Domino (agents, views and forms) with jQuery in order to integrate Domino based data into your web applications.

Happy coding!

This Post Has One Comment

  1. Alan Hurt

    If you’re looking for a really nice UI, try the fullCalendar jQuery plugin. Then all you would need is an agent, view, etc to produce JSON that the calendar reads. It’s actually very nice looking and very easy to work with.

Leave a Reply