Previous Page TOC Next Page See Page



- 13 -
Programming with LiveWire and JavaScript


Now that you have examined the main objects and functions available, you can study some examples of how to use these features in actual applications. In this chapter, you will progressively develop a small client-server application that illustrates how LiveWire can be used to set up interactive applications.

By studying how this simple program works, you will learn about the following:

In addition, another program is examined. It implements a simple ordering system for a collection of three products, all using a single HTML file. This application demonstrates:


IP-Based Authentication


In this chapter, you will create a simple authentication program that decides whether to provide access to a site based on the client's IP address. You will develop this program in four stages:


IP-based Authentication: Stage One


The following listing sets up a simple IP address authentication:

<SERVER>
function myArray(length) {
    for (i = 0; i < length; i++) {
        this[i] = "";
    }
    this.length = length;
}
var validIPs = new myArray(3);
validIPs[0] = "192.115.168.1";
validIPs[1] = "192.115.168.11";
validIPs[2] = "192.114.74.1";
var gotoURL = "http://spock/authentic/sorry.html";
for (i = 0; i < validIPs.length; i++) {
    if (request.ip == validIPs[i]) {
        gotoURL = "http://spock/authentic/welcome.html";
    }
}
redirect(gotoURL);
</SERVER>

This script is actually fairly simple. It's written entirely in server JavaScript, which is placed between <SERVER> and </SERVER> tags. The script relies on the IP property of the request object to find out the client's IP address for the current request. In addition, the redirect() function is used to redirect the client to the appropriate page once validation has taken place.

The script starts by defining the myArray() constructor function because server JavaScript currently lacks the Array() object and you want to store the list of valid IP addresses in an array. The myArray() function accepts the number of elements as an argument, then uses a for loop to assign an empty string to each element, defines the length property, and assigns the value of the length argument to it.

Next, an array called validIPs is created by using the myArray() function, and an IP address is assigned to each element in the array.

A variable called gotoURL is then created to hold the array you're redirecting the client to. The assumption is made that the client is invalid, so the URL for the page displayed to users without valid IP addresses is assigned to gotoURL.

A for loop is then used to loop through the validIPs array, comparing each entry to request.ip. If a match is found, then the alternative URL is assigned to gotoURL.

When the loop exits, the redirect() function is used to make the client browser access the appropriate page. The results look like those in Figures 13.1 and 13.2.

Figure 13.1. If users have a valid IP address, they are admitted to the site.

Figure 13.2. Invalid users are informed that they don't have access to the site.

IP Authentication: Stage Two


In this addition to the basic authentication program, you move the list of valid IP addresses to a separate file. This offers easier configuration to the application administrator and enables new IP addresses to be added without requiring a recompilation of the application; a recompilation would be needed in the first stage, where the list of valid IP addresses is an integral part of the application source code.

The following listing performs this authentication:

<SERVER>
function myArray(length) {
    for (i = 0; i < length; i++) {
        this[i] = "";
    }
    this.length = length;
}
var validFile = new File("d:/users/armand/livetest/valid2.txt");
var success =validFile.open("r");
if (success) {
    validFile.setPosition(0);
    var numIPs = validFile.readln();
    var getIP = "";
    var validIPs = new myArray(numIPs);
    for (i = 0; i < numIPs; i++) {
        getIP = validFile.readln();
        validIPs[i] = getIP;
    }
    validFile.close();
}
var gotoURL = "http://spock/authentic2/sorry.html";
for (i = 0; i < validIPs.length; i++) {
    if (request.ip == validIPs[i]) {
        gotoURL = "http://spock/authentic2/welcome.html";
    }
}
redirect(gotoURL);
</SERVER>

This script doesn't change the actual validation portion at the end of the listing. Rather, the section where the IP addresses were assigned to the validIPs array has been changed to load the list from a file.

The format of this list of files stores each IP address on a separate line; the first line should contain a single integer that indicates the number of IP addresses in the file.

The script first creates a new instance of the File object called validFile, which points to the file containing the IP list. Next, the object's open() method is called, and the result is assigned to success to test for the successful completion of the attempt to open the file. The mode for the open() method is "r", indicating that the file should be opened from the beginning for reading.

If success is true, the file is open and the script can continue to read in the values. First, setPosition() is called to make sure the pointer is set to the start of the file. Then the first line is read, using the readln() method of the File object, and stored in numIPs, which is used to initialize the validIPs array. Finally, a for loop is used to read each subsequent line from the file and store it in the array, and the close() method is called to close the file.

