CSCI A348/A548

Lab Notes 7

Fall 1999


This lab should help you get started with the fifth assignment due next week on Thursday.

This assumes that you've read through the JavaScript chapter in the text as assigned last week. The section of interest for this particular lab starts on page 627, be sure to refer to it throughout the lab.

You first need a catalog of products that you'd want to sell. Page 629 in the text describes the format of these files: essentially they're HTML files that have a form inside that contains a hidden field whose name is "description" and whose value is a a combination of elements that describe the item and its price.

In my case let's say I am selling a few of my extra copies of A348 books and I have prepared the following five files:

Each one of them could be improved but for now they have the right structure and each provides a link to the next item in the catalog. I am not going to include the HTML for any of the catalog pages here since it's already available to you in your browser.

Now we need to set up a user interface for our shopping cart. Let's make it such that the user can browse the catalog in the upper part of the browser's window while the lower half of the screen offers

We use a frameset layout (frameset.html) as follows:
<html>
<head><title>Shopping Cart</title></head>

<frameset rows="60%,*">
  <frame src="item1.html" name="catalog"> 
  <frameset cols="30%,*"> 
    <frame src="control_panel.html" name="control_panel"> 
    <frame src="blank.html"         name="cart"> 
  </frameset> 
</frameset>

</html>
Try this even before you have the other two HTML files for the control panel and for the originally empty shopping cart. It should look like this. Notice that this entry point and the catalog files need not be in the same location, since we can specify the frame source through its URL.

We now create the blank.html file and then start working on the control panel (that will contain all our JavaScript code). We don't need any other file except if you have more merchandise you should add more item*.html in your catalog of items.

The blank.html file could look like this:

<html><head><title>Welcome!</title></head>
<body bgcolor=white> 
Welcome to the Shopping Cart application! <p>
 
Please browse through our on-line catalog and choose those
items of interest to you by using the control panel on the left. <p> 
 
We hope you have a useful and profitable shopping experience. 
</body>
</html>
But that, of course, is by and large irrelevant.

Let's start on the control panel: it should contain three buttons, to add or remove the current item to the shopping cart and/or to place an order. We plan to implement the "add item" functionality in these lab notes together with the order submission, and we'll leave it to you to implement the "remove item" function and to complete the order processing routine.

So the control_panel.html file could start by looking as follows:

<html><head><title>Shopping Cart Controls </title></head>

<body bgcolor=white>
<form name="form1">
<center>
<input type="button" name="add"    value="Add Item"> 
<input type="button" name="delete" value="Remove Item"> 
<input type="button" name="order"  value="Place Order"> 
</center>
</form> 
</body>
</html>
This is where our JavaScript code will go and to start with we only have the three buttons. In this lab we are going to work on the "Add Item" and "Place Order" buttons so might as well start with "Add Item" right away. The main data structure used by the control panel code is a "shopping cart" object that is called cart. The shopping cart is defined by the following constructor:
function cart() {
  this.cart           = new array(); 
  this.entries        = new array(); 
  this.add            = add; 
  this.remove         = remove; 
  this.list           = list; 
  this.show           = show; 
  this.order          = order;
  this.make_orderForm = make_orderForm; 
  return this; 
} 
Of course, to understand this function we need to clarify the meaning of seven other functions: array, add, remove, list, show, order, and make_orderForm. Since we don't set to understand everything at once we will keep the signatures of the functions as in the text and will provide empty definitions for some of them for the time being.

We need to make a global initialization, to create a new cart object when the page is loaded and we do it with the assignment

theCart = new cart(); 
in the JavaScript code that augments the control_panel.html file. And for the code to be interpreted properly we need to provide definitions for all the functions involved, so we add those in between the <SCRIPT> and </SCRIPT> tags.

function array() { }

function add(item) { }

function remove(item) { }

function list() { }

function show(aDoc) { }

function order() { }

function make_orderForm() { }
We'll add code to them at the right time. Let's now add an event handler to the "Add Item" button because otherwise all JavaScript is of no good. We'll issue two commands when the "Add Item" button is pressed:
theCart.add(getCurrentItem());
theCart.show(parent.cart.document)
Essentially this means: So let's add a definition for getCurrentItem.

We want to keep things simple so to start with we have:

function getCurrentItem() {
  alert('A-ha! getCurrentItem has been called!'); 
  return null; 
}
And now the event handler for the "Add Item" button.

<form name="form1">
<center>
<input type="button" name="add"    value="Add Item"
       onClick="theCart.add(getCurrentItem()); 
                theCart.show(parent.cart.document)"   > 
<input type="button" name="delete" value="Remove Item"> 
<input type="button" name="order"  value="Place Order"> 
</center>
</form>
The new part is in blue.

OK, so you should try this and verify that when you click the "Add Item" button the alert window pops up, started from the new getCurrentItem function. Now we make the following changes (marked in blue) to allow add to report the event while also giving an idea of whether the item was correctly picked or not.

function add(item) {
  alert('add has been called with ' + item.description); 
}

function remove(item) { }

function list() { }

function show(aDoc) { }

function order() { }

function make_orderForm() { }

function getCurrentItem() {
  if (parent.catalog.document.forms.length == 0) 
    return null; 
  var itemDesc = parent.catalog.document.forms[0].description.value; 
  if (itemDesc == null)
    return null; 
  return new catEntry(itemDesc);  
} 

