Using Dynamic Faces for Coordinated Drop Down Lists

In an earlier blog, we showed how to build coordinated drop down lists in a NetBeans Visual Web application. One problem with the techniques in that blog you is that you have to submit the whole page whenever a new selection is made from any of the drop down lists. If you have other components that get validated on page submission, it gets a bit messy, as you have to distinguish between when the page is submitted because of a new list selection and when the user is actually submitting the form.

The Dynamic Faces component library provides a solution to this problem. You can put the Drop Down List components in a Dynamic Faces Ajax Zone so that only the input from those components is sent to the server (as part of an Ajax request) when the user makes a new drop down selection. You can also specify which components to rerender when the server returns the Ajax response.

The following steps show how to do this using the tables from the sample Travel database. This example involves a coordinated person-trip-flight selection and the detail data for the selected flight. When you select a new person, the trip drop-down list shows all the person's trips and pre-selects the first trip on the list. The flight drop-down list shows all the flights for the selected trip, and pre-selects the first flight on the list. The page also redisplays the detail data for the selected flight. All other fields are not touched unless the whole page is submitted.

Install the Dynamic Faces Component Library

If you haven't already done so, you need to install the Dynamic Faces component library into the NetBeans IDE. Follow the steps in the Installing the Dynamic Faces Component Library section of the Installing the Currency Trader Sample Application tutorial.

Set Up the Project

  1. Create a new Visual Web Application project named DynaDropdowns.

  2. Add the Dynamic Faces component library to the project by right-clicking Component Libraries in the Projects window and choosing Add Component Library. Select the entry for Dynamic Faces Components and click Add Component Library.

  3. If you haven't already, connect to the Visual Web Pack sample travel database. Switch to the Runtime window, expand Databases, right-click the node for the travel database, and choose connect. Type travel for the password, and click OK. (The travel database is supplied by default if the Sun Java System Application Server was added to the IDE before you installed the Visual Web Pack. If you do not see the travel database, then check out the Installation Instructions).

Add an Entry for the Dynamic Faces Lifecycle Object to the Deployment Descriptor

Dynamic Faces provides a custom Lifecycle object that enables the JavaServer Faces lifecycle to handle Ajax transactions and to re-render portions of a component tree without requiring a full-page refresh.

To make the runtime aware of the custom Lifecycle object:

  1. In the Projects window, expand Web Pages and expand WEB-INF.

  2. Double-click web.xml to open it in the editing window.

  3. In the editing toolbar, click Servlets.

  4. Click the Add button that appears under Initialization Parameters.

  5. Type javax.faces.LIFECYCLE_ID in the Param Name text box, type com.sun.faces.lifecycle.PARTIAL in the Param Value text box, and click OK.

  6. Close and save the file.

Add the Ajax Zone and Drop Down List Components

The Ajax Zone component arms its children with the capability of sending Ajax requests. In this example, you add the three Drop Down List components to an Ajax Zone. The default behavior for an Ajax Zone component is to submit, execute, and rerender all the components in the zone whenever there is an onclick event for an input component within the zone.

  1. If necessary, click the tab for Page1 in the editing earea to bring the page into focus.

  2. Expand the Dynamic Faces section in the Palette, then drag an Ajax Zone component from the Dynamic Faces section and drop on the upper-left corner of the page

  3. In the Visual Designer, select the Ajax Zone component, and drag the bottom right sizing box down and to the right until the component is approximately 12 squares wide and 5 squares tall (that is, big enough to contain three Drop Down List components).

  4. Drag a Drop Down List component from the Basic section of the Palette onto the upper left-hand corner of the Ajax Zone component. Make sure the outline of the Ajax Zone component is highlighted in blue before releasing the mouse button, so that the Ajax Zone component contains the Drop Down List component.

  5. In the Properties window, type personDD in the text box for the id property, and type Person: in the text box for the label property.

  6. Switch to the Runtime window and expand Databases > jdbc travel node > Tables. Drag the PERSON node and drop it on the personDD Drop Down List component.

  7. Drag a second Drop Down List component and drop into the Ajax Zone component, placing it under the personDD Drop Down List component.

  8. In the Properties window, type tripDD in the text box for the id property, and type Trip Id: in the text box for the label property.

  9. Drag the TRIP node from the Runtime window and and drop it on the tripDD Drop Down List component.

  10. Right-click the tripDD Drop Down List component and choose Bind to Data. In the dialog box, select TRIP.TRIPID for the Display Field, and click OK.

  11. Drag a third Drop Down List component and drop into the Ajax Zone component, placing it under the tripDD Drop Down List component.

  12. In the Properties window, type flightDD in the text box for the id property, and type Flight Id: in the text box for the label property.

  13. Drag the FLIGHT node from the Runtime window and and drop it on the flightDD Drop Down List component.

  14. Right-click the flightDD Drop Down List component and choose Bind to Data. In the dialog box, select FLIGHT.FLIGHTID for both the Value Field and the Display Field, and click OK.

  15. In the Outline window, double-click the SessionBean1 > tripRowSet node to open it in the Query Editor. In the Query Editor, right-click on the PERSONID cell in the first column and choose Add Query Criteria. Select the Parameter radio button, click OK and close the Query Editor.

  16. Do the same for the flightRowSet, but add the query criteria for the TRIPID. Close the Query Editor.