Because the length property of validIPs doesn't exist if the file fails to open, authentication will fail in this circumstance because the authentication for loop never executes.

IP Authentication: Stage Three


In this stage, you go a step further by enabling valid users to add IP addresses to the list of valid IP addresses. To do this, you need two additional pieces of source code:

The welcome file includes the following form:

<FORM METHOD=POST ACTION="http://spock/authentic3/add3.html">
<INPUT TYPE=text NAME=ipAddress SIZE=15>
<INPUT TYPE=submit>
</FORM>

This produces a form like the one shown in Figure 13.3.

Figure 13.3. Valid users can submit an IP address to be added to the list of valid IP addresses.

This is the script that adds the IP address to the file containing the list of addresses:

<SERVER>
function myArray(length) {
    for (i = 0; i < length; i++) {
        this[i] = "";
    }
    this.length = length;
}
var validFile = new File("d:/users/armand/livetest/valid2.txt");
var success = validFile.open("r");
if (success) {
    validFile.setPosition(0);
    var numIPs = validFile.readln();
    var getIP = "";
    var validIPs = new myArray(numIPs);
    for (i = 0; i < numIPs; i++) {
        getIP = validFile.readln();
        validIPs[i] = getIP;
    }
    validFile.close();
    validIPs[numIPs] = request.ipAddress;
    numIPs = 1 + parseInt(numIPs,10);
    var newSuccess = validFile.open("w");
    if (newSuccess) {
        validFile.writeln(numIPs);
        for (i = 0; i < numIPs; i++) {
            validFile.writeln(validIPs[i]);
        }
        validFile.close();
    }
}
redirect("http://spock/authentic3/");
</SERVER>

This script resembles the authentication script you used in the second stage. It starts by reading the list of IP addresses into the validIPs array, as you did in the authentication script.

Next, the submitted IP address is assigned to a new entry at the end of the array. The IP address is stored in a property of the request object with the same name as the text field in the form used to submit the IP address. Once this is done, the value of numIPs can be increased by one.

Then the file can be reopened for writing. The "w" mode is used to open the file as an empty text file for writing. The number of addresses and each address can be written on separate lines in the file by using the writeln() method of the File object.

Finally, after the file is closed, the client is redirected to the main home page, which reauthenticates the client and displays the welcome page.

IP Authentication: Stage Four


In this, the final stage of your small authentication program, you add the ability to make sure users can't access a sub-document without first being authenticated by the application's main document . In this way, you can ensure that a user can't directly open the welcome page or any other page without first opening the top-level URL for the application where the IP address authentication takes place.

To do this, you need to add a section to the main authentication script that creates a property to be used throughout clients' sessions to track whether they have been validated.

In addition, you need to add a small header script to all other pages in the application that should be accessible only to validated users.

This is what the final authentication script looks like:

<SERVER>
function myArray(length) {
    for (i = 0; i < length; i++) {
        this[i] = "";
    }
    this.length = length;
}
var validFile = new File("d:/users/armand/livetest/valid2.txt");
var success =validFile.open("r");
if (success) {
    validFile.setPosition(0);
    var numIPs = validFile.readln();
    var getIP = "";
    var validIPs = new myArray(numIPs);
    for (i = 0; i < numIPs; i++) {
        getIP = validFile.readln();
        validIPs[i] = getIP;
    }
    validFile.close();
}
var gotoURL = "http://spock/authentic5/sorry5.html";
client.valid = false;
for (i = 0; i < validIPs.length; i++) {
    if (request.ip == validIPs[i]) {
        gotoURL = "http://spock/authentic5/welcome5.html";
        client.valid = "true";
    }
}
redirect(gotoURL);
</SERVER>

This script adds two simple steps. Before the final for loop that performs the actual validation of the IP address, a property of the client object is created called valid. Its initial value is set to false, which assumes that the IP address is invalid. However, if a valid address is found, client.valid is set to true.

The client object and its properties persist either until the end of the client session—in the case of client objects stored in the client—or for 10 minutes of inactivity, in the case of server-storied client objects.

The header of files that need authentication should include the following script:

<SERVER>
if (!client.valid || client.valid == null) {
    redirect("http://spock/authentic5/");
}
</SERVER>

This simply checks the value of client.valid. If it's false or null (meaning that the property has never been defined), then the client is redirected to the main authentication page before the page's content can be displayed.

A LiveWire Ordering System