function catEntry(string) {
  var firstColon = string.indexOf(":"); 
  var lastColon = string.lastIndexOf(":"); 
  this.catNo = string.substring(0, firstColon); 
  this.description = string.substring(firstColon + 1, lastColon); 
  this.price = string.substring(lastColon + 1, string.length); 
  return this; 
} 
We use a new type of object, catEntry, to store an item or a catalog entry (which contains a catalog number, an item description and a price).

So far so good. We therefore turn add into its final version:

function add(item) {
  if (item == null) return; 
  if (this.cart[item.catNo]) this.cart[item.catNo]++;
  else this.cart[item.catNo] = 1; 
  this.entries [item.catNo] = item; 
}
which does not invoke another function, then we add the definition for show:
function show(aDoc) { 
  aDoc.clear(); 
  aDoc.open("text/html"); 
  aDoc.writeln("<html><head><title>Current Shopping Cart");
  aDoc.writeln("</title></head><body bgcolor=white>"); 
  aDoc.writeln(this.list()); 
  aDoc.writeln("</body></html>"); 
  aDoc.close(); 
}
This in turn calls list() so we need to provide it:
function list() { 
  var totalPrice = 0.0; 
  var result = "<table border>" + 
    "<th>Cat #<th>Description<th>Quantity<th>Unit Price"; 

  var keys = new sortKeys(this.cart); 

  for (i = 1; i <= keys.length; i++) {
    var a = keys[i]; 
    var catNo = this.entries[a].catNo; 
    var description = this.entries[a].description; 

    result += "<tr><td>" + catNo + "<td>" + description + 
      "<td>" + this.cart[a] + "<td>$" + this.entries[a].price; 

    totalPrice += this.entries[a].price * this.cart[a]; 
  }

  return result + "<tr><td><td><th>Total<td>$" + 
         formatAsPrice(totalPrice) + "</table>";   
}
And that, in turn, invokes sortKeys which is defined below:
function sortKeys (object) {
  this.length = 0; 
  for (var a in object) {
    var pos = 1; 
    while (pos <= this.length) {
      if (this[pos] > a) 
        break;
      pos++; 
    } 
    for (var i = this.length; i >= pos; i--) 
      this[i+1] = this[i]; 
    this[pos] = a; 
    this.length++; 
  } 
  return this; 
}
It also invokes formatAsPrice that needs to be defined (minimally):
function formatAsPrice (price) {
  return price; 
}
At this point you should have a working shopping cart.

Now let's place the order. This requires two things:

So we do those two things (minimally)
function order() { 
  alert('A-ha! You are trying to place an order.'); 
}

<form name="form1">
<center>
<input type="button" name="add"    value="Add Item"
       onClick="theCart.add(getCurrentItem()); 
                theCart.show(parent.cart.document)"   > 
<input type="button" name="delete" value="Remove Item"> 
<input type="button" name="order"  value="Place Order"
       onClick="theCart.order()"> 
</center>
</form> 
and once it tests fine we replace order with its real source code:
function order() { 
  var orderWin = window.open(""); 
  var a = orderWin.document;
  a.clear(); 
  a.open("text/html"); 
  a.writeln("<html><head><title>Order Form</title>" + 
    "</head><body bgcolor=white><h1>Order Form</h1>"); 
  a.writeln(this.make_orderForm()); 
  a.writeln("</body></html>"); 
  a.close();   
}
And we give make_orderForm its final version:
function make_orderForm() { 
  var result = "Please confirm this order list. Change the quantity " +
    "ordered to 0 to cancel an item. Press \"Order\" to submit the order." + 
    '<form action="/cgi-bin/shcart" method=POST>' + "<table BORDER>" + 
    "<th>Cat #<th>Description<th>Quantity"; 
  var keys = new sortKeys(this.cart); 
  for (i = 1; i <= keys.length; i++) {
    var a = keys[i]; 
    var catNo = this.entries[a].catNo; 
    var quantity = '<input type="text" name="item:' + 
      catNo + '" value="' + this.cart[a] + '" size=2>'; 
    result += "<tr><td>" + catNo + "<td>" + 
      this.entries[a].description + "<td>" + quantity + "\n"; 
  }
  result += "</table><p>" + 
    '<table><tr><th>Your name<td><input type="text" name="name">' + 
    '<tr><th>Customer Number<td><input type="text" name="custNo">' +
    '<tr><th>PO Number<td><input type="text" name="PO"></table><p>' + 
    '<strong>Shipping address:</string><br><textarea name="address" ' + 
    'rows = 4 cols=40></textarea><p><input type="submit" value="' + 
    'Place Order">'; 
  return result; 
}
Then we write a shcart script that we place in cgi-bin:
#!/usr/bin/perl
 
use CGI;
 
$query = new CGI;
 
print $query->header, 
      $query->start_html(-bgcolor=>'white'); 
if ($query->request_method() eq 'POST') {
  print $query->dump,
} else {
  print "Sorry this script can process only POST requests."; 
}       
$query->end_html;
and test that it receives (and returns) the key elements of the order. You should now finish the project by adding the rest of the JavaScript code and also making the CGI script that receives the order send a mail message to the account that should be in charge of all purchases (presumably yours).


Last updated: October 12, 1999 by Adrian German