JavaScript Journal

Subscribe to JavaScript Journal: eMailAlertsEmail Alerts newslettersWeekly Newsletters
Get JavaScript Journal: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


JavaScript Authors: Sematext Blog, Klaus Enzenhofer, Mehdi Daoudi, Yakov Fain, AppDynamics Blog

Related Topics: JavaScript

JavaScript: Article

Prototyping an Advanced Calendar Class Using JavaScript

Prototyping an Advanced Calendar Class Using JavaScript

It is possible to create a very attractive look-and-feel prototype of a Calendar-based browser application in JavaScript, but to compete with tough-minded mainframe legacy systems such as MEMO requires a highly functional and scalable working prototype to justify the continued investment and potential encapsulation of a large mainframe system.

After it was suggested that an existing MEMO-based system was to be superceded, an opportunity arose to port some of the legacy application functionality to the new Intranet and browser medium, hopefully gaining a more attractive interface, platform independence and the ability to interact with other desktop clients.

A functional prototype was implemented using JavaScript. The approach taken was to implement an object model that would not need redesign once the back end was attached. For simplicity, elementary datasets are implemented using cookies on the client. Almost all of the user interface is written dynamically on the client using JavaScript to generate the HTML and object references between windows. Practical snippets of JavaScript and HTML are included which demonstrate the more advanced features of JavaScript required by an application that attempts to supercede an older and more mature user interface. The overall technical detail will show how JavaScript can be used to generate HTML, parse formatted data streams and handle events asynchronously. The implementation details covered can be used in any JavaScript application for which scalability and upward compatibility are critical design constraints.

In Part One, the application functionality, design constraints and prototyping approach is outlined. A simple event-action model is implemented in JavaScript that allows for arbitrarily complex rules. This is designed to manage window-to-window communication and other events via asynchronous event handling using JavaScript threads'.

The screen shots used here are taken from Navigator 3.0 running under NT4.0.

Calendar Functionality
Although the Calendar user does not need to know what a cookie is, the next section introduces the concept of a cookie. The Calendar functions are then described.

Calendar Data
All of the data entered or displayed by the user is stored in an elementary cookie database.

The term cookie database' refers to a collection of objects called cookies' which, under Navigator, are stored in a browser-generated file on the user's PC called cookies.txt'.

A cookie represents persistent' information, which means that, where the stateless HTTP protocol itself cannot store information about anything except its current action, a cookie can be used to store that information, which may be as simple as the user's name or favorite URL.