Another LiveWire example should help you get more of a sense of what the possibilities of LiveWire are. This application implements a simple ordering system for a collection of three products.

The application needs to provide the following functionality:

In order to do this, a single HTML file has been built, using server JavaScript to dynamically build the appropriate HTML output based on user actions:

<HTML>
<HEAD>
<TITLE>
LiveWire Order System
</TITLE>
</HEAD>
<BODY BGCOLOR="cornsilk">
<DIV ALIGN=CENTER>
<SERVER>
client.name = (client.name == null) ? "" : client.name;
client.address1 = (client.address1 == null) ? "" : client.address1;
client.address2 = (client.address2 == null) ? "" : client.address2;
client.phone = (client.phone == null) ? "" : client.phone;
client.fax = (client.fax == null) ? "" : client.fax;
client.email = (client.email == null) ? "" : client.email;
client.item1 = (client.item1 == null) ? "false" : client.item1;
client.item2 = (client.item2 == null) ? "false" : client.item2;
client.item3 = (client.item3 == null) ? "false" : client.item3;
if (request.newdata == "yes") {
    client.name = (request.name == null) ? client.name : request.name;
    client.address1 = (request.address1 == null) ? client.address1 : request.address1;
    client.address2 = (request.address2 == null) ? client.address2 : request.address2;
    client.phone = (request.phone == null) ? client.phone : request.phone;
    client.fax = (request.fax == null) ? client.fax : request.fax;
    client.email = (request.email == null) ? client.email : request.email;
    client.item1 = (request.item1 == null) ? client.item1 : request.item1;
    client.item2 = (request.item2 == null) ? client.item2 : request.item2;
    client.item3 = (request.item3 == null) ? client.item3 : request.item3;
}
if (client.name == "" || request.action == "data") {
    write('<H1>Personal Data</H1>');
    write('<HR>');
    write('<FORM ACTION="shop2.html" METHOD="GET">');
    write('<TABLE ALIGN=CENTER BORDER=0 CELLPADDING=10 CELLSPACING=1>');
    write('<TR>');
    write('<TD BGCOLOR="black"><FONT COLOR="white">Name:</FONT></TD>');
    write('<TD BGCOLOR="maroon"><INPUT TYPE=text SIZE=30 NAME=name VALUE="' + client.name + '"></TD>');
    write('</TR>');
    write('<TR>');
    write('<TD BGCOLOR="black"><FONT COLOR="white">Address:</FONT></TD>');
    write('<TD BGCOLOR="maroon"><INPUT TYPE=text SIZE=30 NAME=address1 VALUE="' + client.address1 + '">');
    write('<BR><INPUT TYPE=text SIZE=30 NAME=address2 VALUE="' + client.address2 + '"></TD>');
    write('</TR>');
    write('<TR>');
    write('<TD BGCOLOR="black"><FONT COLOR="white">Phone:</FONT></TD>');
    write('<TD BGCOLOR="maroon"><INPUT TYPE=text SIZE=30 NAME=phone VALUE="' + client.phone + '"></TD>');
    write('</TR>');
    write('<TR>');
    write('<TD BGCOLOR="black"><FONT COLOR="white">Fax:</FONT></TD>');
    write('<TD BGCOLOR="maroon"><INPUT TYPE=text SIZE=30 NAME=fax VALUE="' + client.fax + '"></TD>');
    write('</TR>');
    write('<TR>');
    write('<TD BGCOLOR="black"><FONT COLOR="white">E-mail:</FONT></TD>');
    write('<TD BGCOLOR="maroon"><INPUT TYPE=text SIZE=30 NAME=email VALUE="' + client.email + '"></TD>');
    write('</TR>');
    write('<TR>');
    write('<TD ALIGN=CENTER BGCOLOR="midnightblue" COLSPAN=2><INPUT TYPE=SUBMIT VALUE="Save Data"></TD>');
    write('</TR>');
    write('</TABLE>');
    write('<INPUT TYPE=hidden NAME="action" VALUE="view">');
    write('<INPUT TYPE=hidden NAME="newdata" VALUE="yes">');
    write('</FORM>');
} else if (request.action == null || request.action == "view") {
    write('<H1>Current Status</H1>');
    write('<HR>');
    write('<TABLE ALIGN=CENTER BORDER=0 CELLPADDING=10 CELLSPACING=10>');
    write('<TR VALIGN=TOP>');
    write('<TD BGCOLOR="cyan" WIDTH=50%>');
    write('<H2>Personal Data</H2>');
    write('<HR>');
    write('NAME: <EM>' + client.name + '</EM><BR>');
    write('ADDRESS: <EM>' + client.address1 + ', ' + client.address2 + '</EM><BR>');
    write('PHONE: <EM>' + client.phone + '</EM><BR>');
    write('FAX: <EM>' + client.fax + '</EM><BR>');
    write('E-MAIL: <EM>' + client.email + '</EM><BR>');
    write('</TD><TD BGCOLOR="#020A33">');
    write('<FONT COLOR="cornsilk">');
    write('<H2>Order Status</H2>');
    write('<HR>');
    var ordered = "false";
    if (client.item1 == "true") {
        write('Item 1 ordered1<P>');
        ordered = "true";
    }
    if (client.item2 == "true") {
        write('Item 2 ordered1<P>');
        ordered = "true";
    }
    if (client.item3 == "true") {
        write('Item 3 ordered1<P>');
        ordered = "true";
    }
    if (ordered == "false") {
        write('No items ordered.');
    }
    write('</TD></TR></TABLE>');
} else if (request.action == "item1") {
    write('<H1>Item One</H1>');
    write('<HR>');
    write('<TABLE BORDER=0 CELLPADDING=10 CELLSPACING=1>');
    write('<TR>');
    write('<TD BGCOLOR="maroon" ALIGN=CENTER><FONT COLOR="white">');
    write('Current Order Status: ');
    if (client.item1 == "true") {
        write("Ordered");
    } else {
        write("Not Ordered");
    }
    write('</FONT></TD></TR>');
    write('<TR>');
    write('<TD BGCOLOR="cyan" ALIGN=CENTER>');
    if (client.item1 == "true") {
        write('<A HREF="shop2.html?item1=false&newdata=yes">Remove From Order</A>');
    } else {
        write('<A HREF="shop2.html?item1=true&newdata=yes">Add to Order</A>');
    }
    write('</TD></TR></TABLE>');
    
} else if (request.action == "item2") {
    write('<H1>Item Two</H1>');
    write('<HR>');
    write('<TABLE BORDER=0 CELLPADDING=10 CELLSPACING=1>');
    write('<TR>');
    write('<TD BGCOLOR="maroon" ALIGN=CENTER><FONT COLOR="white">');
    write('Current Order Status: ');
    if (client.item2 == "true") {
        write("Ordered");
    } else {
        write("Not Ordered");
    }
    write('</FONT></TD></TR>');
    write('<TR>');
    write('<TD BGCOLOR="cyan" ALIGN=CENTER>');
    if (client.item2 == "true") {
        write('<A HREF="shop2.html?item2=false&newdata=yes">Remove From Order</A>');
    } else {
        write('<A HREF="shop2.html?item2=true&newdata=yes">Add to Order</A>');
    }
    write('</TD></TR></TABLE>');
    
} else if (request.action == "item3") {
    write('<H1>Item Three</H1>');
    write('<HR>');
    write('<TABLE BORDER=0 CELLPADDING=10 CELLSPACING=1>');
    write('<TR>');
    write('<TD BGCOLOR="maroon" ALIGN=CENTER><FONT COLOR="white">');
    write('Current Order Status: ');
    if (client.item3 == "true") {
        write("Ordered");
    } else {
        write("Not Ordered");
    }
    write('</FONT></TD></TR>');
    write('<TR>');
    write('<TD BGCOLOR="cyan" ALIGN=CENTER>');
    if (client.item3 == "true") {
        write('<A HREF="shop2.html?item3=false&newdata=yes">Remove From Order</A>');
    } else {
        write('<A HREF="shop2.html?item3=true&newdata=yes">Add to Order</A>');
    }
    write('</TD></TR></TABLE>');
    
} else if (request.action == "order") {
    write('<H1>Order Placed</H1>');
    write('<HR>');
    write('<TABLE BORDER=0 CELLPADDING=10>');
    write('<TR>');
    write('<TD BGCOLOR="midnightblue"><FONT COLOR="yellow">');
    var ordered = "false";
    if (client.item1 == "true") {
        write('Item 1 ordered<P>');
        ordered = "true";
    }
    if (client.item2 == "true") {
        write('Item 2 ordered<P>');
        ordered = "true";
    }
    if (client.item3 == "true") {
        write('Item 3 ordered<P>');
        ordered = "true";
    }
    if (ordered == "false") {
        write("No items ordered.");
    }
    write('</FONT></TD></TR></TABLE>');
}
</SERVER>
<HR>
<A HREF="shop2.html?action=view">View Status</A> | 
<A HREF="shop2.html?action=item1">Item 1</A> | 
<A HREF="shop2.html?action=item2">Item 2</A> | 
<A HREF="shop2.html?action=item3">Item 3</A> | 
<A HREF="shop2.html?action=data">Edit Personal Data</A> | 
<A HREF="shop2.html?action=order">Place Order</A>
</DIV></BODY>
</HTML>