Add a Flight Detail Property List

You next add a Property List component to display the flight details, and you configure the Ajax Zone to render the Property List in addition to the components in the Ajax Zone.

  1. Once again, drag the FLIGHT node from the Runtime window, but this time drop it on a blank spot on the page. A dialog box appears. Select the radio button for creating a flightRowSet in the Page1 page bean, and click OK. Close the Query Editor.

    I chose to put the other queries in the session bean because I didn't want to lose the query data everytime the page is submitted. As the detail data pretty much changes on each submission, I am choosing to put this rowset in the page bean.

  2. In the Outline window, select Page1 > flightDataProvider1 (not flightDataProvider). Then, in the Properties window, type detailFlightDataProvider in the id text box. By changing the name, it is easier to tell the two FLIGHT data providers apart.

  3. In the Outline window, double-click the page1 > flightRowSet node. In the Query Editor, right-click on the FLIGHTID cell in the first column and choose Add Query Criteria. Select the Parameter radio button, click OK and close the Query Editor.

  4. Drag a Property Sheet component from the Layout section of the Palette, and drop it on the lower-left corner of the page. Do not drop it inside the Ajax Zone component.

  5. In the Outline window, expand propertySheet1 and select the section1 node. In the Properties window, type Flight Information in the text box for the label property.

  6. In the Outline window, expand section1, and select property1.

  7. In the Properties window, change the label property to Airline:.

  8. Drop a Static Text component on property1 in the Outline window.

  9. Expand property1, right-click staticText1, and choose Bind to Data.

  10. Choose detailFlightDataProvider (not flightDataProvider) from the drop-down list, select FLIGHT.AIRLINENAME, and click OK.

  11. If you want, you can add more Property components to the Property Sheet Section, add Static Text components to the Property components, and bind the Static Text components to other columns in the database table, as I did in the above screenshot.

  12. Now, you need to add the property sheet to the list of components to rerender after the Ajax response is returned. Select the Ajax Zone component, and notice that the render property is blank. When this property is blank, the default behavior is to rerender all the components in the Ajax Zone. Type form1:propertySheet1,form1:ajaxZone1 in the text box for the render property. Note that you are supplying the client side ids.

Add the Remaining Form Fields

Now, add a Text Field and a Button component to the page. By doing this, you can see for yourself how the Ajax Zone is a partial page submit, whereas, when the button is clicked, the whole page is submitted.

  1. Drag and drop a Text Field component on the page. Set its label property to Special Requests, and select the checkbox for the required property, to set it to true.

  2. Drag a Button component onto the page, type Submit, and press Enter.

  3. Drop a Message Group component on the right side of the Text Field component.

Add Methods for Coordinating the Drop Down Components

