|
CSCI A348/548
|
I am further delayed posting the grades, but that's because I'd like to be more thorough and not very redundant, so here's the schedule I propose:
Since the class is Open Source posting a complete shopping cart here is no longer a problem. The rest of the notes will be building the project. To keep things distinct, yet provide many useful references, I decided to not update my notes from years past, and just refer to them here.
With all this behind us, let's build the shopping cart. We'll need this:
The rest is fairly quick (and easy).![]()
You first need a catalog of products that you'd want to sell. The format of these files is as follows: they're (basically) plain 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.
This is as far as processing goes. Of course, for the user to be able to browse the books available some text and probably a picture need to be added as well, as detailed below.
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 (and you should look at the source code before going further).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
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 likeNotice 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.this.
Put this in your htdocs in a directory called cart.
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:
But that, of course, is by and large irrelevant.<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>
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:
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<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>
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 are not
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
in the JavaScript code that augments thetheCart = new cart();
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:
Essentially this means:theCart.add(getCurrentItem()); theCart.show(parent.cart.document)
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.
If you don't test now, and have a mistake, it will come to haunt you later big time.
So test it now, and re-test it often.
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;
}
Test it!
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:
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 test it!.
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;
}
and test it!
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');
print "Placing an order: needs to be finished.<p><hr><p>";
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 (to implement a remove method) 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). Later (after the spring break) we will show you how you could add this to a relational database and maintain it (initialize, update, query) properly.
A348/A548