This script uses several underlying principles that you need to keep in mind as you analyze how the script works:

The script itself is divided into two main portions: The first performs basic initialization and the second determines what HTML code needs to be displayed and then displays it.

Script Initialization


The script initializes by setting up the properties of the client object, which is used to track the user throughout his or her session.

Specifically, you want to track two types of data:

Initialization takes place in two steps. First, a check is made to see if any of the personal data or order status properties contain a null value. This indicates that the value has never been set and it is likely the initial access to the application by the client. For each property, a conditional expression is used to assign an empty string (or false string in the case of the order status properties) to the property if it's value is null.

Next, a check is made to see if the user has attempted to update the data stored in the personal data or order status properties. Because the same page is repeatedly reloading with each user action, it's possible to pass an indicated called newdata as a property of the request object indicating if you are in fact updating existing data.

If data is being updated, then another set of conditional expressions can be used to assign the new values to the correct properties of the client object. This is done by passing the values as properties of the request object, with names corresponding to the properties of the client object. For each property of the request object that isn't a null value, the value of this property is assigned to the corresponding property of the client object.

Page Display


After initialization takes place, the appropriate HTML is generated for the user's actions. As the user performs different actions by clicking on links or submitting forms, the requested action is passed as property of the request object called action.

The possible actions are:


Editing personal data

When users are editing personal data, they are presented with a form like the one shown in Figure 13.4.

Figure 13.4. Users enter their personal data in a form like this one.

The form has a field for each personal data property of the client object. The action for the form is set to reload the current page with the GET method. In this way, the value of each field in the form is appended to the URL and becomes properties of the request object. The form also has two hidden fields: one sets action to the value "view" and the other sets the newdata property of the request object so that the new values get assigned to the client object when the page is reloaded.

Viewing Order Status

When the order status is being viewed, the information stored in the client object is displayed as formatted HTML.

The actual order status information is displayed either as a list of items being ordered or by a phrase indicating the user hasn't chosen to order any items yet.

The view status page looks like Figure 13.5.

Figure 13.5. Personal data and order status data is displayed when the user decides to view the current status.

Changing Item Order Status

If a user selected to change the order status of an item, then he or she is presented with two main pieces of information: the order status for the current item plus a link to change the order status of the item.

First the value of the relevant property of the client object is checked for the item in question (client.item1, client.item2 or client.item3). Based on this value, the order status for the item is displayed.

Then, again based on the value of the same property, one of two links is displayed. These links reload the current page with appended name-value pairs: the relevant item set to true or false as appropriate and newdata set to force the new data to be stored in the client object when the page is reloaded.

The results look like those shown in Figure 13.6 and 13.7.

Figure 13.6. If the item is already ordered, a link enables the user to choose not to order the item.

Figure 13.7. When an item isn't ordered, a link enables the user to add the item to their order.

Placing the Order

When the user places the order, a list of objects ordered is displayed. If no items have been ordered by the user, then an appropriate message is displayed. This looks like Figure 13.8.

Figure 13.8. When the user places an order, he or she is presented with a list of ordered item.

Global Menu

At the bottom of each page a global control menu is displayed. This menu consists of a set of hyperlinks for each action the user can take. The URL for each of these links reloads the current page with an appended name-value pair setting the action property as necessary.

Summary


In this chapter, you have developed two simple programs. One that authenticates clients based on their IP addresses while the other implements a simple online order system. In doing this, you have gained experience working with the following:

Server JavaScript offers more functions than you have covered in this chapter, but the principles are the same as the ones you learned in the previous chapter, where you studied objects available in server JavaScript.

In the next chapter, you move back to the client side and take a look at live objects—Java applets and Netscape plug-ins—and how you can use JavaScript to manipulate them and extend their usefulness. Using LiveConnect, the mechanism that interconnects these three features of JavaScript, you can create sophisticated, multimedia, interactive applications, using JavaScript as the connecting layer.

Previous Page Page Top TOC Next Page See Page