Add the following code to the backing bean for coordinating the drop-down lists and flight detail data. (See http://blogs.sun.com/divas/entry/coordinated_drop_down_lists for more information about coordinated drop-down lists).

  1. In the editing toolbar, click Java, to switch to the source code for the page's backing bean.

  2. Add the following code to the bottom of the source file, just before the final end brace (}):
    	
        public void initFlightDropDown() {
            try {
                tripDataProvider.cursorFirst();
                getSessionBean1().getFlightRowSet().setObject(
                        1, tripDataProvider.getValue("TRIP.TRIPID"));
                flightDataProvider.refresh();
                tripDD.setSelected(tripDataProvider.getValue("TRIP.TRIPID"));
            } catch (Exception e) {
                error("Cannot switch trips");
                log("Cannot switch trips", e);
            }
        }
        
        public void initFlightDetail() {
            try {
                flightDataProvider.cursorFirst();
                // detail data is in the page bean
                flightRowSet.setObject(
                        1, flightDataProvider.getValue("FLIGHT.FLIGHTID"));
                detailFlightDataProvider.refresh();
                flightDD.setSelected(flightDataProvider.getValue("FLIGHT.FLIGHTID"));
            } catch (Exception e) {
                error("Cannot switch flights");
                log("Cannot switch flights", e);
            }
        }
    	
  3. Replace the prerender method with the following code:
    	
        public void prerender() {
            if ( personDD.getSelected() == null ) {
                try {
                    personDataProvider.cursorFirst();
                    getSessionBean1().getTripRowSet().setObject(
                            1, personDataProvider.getValue("PERSON.PERSONID"));
                    tripDataProvider.refresh();
                    personDD.setSelected(
                            personDataProvider.getValue("PERSON.PERSONID"));
                } catch (Exception e) {
                    error("Cannot switch to selections");
                    log("Cannot switch to selections", e);
                }
                initFlightDropDown();
                initFlightDetail();
            } else {
                try {
                    // Synchronize data providers with current selections
                    personDataProvider.setCursorRow(
                            personDataProvider.findFirst(
                            "PERSON.PERSONID", personDD.getSelected()));
                    tripDataProvider.setCursorRow(
                            tripDataProvider.findFirst(
                            "TRIP.TRIPID", tripDD.getSelected()));
                    flightDataProvider.setCursorRow(
                            flightDataProvider.findFirst(
                            "FLIGHT.FLIGHTID", flightDD.getSelected()));
                    // rowset for the detail flight is in the page bean
                    flightRowSet.setObject(
                            1, flightDD.getSelected());
                    detailFlightDataProvider.refresh();
                    
                } catch (Exception e) {
                    error("Cannot switch to selections");
                    log("Cannot switch to selections", e);
                }
            }
        }
    	
  4. Click Design to return to the Visual Designer, and double-click the personDD Drop Down List component to add and register a value change event handler for the component. Replace the method with the following code:
        
        public void personDD_processValueChange(ValueChangeEvent event) {
    
            try {
                getSessionBean1().getTripRowSet().setObject(
                        1, personDD.getSelected());
                tripDataProvider.refresh();
            } catch (Exception e) {
                error("Cannot switch person id");
                log("Cannot switch person id", e);
            }
            initFlightDropDown();
            initFlightDetail();
        }
    	
  5. Click Design to return to the Visual Designer, and double-click the tripDD Drop Down List component to add and register a value change event handler for the component. Replace the method with the following code:
    	
        public void tripDD_processValueChange(ValueChangeEvent event) {
            try {
                getSessionBean1().getFlightRowSet().setObject(
                        1, tripDD.getSelected());
                flightDataProvider.refresh();
            } catch (Exception e) {
                error("Cannot switch trips");
                log("Cannot switch to trips", e);
            }
            initFlightDetail();        
        }
    	
  6. Click Design to return to the Visual Designer, and double-click the flightDD Drop Down List component to add and register a value change event handler for the component. Replace the method with the following code:
        
        public void flightDD_processValueChange(ValueChangeEvent event) {
    
            try {
                flightRowSet.setObject(
                        1, flightDD.getSelected());
                detailFlightDataProvider.refresh();
            } catch (Exception e) {
                error("Cannot switch flights");
                log("Cannot switch flights", e);
            }
        }
    	

Test the Web Application

Click the Run Main Project button to test out the web application. Select different people, trips, and flights to see the partial form updates. Notice how even though the text field is required, you do not see any validation errors, because the Text Field component is not part of the partial page submission. However, if you submit the form by clicking the Submit button, an error message appears in the Message Group component if the text field is blank.

For More Information

Comments:

'dropdownlist' has a selectedvalue which is invalid because it does not exist in the list of items.parameter name:value <%@ Page Language="C#" MasterPageFile="~/Master/Sub.master" AutoEventWireup="true" CodeFile="CompanyDepartments.aspx.cs" Inherits="CompanyDepartments" Theme="Blue" %> <%@ Register TagPrefix="uc" TagName=CompanyTabs Src="~/Controls/CompanyTabs.ascx" %> <asp:Content ID="Content1" ContentPlaceHolderID="CPH1" Runat="Server"> <div id="divWrapC" class="divWrapperC"></div> <asp:UpdatePanel ID="GVPanel" runat="server"> <ContentTemplate> <DIV id="divConfirm" class="divConfirm" runat="server" visible="false"> <table class="tableContent" cellSpacing=0 cellPadding=0 width="500px"> <tr class="trContent"> <td align=left colspan="2" style="padding-left:5px;">Confirmation</td> </tr> <tr style="height:85px;"> <td style="width:25%;" align="center" valign="middle"><asp:Image id="ImgConfirm" runat="server" /></td> <td align="left" valign="middle" style="padding-right:5px;" > <asp:Label id="lblConfirm" runat="server"></asp:Label> </td> </tr> <tr> <td align="center" valign="top" colspan="2"> <asp:LinkButton CausesValidation="false" id="lnkConfirmY1" onclick="lnkConfirmY1_Click" runat="server" Text="OK" CssClass="but_sub" OnClientClick="hideWrap('divWrap');hideWrap('divWrapC');" Height="18" Width="60"></asp:LinkButton> <asp:LinkButton CausesValidation="false" id="lnkErrorY1" onclick="lnkErrorY1_Click" runat="server" Text="OK" CssClass="but_sub" OnClientClick="hideWrap('divWrapC');" Height="18" Width="60"></asp:LinkButton> </td> </tr> </table> </div> <DIV id="delConfirm" class="divContent" runat="server" visible="false"> <table class="tableContent" cellSpacing=0 cellPadding=0> <TR class="trContent"> <td align=left style="padding-left:5px;"></td> </tr> <tr> <td> <DIV style="OVERFLOW: auto; WIDTH:500px; HEIGHT:120px;"> <table width="100%" > <tr height="85px"> <td align="center"> <asp:Label id="lblConfirmDel" runat="server"></asp:Label> </td> </tr> <tr> <td align="center"> <asp:LinkButton id="DPYes" runat="server" Text="Yes" CssClass="but_sub" Height="18" Width="60" OnClientClick="showWrap('divWrapC', false);" onClick="DPYes_Click"></asp:LinkButton> &nbsp;<asp:LinkButton id="No" onClick="No_Click" runat="server" Text="No" CssClass="but_sub" OnClientClick="hideWrap('divWrap');" Height="18" Width="60"></asp:LinkButton> </td> </tr> </table> </div> </td> </tr> </table> </div> <table width="100%" border="0" cellspacing="0" cellpadding="0" style="padding-left: 10px; padding-right: 10px;"> <tr> <td> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td style="height:18px;" valign="bottom" class="textheading">Company Setup</td> </tr> <tr> <td style="height:1px;" class="dotline"></td> </tr> <tr> <td style="height:10px;" align="right"></td> </tr> </table> </td> </tr> <tr> <td> <uc:CompanyTabs ID="CompanyTabs" runat="server" /> </td> </tr> </table> <table id="tableDepartments" runat="server" width="100%" border="0" cellspacing="0" cellpadding="0" style="padding-left: 10px; padding-right: 10px;"> <tr> <td> <table width="100%" border="0" cellpadding="0" cellspacing="0"> <tr> <td style="height:5px;"></td> </tr> <tr> <td class="textsubheading">Departments Info</td> <td align="right"><asp:LinkButton ID="DPAddNew" runat="server" Text="Add New" CssClass="but_sub" Height="18" Width="60" OnClientClick="showWrap('divWrap', false);" OnClick="DPAddNew_Click"></asp:LinkButton>&nbsp;<asp:LinkButton ID="DPEdit" runat="server" Text="Edit" CssClass="but_sub" Height="18" Width="60" OnClientClick="showWrap('divWrap',true);" OnClick="DPEdit_Click"></asp:LinkButton>&nbsp;<asp:LinkButton ID="DPDelete" runat="server" Text="Delete" CssClass="but_sub" Height="18" Width="60" OnClientClick="showWrap('divWrap',true);" onClick="DPDelete_Click"></asp:LinkButton></td> </tr> <tr> <td colspan="2"> <asp:GridView SkinID="List" ID="DPGridView" runat="server" DataKeyNames="HDepartmentId,HLocationId,HCompanyId,HDivisionId,DepartmentName,DepartmentCode,LocationName" OnDataBound="DPGridView_OnDataBound"> <Columns> <asp:BoundField HeaderText="Company" DataField="CompanyName" ItemStyle-Width="19%" /> <asp:BoundField HeaderText="LocationName" DataField="LocationName" ItemStyle-Width="19%" /> <asp:BoundField HeaderText="DivisionName" DataField="DivisionName" ItemStyle-Width="19%" /> <asp:BoundField HeaderText="DepartmentName" DataField="DepartmentName" ItemStyle-Width="24%" /> </Columns> </asp:GridView> </td> </tr> </table> </td> </tr> <tr> <td style="height:5px;"></td> </tr> </table> <div id="divDepartmentsInfo" runat="server" class="divContent" visible="false"> <table class="tableContent" cellSpacing=0 cellPadding=0> <tr class="trContent"> <td align=left style="padding-left:5px;">Add/Edit Departments Info</td> <td align=right> <asp:LinkButton id="lnkAClose" onclick="lnkAClose_Click" runat="server" Text="X" CssClass="" CausesValidation="False" OnClientClick="hideWrap('divWrap');" ToolTip="Close"></asp:LinkButton>&nbsp; </td> </tr> <tr> <td style="height:5px;"></td> </tr> <tr> <td colspan=2> <div style="OVERFLOW: auto; WIDTH: 700px; HEIGHT: 300px";> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td colspan="2" style="height:9px; padding-left:3px;"><asp:ValidationSummary ValidationGroup="Departments" ID="vsDepartments" runat="server" HeaderText="Invalid Values: " DisplayMode="SingleParagraph" /></td> </tr> <tr> <td style="height:15px;"></td> </tr> <tr> <td style="height:22px;" align="right">Select Company:&nbsp;</td> <td><asp:DropDownList id="ddlSelectCompany" runat="server" CssClass="textboxmust" Width="115px" DataTextField="CompanyName" DataValueField="HCompanyId" TabIndex="1" AutoPostBack=true OnSelectedIndexChanged="ddlSelectCompany_SelectedIndexChanged" AppendDataBoundItems="True"></asp:DropDownList><asp:RequiredFieldValidator ID="rfvSelectCompany" runat="Server" ValidationGroup="Departments" ControlToValidate="ddlSelectCompany" ErrorMessage="Company;" ToolTip="Select Company.">\*</asp:RequiredFieldValidator></td> </tr> <tr> <td style="height:22px;" align=right>Select Location:&nbsp;</td> <td><asp:DropDownList id="ddlSelectLocation" runat="server" CssClass="textboxmust" Width="115px" DataTextField="LocationName" DataValueField="HLocationId" TabIndex="2" AutoPostBack=true OnSelectedIndexChanged="ddlSelectLocation_SelectedIndexChanged" AppendDataBoundItems="True"></asp:DropDownList><asp:RequiredFieldValidator ID="rfvSelectLocation" runat="Server" ValidationGroup="Departments" ControlToValidate="ddlSelectLocation" ErrorMessage="Location;" ToolTip="Select Location." SetFocusOnError="True">\*</asp:RequiredFieldValidator></td> </tr> <tr> <td style="height:22px;" align=right>Select Division:&nbsp;</td> <td><asp:DropDownList id="ddlSelectDivision" runat="server" CssClass="textboxmust" Width="115px" DataTextField="DivisionName" DataValueField="HDivisionId" TabIndex="2" AutoPostBack=true AppendDataBoundItems="True"></asp:DropDownList><asp:RequiredFieldValidator ID="rfvSelectDivision" runat="Server" ValidationGroup="Departments" ControlToValidate="ddlSelectDivision" ErrorMessage="Division;" ToolTip="Select Division." SetFocusOnError="True">\*</asp:RequiredFieldValidator></td> </tr> <tr> <td style="height:22px;" align="right">Department Name:&nbsp;</td> <td><asp:TextBox ID="txtDepartmentName" runat="server" CssClass="textboxmust" TabIndex="3"></asp:TextBox><asp:RequiredFieldValidator ID="rfvtxtDepartmentName" runat="Server" ValidationGroup="Departments" ControlToValidate="txtDepartmentName" ErrorMessage="Department;" ToolTip="Enter Department." SetFocusOnError="True">\*</asp:RequiredFieldValidator></td> </tr> <tr> <td style="height:22px;" align="right">Code #:&nbsp;</td> <td><asp:TextBox ID="txtDepartmentCode" runat="server" CssClass="textboxmust" TabIndex="5"></asp:TextBox><asp:RequiredFieldValidator ID="rfvDepartmentCode" runat="server" ValidationGroup="Departments" ControlToValidate="txtDepartmentCode" ErrorMessage="Code;" ToolTip="Enter Department Code." SetFocusOnError="True">\*</asp:RequiredFieldValidator></td> </tr> <tr> <td style="height:15px;"></td> </tr> <tr> <td colspan=1 ></td> <td style="height:22px;"><asp:LinkButton ValidationGroup="Departments" ID="DPInsert" runat="server" Text="Insert" CssClass="but_sub" Height="18" Width="60" OnClientClick="showWrap('divWrapC',true);" onClick="DPInsert_Click" TabIndex="9"></asp:LinkButton><asp:LinkButton ValidationGroup="Departments" ID="DPUpdate" runat="server" Text="Update" CssClass="but_sub" Height="18" Width="60" OnClientClick="showWrap('divWrapC',true);" onClick="DPUpdate_Click" TabIndex="9"></asp:LinkButton>&nbsp;<asp:LinkButton CausesValidation="false" ID="DPCancel" runat="server" Text="Cancel" CssClass="but_sub" Height="18" Width="60" OnClientClick="hideWrap('divWrap');" OnClick="DPCancel_Click" TabIndex="10"></asp:LinkButton></td> </tr> </table> </div> </td> </tr> </table> </div> </ContentTemplate> </asp:UpdatePanel> </asp:Content>

