![]() |
![]() Spring Semester 2003 |
With all the Javascript covered thus far behind us, let's build a shopping cart.
We'll need this (basically, the document object model, or DOM):
That's the hardest part, 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.
(Please note that this format is entirely up to you and what's shown here is simply a possible solution. In any event, once we agree to a specific way of encoding, structuring data, that committment becomes extremely important).
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.
So let's say I am selling a few of my 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. Looking at the five HTML files is extremely instructive, and I provide the source code for the first two and the last of the five files in the "catalog" below.
For the last one, below, notice that the link takes you back to the first file.frilled.cs.indiana.edu%ls -ld it* -rw-r--r-- 1 dgerman 455 Aug 29 2001 item1.html -rw-r--r-- 1 dgerman 451 Aug 29 2001 item2.html -rw-r--r-- 1 dgerman 475 Aug 29 2001 item3.html -rw-r--r-- 1 dgerman 459 Aug 29 2001 item4.html -rw-r--r-- 1 dgerman 437 Aug 29 2001 item5.html frilled.cs.indiana.edu%cat item1.html <html> <head><title>Item 1</title></head> <body bgcolor=white> <img src="http://www.cs.indiana.edu/classes/a348-dger/fall2000/resources/apache.gif" align=left> This is <em>Professional Apache</em> <p> The price for this item is: $32.56<p> <img src="http://www.cs.indiana.edu/classes/a113-dger/left.gif"> <a href="item2.html">Next</a> item. <p> <form> <input type=hidden name=description value="PA34:Professional Apache:32.56"> </form> </body> </html> frilled.cs.indiana.edu%cat item2.html <html> <head><title>Item 2</title></head> <body bgcolor=white> <img src="http://www.cs.indiana.edu/classes/a348-dger/fall2000/resources/cookbook.jpg" align=left> This is <em>The Perl Cookbook</em> <p> The price for this item is: $29.12<p> <img src="http://www.cs.indiana.edu/classes/a113-dger/left.gif"> <a href="item3.html">Next</a> item. <p> <form> <input type=hidden name=description value="PC01:Perl Cookbook:29.12"> </form> </body> </html>
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 offersfrilled.cs.indiana.edu%cat item5.html <html> <head><title>Item 5</title></head> <body bgcolor=white> <img src="http://www.cs.indiana.edu/classes/a348-dger/fall2000/resources/llama.jpg" align=left> This is <em>Learning Perl</em> <p> The price for this item is: $18.95<p> <img src="http://www.cs.indiana.edu/classes/a113-dger/left.gif"> <a href="item1.html">Next</a> item. <p> <form> <input type=hidden name=description value="LP95:Learning Perl:18.95"> </form> </body> </html> frilled.cs.indiana.edu%
frameset.html
) as follows:
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<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>
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:
Of course, to understand this function we need to clarify the meaning of seven other functions: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; }
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.
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:function array() { } function add(item) { } function remove(item) { } function list() { } function show(aDoc) { } function order() { } function make_orderForm() { }
Essentially this means:theCart.add(getCurrentItem()); theCart.show(parent.cart.document)
getCurrentItem
. We want to keep things simple so to start with we have:
And now the event handler for the "Add Item" button.function getCurrentItem() { alert('A-ha! getCurrentItem has been called!'); return null; }
The new part is in<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>
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; }
We use a new type of object, catEntry
, to store
an item, or a catalog entry, which contains a
So far so good. We therefore turn add
into its final version:
which does not invoke another function, then we add the definition forfunction 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; }
show
:
This in turn callsfunction 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(); }
list()
so we need to provide it:
And that, in turn, invokesfunction 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>"; }
sortKeys
which is defined below:
It also invokesfunction 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; }
formatAsPrice
that needs to be defined (minimally):
At this point you should have a working shopping cart.function formatAsPrice (price) { return price; }
Now let's place the order. This requires two things:
and test it!.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:
And we givefunction 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(); }
make_orderForm
its final version:
and test it!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
:
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#!/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;
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). Of course, the
purchases should actually be stored in a database eventually, for order
processing, and next time we will look into building a similar functionality
on the server-side.