The HTTP protocol allows the setting of cookies in the HTTP header when the page loads. However, using JavaScript to manage cookie data allows us the freedom to:

  • manage the data on the client without incurring the network overhead of Web server dialogue
  • change a cookie multiple times within one HTML page load

    Only the browser, or the user sitting at the PC, can write to cookies.txt, but the browser generates the file so any in-line updates are almost certain to be lost at some point.

    Table 1 shows the components that make up a cookie string.

    JavaScript has access to the set of cookies on the client using the property document.cookie'.

    There are restrictions however. Web browsers are not required to retain the value of more than 20 cookies per Web server (not just your site). A name/value pair cannot be more than 4k in length. For the purpose of functional prototyping, however, using cookies as described in this article is an acceptable short-term method for storing data.

    Calendar Screens
    The screens are generated dynamically using JavaScript compliant with version 1.0 and upwardly compliant with version 1.1. Each function heading that follows represents one screen, which may contain two or more HTML frames (a frame is a separate part of a browser window that can be redrawn without affecting the remainder of the browser window).

    User Defined Month View
    This screen renders the calendar events for the whole month. The user can select the appropriate month and year, browse the meetings in a given day and choose how to view the meetings in a day from a small number of options (useful if there are lots of calendar events in a day).

    Browse Meetings in Day
    From the month view screen, the user selects a day for which all of the meeting summary details are displayed in a popup window. On clicking an item in the summary frame, the full meeting details are displayed in the adjacent frame.

    Maintain Calendar Events
    The Maintain Calendar Events screen is divided into four frames that encompass the functions list in Table 2.

    As you will see later, all the functions in Table 2 are implemented by using JavaScript to generate HTML frames which include embedded references to Calendar objects.

    Select Participants
    The Select Participants window is a popup window that allows the user to select meeting participants from a maintainable list, similar to an address book. This is a useful example of how to add vitality to the look and feel of a browser application; it is generated by JavaScript, contains embedded references to objects in another window, and the data is read from cookies; Figure 1 shows the pop-up window:

    Automatically Mail Participants
    Meeting details are written to a mail window from which the user can notify all participants.

    Configure Calendar
    This popup window allows the user to choose the way in which summary meeting details are displayed in the Month View. These preferences are stored in userPreferences' cookies.

    Constraints and Design Decisions
    The design and build of the Calendar was subject to constraints that led to design decisions affecting the whole approach. Some design decisions are based on practical constraints imposed by the client, while others are independent of the computing surface employed.

    For example, Navigator 2.02 running on 12MB 486 PCs is the default corporate platform, but there exist many other variations on top of this within the organization. Mindful of the need to justify both the prototyping exercise and the cost of rolling out future enhancements across a heterogeneous surface, the decision was made to build against JavaScript 1.0 with a view to upward compatibility.

    The following sections list the major constraints and decisions taken.

    Screens Written Dynamically Using JavaScript 1.0
    Because the back end server software was not available, HTML could not be written dynamically by CGI scripts. So, each screen was written dynamically on the client using JavaScript. Screens that contain variable data (see Figure 2 ) must be written dynamically. However, creating a window does not mean that it can be referenced. JavaScript variables and methods can only be referenced once the browser has loaded the window.

    Event Handling and Parent/Child Window Communication
    One controlling window generates the text of the other windows and embeds references in child windows to objects that are properties of the controlling window. As a result, in order to manage the way a browser writes the document to the screen, an event handler was written which can be extended by implementing event testing and action functions.

    16-bit Target platform (NS2.02) - No Java and Back End not available
    Most corporate users were restricted to 16-bit Windows, so it was not possible to use Java in a RAD context. The preferred browser was Netscape 2.02. Upward compatibility with NS 3.0 was required, so the prototype was tested against both NS 2.0 and NS 3.0. However, this meant that native methods in JS 1.1 were coded in JS 1.0, particularly the string parser.

    JavaScript Classes Store Format and Content of Data
    JavaScript 1.0 does not support inheritance. Specialization of a class is not possible and therefore a class cannot inherit properties and methods from a superclass. It is not possible to use super() to call the overriden code, as it is in Java (see Listing 1).

    Meeting.today() overrides DayEvent.today() but invokes super.today() to fetch the day number within the specific formatting context.

    Because the requirement is to render the screens dynamically, formatting becomes a method of the class. And because calendar events are nested within calendar objects (such as a Calendar Window, Month and a Day), they have a formatting context.

    It would be nice to be able to invoke super() to create a string that contains the HTML tags and links which enable an object such as a Meeting to be correctly formatted within the display context of its containing window. Because inheritance is not available, a dependency structure was introduced that would allow the creation of a parent dependency when constructing the child, similar to constructor chaining in Java. What seems like over-engineering yields effective results when passing objects or object references between windows with different display requirements for the child object's data.

    Cookies and Client Side Persistent Data
    The functional prototype is for the client only. As described in the section entitled Calendar Data', cookies are used to store the data on the client. However, once the data becomes server-based, major restructuring of the data interface is not an option, so the interface to the client cookies must operate transparently.

    Upward Scalability of JavaScript Code
    JavaScript was coded against version 1.0 but was expected to run against 1.1+ under 16 and 32-bit implementations without change.

    There are major differences between versions, which mean that some properties and methods have been moved to different objects. However, JavaScript 1.1 has some excellent features that save programming time. In particular:

  • the ability to alter the contents of a list without having to reload the document (Relisting')
  • the ability to split a formatted string into an array of strings (Native Parsing')

    Relisting is useful for maintaining summary lists dynamically and will save some execution time by eliminating the need to regenerate the screen to display a different list, but does not reduce the coding overhead particularly.

    Native parsing, however, does reduce the coding overhead significantly because it is the JavaScript interpreter that parses the string and reduces string processing iterations to a simple statement, as in the following code fragment:

    s = "Team Meeting~Meeting Room 2" +
    "[email protected]"
    a = s.split("~");

    Native parsing is ideal when reading formatted data. However, the need to use version 1.0 means that the parsing of cookie data is performed longhand. It also means that users of both older and newer versions of JavaScript can use the same code.

    Managing JavaScript RunTime Resources
    The amount of memory used by JavaScript when executing should be kept as low as possible, although only generalities can be applied. There is an approximate 32K limit on the size of a HTML file which contains JavaScript code. Since most of the visible prototype is generated on the client, this meant that the JavaScript code had to be as compact as possible.

    In JavaScript 1.0, garbage collection occurs at page unload or reload, whereas in version 1.1, similar to Java, garbage collection is reference-based, which means that if an object has its reference count decremented to 0, the space is reclaimed.

    The decision was made to read data and create the associated objects on demand. For the main browse and update screen (see Figure 3), clicking on a day in the bottom left window will read all meetings for that day of the month and display them in the top left window. Although all the data in the Month cookie is read, only those meetings for the selected day are extracted and created as Meeting objects.

    This strategy will, in most cases, manage resources better than if we read all meetings for the month and created a whole set of objects since we are managing the uncertainty of volume, and the strategy will also migrate effectively to using a server-based datastore, which will return results sets per query.

    Legacy Application Functionality
    The prototype had to be attractive enough to use in parallel with the existing system, which has a mature user base. Because there is competition between Navigator and Notes in some installations, the prototype had to add value in some way to make it an attractive option that could justify the cost of scaling upwards.

    The prototyping of an interface to the existing MEMO database was out of scope, but with such a mature product in a large installation, replacing the database would be required only if:

  • it was not possible to fetch data from the existing database
  • new drill-down requirements would incur large query overhead against the existing database, which due to the size of the installation needs high availability and concurrency

    Clearly, a more attractive interface, platform independence and the ability to interact with other desktop clients are good enough reasons to attempt the prototyping.

    Managing User Expectation
    The prototype must be able to deliver the basic features of a larger system without a full scale Joint Application Development (JAD) stage. Users must be able to maintain calendar events and notify other users of them without using a back end server to store this information. The look and feel of the application must be similar to existing windows-based applications on the corporate platform. This would enable a user to migrate from a PC client-based prototype to a DBMS-based system almost without change to what he expects to see and do with the application as defined by the prototype.

    Functional Prototyping
    The functional prototype must contain the basic calendar functionality, which can then be delivered to a more ambitious client/server platform once the server software was available. Although the proposed network platform was HP-UX, this was to be transparent; not just at the prototyping stage, but when the prototype is enhanced.

    A functional prototype is a Rapid Application Development (RAD) term that defines a system in which the application functions are demonstrated and the user interface defined. On a client/server platform, a functional prototype would contain all the screens and navigation buttons and menus that belong to an application sitting on the user's PC. The idea is to get user-buy in to how they want the system to look and feel while delivering the critical functionality. Again, RAD defines a metric that 80 percent of the critical system functions are contained in 20 percent of the build.

    If you build a functional prototype of a spreadsheet, then most of the functionality will be delivered in the prototype so the subsequent build activity should be reduced. On the other hand, a look and feel prototype (e.g., screens in a shopping cart) would be delivered to a build phase in which the main deliverable to the build was a set of screens; the skeletal code with each screen would be filled in by adding code to handle the database, network, event handling, etc.

    However, the intention was to avoid rewriting the software once server resources were available. So, an interface to data was developed, using cookies, such that the rest of the prototype could be implemented as a working model. Cookies are seen as a disposable resource, with cookie data seen as a formatted data stream which may be superceded by server data, giving rise to the requirement for a transparent data interface.

    It could be argued that with simple scripting languages which are used in a prototyping context, there is no need for a thought out design; it is better to start building a look and feel prototype which acts like a shell into which the non-functional back-end can be coded later. In a lot of cases this is appropriate; for example, using Visual Basic for the user interface with a follow-up in C++ for critical sections of functionality (e.g., calculations).

    For a browser-based application, the simplest model assumes that the screens in the application are coded in HTML first (with dummy data) to establish the look and feel of each screen and the navigation between screens. Then, the HTML and JavaScript is built to support the rendering of those screens dynamically. A working prototype, however, requires actual data; hence, the need for working code is introduced early on. Proving working code early on is also important because the requirements of JavaScript may render the application unworkable due to the amount of code being interpreted on the client PC.

    But, remember the analogy with spreadsheets. You can't really prototype spreadsheet functions unless you have almost built a fully working spreadsheet. The same applies to client-based browser applications when the decision is taken to generate the format and content of screens on the client.

    By the time the JavaScript has been written to generate the HTML for all the windows which are being prototyped, the application code has, in fact, been written; unless you intend to rewrite it later, which only makes sense if it doesn't work in the first place.

    Critical Success Factors
    To implement a working prototype in JavaScript that could be enhanced without rewrite, the following success factors were identified.

  • Enough functionality to replace basic legacy functions: The prototype would deliver enough functionality to enable calendar events (usually meetings) to be notified via e-mail. This means that meetings can be described and participants agreed. Upon receipt of a meeting notification, the user can copy the data into his calendar.
  • Classes can be used to interface with a Non-Functional back end without restructuring.
  • Reduction of client/server dialogue: Using JavaScript enables the screens to be rendered on the client without introducing a dialogue with the server (to fetch the next formatted page).
  • Efficient use of elementary cookie functionality to add, update and delete data
  • Event handling must be introduced using JavaScript to cater for mature requirements.

    JavaScript Threads Using Asynchronous Wait and Event Handling
    In order to manage window-to-window communication and events of arbitrary complexity, a simple co-operative event-action model is introduced which is implemented using the JavaScript setTimeout() method and a new class to store method references. By implementing event handling on the client in JavaScript, what might seem like over-engineering has advantages when we hook the client to a back end:

  • Reduction of client/server overhead between the tiers; browser to Web server, Web server to DBMS, remembering that they may be on different hosts and consequently incur network overhead
  • Reduction of the amount of commitment control between the client and the DBMS server (and, hence, reducing the synchronous code path in the above tiers)

    Managing Popup Windows using Simple Asynchronous Event Handling
    When child windows are created, a method is submitted to background and waits for the window to be fully loaded before initializing it by setting the window.opener property (implicit in NS 3.0).

    Each class shares the same reference to an event handling method, pollEvent(). Each class also has a reference to an object (CalendarChild) which manages child objects, principally child windows. The CalendarChild object is used in the event handler.

    On Event then Action - Opening the Popup Browse Meetings Window and Passing Object References to it
    The window shown in Figure 2 pops up when the user clicks the folder in a Day cell on the Month View Screen. The window will display a scrollable list of meetings in one frame. On clicking a subject, the meeting detail appears in another frame.

    The window is opened as described in Listing 2. Because of the latency involved when the browser opens a window and displays the screen detail, an asynchronous thread is set up which polls the child window every half second until the basic frames are loaded. Once loaded, the thread invokes the methods which draw the frames in the popup window.

    Each class (Calendar, Day, Meeting) has the ability to create a child dependency (e.g., a window), and the default assumption is that any children are managed asynchronously.

    The dependencies are handled using the rule on Event then Action'. Hence, each class has a reference to an instance of the class CalendarChild', which contains the properties described in Table 3.

    In the prototype implementation, this is a very basic object to capture the arbitrary complexity of the on Event' rule. Extra functionality can be handled as a property of the data by adding action and event functions. Function references are assigned to the action and event properties as required. Listing 2 contains the code to open a window and spawn a thread to poll for the event which states that the child window has successfully loaded. The Day object then writes a substring of the dependent Meeting object to that window; namely, the Meeting subject. The event is tested against using the check_frame()' method reference. The event is listened on' by the render_subject()' method of the Day object.

    References to both the event method and the action method (which belong to the Day object) are assigned to instance variables in the CalendarChild class described above, and it is pollEvent code which runs both.

    An alternative way of handling communication between windows is by using cookies, especially when the frame and object references are longwinded or complicated. A cookie is used to store the signal that the window has posted some data. Another cookie contains the data. The event' property is set to reference a JavaScript function, as follows:

    this.CalendarChild.event = f_poll_signal;

    The statement this.CalendarChild.event() invokes the f_poll_signal() in Listing 3, which tests the cookie called SIGNAL for the value READY. If found, the cookie is set to NONE. The event handler ensures that the f_poll_signal() method is called every half second.

    In summary, JavaScript allows the CalendarChild class to be a useful container for storing variable references to methods that are to be actioned in background when an event occurs.

    Automatic Generation of Mail Headers
    A full production version of the Calendar would interact with a server-based DBMS; at least, a shared, formatted database of some description.

    In order to allow the prototype to have a minimal, working implementation, each participant of the scheduled event is mailed a copy of the event information instead of having the information stored on a central database. The mail contents are generated automatically as far as possible.

    With Netscape Navigator 2.02+, JavaScript allows us to generate a mail message in a Mail window. All of the vital pieces of information are contained in the header. The addressee, CC and subject can all be set, using JavaScript, from the data contained in the form. However, the body of the message cannot be set (at the time of this writing) if there is any text to set it. The quick workaround is to cut and paste the text into the body of the mail window.

    The JavaScript to do this is tricky because we have to set a hyperlink dynamically, action the link and then manage the return from the link. The basic link that will be manipulated is expressed in HTML and JavaScript as follows:

    <A href="javascript:void" name='notify'
    onClick="subject_mail_format();return true;">
    <IMG SRC=mail.gif BORDER=0>

    The link named notify' in the above code fragment is referenced as document.links[0] by the subsequent JavaScript code.

    The subject_mail_format() function is invoked when the user clicks the red postbox. The return true statement tells the browser that no further action needs to be taken.

    The function will set the HREF property of the hyperlink, and then action it.

    The problem is compounded by the fact that the participant list is a textarea; each member of the list needs to be extracted and formatted for the addressee section.

    The expression document.MEETING.
    PARTICIPANTS.value in Listing 5 gives us the contents of the textarea. Each participant is delimited by a newline (\r\n).

    Listing 5 shows the detailed JavaScript in the subject_mail_format() function, referenced in the onClick' event handler in the JavaScript code fragment shown above.

    For the screen in Figure 3, this will yield the mail header in Figure 4 when the red postbox is pressed.

    Conclusion
    A simple reminder calendar is easily rendered using JavaScript. However, the challenge of building a reminder calendar that can be used as a corporate memo platform lies in designing both an attractive user interface and managing the calendar data. Implementing an event-action model allows arbitrarily complex rules to be implemented from the outset. In Part Two, I will demonstrate the use of cookies and show you how class methods can have the appearance of being overridden in a language that does not support inheritance. This will show you how JavaScript interacts with HTML and browser objects to give vitality to the look and feel of the application.

  • More Stories By Graham P. Harrison

    Previously a Senior Consultant with BEA, Graham is the author of Dynamic Web Programming using Java (Prentice Hall, 2000) in addition to a number of articles for the Java Developers Journal and IBM DeveloperWorks. He has a focus on Enterprise Architecture, Performance Tuning and Capacity Planning

    Comments (0)

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.