Posted by sree on June 21, 2007 at 03:24 PM PDT #

This blog entry explains how to do coordinated drop down lists with the NetBeans Visual Web Pack, and not with Microsoft.Net.

Posted by Diva #2 on June 22, 2007 at 12:49 AM PDT #

Hi,
I tried this to the letter but it didn't work! When I select a dropdown (anyone of the 3) nothing happens! (no partial update). I'm using netbeans 6.0 jdk 6.0 update 3 on win xp sp2 with firefox 2.0.0.12. When i click the submit button though, the page refreshes and the dropdowns are updated as expected

Posted by Musa Musa on February 14, 2008 at 01:36 AM PST #

Sorry I forgot to add that I updated the Netbeans (installed the sample currency trader app., Visual JSF backwards compatibility kit, everything!). Help me out here!

Posted by Musa Musa on February 14, 2008 at 01:39 AM PST #

I got it! I just had to set the Java EE version to 1.4 (the jsp page now uses the ui: tags instead of the webuijsf tags when you use Java EE 5)

Posted by Musa Musa on February 14, 2008 at 08:32 PM PST #

Musa Musa,

Glad you figured it out. I had sent email to the engineer, but he was out yesterday.

Posted by Diva #2 on February 15, 2008 at 12:28 AM PST #

I tried to do this example but it didn't work! When I select a dropdown (anyone of the 3) nothing happens! (no partial update). I'm using netbeans 6.1 jdk 1.6 When i click the submit button though, the page refreshes and the dropdowns are updated as expected

Posted by Roberto on August 10, 2008 at 12:18 PM PDT #

Roberto,

There have been changes to the VWP components and to Dynamic Faces since this blog entry was written over a year ago. I was switched from writing about VWP to writing about Ruby and I have not kept up with the changes, so I cannot say why this isn't working with 6.1. Maybe someone else reading this can help. Here is a 6.1 tutorial that might help: http://www.netbeans.org/kb/60/web/ajaxchatroom.html

Posted by diva#2 on August 11, 2008 at 03:00 AM PDT #

Post a Comment:
Comments are closed for this entry.
About

divas

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today