Project: Building a Shopping Cart

Just as the last two chapters presented groups of scripts that worked together toward a common goal, the scripts in this chapter work together to allow a user to browse the contents of a catalog of goods for sale, select some of those items, and purchase them.

With a little bit of design work, these scripts offer a quick and functional solution. Because any of the scripts can be easily edited, customizing the shopping cart is often simple. Except, as ever, when it isn't. :)

Security

This chapter does not deal with security issues, which are beyond the scope of this book. However, security is a very real issue, so here's a quick list of things to think about:

  • Communication between a browser and a server can be encrypted using SSL. Without SSL, any information (for example, credit card information) that is being passed to a Web server can be intercepted. SSL is not only safer than unencrypted communication, it makes people more comfortable giving you credit card information. Comfort is good.

  • This script stores payment information (credit cards, prepaid Internet cards, etc. …) unencrypted on the server. This is a security issue. You have a couple of solutions here. The best would be to encrypt the information. This can be done by the operating system, I think (I've never done it). I've also seen a JavaScript MD5 library that you could use. If you do store unencrypted information on your server, make sure that the server is as secure as you can make it and that you take the information off the server quickly. It's equally important to protect names and addresses of your customers, who generally expect the information they give you to be protected.

  • If a merchant ships products to someone using stolen credit cards, the merchant is liable for the product. So make sure that your credit card validation process includes antifraud measures. Talk to your bank for more information on this subject.

  • A lot of people argue (and I'm one of them) that you should be more worried about people you know than unknown bad guys on the Internet. Password-protect computers that store important information and don't share the passwords. Keep computers in locked rooms. Exercise common sense! You probably wouldn't leave a pile of credit cards or information about your customers unattended in a public place. Digital valuables also need to be protected.

That said, security is an engineering problem. There are good solutions to security problems that make it possible for you to protect yourself to whatever level you think is appropriate. Take the time to educate yourself on the issue so that you are competent to create a working environment that is secure enough to meet your needs. And because security involves cool software and hardware, you can usually have a lot of fun with it. ;)

A Quick Tour

The cart in Figure 5-1 lets buyers browse items for sale by category or via keyword search. Each item supports an unlimited number of different options, any of which can be selected by clicking on a "Buy" link (which you can rename or replace with an image).

Figure 5-1. View items available for sale; search by category or keyword


The cart is pretty much what you'd expect it to be. Item quantities can be edited using a text box and Submit button while "Delete This Item" links make it easy to remove unwanted items. A link lets shoppers check out when they're ready (See Figure 5-2).

Figure 5-2. View contents of cart, change quantities, remove items


There is a two-step purchase process. The first step requires that the buyer submit shipping information—how the item will be shipped and where it will be sent (Figure 5-3).

Figure 5-3. First, shipping information is collected …


Next, the customer types in payment information (tax is automatically applied to in-state orders). Because only the front end of the cart is automated, it is possible to accept international orders, although you'll probably want to tweak the interface a little bit to do that (see Figure 5-4).

Figure 5-4. … then billing information


At this point, the order is complete.

The cart generates an "Order Complete" page that summarizes the order information and gives the customer a unique order number (see Figure 5-5).

Figure 5-5. Order acknowledgment screen


At the same time, orders are written to a folder on the hard drive of your Web server, where they can easily be collected (see Chapter 6 for information on how to have orders emailed to you). See Figure 5-6.

Figure 5-6. Orders in the orders folder


Each order file looks like this:

Order Number:960800441188-623866419

Order Summary:
1: BK2PB (The dilemma of 2 (paperback))
Ship Method: basic
Grand Total: $11.68

Contact Information:
john doe
[email protected]
805-966-0611

Shipping To:
john doe
8 Main St.
santa barbara, CA 93101

Billing Information:

Visa
4111 1111 1111 1111
02/03

New Features

Storing Information in the Session Object

No sooner was HTML successful than it became necessary to figure out how to get around the fact that HTML is a stateless protocol. As originally conceived, Web servers were supposed to forget about who they were dealing with as soon as they were done sending a user a Web page.

This creates a real problem for a number of Web applications, including shopping carts. The whole point of a shopping cart is that it lets you browse a Web site, looking for stuff that you want to buy, picking stuff out to add to your "cart," then buying it all when you're ready. Not what HTML was designed for.

Early innovators figured out a way to get around this by passing form information on every single link on their sites, so that, for example, a link to a Web page would include an id of some kind:

http://www.lovejoy.com/cgi-bin/store/page2.cgi?user=123451234512345

This is pretty cumbersome. Some sites still do this, because to let people who don't have cookies shop on your site, you pretty much need to do something along these lines. Just make sure that you pass that id field on every single link, because otherwise the contents of a person's shopping cart are gone. Another irate customer.

The cookie was the next step in the evolution of things. With cookies, you could store a limited amount of information on the user's browser, which would be accessible to only you because the browser would share cookie information for a given domain only with a Web server for that domain.

Cookies are actually a very cool thing, even if they are kind of creepy ("You mean a Web site can put something on my computer?!?! Outrage!") and horribly unsafe (there have been too many security holes that have allowed unauthorized users to get hold of cookies).

Although cookies are poorly suited for storing credit card numbers and phone numbers, they are well suited to storing meaningless information, which means that it's possible to use cookies to assign unique ids to everyone who visits your site—then to store information that one would not want to share (credit card info, etc. …) in a safe place and reference the private information with the unique id stored in the cookie.

Which is what the ASP Session object does.

This means that one of the great challenges of building a shopping cart—keeping track of unique visitors to a site—is already taken care of with ASP. Any user-specific information that you want to keep track of can be stored in the ASP Session object. It's very sweet.

The scripts in this chapter take full advantage of this to store two sets of information in the Session object: things that users put into their carts and information that they give us about themselves. Only when all the information necessary for an order has been collected is an order file created and saved on the hard drive.

cart_items

cart_items is an object that is used to store items that a user has placed in his or her cart. In the interest of simplicity, all the information that the scripts will need about items that a user wants to purchase is stored here: item part numbers, quantities, names, descriptions, prices, etc.

order_info

The order_info object is used to store all the other information that is collected about a user, which is basically shipping and billing information (you can always get additional information by modifying the scripts).

Creating Templates

Creating an effective Web site is wonderfully complicated. One of the more interesting challenges is that Web sites are increasingly built by teams. Although in the first half of the 1990s, I used to kid myself that I could design an effective site, for the last few years I've left navigation and interface design to graphic designers and information architects.

In some cases where I've done all of the maintenance on a site, design and navigation elements have been freely mixed in with the code, so that only someone who was very comfortable with my scripts could actually edit a page.

Increasingly, however, I've lost interest in the day-to-day maintenance of Web pages, and this has meant structuring the programmatic elements of a Web application in such a way that people unfamiliar with the function of the application could work on that application's interface. This has meant using templates that contained as little code as possible, so that people who were familiar with html would be able to more or less easily modify the front end of a script.

In this chapter, the interface has been to a large extent separated from the functional elements of the script, so that the interface consists of templates that are relatively easy for people who don't understand the underlying ASP to edit. There's still some code but, fortunately, most people who are familiar with HTML are hard to intimidate, and I've had good luck letting designers put the front end on these scripts without having to go through the files with me line by line to figure out what's going on.

Storing Related Data in Two Text Files

To make it as easy as possible to set up the shopping cart, information about items that are for sale is stored in two text files: catalog_file.txt and option_file.txt.

Each item that is for sale is listed in catalog_file.txt. Every item in this file must have at least one option, which is, in turn, stored in option_file.txt. Thus, if you're selling a T-shirt that is available in three sizes, you would create one record in catalog_file.txt and three records in option_file.txt. A record is a line of information about an item. For example, a simplified illustration of how the data is organized can be depicted in a table:

catalog_file.txt option_file.txt
Part Number Description Part Number Extension Option Desc. Price
b_shrt A nice blue shirt b_shrt -Sm Small $12.00
b_shrt -Md Medium $15.00
b_shrt -Lg Large $17.00
w_shrt good white shirt w_shrt  One size fits all $16.00

The table above illustrates two cases: A blue shirt, which is available in three sizes, and a white shirt, which is available in only one size. Both shirts have a single line of information in catalog_file.txt. In option_file.txt, however, it's a different story: Here, there are three lines for the blue shirt, which has three options, and one line for the white shirt, which has one option.

Note that the part number field appears in both files, which makes it possible to map records in the catalog file to records in the option file. The full part number of an item consists of a concatenation of the part number and the extension fields (extensions can be blank if an item has only one option). Thus, w_shrt and b_shrt-sm are valid part numbers, whereas b_shirt is not a valid part number, because it does not include an extension, and there is more than one option for the blue shirt.

The actual text files used by the shopping cart are only slightly more complicated than the table above. cata log_file.txt has five fields:

  • id: Same as Part Number, above.

  • category: A field that can be used to organize items for sale into categories.

  • name: The name of an item that is for sale.

  • description: A description of an item.

  • image: Usually an HTML image tag, but any legal HTML is fine.

For example (wraps indented for clarity):

//id|category|name|description|image
bk1|Books|Book 1|The story of the number 1|<img
 src="images/logo.gif">
bk2|Books|Book 2|The dilemma of 2|<img
 src="images/logo.gif">
ck1|Pastries|Croissant|Snooty French pastry|<img
 src="images/logo.gif">
ck2|Pastries|Bagel|Bread disguised as a donut|<img
 src="images/logo.gif">

option_file.txt also has five fields:

  • id: Same as part number, above

  • ext: An extension to the id used to identify an option uniquely. This field can be blank if an item has only one option.

  • price: The price of an option.

  • weight: The weight factor of an option. This is used to calculate shipping and can be an actual weight or an arbitrary value used to calculate how much shipping to charge for a given item.

  • description: A description of an option. Note that this should be used only to describe the option, not the item itself, which should be described in the description field in catalog_file.txt.

For example:

//id|ext|price|weight|description
bk1||12.95|2|
bk2|pb|8.95|1|paperback
bk2|hb|23.22|2|cloth cover
ck1|chok|2.25|1|Chocolate Croissant
ck1|plain|2.15|1|Plain Croissant
ck2|on|4.25|3|Onion (dozen)
ck2|pl|4.25|3|Plain (dozen)
ck2|po|4.25|3|Poppy Seed (dozen)

The ASP Shopping Cart Code

Many of the scripts in this chapter make use of the cart.js file, which is discussed last. So if you run into an undefined variable or function that you're curious about, you can always jump back there and see how it works.

How the Pieces Fit Together

Figure 5-7 shows the files for the ASP shopping cart scripts and gives you a preview of how they all fit together.

Figure 5-7. How the scripts in this chapter fit together


Browsing the Catalog and Filling the Shopping Cart with Goodies

The scripts in this section let users browse items that are for sale, add them to their carts, and later remove them if they are so inclined.

Script 5-1 cart_list_items_for_sale.asp

						 1. <%@ Language=JavaScript %>
 2. <!-- #include file="cart.js" -->
 3.
						 4.
						 5. <%
 6. cart_header();
 7. items = create_items_object ();
 8. %>
 9.
						10. <!---------------start item display template-------->
11. <%
12. for (key in items){
13. %>
14. <!--------- start header ------------->
15. <table border=1 width=80% align=center>
16. <!--------- end header ------------->
17. <!--------- start item ------------->
18.   <tr><td colspan=2>
19.   <% = items[key].name %>
20.   </td></tr>
21.   <tr><td>
22.   <% = items[key].image %>
23.   </td><td valign=top>
24.   <% = items[key].description %><p>
25.     <!------ start item option information --------->
26.     <% for (key2 in items[key].options){
27.     if (String(items[key].options[key2].
                description) != "none"){
28.       %>
29.       <div align=right><% =
                  items[key].options[key2].description %>: $<% =
                  items[key].options[key2].price %> (<% = key
                  %><% = items[key].options[key2].ext %>) <% =
                  items[key].options[key2].buy_link %></div>
30.     <% }
31.     else {
32.       %>
33.       <div align=right>$<% =
                  items[key].options[key2].price %>(<% = key %><%
                  = items[key].options[key2].ext %>)  <% =
                  items[key].options[key2].buy_link %></div>
34.     <% }
35.     } // end for key2 loop %>
36.     <!------- end item option information ---------->
37. </td></tr>
38. <!--------- end item ------------->
39. <!--------- start footer ------------->
40. </table>
41. <!--------- end footer ------------->
42. <%
43. }  //end looping through items
44. %>
45. <!---------------end item display template---------->
46. </BODY></HTML>

How the Script Works

Most of the functionality used in this script is actually stored in the include file cart.js to make this as much as possible a "template" that can be edited by any designer who is comfortable with HTML.

Before diving into the details, it's worth remembering that this script essentially does only two things: First, it creates the HTML for a header that lets visitors navigate the cart; second, it displays information about one or more products or services that are for sale, using an object called items.

As always, the devil is in the details:

1. Set JavaScript as the scripting language.

2. Include cart.js, the file that contains many of the functions that are used throughout the shopping cart.

6. The cart_header() function (from the cart.js include file) generates the html for a header that lets users browse cart products by category, perform a keyword search, or view the contents of their carts (See Figure 5-8).

Figure 5-8. cart_header( ) creates a header used throughout the shopping cart

7. This line creates an object called items, using the function create_items_object(). create_items_object(), which is in the cart.js include file, performs most of the work required to display a list of products and/or services that are for sale through the shopping cart.

Depending on whether parameters are passed to the current script, create_items_object() will contain either all of the items that are available for sale, only those items that are in a specific category, or those items that match a keyword search.

To display the products or services stored in the items object, all that's required is to loop through the items object and put the information stored within it into an HTML page--which is, curiously enough, exactly what the rest of this script does.

8–46. While there is a lot of ASP code in these lines, it's mostly little bits and pieces embedded in an HTML page. The purpose here is to abstract the look of the page (HTML) from the functionality of the page (ASP) as much as possible. Thus, even someone who'se entirely unfamiliar with ASP can edit the HTML portion of this page, so long as he or she is reasonably careful not to break any of the ASP pieces.

12–43. Line 12 is the beginning of a for() loop that loops through the items object, displaying the information about each product or service stored in items.

One of the great things about objects and arrays is that it's possible to loop through them without having to know exactly what's inside of them. items is actually an object that consists of zero or more things that we want to sell (also objects, which are nested inside of items). In this case, the for loop works by creating the string key. By using each value of key in turn, it is then easy to access all of the good stuff stored inside of items.

For example, if all you wanted to do was get a list of the stuff inside of items, you could do that as follows:

for (key in items){
Response.Write(" n<br> " + items[key].name );
}

The name property of any given object inside of the items object is what you would expect—the name of a product as it was typed in one of the data files used by the cart scripts. If, for example, items contains information about three items called item1, item2, and item3, the output generated would be:

<br> item1
<br> item2
<br> item3

Of course, it's always possible that, for any given product or service, there are several versions of any particular product: books may be available in both hard cover and soft cover. Other items may be available in several different colors. This is addressed by yet another nested object, called options, which is created for each cart item stored in the items object.

Each product or service in the cart, by definition (see description of data files earlier in this chapter), has at least one option. Thus, to get not only the names of the various items for sale, but also the part numbers and the prices of the items, we need to loop not just through the items object, but also through the options object within each item. Simply done, this would look like this:

							1. for (key in items){
2. Response.Write(" n<br> " + items[key].name );
3. for (key2 in items[key].options){
4.  Response.Write(" n<br>---" + key +
    items[key].options[key2].ext)
5.  Response.Write(": "
    +items[key].options[key2].price)
6. }  // end looping through options
7.}  // end looping through items

Assuming that we're working with the same three items posited above and that each is available in two colors, we might get something like this:

<br> item1
<br> ---item1-yellow: $1.00
<br> ---item1-purple: $2.00
<br> item2
<br> ---item2-yellow: $5.00
<br> ---item2-purple: $6.00
<br> item3
<br> ---item2-yellow: $10.00
<br> ---item2-purple: $9.00

In fact, this is pretty much what happens in this script: Lines 12–43 loop through the items object, while lines 26–35 loop through the options object that is created for each item. During the loop through, various properties are dropped into the page. These are, including the properties we've described so far:

Table 5-1. The properties of the items object
items[key].name the name of the item
items[key].image the filename of the image associated with the image.
items[key].description text description of the item
items[key].options [key2].description description of a specific option
key the unique id of the item
items[key].options[key2].ext the unique id of the item's extension
items[key].options[key2].buy_link a link that lets a visitor add the item to the cart
items[key].options[key2].price the price for a specific item/option pair (only options have prices)

There is only one other piece of code in the script:

27. Here, the script checks to see whether items[key].options[key2].price is set to none, so that the information displayed will be different, depending on whether a description exists for an option.

If no option exists, the HTML generated on lines 28–30 is displayed. Otherwise, we get lines 31–34.

Warning

To keep the cart as simple as possible to use and to hack, the cart relies on pricing information stored in items[key].options[key2].buy_link (described above). This means that a malicious shopper could attempt to defraud you by creating a fraudulent buy_link. All orders should be reviewed to make sure that the total amount is correct.


Script 5-2 cart_set_session.asp

						 1. <%@ Language=JavaScript %>
 2. <!-- #include file="cart.js" --><%
 3. //Session.Timeout=1
 4. var add = String(Request("add"));
 5.
						 6. add_array = add.split("---")
 7.
						 8. if (Session(add_array[2]+".qty") > 0){
 9. Session(add_array[2]+".qty")++
10. }
11.
						12. else{
13.   Session(add_array[2]) = new Object()
14.   Session(add_array[2] + ".qty")= 1
15.   Session(add_array[2] + ".name")= add_array[0]
16.   Session(add_array[2] + ".desc")= add_array[1]
17.   Session(add_array[2] + ".price")= add_array[3]
18.   Session(add_array[2] + ".weight")= add_array[4]
19. }
20.
						21.
						22. Response.Redirect(cart_view_contents_script)
23. %>

How the Script Works

cart_set_session.asp is a relatively simple script that is used to add an item to the cart. Once it's done its work, it redirects a shopper to the cart_view_contents_script.

cart_set_session.asp is invoked when a user clicks on a link that was created by the create_items_object (). The format of the link is:

<a href="cart_set_session.asp?add=Book+1%2D%2D%2DThe+story+
of+the+number+1%2D%2D%2Dbk1%2D%2D%2D12%2E95%2D%2D%2D2"
>Buy</a>

Information is stored in a multipart form variable called add, which includes the name, description, part number, price, and weight of the item being added to the cart. In the example above, the item being added is called Book 1; the item description is The story of the number 1; the part number of the item is bk1; the price of the item is 12.95 (currency is not specified); and the weight of the item is 2.

So here's how the script works:

1. Set JavaScript as language.

3. This line, which is commented out, is useful if you're tinkering with things and you want to start with a clean slate. Setting Session.Timeout to one means that after one minute, the session object will be dumped.

If you're modifying the cart and want to zap the Session object, all you have to do is comment out the line and add something to the cart, comment the line back in, then wait a minute. If you can't stand the thought of waiting a minute, you should probably set it to five and force yourself to take a break. ;)

4. As discussed above, incoming form information is stored in a variable called add. Here, it gets shoved into a local variable of the same name.

6. Because the add variable is actually a multifield variable, the first thing to do is to use the split() method intrinsic to any JavaScript string to create an array called add_array.

8–19. As discussed in the section on storing information in the Session object earlier in this chapter, order information for the cart is stored using the ASP Session object. What happens next depends on whether the item being added to the cart already exists in the cart. If it does, all that needs to happen is to increment the qty property of that item by executing the code on lines 8–10. If the object does not exist, all of the information that we have about the item needs to be added to the object, which is what happens on lines 12–19.

8–10. If the item already exists in the cart, its quantity will be zero or greater, and all that needs to happen is for the quantity to be incremented using the ++ operator.

12–19. If the item's quantity is not greater than zero, the script assumes that the item does not exist, and the item is added to the Session object.

13. The first step is to create a new object inside of the Session object. The object is named after the third element of add_array (add_array[2]), which is the item's part number (a concatenation of the item number and the option number), which is unique.

14. Next, the qty property of the object is set to one, because this is the first time that we're adding this object to the cart.

15–18. Finally, the name, desc, price, and weight properties of the item are set, using information from the appropriate fields of add_array.

At this point, the item has either been added to the cart, or, if it was already in the cart its quantity has been increased

22. The last line forwards the shopper to the cart_view_contents_script, where the shopper can view all of the items that have been added to the cart so far.

Script 5-3 cart_view_contents.asp

						 1. <%@ Language=JavaScript %>
 2. <!-- #include file="cart.js" -->
 3.
						 4.     <center><% = cart_header() %></center>
 5.
						 6.     <center>View/Edit Shopping Cart
        Contents</center>
 7.
						 8. <form action="<%=change_qty_script%>">
 9. <table border=1 align=center>
10. <% = cart_contents_header_row() %>
11. <% = cart_contents_editable_row () %>
12. <% = cart_contents_subtotal_row() %>
13. </table>
14. <div align=center><input type=submit name=toss
    value="Change Qty"></div>
15. </form>
16.
						17. <div align=right><a href="<% =
    collect_shipping_information_script %>">Check Out ->
18. <hr width="50%">
19. <a href="<% = cart_remove_all_from_cart_script
    %>">Clear Cart</a>
20. </div>
21. </BODY></HTML>

How the Script Works

Like cart_list_items_for_sale.asp, cart_view_contents.asp displays information to the shopper. Again, to make it as easy as possible to edit the look and feel of the page, most of the code that gets executed on this page actually lives in the cart.js include file.

Although the page generated by the script is fairly complex (it can be used to edit the quantity of items being purchased or remove items from the cart, as well as contains links to continue shopping or "check out"), the ASP on this page is not:

1. Set JavaScript as the scripting language.

2. Grab the include file, which contains all of the functions that will be executed on this page.

4. The cart_header() function creates the header that lets shoppers navigate around the cart.

8. None of the cart scripts contain links to specific URLs. Instead, URLs are defined in variables set in cart.js. The form that starts on line 8 is configured to send shoppers to the change_qty_script if they click on the Submit button on line 14, which would be used if they wanted to change the quantity of one or more items in their carts.

10–12. Create the table that displays the contents of the cart.

10. cart_contents_header_row() spits out the HTML for the top row of the table.

11. cart_contents_editable_row() is a more sophisticated function that creates a row for every item in the ASP Session object that has a quantity of one or more. The row contains information about the item, a text box that can be used to change the quantity of a particular item being purchased, and a "Delete This Item" link that can be used to remove the item from the cart altogether.

12. cart_contents_subtotal_row() creates a row that shows the subtotal for the items currently being purchased.

17,19. The link to the checkout scripts is set using a variable defined in cart.js, rather than being hard-coded. The same is true of the link to cart_remove_all_from_cart_script, which zaps everything from the cart.

Script 5-4 cart_change_qty.asp

						 1. <%@ Language=JavaScript %>
 2. <!-- #include file="cart.js" -->
 3. <%
 4. var uid = String(Request("uid"));
 5.
						 6. // delete links use the "uid" variable:
 7. if (uid != "undefined"){
 8. var set_qty = parseInt(Request("set_qty"));
 9. Session(uid+".qty")=set_qty
10. Response.Redirect(cart_view_contents_script)
11. }
12.
						13. // change qty button requires resetting qty on all
    items in the basket.
14. else{
15. // change button scrolls through all items:
16. for ( field_number = 1; field_number <=
    Request.QueryString.count ; field_number++ ){
17.
						18.  temp_item =
             String(Request.QueryString.Key(field_number));
19.  temp_qty =
     String(Request.QueryString.item(temp_item))
20.
						21.  // make sure field is one we want to use
22.  if (temp_item.search(/uid_/) >= 0 ){
23.    temp_uid =
       temp_item.substring(4,temp_item.length)
24.    //Response.Write(temp_uid + "<br>");
25.    Session(temp_uid+".qty")=temp_qty
26.
						27.  } // end if
28. } // end for loop through incoming fields
29. Response.Redirect(cart_view_contents_script)
30. }  // end else
31. %>

How the Script Works

cart_change_qty.asp can be invoked in two ways: when a shopper clicks on a "Delete This Item" link for a specific item or when someone clicks on the "Change Qty" Submit button, which can be used to change the quantity of any number of items in the cart.

In both cases, the script makes the desired change, then sends people back to the cart_view_contents_script.

1. Language is JavaScript.

2. Include cart.js.

4. The "Delete This Item" link sets a form variable called uid, which is assigned to a local variable of the same name.

7–11. If the value of uid is set to something (and is, therefore, not equal to "undefined"), someone clicked on the "Delete This Item" link, and lines 8–10 get executed. Otherwise, we know that someone clicked on the Change Qty button, and lines 14–30 get executed.

8. The "Delete This Item" link sets a form variable called set_qty, which is used to set the value of a local variable of the same name. Unless you hack it to do something else, set_qty will always be set to zero, because the point of the link is to delete the item.

9. Each item in the cart has a corresponding object within the ASP Session object. This line sets the qty property of the desired item to the value of set_qty (zero). Note that the item is not actually deleted: Rather, its qty property is set to zero.

10. At this point, the script is done, and the shopper is redirected to the URL assigned to the cart_view_contents_script variable.

14. Start of an else block triggered by the logic in line 7.

16. The script that lets people view the contents of their carts contains a relatively simple form that consists of zero or more text fields, where the users can edit the quantity of every item in their carts. There is also a Submit button that is used to submit the form.

Each text box is named uid_part-number, where part-number is the actual unique id of an item in the cart's database (described in the section on storing related data in two text files, earlier in this chapter).

What happens next is that the script loops through the incoming form data, looking for form fields that start with the string uid_. When it finds one, it uses its value to set the quantity for that item to the value assigned to it by the text box. This might be the value it already had or a new value, if it was changed by the shopper.

16–28. The for() loop started on line 16 loops through each form variable in the ASP Request.QueryString object. The for() loop uses a variable called field_number, which is a simple counter and starts at one rather than zero because that seems to be the Microsoft way. Whatever.

18,19. Starting with the value in the field_number counter, two variables are created: temp_item and temp_qty. temp_item is the the name of a form variable, and temp_qty is its value.

22. If temp_item matches the regular expression /uid_/, the script decides that it has a winner, and lines 23–25 are executed.

23. As discussed a few lines up, the unique part number (or uid) of the item whose quantity is being set is part of the form variable's name--which is now the variable temp_item. This line creates a new variable called temp_uid by extracting a stubstring from temp_item that runs from its fifth character (the first character after the prefix uid_) all the way to its last character (temp_item.length).

25. Now that the script knows the part number of the item (temp_uid) and the quantity that it needs to be set to (temp_qty), the information stored in the Session object is updated.

29. Finally, its work complete, the script redirects the shopper to the URL assigned to the cart_view_contents_script variable in cart.js.

Script 5-5 cart_remove_all.asp

					1. <%@ Language=JavaScript %>
2. <%
3. for (i = 1 ; i <= Session.Contents.Count ; i++){
4.  if ((typeof Session.Contents(i) == "object") &&
    (Session(Session.Contents.key(i)+".qty") > 0)){
5.      Session(Session.Contents.key(i)+".qty") = 0
6.  }
7. }
8. Response.Redirect("cart_view_contents.asp")
9. %>

How the Script Works

When shoppers are looking at the contents of their carts (cart_view_contents.asp), they can use the "Clear Cart" link to remove the entire contents. This is the script that gets executed when this happens.

Zapping the contents of a user's shopping cart does not actually involve deleting the information that's been stored in the Session object about that user. Instead, the script scans the Session object for shopping cart objects and sets their quantity attribute to zero. Here's how it works.

1. Set language to JavaScript.

3. Start a for()loop that will loop through everything that is in the Session object. Session.Contents.Count gives us an integer that tells us how many things we have in the Session object (which we'll access via the Session.Contents object).

Using a counter i that gets incremented from 1 to Ses sion.Contents.Count (another special Microsoft array that starts at one instead of zero), we can access each item in the Session.Contents object by treating it as an array and using the syntax:

Session.Contents(i)

4. If the thing that we find is an object and it has a qty attribute that is greater than zero, line 5 gets executed, which sets the qty attribute to zero.

Thus, once the script has looped through everything in the Session.Contents object, every item in the cart will have its qty set to zero, and for all practical purposes the cart will be empty.

The if block started on line 4 closes on line 6.

7. End of the for() loop.

8. Our work done, the bereft shopper is returned to cart_view_contents.asp. Note that this link, unlike most in the script, is hard-wired. You might want to fix that sometime.

Collecting Shipping and Billing Information and Generating an Order

Once a shopper's decided what to buy, the scripts below collect shipping and billing information, and finally create an order file.

Script 5-6 collect_shipping_information.asp

						 1. <%@ Language=JavaScript %>
 2. <!-- #include file="cart.js" -->
 3.
						 4.      <center>Cart Contents</center>
 5.
						 6. <table border=1 align=center>
 7. <% = cart_contents_header_row() %>
 8. <% = cart_contents_non_editable_row () %>
 9. <% = cart_contents_subtotal_row() %>
10. <% = cart_contents_shipping_row() %>
11. </table>
12.
						13. <form action="<% = set_shipping_info_script %>">
14. <hr>
15. Upgrade Shipping:
16. <br>2 day<input type=radio name=ship_upgrade
    value="2_day">(costs <% =
    dollarise((order_info.weight*two_day_ship_cost)-
    (order_info.weight*basic_ship_cost)) %> extra)
17. <br>overnight<input type=radio name=ship_upgrade
    value="1_day"> (costs <% =
    dollarise((order_info.weight*one_day_ship_cost)-
    (order_info.weight*basic_ship_cost)) %> extra)
18.
						19. <hr>
20. Contact Information:
21. <table align = center>
22. <tr>
23.  <td align=right>First Name: </td>
24.    <td><input type=text name="f_name"
       value="john" size=30></td>
25. </tr>
26. <tr>
27.   <td align=right>Last Name: </td>
28.     <td><input type=text name="l_name"
        value="doe" size=30></td>
29. </tr>
30.   <td align=right>Email Address: </td>
31.     <td><input type=text name="email"
        value="[email protected]" size=30></td>
32. </tr>
33.   <td align=right>Phone Number: </td>
34.     <td><input type=text name="phone"
        value="805-966-0611" size=30></td>
35. </tr>
36. </table>
37. <hr>
38. Shipping Information (US Only!):
39. <table align = center>
40. <tr>
41.   <td align=right>Address 1: </td>
42.     <td><input type=text name="address_1"
        value="8 Main St." size=30></td>
43. </tr>
44. <tr>
45.   <td align=right>Address 2: </td>
46.     <td><input type=text name="address_2"
        value="" size=30></td>
47. </tr>
48. <tr>
49.   <td align=right>City: </td>
50.     <td><input type=text name="city" value="santa
        barbara" size=30></td>
51. </tr>
52. <tr>
53.   <td align=right>State: </td>
54.     <td><select name="state" >
55.     <% for (i in array_of_states){  %>
56.       <option value=<% = array_of_states[i]
          %> ><% = array_of_states[i] %>
57.     <% }  %>
58.     </select>
59.     </td>
60. </tr>
61. <tr>
62.   <td align=right>Zip Code: </td>
63.      <td><input type=text name="zip" value="93101"
         size=30></td>
64. </tr>
65. </table>
66.
						67. <hr>
68.
						69. <center> <input type=submit name=toss
    value="submit"></center>
70. </form>
71.
						72.
						73. </BODY></HTMLx>

How the Script Works

This script allows shoppers to:

  • Review their orders.

  • Select a shipping option.

  • Enter contact information.

  • Enter the address to which they want their product shipped.

The cart supports simple shipping options based on a "weight factor" that is specified for each item that is for sale. The weight factor is multiplied by one of three values that are defined in the cart.js include file:

basic_ship_cost = 1.95
two_day_ship_cost = 6.95
one_day_ship_cost = 8.95

Thus, if a visitor purchases a croissant with a ship factor of 1 and two dozen bagels, each dozen of which has a ship factor of 2, the order will have a ship factor of 5, which will be multiplied by either $1.95, $6.95, or $8.95, depending on the shipping option selected (basic_ship_cost is used by default).

Increasingly, sites include shipping costs in an item's price. If this is the case with your business, you can simply set the variables above to zero, and no shipping cost will be added to orders.

Details:

1. Set language to be JavaScript.

2. Include cart.js, which will do a lot of our work for us.

6, 11. These lines open and close a table that will be generated by some functions in the cart.js file.

7–10. The functions cart_contents_header_row(), cart_contents_non_editable_row(), cart_contents_subtotal_row(), and cart_contents_shipping_row() are used to print the contents of the cart to the page. See Figure 5-9 for an example.

Figure 5-9. Lines 7–10 generate cart contents, shipping cost, and subtotal

The functions are described in detail later in this chapter. Basically, they grab order information that is stored in the Session object and put it into table rows that are easy to stack together in an HTML page.

13. The purpose of this page is to collect shipping information, using a form that will be sent to the set_shipping_info_script, a variable set in cart.js that resolves to the string set_shipping_information.asp, which will collect the information entered in the form.

16,17. These lines create two radio buttons that can be used to upgrade from the basic shipping option to the 2_day or 1_day options. Next to each line, the script calculates how much will be added to the cost of the order for that option. This is done by multiplying the weight factor (which is set for each item in option_file.txt) by the rate for 1-day or 2-day shipping. The cost of basic shipping is then subtracted, so that only the difference between the two costs is shown (Figure 5-10).

Figure 5-10. Shipping radio buttons

The balance of the script, with one exception, is a standard html form consisting of a series of text boxes that set the value for a number of variables that store contact and shipping information. Note that the variable names are not arbitrary. If you change their names, you'll probably break a couple of things and need to track down where the variables are used and change the names there, as well.

The one place where there is some ASP code is on lines 55–58, to create the select list that lets shoppers select the state where they live.

The select list is more than just a convenience: To figure out whether or not to charge sales tax, it's necessary to know what state things are being shipped to. By using a select list, it's easy to control how the name of a state is spelled, which makes it easy for the script to determine whether or not to charge sales tax.

Making long select lists is kind of boring, so the task is automated here by using an array that contains the names of all the states. The array is created in cart.js (for the sake of simplicity, only three states are included—you can add the rest):

array_of_states = new Array("CA","PA","NV")

The select list is created as follows:

54. Normal HTML is used to open a <select> tag with a name attribute of "state".

55–57. A for() loop is used to loop through the array array_of_states and create an <option> tag for each state in the array.

That's about it. At that point, the script once again lapses into HTML. As done elsewhere in the book, the value of the submit tag is set to toss because it is not used except as a means of submitting the form.

One last thing: A lot of the form fields already have their value attributes set. This is for debugging purposes: Getting all the different pieces of the cart to work together often requires going through the shopping process from start to finish a few times. Having the fields filled out makes the first part of debugging a whole lot easier.

Once you're pretty sure that everything works, you can set the value attribute of the various form elements to empty string ("")for a final round of testing, but until then, do the easy thing and leave everything filled in.

Script 5-7 set_shipping_information.asp

						 1. <%@ Language=JavaScript %>
 2. <!-- #include file="cart.js" -->
 3.
						 4. <%
 5. //Session.Timeout=1
 6.
						 7.
						 8. // set ship type and ship cost info:
 9. if (String(Request("ship_upgrade")) == "2_day"){
10.   order_info.ship_type = "2_day";
11.   order_info.ship_cost =
      order_info.weight*two_day_ship_cost;
12. }
13. else if (String(Request("ship_upgrade")) ==
    "1_day"){
14.   order_info.ship_type = "1_day";
15.   order_info.ship_cost =
      order_info.weight*one_day_ship_cost;
16. }
17. else {
18.   order_info.ship_type = "basic"
19.   order_info.ship_cost =
      order_info.weight*basic_ship_cost;
20. }
21.
						22.
						23. // loop through other shipping info
24. shipping_info_array = new
    Array("f_name","l_name","email","phone","address_1", 
    " address_2","city","state","zip")
25.
						26.for (i in shipping_info_array){
27.  if (String(Request(shipping_info_array[i])) != ""
     || shipping_info_array[i] == "address_2"){
28.   order_info[shipping_info_array[i]] =
      String(Request(shipping_info_array[i]))
29.  }
30.  else{  // required fields:
31.    out += "<br> " + shipping_info_array[i];
32.  }
33.}
34.
						35.if (String(Request("state")) == "CA"){
36.  order_info.tax = order_info.subtotal * .0875
37.}
38. else {
39.   order_info.tax = 0;
40. }
41.
						42. // concatenate ship address into easy-to-use
    information
43. order_info.ship_address = order_info.f_name + " " +
    order_info.l_name;
44. order_info.ship_address   += " n" +
    order_info.address_1
45. if (order_info.address_2.length > 0) {
46. order_info.ship_address   += " n" +
    order_info.address_2 }
47. order_info.ship_address   += " n" +
    order_info.city + ", " + order_info.state + " " +
    order_info.zip;
48.
						49. // create contact info string:
50. order_info.contact_info = order_info.f_name + " " +
    order_info.l_name;
51. order_info.contact_info += " n" + order_info.email
52. order_info.contact_info += " n" + order_info.phone
53.
						54. // create order summary string:
55. order_info.order_summary = "";
56. for (i in cart_items){
57.   order_info.order_summary += cart_items[i].qty + ":   
      " + cart_items[i].uid
58.   order_info.order_summary += " (" +
      cart_items[i].desc + ")" + " n"
59. }
60. order_info.order_summary += "Ship Method: " +
    order_info.ship_type
61. order_info.order_summary += " nGrand Total: " +  
    dollarise(order_info.subtotal + order_info.ship_cost  
    + order_info.tax);
62.
						63.
						64.
						65.
						66. // if mandatory field is missing, tell user to fix
    it. otherwise, redirect to billing information page
67.
						68. if (out != ""){
69. %>
70. The following field must be filled out:
71. out: <% = out %>
72. <%
73. }
74.
						75. else {
76.      Session("order_info") = new Object()
77.      for (i in order_info){
78.        Session("order_info." +  i) =
           order_info[i]
79.        }
80.        Response.
           Redirect(collect_billing_info_script)
81. }  // end else
82. %>

How the Script Works

This script takes the information collected by collect_bill ing_information.asp and stores it in the session variable via an object called order_info.

In addition to simply collecting the data and placing it in the Session object, the script checks to make sure that required fields are filled out and begins the process of creating an order by assembling different pieces of information in the correct order.

1. Set Language to JavaScript.

2. Grab cart.js.

5. This line is some debug code that I left in there in case you need it later. This line sets the Timeout value of the Session object to one second, which is a convenient way to zap the Session object and get a new one (if you don't mind waiting a second. Maybe you should set it to 60 and get up for a minute and stretch. I dunno).

You could also use the Timeout property if you felt that shoppers' sessions were timing out too quickly. For example, if you wanted sessions to last an hour (the default is usually 20–30 minutes, if memory serves), you could set it to 3600 (60 seconds times 60 minutes). Of course, you'd want to do this in one of the scripts that gets used earlier in the process, like cart_list_items_for_sale.asp.

8–20. The first thing that the script does is to store information about how the item is being shipped in the order_info object, which is an object created in the cart.js include file and used to organize information before it is put in the Session object. Note that order_info doesn't have any properties: It's essentially an associative array. But it works, and I guess that's really what matters, right?

Anyhow, if you take a look at the form that we're collecting information from (the form generated by the previous script) you'll see that shoppers have three options: They can select the "2 Day" shipping upgrade, which will set a form variable called ship_upgrade to a value of 2_day; They can select the "Overnight" shipping upgrade, which sets the same variable to 1_day; or they can do nothing, in which case, we know that they want basic shipping, and ship_upgrade will not have a value of 1_day or 2_day.

Thus, the logic of this section is that in line 9 the script checks to see whether ship_upgrade is set to 2_day. If it is, order_info.ship_type is set to 2_day (in line 10), and order_info.ship_cost is set to the product of order_info.weight and a variable called two_day_ship_cost (in line 11). The values of both order_info.weight and two_day_ship_cost are set in cart.js, which is discussed later in this chapter. two_day_ship_cost is an arbitrary string set to whatever the suits decide they want to gouge people for shipping. order_info.weight is simply the sum of the weight factors of all the items in the shopper's cart (times the number of each item purchased).

Okay. If you're still reading at this point, you're probably realizing that this isn't exactly how you want cost of shipping to work. I'll cover that in a second.

You'll notice that lines 10, 13, and 17 create an if … else if … else statement. This means that if ship_upgrade isn't set to 2_day, the next thing the script does (line 13) is to check to see if it's set to 1_day. If so, the ship_type and ship_cost properties of the order_info object are set to the appropriate values, this time using the cost stored in the one_day_ship_cost variable.

17. else, that is, if ship_upgrade is not set to either 1_day or 2_day, order_info.ship_type is set to basic, and the cost of shipping is calculated using basic_ship_cost.

Hacking the ship cost:

You might decide that you want to set a base shipping fee that gets applied to any order, then incremented based on the weight factor (I talk about weight factor rather than weight because, for some items, dimensional weight is more of an issue than physical weight). To do this, you'd want to add a base cost, perhaps using a variable such as base_ship_cost. Net result might be:

order_info.ship_cost = base_ship_cost +
(order_info.weight*two_day_ship_cost);

Don't forget that you'd need to define base_ship_cost before you use it.

Another option would be to assign ship costs based on the dollar value of the order. One of the properties of the order_info object is order_info.subtotal. This value could be multiplied by a fixed amount in order to set the order_info.ship_cost property.

That's probably enough about shipping.

26–33. The script makes sure that required fields are filled out and puts them into the order_info object. You might want to fancy this up, because all the script does is check for empty strings in required fields.

24. shipping_info_array is an array used to list the fields that we want to put into the order_info object, so called because that's where this script puts all its order information before storing it in the Session object. Note that fields in this array are not necessarily required fields—the address_2 field can be left blank. These are simply the fields we care about.

26–33. The next step is to loop through the fields in shipping_info_array and do two things.

First, in line 27, we check to make sure that required fields are filled out. This is done by making sure that the fields are not empty (!= ""). The address_2 field is exempt, because it's an optional field that many shoppers may not use. If a field is empty, that field's name gets concatenated into the out string on line 31, which will be used to generate an error message asking the shopper to fill in that field.

Second, assuming the condition set in line 27, in line 28, the information collected by the form is added to the order_info variable. Thus, for example, the form variable f_name becomes order_info.f_name.

The next step is to figure out whether the order is taxable.

35. Using an if… else statement, the form variable state is checked to see whether its variable is set to the string "CA" (I live in California. You'll need to change this if you're in a different state).

36. If the order is being shipped to California, the tax property of order_info is set to 8.75% (that's the same thing as .0875) of order_info.subtotal.

39. If the order is not being shipped to California, or der_info.tax is set to zero.

order_info.tax will be used to create a grand total later in this script.

Next, various properties of the order_info object are concatenated into three summary strings: order_info.ship_address, order_info.contact_info, and order_in fo.order_summary.

43–47. These lines create a shipping address by concatenating the shopper's name and address information into a single string. Note that Newline characters " n" are used to create line breaks in the appropriate places, so that order_info.ship_address will end up creating a string that looks something like this:

john doe
8 Main St.
 santa barbara, CA 93101

50–52. The shopper's name, email address, and phone number are put into order_info.contact_info, creating a string similar to:

john doe
[email protected]
805-966-0611

55–61. The order_summary string pulls information both from the cart_items object that contains the items that the shopper is purchasing and the order_info object.

In lines 56–59, the script iterates through the cart_items object. Every item in the shopper's cart is added to the order_summary string.

Once the contents of the order have been collected, the ship method and grand total are added. The grand total is calculated as the sum of order_info.subtotal, order_info.ship_cost, and order_info.tax. The dollarise() function is used to make sure that the total is expressed as a figure with two decimal places, the way a dollar figure is expected to look.

For example, the net result might look like this:

1: BK2PB (The dilemma of 2 (paperback))
1: CK1CHOK (Snooty French pastry. (Chocolate
Croissant))
Ship Method: basic
Grand Total: $16.08

At this point, the script's work is almost finished.

68. The if() statement here checks to see whether the out variable is empty (it contains a list of mandatory fields that were not filled out, if any).

If out is not empty, lines 69–73 are executed, and an error message is displayed, asking the shopper to fill out the missing fields.

If out is empty, the else statement (lines 75–81) are executed. Two things happen: Order information is passed to the Session object, and the user is redirected to the next script, where billing information will be collected.

Passing information to the Session object is a matter of the following:

76. A new object, called order_info, is created inside of the Session object.

77–79. A for() loop is used to iterate through the order_info object and replicate each of the properties of order_info in Session("order_info"). It seems like there should be a way just to put the entire object into the Session object, but I couldn't quickly figure out a way to do that, and this works. Good enough.

80. At last, our script's work is done, and the shopper is forwarded to the collect_billing_info_script.

Script 5-8 collect_billing_information.asp

						 1. <%@ Language=JavaScript %>
 2. <!-- #include file="cart.js" -->
 3.
						 4.       <center>Order Summary</center>
 5.
						 6. <table border=1 align=center>
 7. <% = cart_contents_header_row() %>
 8. <% = cart_contents_non_editable_row () %>
 9. <% = cart_contents_subtotal_row() %>
10. <% = cart_contents_shipping_row() %>
11. <% = cart_contents_tax_row() %>
12. <% = cart_contents_grand_total_row() %>
13. </table>
14.
						15.
						16. <hr><!-- confirm address -->
17. This order will be shipped to:
18. <table align=center border=0><tr><td>
19. <% = order_info.ship_address.replace(/ n/g,"<br>") %>
20. </td></tr></table>
21. <hr>
22.
						23. <table border =1 width="100%">
24. <tr>
25. <td width="50%" valign=top>
26.   Pay With Prepaid Web Card:
27.   <form action="<% =
        set_pwc_billing_information_script %>">
28.      <table>
29.      <tr>
30.       <td>Card Number: </td>
31.         <td><input type=text
             name=card_number value="4111 1111 1111 1111"   
             size=20></td>
32.      <tr>
33.      <tr>
34.       <td>Pin Number: </td>
35.         <td><input type=text
             name=pin_number value="5678" size=4></td>
36.       <tr>
37.       </table>
38.       <center><input type=submit name=toss
           value="submit"></center>
39.   </form>
40. </td>
41.
						42. <td width="50%">
43.  Pay with Credit Card:
44.   <form action="<% =
       set_cc_billing_information_script %>">
45.     <table>
46.     <tr>
47.       <td align=right>Method of Payment: </td>
48.         <td>
49.         <select name="payment_method">
50.         <option name="visa">Visa
51.         <option name="mastercard">Mastercard
52.         </select>
53.         </td>
54.     </tr>
55.     <tr>
56.       <td>Card Number: </td>
57.         <td><input type=text
             name=card_number value="4111 1111 1111
             1111"></td>
58.     <tr>
59.     <tr>
60.       <td>Expiration Date: </td>
61.         <td><input type=text
             name=expiration_date value="02/03"></td>
62.     <tr>
63.     </table>
64.
						65.     If billing address is different from shipping
         address, you must fill out billing address
         below:
66.
						67.     <table>
68.     <tr>
69.       <td align=right>First Name: </td>
70.         <td><input type=text
             name="f_name" value="" size=30></td>
71.     </tr>
72.     <tr>
73.       <td align=right>Last Name: </td>
74.         <td><input type=text name="l_name" value=""
             size=30></td>
75.      </tr>
76.      <tr>
77.       <td align=right>Address 1: </td>
78.         <td><input type=text name="address_1"
             value="" size=30></td>
79.     </tr>
80.      <tr>
81.        <td align=right>Address 2: </td>
82.          <td><input type=text name="address_2"
              value="" size=30></td>
83.      </tr>
84.    <tr>
85.       <td align=right>City: </td>
86.         <td><input type=text name="city" value=""
              size=30></td>
87.      </tr>
88.      <tr>
89.        <td align=right>State: </td>
90.          <td><select name="state" value="" >
91.          <option value="">
92.          <% for (i in array_of_states){  %>
93.            <option value=<% = array_of_states[i] %>
                ><% = array_of_states[i] %>
94.          <% }  %>
95.          </select>
96.          </td>
97.      </tr>
98.      <tr>
99.        <td align=right>Zip Code: </td>
100.         <td><input type=text name="zip" value=""
             size=30></td>
101.     </tr>
102.    </table>
103.     <center><input type=submit name=toss
         value="submit"></center>
104.   </form>
105.
						106. </td>
107. </tr>
108. </table>
109. </BODY></HTML>

How the Script Works

Briefly, this script:

  • Presents the shopper with all the details of the order, including the cost of shipping and tax.

  • Displays the shipping address collected by the previous script to prevent stupid mistakes.

  • Collects the information required to bill the shopper for the purchase being made.

If you've looked at the script source code or the HTML page that it generates, you've probably noticed a curious thing—a payment option called "Prepaid Web Card." Increasingly, Web sites are accepting non-credit-card payment options, including pre paid Web cards. The prepaid Web card option illustrates how even a simple shopping cart can be set up to accept multiple payment options.

1. Set language to JavaScript.

2. Include the cart.js include file.

6–13. Table tags here enclose a series of functions, each of which create one or more rows of a table that displays the contents of the shopper's cart and the grand total of the purchase, including shipping and, if applicable, tax. These functions, which are described in detail in cart.js, were also used in cart_view_contents.asp and collect_billing_information.asp.

19. This line displays the shipping information, which was concatenated into order_info.ship_address (full name and address). Note how the replace() method, which is intrinsic to any string, including the property of an object, is used to replace Newlines with HTML break tags.

The balance of the script is straightforward HTML. Note that there are two forms on this page. The first collects payment information for shoppers using prepaid Web cards, which is processed by the set_pwc_billing_information_script. The second form collects payment information for shoppers using credit cards and is processed by set_cc_billing_information_script.

There's another snippet of ASP code on lines 91–93, used to generate a select list of the states contained in the array_of_states variable created in cart.js.

Script 5-9 set_cc_billing_information.asp

						 1. <%@ Language=JavaScript %>
 2. <!-- #include file="cart.js" -->
 3.
						 4. <%
 5. // loop through billing info. check for required
   fields & put data into order_info
 6. billing_info_array = new
    Array("payment_method","card_number",
    "expiration_date","f_name","l_name",
    "address_1","address_2","city","state","zip")
 7.
						 8. var optional_fields_regex =
    /f_name|l_name|address_1|address_2|city|state|zip/
 9.
						10. for (i in billing_info_array){
11.   if (String(Request(billing_info_array[i])) != "" ||
      billing_info_array[i].search
      (optional_fields_regex)>=0){
12.     order_info["billing_" +
        billing_info_array[i]] =
        String(Request(billing_info_array[i]))
13.   }
14.   else{
15.     out += "<br> " + billing_info_array[i];
16.   }
17. }
18.
						19. // create a uid:
20.   temp_date = new Date()
21.   order_info.uid = temp_date.getTime() + "-" +
      Session.SessionID
22.
						23.
						24. // summary of billing info:
25.
						26. order_info.billing_info = order_info.billing_f_name +
    " " + order_info.billing_l_name;
27. order_info.billing_info      += " n" +
    order_info.billing_payment_method
28. order_info.billing_info      += " n" +
    order_info.billing_card_number
29. order_info.billing_info      += " n" +
    order_info.billing_expiration_date
30. order_info.billing_info      += " n" +
    order_info.billing_address_1
31. if (order_info.billing_address_2.length > 0) {
32.   order_info.billing_info    += " n" +
      order_info.billing_address_2 }
33. if (order_info.billing_city.length > 0) {
34.   order_info.billing_info    += " n" +
      order_info.billing_city + ", " +
      order_info.billing_state + " " +
      order_info.billing_zip;
35. }
36.
						37.
						38. // if mandatory field is missing, tell user to fix
    it. otherwise, redirect to billing information page
39.
						40. if (out != ""){
41. %>
42. The following field must be filled out:
43.   <% = out %>
44. <%
45. }
46.
						47. else {
48.
						49.  // 1st put the information into the session
      variable
50.   Session("order_info") = new Object()
51.   for (i in order_info){
52.     Session("order_info." +  i) = order_info[i]
53.   }  // end for
54.
						55.   // 2nd, put it into an order file:
56.
						57.   // get rid of script name in absolute path
58.   // and replace with name of file we want to open
59.   var abs_path =
      String(Request.ServerVariables("PATH_TRANSLATED"));
60.   var file_to_create =
      abs_path.replace(/   w* .asp/,"  orders  ") +
      order_info.uid + ".txt";
61.
						62.   var fso = new
      ActiveXObject("Scripting.FileSystemObject");
63.   var temp_file = fso.CreateTextFile(file_to_create,
      true);
64.   temp_file.Write("Order Number:" + order_info.uid);
65.   temp_file.Write(" n nOrder Summary: n" +
      order_info.order_summary);
66.   temp_file.Write(" n nContact Information: n" +
      order_info.contact_info);
67.   temp_file.Write(" n nShipping To: n" +
      order_info.ship_address);
68.   temp_file.Write(" n nBilling Information: n" +
      order_info.billing_info);
69.   temp_file.Close();
70.
						71.   Response.Redirect(order_complete_script)
72. }
73. %>

How the Script Works

This script performs the following tasks:

  • It makes sure that required billing information is collected.

  • It creates a string that summarizes billing information.

  • It creates an order file that stores all of the information collected by the various scripts to date as a text (.txt) file.

  • It redirects shoppers to the final script in the cart: order_complete.asp.

1. Tell IIS we're using JavaScript.

2. Include cart.js.

6. Create billing_info_array, an array that contains all of the incoming form fields that we want to pass to the order_info object.

8. Create optional_fields_regex, a regular expression that will match the names of fields that are optional. We'll use this in line 11.

10–17. These lines iterate through the form elements in billing_info_array using a for() loop, making sure that required fields are filled out and stuffing the information collected into the order_info object.

11. The first thing that happens in the loop is to verify that either a form field has something in it (so that String(Request(billing_info_array[i])) != "" will be true) or that the field name matches one of optional fields in the optional_fields_regex regular expression (so that billing_info_array[i].search(optional_fields_regex)>=0 will be true).

12. If either condition is true, the field is stored in the order_info object.

Note that the prefix billing_ is added to every field to differentiate between the first name of the contact person for the order (order_info.f_name) and the first name of the credit card holder (order_info.billing_f_name).

15. If one of the required fields is missing, the if() statement in line 11 will be false, and the missing field will be added to the out variable, which will be used to ask the shopper to fill in the required field later in this script.

20,21. Each order generated by the script gets a unique order number, which can be used to track orders, manage post-order customer questions, etc. … IIS generates a unique SessionID for every visitor, which is guaranteed to be unique unless the server is rebooted. Because servers get rebooted, the script generates order numbers that are a concatenation of the time that the order is created (a rather large number of milliseconds, for example, 958951109525) and the IIS SessionID (also a rather large number, for example, 52468604). Net result:

958951109525-52468604

To generate the time that the order is created, based on when this script is executed, first a new Date() object has to be created. This is temp_date, in line 20. Next, the getTime() method has to be applied to the object, which returns, in this case, something on the order of 959 billion milliseconds.

Getting the SessionID is considerably easier, because it is a property of the ASP Session object.

26–34. These lines create a property of order_info called billing_info, which summarizes the billing information for the order: name, credit card number, etc. … lines 32 and 34 only add stuff only if there's something to add, which prevents a mysterious comma from appearing in the order when the billing address is the same as the shipping address. The net result is something like this:

john doe
Visa
4111 1111 1111 1111
02/03
po box 72
santa barbara, CA 93103

40. If out is not empty, a required field is missing, and lines 41–45 get executed, printing an error message that asks the shopper to fill out the missing information.

Otherwise, the order is complete and lines 49–72 are executed.

50–53. These lines create a new Session object called order_info and populate it with the information in the order_info object used by the script, overwriting the order_info object that already exists in the Session object.

59–69. These are the lines that create the order file that stores completed orders as separate files. These files can then be processed automatically or by hand, as you wish.

59,60. The first step is to figure out what the file name of the order file is going to be. The file itself will be named after the order number, so that it'll end up being something like 958951109525-52468604.txt, which is easy enough. However, to create a file, we need to give the operating system the full path to the file. Rather than hard-coding this information, the script figures out where it is in line 59 by using the Request object (Request.ServerVariables("PATH_TRANSLATED")) to create a variable called abs_path.

60. Next, the replace method intrinsic to all strings is applied to abs_path to substitute the name of the current script for the order file we want to create. Here's how it works:

The regular expression / w* .asp/ will match a single backslash (" "), followed by zero or more word characters (" w*"), followed by a period (" .") and, finally, the string asp ("asp"). For example, if the current script is:

  C:cartset_cc_billing_information.asp

the regular expression will match the last backslash and the name of the script (in bold):

  C:cartset_cc_billing_information.asp

This will be replaced with the string " orders " (backslashes need to be escaped):

  C:cartorders}

to which we concatenate order_info.uid and the string ".txt", for example:

  C:cartorders958951109525-52468604.txt

all of which is tucked away inside of file_to_create.

62. Next, we create a FileSystemObject called fso that will handle all the file system magic for us.

63. This creates a file object called temp_file, using the file name we just created (file_to_create).

64–68. These lines use the Write method of the temp_file object to take a bunch of order information out of the order_info object: the order number, the order summary, and the contact, shipping, and billing information.

69. At this point, the order information safely stored on the file system, the script closes the file.

71. Finally, the script forwards the shopper to the last script in the cart, the order_complete_script (a variable assigned to a real file's name in cart.js).

Note

If you get this error at this point, it means you have to create an orders directory inside the directory where this script is running:

Microsoft JScript runtime error '800a004c'
Path not found
/c-drive/asp_book/ch5code/set_cc_billing_
information.asp, line 64

Just make a folder called orders and the error message will go away.


Script 5-10 set_pwc_billing_information.asp

If the shopper uses a prepaid Web card instead of credit card, this script is invoked.

						 1. <%@ Language=JavaScript %>
 2. <!-- #include file="cart.js" -->
 3.
						 4. <%
 5. // loop through other shipping info
 6. billing_info_array = new
    Array("card_number","pin_number")
 7. for (i in billing_info_array){
 8.   if (String(Request(billing_info_array[i])) != ""    
      ){
 9.      order_info["billing_" + billing_info_array[i]] =
         String(Request(billing_info_array[i]))
10.   }
11.   else{  // required fields:
12.     out += "<br> " + billing_info_array[i];
13.   }
14. }
15.
						16. // create a uid:
17.   temp_date = new Date()
18.   order_info.uid = temp_date.getTime() + "-" +
      Session.SessionID
19.
						20.
						21. // summary of billing info:
22.
						23. order_info.billing_info = "Prepaid Web Card";
24. order_info.billing_info+= " nCard Number:" +
    order_info.billing_card_number
25. order_info.billing_info+= " nPin Number:" +
    order_info.billing_pin_number
26.
						27.
						28. // if mandatory field is missing, tell user to fix
    it. otherwise, redirect to billing information page
29.
						30. if (out != ""){
31. %>
32. The following field must be filled out:
33. out:   <% = out %>
34. <%
35. }
36.
						37. else {
38.
						39.   // 1st put the information into the session
      variable
40.   for (i in order_info){
41      //Response.Write(i + ": " + order_info[i] +
        "<br>")
42.     Session("order_info") = new Object()
43.     Session("order_info." +  i) = order_info[i]
44.   } // end for
45.
						46.   // 2nd, put it into an order file:
47.
						48.   // get rid of script name in absolute path
49.   // and replace with name of file we want to open
50.   var abs_path =
      String(Request.ServerVariables("PATH_TRANSLATED"));
51.   var file_to_create =
      abs_path.replace(/   w* .asp/,"  orders  ") +
      order_info.uid + ".txt";
52.
						53.   var fso = new
      ActiveXObject("Scripting.FileSystemObject");
54.
						55.   var temp_file = fso.CreateTextFile(file_to_create,
      true);
56.   temp_file.Write("Order Number: " + order_info.uid
      );
57.   temp_file.Write(" n nOrder Completed: " +
      Date());
58.   temp_file.Write(" n nOrder Summary: n" +
      order_info.order_summary);
59.   temp_file.Write(" n nContact Information: n" +
      order_info.contact_info);
60.   temp_file.Write(" n nShipping To: n" +
      order_info.ship_address);
61.   temp_file.Write(" n nBilling Information: n" +
      order_info.billing_info);
62.   temp_file.Close();
63.
						64.
						65.   Response.Redirect(order_complete_script)
66. } // end else
67.
						68. %>

How the Script Works

This script is almost identical to the previous script, set_cc_bill ing_information.asp, so I'm going to run through it a bit more quickly. At a high level, this script does exactly the same thing: It collects the payment information (card number and pin), creates an order number, stores the information it collected in the Session object, and creates an order file.

Why a separate script then? At some point, you may want to automate the order process further and perform payment authorization at the time of the sale, rather than doing it by hand later. Frequently, this will require that credit card transactions be handled by one process and that prepaid Web card payments are handled by a second, separate process. By handling different kinds of payment separately, the cart gives you the flexibility to accept as many different kinds of payment as you want and leaves room to automate it all later.

So let's look at how this script differs from credit card Script 5-9.

6–14. Collecting incoming form information is a little simpler here, because only two fields are collected, and both are required fields. As a result, both fields must have something in them (line 8, where we make sure that they're !=""). Form information is put into the order_info object, and if any fields are empty, the field names are placed in the out variable so that we can generate an error message later in the script.

17–18. The order number (uid) is generated here in the same way as the previous script.

23–25. A summary of the billing information collected (the card number and the PIN) is placed into order_info.billing_info.

30. To make sure that sufficient information to go ahead was collected, the script makes sure that out is empty. If it is not, in lines 31–35, an error message is created. Otherwise, lines 37–66 are executed.

40–44. Information stored in order_info is passed to the Session object.

50–62. An order file is created, and all the information we have about the order is written to the file.

65. Shoppers are redirected to the order_complete_script.

Script 5-11 order_complete.asp

						 1. <%@ Language=JavaScript %>
 2. <!-- #include file="cart.js" -->
 3. <HTML><HEAD><TITLE>Order Complete</TITLE></HEAD><BODY
    BGCOLOR="#FFFFFF" >
 4.
						 5. <center>Order Complete</center>
 6. <p><b>Order Number:</b><br>
 7. <% = order_info.uid %>
 8.
						 9. p><b>Order Summary:</b><br>
10. <% = order_info.order_summary.replace(/ n/g,"<br>")
    %>
11.
						12. <p><b>Contact Information:</b><br>
13. <% = order_info.contact_info.replace(/ n/g,"<br>") %>
14.
						15. <p><b>Shipping to:</b><br>
16. <% = order_info.ship_address.replace(/ n/g,"<br>") %>
17.
						18. <p><b>Billing info</b>:<br>
19. <% = order_info.billing_info.replace(/ n/g,"<br>") %>
20.
						21. <p>
22. <p>
23.<center>
24. Questions? We've hidden contact information somewhere
    on this site. Try and find it!
25. </center>
26. </BODY></HTML>

How the Script Works

The script above presents one way of wrapping things up: It gives shoppers a brief message, "Order Complete," then presents them with their order number, what they ordered, who we think they are, where we're sending it, and how they're paying for it.

Note that this script doesn't affect the function of the cart in any way. The cart would work just as well if the code were this:

						1. <HTML><HEAD><TITLE>Order Complete</TITLE></HEAD><BODY
     BGCOLOR="#FFFFFF" >
2. <CENTER>Thanks for your order</CENTER>
3.  </BODY></HTML>

So feel free to hack this up in any way you want. For what it's worth, giving shoppers their order numbers and a way to contact you if they have any questions is probably a good idea.

The code:

1. Scripting language is JavaScript.

2. Include cart.js, because the script uses the order_info object created there.

7. Print out the order number.

10. Print out a summary of the products ordered. Note that the replace() method of the String object is applied to substitute break tags for the line breaks.

13. Print out the contact information we collected about the shopper.

16. Print out where we plan to ship the order.

17. Print out how we're billing the shopper for the order. This means displaying the shopper's credit card or prepaid Web card information on the screen, which is obviously less secure than not displaying it.

Order complete. Next, cart.js, that does so much of the work for this script.

Script 5-12 cart.js

						  1. <%
  2.
						  3. /////////////////////////////////////////////////////
  4. //   misc. strings:
  5. /////////////////////////////////////////////////////
  6.
						  7. var current_script=
     String(Request.ServerVariables("SCRIPT_NAME"));
  8.
						  9. out = "";
 10.
						 11. basic_ship_cost = 1.95;
 12. two_day_ship_cost = 6.95;
 13. one_day_ship_cost = 8.95;
 14.
						 15.
						 16. array_of_states = new Array("CA","PA","NV");
 17.
						 18. /////////////////////////////////////////////////////
 19. //   script mapping
 20. /////////////////////////////////////////////////////
 21.
						 22. display_records_script =
     "cart_list_items_for_sale.asp";
 23.
						 24. cart_set_session_script = "cart_set_session.asp";
 25. cart_view_contents_script = "cart_view_contents.asp";
 26.
						 27. change_qty_script = "cart_change_qty.asp";
 28. cart_remove_all_from_cart_script =
     "cart_remove_all.asp";
 29. //clear_session_script = "cart_clear_session.asp";
 30.
						 31. collect_shipping_information_script =
     "collect_shipping_information.asp";
 32. set_shipping_info_script =
     "set_shipping_information.asp";
 33. collect_billing_info_script =
     "collect_billing_information.asp";
 34. set_cc_billing_information_script =
     "set_cc_billing_information.asp";
 35. set_pwc_billing_information_script =
     "set_pwc_billing_information.asp";
 36.
						 37. order_complete_script = "order_complete.asp";
 38.
						 39.view_session_info_script =
     "view_session_contents.asp";
 40.
						 41.
						 42. /////////////////////////////////////////////////////
 43. //   Stuff relating to displaying items for sale
 44. /////////////////////////////////////////////////////
 45.
						 46. var catalog_file = "catalog_file.txt";
 47. var option_file = "option_file.txt";
 48.
						 49. var category = String(Request("category"));
 50. var keyword = String(Request("keyword"));
 51.
						 52. if (category != "undefined"){
 53.   varsearch_term = category
 54.   varsearch_type = "category"
 55.}
 56.
						 57. else if (keyword != "undefined"){
 58.   varsearch_term = keyword
 59.   varsearch_type = "keyword"
 60. }
 61.
						 62. else {
 63.   var search_type = "all"
 64. }
 65.
						 66. //// create_items_object () returns an object that
     contains zero or more items that
 67. //// are for sale
 68.
						 69. function create_items_object (){
 70. var items = new Object();
 71.
						 72. // get rid of script name in absolute path
 73. // and replace with name of file we want to open
 74. var abs_path =
     String(Request.ServerVariables("PATH_TRANSLATED"));
 75. var file_to_open =
     abs_path.replace(/   w* .asp/,"  ") + catalog_file;
 76.
						 77. fso = new
     ActiveXObject("Scripting.FileSystemObject");
 78.
						 79. if (!fso.FileExists(file_to_open)){
 80.   Response.Write("can't open " + file_to_open)
 81. }
 82. else {
 83.   fs_stream = fso.OpenTextFile(file_to_open);
 84.
						 85. var temp_regex = new RegExp(search_term,"i")
 86.  while (! fs_stream.AtEndOfStream){
 87.
						 88.    temp = fs_stream.ReadLine() ;
 89.     temp_array = temp.split(/ |/);
 90.
						 91.    if ((search_type=="category" &&
        temp_array[1].search(temp_regex)>=0) ||
        (search_type=="keyword" &&
        (temp_array[1].search(temp_regex)>=0 ||
        temp_array[2].search(temp_regex)>=0 ||
        temp_array[3].search(temp_regex)>=0)) ||
        (search_type =="all" && temp_array[0] != "//id"))
        {
 92.       items[temp_array[0]] = new Object();
 93.       items[temp_array[0]].category =
           temp_array[1];
 94.       items[temp_array[0]].name =
           temp_array[2];
 95.       items[temp_array[0]].description =
           temp_array[3];
 96.       items[temp_array[0]].image =
           temp_array[4];
 97.       items[temp_array[0]].options = new
           Object();
 98.       items[temp_array[0]].options_qty = 0;
 99.     }
100.
						101.  } // end while
102.
						103.
						104. fs_stream.close();
105. }  // end if file opens
106.
						107. // open option file and grab option information
108.
						109. file_to_open = abs_path.replace(/   w* .asp/,"  ") +
     option_file;
110.
						111. if (!fso.FileExists(file_to_open)){
112. Response.Write("can't open " + file_to_open)
113. }
114. else {
115.  fs_stream = fso.OpenTextFile(file_to_open);
116.
						117.  while (! fs_stream.AtEndOfStream){
118.    temp = fs_stream.ReadLine() ;
119.    // if there are two pipes next to each
        other, add a space.
120.    temp = temp.replace(/ | |/g,"| |");
121.    temp_array = temp.split(/ |/);
122.
						123.    if (temp_array[0] != "//id" &&
        String(items[temp_array[0]]) != "undefined"){
124.
						125.      temp_qty = items[temp_array[0]].options_qty
126.      items[temp_array[0]].options[temp_qty]
          = new Object
127.      if (temp_array[1] == " "){  temp_array[1] = ""
          }
128.      items[temp_array[0]].options[temp_qty].ext =
          temp_array[1]
129.      items[temp_array[0]].options[temp_qty].price =
          temp_array[2]
130.      items[temp_array[0]].options[temp_qty].weight =
          temp_array[3]
131.
						132.      if (String(temp_array[4]) == "undefined"){
133.        items[temp_array[0]].options
            [temp_qty].description = "none"
134.      }
135.      else{
136.        items[temp_array[0]].options
            [temp_qty].description = temp_array[4]
137.      }
138.      temp_buy_link = '<a href="' +
          cart_set_session_script + '?add='
139.      temp_buy_link2 = items[temp_array[0]].name +
          "---" + items[temp_array[0]].description
140.      if (items[temp_array[0]].options
          [temp_qty].description != "none"){
141.        temp_buy_link2 += " ("+
            items[temp_array[0]].options
            [temp_qty].description +")"}
142.      temp_buy_link2 += "---" + temp_array[0] +
          temp_array[1] + "---" + temp_array[2] + "---"
          + temp_array[3]
143.      temp_buy_link +=
          Server.URLEncode(temp_buy_link2) + '">Buy</a>';
144.
						145.      items[temp_array[0]].options[temp_qty].buy_link
          = temp_buy_link
146.
						147.      items[temp_array[0]].options_qty++
148.      //Response.Write(temp_array+ "<br>")
149.    }
150.  }  // end while
151. }  // end else
152. return items
153. }  // end defining function create_items_object ()
154.
						155. /////////////////////////////////////////////////////
156.//   Create the cart_items and order_info object
157. /////////////////////////////////////////////////////
158.
						159. // first create both objects
160.
						161. cart_items = new Object();
162. order_info = new Object();
163. order_info.subtotal = 0;
164. order_info.weight = 0;
165.
						166. // loop through the Session object and provision
     cart_items:
167.
						168. for (i = 1 ; i <= Session.Contents.Count ; i++){
169.   if ((typeof Session.Contents(i) == "object") &&
       (Session(Session.Contents.key(i)+".qty") > 0)){
170.     cart_items[i-1] = new Object;
171.     temp_uid = Session.Contents.key(i)
172.     cart_items[i-1].uid = temp_uid
173.     cart_items[i-1].qty =
         parseInt(Session(temp_uid+".qty"))
174.     cart_items[i-1].name = Session(temp_uid+".name")
175.     cart_items[i-1].desc = Session(temp_uid+".desc")
176.     cart_items[i-1].price =
         parseFloat(Session(temp_uid+".price"))
177.     cart_items[i-1].weight =
         parseFloat(Session(temp_uid+".weight"))
178.     order_info.subtotal +=
         cart_items[i-1].price*cart_items[i-1].qty
179.     order_info.weight +=
         cart_items[i-1].weight*cart_items[i-1].qty
180.   }// end if
181.
						182. }// end for
183.
						184.
						185. // set order_info stuff
186.
						187. order_info.uid = Session("order_info.uid")
188.
						189. order_info.ship_type =
     Session("order_info.ship_type")
190. order_info.ship_cost =
     Session("order_info.ship_cost")
191.
						192. order_info.tax =
     parseFloat(Session("order_info.tax"))
193.
						194. order_info.f_name = Session("order_info.f_name")
195. order_info.l_name = Session("order_info.l_name")
196. order_info.email = Session("order_info.email")
197.order_info.phone = Session("order_info.phone")
198.
						199. order_info.address_1 =
     Session("order_info.address_1")
200. order_info.address_2 =
     Session("order_info.address_2")
201. order_info.city = Session("order_info.city")
202. order_info.state = Session("order_info.state")
203. order_info.zip = Session("order_info.zip")
204.
						205. // summary fields:
206. order_info.ship_address =
     Session("order_info.ship_address")
207. order_info.contact_info =
     Session("order_info.contact_info")
208. order_info.order_summary =
     Session("order_info.order_summary")
209. order_info.billing_info =
     Session("order_info.billing_info")
210.
						211.
						212. /////////////////////////////////////////////////////
213. // functions that build rows in the tables used to
     display the
214. // contents of the shopping cart:
215. /////////////////////////////////////////////////////
216.
						217.
						218. //// zero or more rows that include a text box to
     change the qty of an item
219. //// and a delete link that lets shopper remove an
     item from cart.
220.
						221. function cart_contents_editable_row (){
222. if (order_info.subtotal == 0){
223.   %><tr><td colspan=6 align=center>No Items in
        cart</td></tr><%}
224. else{
225. // loop throught the cart_items object and add the
     contents of the cart to the table
226. for (i in cart_items){
227.   %>
228.   <tr>
229.     <td><% =  cart_items[i].uid %></td>
230.     <td><% = cart_items[i].name%></td>
231.     <td><% = cart_items[i].desc%>(Weight Factor:
          <%= cart_items[i].weight %>)</td>
232.     <td>
233.       <input type=text
           name="uid_<%=cart_items[i].uid%>" value="<% =
           cart_items[i].qty %>" size=2>
234.     </td>
235.     <td><% = dollarise(cart_items[i].price)%></td>
236.     <td><% = dollarise(cart_items[i].price *
          cart_items[i].qty)%></td>
237.     <td></td>
238.     <td>
239.       <a href="<%=change_qty_script%>?set_qty=
            0&uid=<%=cart_items[i].uid%>"><font color=red
            size=1>Delete This Item</font></a>
240.       </td>
241.     </tr>
242.   <%
243. } // end looping through cart_items
244. }//end else
245. } // end cart_contents_editable_row ()
246.
						247.
						248. //// a row of headers
249.
						250. function cart_contents_header_row(){  %>
251. <tr><td>p/n</td><td>name</td><td>desc</
     td><td>qty.</td><td>price</td><td>ext.<br>price</
     td></tr>  <%
252. } // end cart_contents_start_table()
253.
						254.
						255. //// a subtotal row
256.
						257. function cart_contents_subtotal_row(){  %>
258. <tr><td colspan=5 align=right>subtotal</td><td><% =
      dollarise(order_info.subtotal) %></td></tr>  <%
259. } // end cart_contents_subtotal_row()
260.
						261.
						262. //// zero or more rows that display contents of
     cart. not editable
263.
						264. function cart_contents_non_editable_row (){
265. if (order_info.subtotal == 0){
266.   %>
267.   <tr><td>&nbsp;</td><td>&nbsp;</td><td>No Items in
       cart</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;
       </td><td></td><td></td></tr>
268. <%}
269.
						270. else {
271.   // loop throught the cart_items object and add the
       contents of the cart to the table
272.   for (i in cart_items){
273.     %>
274.     <tr>
275.   <td><% =  cart_items[i].uid %></td>
276.      <td><% = cart_items[i].name%></td>
277.      <td><% = cart_items[i].desc%>(Weight Factor:
          <%= cart_items[i].weight %>)</td>
278.      <td><% =  cart_items[i].qty %></td>
279.      <td><% = dollarise(cart_items[i].price)%></td>
280.      <td><% = dollarise(cart_items[i].price *
          cart_items[i].qty)%></td>
281.     </tr>
282.   <%   } // end looping through cart_items object
283. }  // end else
284. }  // end function cart_contents_non_editable_row ()
285.
						286.
						287.
						288.
						289. //// row with shipping cost
290.
						291. function cart_contents_shipping_row() {
292. // if ship_type is null, set it to basic:
293. if (order_info.ship_type == null){
294.   order_info.ship_type = "basic"
295.   order_info.ship_cost =
       order_info.weight*basic_ship_cost
296. }
297. %>
298. <tr><td colspan=5 align=right>shipping
     cost</td><td><% =
     dollarise(order_info.weight*order_info.ship_cost)
     %></td></tr>
     299. <%
300. } // end cart_contents_shipping_row()
301.
						302.
						303. //// row with amount of tax, if any
304.
						305. function cart_contents_tax_row() {
306.   %>
307. <tr><td colspan=5 align=right>tax</td><td><% =
      dollarise(order_info.tax) %></td></tr>
308.   <%
309. }
310.
						311. //// row with grand total
312.
						313. function cart_contents_grand_total_row() {
314.   %>
315.   <tr><td colspan=5 align=right>total</td><td><% =
       dollarise(order_info.subtotal + order_info.tax +
       order_info.ship_cost) %></td></tr>
316.   <%
317.}
318.
						319.
						320. /////////////////////////////////////////////////////
321. // Misc. Functions
322. /////////////////////////////////////////////////////
323.
						324. function dollarise(temp_amount){
325. // interesting bug: this returns: 894.9999999999999
326. //temp_amount2 = String(8.95 * 100)
327. if (temp_amount > 0){
328.   temp_amount2 = String(Math.round(temp_amount *
       100))
329.   temp_length = parseInt(temp_amount2.length)
330.   temp_price_1 = temp_amount2.substring(0,
       (temp_length-2))
331.   temp_price_2 = temp_amount2.substring
       ((temp_length-2),temp_length)
332.   return("$"+ temp_price_1 + "." + temp_price_2)
333. }
334. else
335.   return("$0.00")
336. }  // end dollarise
337.
						338.
						339.
						340. //// creates a header used by some of the scripts.
     header includes links to a couple
341. //// of categories, a keyword search box, and a link
     to the cart page
342.
						343. function cart_header(){
344. %>
345. <HTML><HEAD><TITLE><% = current_script
     %></TITLE></HEAD><BODY BGCOLOR="#FFFFFF" >
346. <table border=1 width=90% align=center>
347.   <tr>
348.     <td colspan=2 align=center>View by Category</td>
349.     <td align=center>Keyword Search</td>
350.     <td align=center>View Contents of</td>
351.   <tr><td align=center>
352.     <a href="<% = display_records_script
         %>?category=books">books</a>
353.     </td><td align=center>
354.      <a href="<% = display_records_script
          %>?category=pastries">pastries</a>
355.     </td><td align=center>
356.       <form action="<% = display_records_script %>">
357.       Search For: <input type=text name=keyword
           value="">
358.      <input type=submit name=toss
          value="Go->"></form>
359.     </td><td align=center>
360.      <a href="<% =cart_view_contents_script
          %>">Cart</a>
361.
						362.     </td></tr>
363.
						364. </table>
365. <hr>
366. <%
367. }  // end cart_header
368.
						369.
						370. //// session_contents() creates a snapshot of the
     contents of the session object
371. //// which is useful for debugging.
372.
						373. function session_contents(){
374.
						375. for (i = 1 ; i <= Session.Contents.Count ; i++){
376.   if (typeof(Session.Contents(i)) == "object") {
377. //  cart_items[i-1] = new Object;
378. %>
379.     <% = i + ". " + Session.Contents.key(i) %> (<% =
         typeof(Session.Contents(i)) %>):
380.     <% //= Session(Session.Contents.key(i))%>
381. <blockquote><table border=1>
382.
						383. <%  // loop through and find "properties" of object:
384.     var temp_regex = new
         RegExp(Session.Contents.key(i))
385.     for (ii = 1 ; ii <= Session.Contents.Count ;
         ii++){
386.       if (Session.Contents.key(ii).search
           (temp_regex) >= 0 &&
           typeof(Session.Contents(ii)) != "object"){
387.         %>
388.         <tr><td>
389.         <% = Session.Contents.key(ii) %> (<% =
             typeof(Session.Contents(ii)) %>)
390.         </td>
391.         <td align=left>
392.         <pre><% =
              Session(Session.Contents.key(ii))
              %></pre>
393.         </td></tr>
394.         <%
395.       }  // end if search good
396.     }  //end inside for
397. %>
398.</table></blockquote>
399. <%
400.
						401.   }  // end if
402. }  // end outside for
403.
						404. }  // end function session_contents
405.
						406. %>

How the Script Works

Something of a catch-all script, cart.js performs a number of different functions:

  • Does most of the work of creating lists of items that are for sale by reading the configuration files (catalog_file.txt and option_file.txt), implementing category and keyword searches, and creating an object that can easily be used to display one or more items that are for sale.

  • Helps manage two objects that are fundamental to how the cart works: cart_items, which stores the contents of a shopper's cart, and order_info, which stores shipping, contact, and billing information about an order.

  • Defines some variables and functions that are used by the different scripts discussed in this chapter.

  • Helps minimize the amount of code that goes into scripts that are visible to users in order to make it as easy as possible to edit the look and feel of those pages.

Let's dive in.

1. Notice that we don't need to tell IIS that we're using JavaScript. This is an include file, and setting the language is done in the script that is using the include file.

7. current_script contains the URL of the current script, something that often comes in handy.

8. The out variable is used in a number of different places in the cart as a place to store output. In some cases, code may assume that out already exists, which is why it's worth defining out as an empty string (otherwise, an error message would be generated).

11–13. Variables that are used to calculate cost of shipping an order. See the section on set_shipping_information.asp, earlier in this chapter, for how these are used.

16. The array_of_states array is used to generate select boxes when collecting shipping and billing information. You may want to add more states.

22–39. Script mapping: For some reason, files always seem to move and get new names over time. This leads to broken links that are sometimes difficult to find. Mapping script names to variables helps solve this problem by creating a single variable to update if a file name is changed.

The last script, view_session_info_script, is a debugging script that is discussed later in this chapter.

46–159. These lines do most of the work involved in taking zero or more records from the text configuration files and putting information about them into an object that is used in cart_list_items_for_sale.asp to display items that are for sale.

There are three ways to define the scope that determines how many items are displayed. This is done by passing or not passing form variables to the display script:

  • If a variable called category is passed, all the items in that category are displayed. For example, the sample configuration file includes two categories, books and pastries.

  • If a variable called keyword is passed, the script searches for an exact match of the keyword or keywords entered in information stored in catalog_file.txt. Note that keyword searches will not match information in option_file.txt.

  • If neither a category nor a keyword variable are passed, all the items for sale are displayed.

46,47. The names of the catalog file and the option file are stored in variables here to make it easy to move or rename the files.

49,50. The form variables category and keyword are collected and assigned to local variables of the same name.

52–64. Using an if/else if/if statement, the variables search_type and search_term are defined. search_type is used to determine how the create_items_object will parse catalog_file.txt (whether it will grab records in a category, records that match a keyword search, or grab everything). search_term contains the category or keyword used by the category and keyword searches, respectively.

69–158. The create_items_object() function: This function creates and returns an object that contains zero or more items that are for sale. It is used in cart_list_items_for_sale.asp to create an object called items:

items = create_items_object ();

Once the function has been used to create an object, in this case an object called items, it is easy to iterate through the items object to display them using a for() loop:

for(key in items) { …}

70. The first thing that the function does is create an object called items that will be used to store the records that are collected by the function. Note that, because items is defined within the function, its scope will be limited to within the function. Using the name items twice—once inside, and once outside the function—is not particularly clever on my part, because it's an easy way to get confused. That said, the code works, and if it ain't broke…

74,75. Using a regular expression described in detail in the discussion of the previous script, the variable catalog_file, assumed to be the name of a file that is in the same directory as the current script, is used to create a variable called file_to_open, which contains the full path to the file.

77. A new FileSystemObject called fso is created. (See Chapter 2 for an introduction to working with the file system if this stuff is new to you.)

79–81. Rather than assuming that file_to_open exists, the script uses the fso.FileExists() method to make sure that the file is there before trying to open it. This makes it possible to generate a custom error message (line 80), rather than to have a user confronted with a cryptic Microsoftian error. So maybe you should edit the message in line 80 to make it less cryptic. Whatever.

If the file exists, which it usually does, once we're done debugging (although you're more or less guaranteed that it'll eventually get moved, renamed, or deleted if you let a novice edit it…), lines 82–110 get executed.

82–110. These lines will open the file, find records that match the criteria defined by the search_type and search_term variables, store them in the items object, then close the file.

83. Using the fso.OpenTextFile() method, a Textstream object called fs_stream is created.

85. The variable search_term may contain the name of a category or a string typed into the keyword search box. Here, it is turned into a regular expression using the JavaScript RegExp() function. The parameter "i" is used to make the search case-insensitive, so that the users trying to use the keyword search feature don't have to worry about what case they're using.

86. This line creates a while() loop that will repeat itself until the script gets to the end of the file, at which point, the fs_stream.AtEndOfStream condition will be true. This works well in conjunction with the next line…

88. This line creates a variable called temp, which contains one line of the file fs_stream. The ReadLine() method does two things: It returns one line from the file and it moves to the next line, so that the next time the ReadLine() method is invoked, the next line will be returned.

The temp variable now contains one line from the file catalog_file.txt, for example:

bk1|Books|Book 1|The story of the number 1|<img
src="images/logo.gif">

89. The record above consists of a number of fields, specifically (in order): id, category, title, description, and image. The fields are separated using pipes ("|"), which makes it easy to turn the record into an array, using the split() method. Note that the pipe needs to be escaped in the regular expression because pipes are normally used to delimit patterns.

91. This rather unreadable line performs the task of deciding whether or not to add the record currently being evaluat ed to the items object that is being created. Although the line is a bit long-winded, the logic is straightforward.

There are three separate sets of conditions, separated by logical ORs ("||"). If any one of them is true, lines 92–98 get executed. First condition:

(search_type=="category" &&
temp_array[1].search(temp_regex)>=0)

This will resolve to true if search_type is set to category and search_term (which is inside of temp_regex) matches the second field in the record, which is the category field. Thus, if search_type is set to category and the category being searched for is books, a record of category books will be put into the items object (in lines 92–98), while a record of category pastries will be ignored.

Second condition:

(search_type=="keyword" &&
(temp_array[1].search(temp_regex)>=0 ||
temp_array[2].search(temp_regex)>=0 ||
temp_array[3].search(temp_regex)>=0))

This will be true if search_type is set to keyword and search_term can be found in the category, name, or description fields. You can muck with this if you want to change how the keyword search behaves. For example, if you want people to be able to look up items using an id, you could modify the script so that it also looked for a match in the first field of the array:

temp_array[0].search(temp_regex)>=0

And the third condition:

(search_type =="all" && temp_array[0] != "//id")

This will be true if search_type is set to all and the first element of the array isn't "//id", which is the first element of the comment row at the top of the file. So every item in the file gets put into items, which is then used to create a page listing items for sale.

For sites with a large amount of items for sale, the all search_type may not be particularly useful.

92–98. Having determined that the information stored in a line of catalog_file.txt should be put into items so that it can be displayed, these lines do the work of adding records to items.

Before we look at what happens here, it's worth understanding how items works. To do this, the first step is to understand how our data is structured: The shopping cart is designed to support any reasonably small number of items for sale, each of which must have one or more options.

Thus, if we have a book that is available in both paperback and hardcover, that book would have one record in catalog_file.txt and two records in the option file (one record for each option).

Another book, which was available only in hardcover, would have a single record in each file. At a minimum, an item that is for sale must have a record in each file.

This means that this cart is quite adequate for selling items that have a reasonably small number of options associated with them and less well suited to selling something like an automobile, where the large number of different option combinations might require hundreds of lines in the option file

Finally, it is important to understand how the part numbering system works: Each item that is for sale must have a unique part number (sometimes referred to as an id). Each option for that item must have a unique identifier as well, which is an extension to the basic part number. The part number and unique option identifier can thus be concatenated, and this concatenated identifier can also be thought of as a part number

items is designed to store information about one or more objects that are for sale efficiently, as follows:

  • For each item stored in the items object there is an object whose name is the unique part number stored in the catalog_file.txt. Thus, if items containes two books that are for sale, items might consist of two objects:

    items.bk1    // an object that stores information
                 // about the first book.
    items.bk2    // an object that stores information
                 // about the second book.}
    

    Note that these objects are nested inside of items, which is also an object.

  • Each object inside of items has properties that store information taken from catalog_file.txt. Thus, if the information about bk1 in catalog_file.txt is:

    bk1|Books|Book 1|The story of the number 1|<img
    src="images/logo.gif">
    

    then items.bk1 will have the following properties:

    items.bk1.category    // which will be set to "Books"
    items.bk1.name              // "Book 1"
    items.bk1.description //  "The story of the number 1"
    items.bk1.image     // "<img src="images/logo.gif">"
    
  • Furthermore, an object is created for every option that an item has, inside of yet another object, called options.

    For example, bk1 is available only in paperback, so it has only one option, which will be called option[0]:

    items.bk1.option[0]
    

    This option object then has properties like price and description:

    items.bk1.option[0].price   // an amount, like 12.95
    items.bk1.option[0].description // an optional string
    

    Some items, of course, have more than one option. bk2, for example, has two:

    items.bk2.option[0]
    items.bk2.option[1]
    

    which makes for a lot of objects. In the case of bk2, we have: the items object, which stores all of the items that are going to be displayed; the bk2 object, which stores in formation about bk2; the option object, which stores information about bk2's options, the 0 object, that stores information about one of bk2's options; and the 1 object, which stores information about bk2's second option.

Although this is a bit complicated, the structure makes it very easy to loop through items and retrieve any number of objects, each of which can have any number of options.

So that's how items works. Let's go back to lines 92–98:

92. This line creates a new object using the part number stored in the first item of temp_array. Thus, if the part number is bk1, this is the line where items.bk1 is created.

93–96. Pulling more information from temp_array, the category, name, description, and image properties of the object created in line 92 are assigned string values.

97. This line creates the options object that will be used to store information about options.

98. The options_qty property of the item being created is set to zero. It will be incremented later when the script parses option_file.txt.

104. After going through every line in catalog_file.txt, the script closes the fs_stream object used to read the contents of the file.

On to the option file!

109. Using the same trick used to get the fully qualified path of catalog_file.txt, file_to_open is now set to the full path to option_file.txt.

111–114. Once again, an if/else statement is used to make sure that the file exists before proceeding to execute lines 115–150.

114. A TextSream is created to look at the options file.

117. A while() loop is used to go through the file one line at a time.

118. On each iteration through the while() loop, a line is stuffed into the temp variable.

120. This line looks for empty fields (two pipes with nothing in between them) and inserts a single space between them. This fixes something that wasn't working correctly, though I can't recall what at the moment.

121. Again, the temp variable is split into an array of fields called temp_array.

123. The if statement on this line determines whether the current record we're looking at is used to add information to the items object.

The first thing we check for is simple enough: If the first element in temp_array is "//id", we know that we're looking at the first row of the file, which is a comment, and we want to ignore it.

The second thing that the if statement does is to make sure that String(items[temp_array[0]])is not equal to the special JavaScript value "undefined". This is a way of checking to make sure that the only options that are added to the items object are those that match items that are currently stored in the items object. Recall that, in most cases, items is storing a subset of the items that are for sale—items that are in a particular category or match a particular keyword.

The first field in temp_array is the id field, which gives us the part number of the item to which the current option record applies. For example, if a record in the option file looks like this:

bk1||12.95|2|

we know that this is an option for part number bk1.

So if this record is the current record in the loop, the if() statement will check to see whether String(items[bk1]) is not equal to "undefined". If it is, we know that the items object does not include an object called bk1, and the if() statement will resolve to false. On the other hand, if the items.bk1 exists (this is the same thing as items[bk1]), then String(items[temp__array[0]]) will not be equal to "undefined", the if() statement will be true, and lines 125–148 will be executed.

Although this is a kind of a tricky way to go about it (which makes it hard to remember how it works), it has the advantage that, for the create_items_object() function to execute, the two configuration files have to be parsed only once each. So, overall, it seems like a fairly efficient way to go.

125–148. These lines take information from the current record and store it in the items object.

125. The first step is to set temp_qty, which is obtained from the value of the options_qty property of the current item. If this is the first option we've discovered, this will be zero. If it's the second, it will be one, and so on.

126. This line creates a new options object. Options are named using the value retrieved from the current item's options_qty field. Thus, the first option will be options[0], the next options[1], and so forth.

127. Because the option extension field is optional for items that have only one option, in some cases, temp_array[1] will consist of a single space (created by the regular expression on line 120). Should this be the case, this line substitutes the single space for an empty string, which avoids ending up with a part number that ends in a space, something that can cause havoc.

128–130. These lines take the option extension, price, and weight information taken from the text file and assign them to the appropriate property of the option object created in line 126.

132–137. In the display script cart_list_items_for_sale.asp, option information is displayed differently, depending on whether a particular option has a description. This is handled by checking to see whether an option's description is set to "none". These lines check to see whether the last field in the option record is undefined. If it is, the description property of the option object is set to "none". Otherwise, the description is assigned to the property as expected.

138–145. The last property of the option object that needs to be set is buy_link. This is the link that a shopper can click on to purchase an item. It consists of a link to the cart_set_session_script, a form variable called add, and the word Buy, a clever subliminal message designed to prey on the subconscious consumer impulse. If you take a look at all the variables that get stuffed into the add form variable (everything that goes into temp_buy_link2), you'll notice that basically every bit of information that we have about the current part number/option combination goes into it: the name of the item, its part number and extension, description, price, etc. … When a shopper clicks on the "Buy" link, the information is stored in the Session object, and from there is used by most of the scripts in the cart.

Note that, in line 143, Server.URLEncode() is used to make sure that all the information that gets stuffed into temp_buy_link2 is URL-friendly.

147. Having finished setting up the current option object, the current item's options_qty property is incremented by one.

152. Once the script has iterated through the entire options file and stored the information collected into the items object, the items object is returned before the function ends on line 153.

155–211. As shoppers add items to their carts and enter their shipping and payment information during checkout, information about a shopper's order is managed in two objects that are stored in the ASP Session object: cart_items stores the list of items that have been placed in the shopper's cart, and order_info stores summary information about the order, as well as the shipping and payment information that is submitted by the shopper.

Not only do these objects store information in the Session object, making it possible to maintain state as the shopper moves from page to page, but many of the scripts in the cart make changes to the information that is stored in these objects. To make it easier to work with the two objects, cart.js pulls all the information stored in these Session objects and stores it in to local JavaScript objects of the same name. Then, if a script makes any changes to either object, the changes are often made first to the local object, then transferred to the Session object at the end of the script. Because the variables have the same name, it may be kind of confusing. The key thing is to remember that there are two sets of objects:

Session("order_info") // an object that is inside of
                      // Session

order_info  // a local object that contains the same
           // information as Session("order_info")

// body of script
// makes changes to order_info

order_info // order_info no longer contains the same
          // information as Session("order_info")

Session("order_info") = new Object() // overwrite
                          // the old Session object.
for (i in order_info){    // Take information
  Session("order_info." +  i) = order_info[i] 
}
                         // from order_info
                         // and store it in
                         // new Session("order_info")

The example above illustrates the relationship between Session("order_info") and the local variable order_info. A similar relationship exists between Session("cart_items") and cart_items.

Note that order_info and cart_items are not necessary: Everywhere that these variables are used, it would be probably be perfectly possible to use their two Session counterparts (which do not behave the same way, so it's not an easy switch). However, working with order_info and cart_items is more convenient (or it seemed like it until I had to sit down and write about it <grin>), if only because it means not having to type Session() all the time (and have it clutter up the code).

At any rate, that's the story, and lines 155–211 do the work of creating the variables cart_items and order_info, based on the information stored in the Session objects of the same name.

161–162. The two objects are created.

163–164. Two properties of order_info, subtotal and weight are declared and set to zero. Because the value of these variables depends on which items are in the cart, their value is dynamically set while the script iterates through Session("cart_items"), below.

168. This is the start of a loop that runs until line 182 that loops through every element in an object called Session.Contents using a property Session.Contents.Count, which tells us how many goodies we have inside of Contents.

169. What the script looks for inside of the Session object is objects, because the only objects that are stored inside of the Session object are items that have been added by the cart. This line uses an if() statement to test whether the current item in the Session.Contents object (Session.Contents(i)) is an object by using the JavaScript typeof function. Furthermore, because it's possible for an item to have a quantity of zero (if the item was selected then removed from a shopper's cart), the script makes sure that there is one or more of any object it finds by checking the qty property of that object.

When an object that matches these criteria is found, the script decides that it has found one of the items in the shopper's cart, and lines 170 –79 are executed.

170. This line creates a new object inside of the cart_items object. The use of [i-1] to identify the object is interesting because it is so specific and so useless: My recollection is that the original idea was that, when the first object was added to cart_items, [i-1] would resolve to [0], the next to [1], and so forth, which would be nice and tidy, just like an array.

Of course, I miscalculated. Because the objects that the script looks for here are interspersed throughout Session.Contents, [i-1] is much more likely to end up being first [0], then [6], and so on. In the end, it ends up being completely irrelevant what [i-1] is, because it is never used. Ah, well. It was a nice idea.

171. Using the method Session.Contents.key(i), it's possible to transform an index value (that's basically what i is) into a key. The key is the id of an object in the cart that was used to create a Session object in the first place in the cart_set_session.asp script, discussed earlier in this chapter. This key is stored in a variable called temp_uid. (Which would have made a much better choice than [i-1], don't you think?)

172–177. Once the key is known, it's a relatively simple matter to use temp_uid to collect the quantity, name, description, price, and weight of the current item and to set the uid, qty, name, desc, price, anweight properties of the object that was created on line 170. Note the use of parseInt() on line 173 and of parseFloat() on lines 176 and 177. Because JavaScript is loosely typed, you never really know what you're getting when you grab a variable. parseInt() and parseFloat() force numbers to be integers and floating points, respectively, which makes life easier in very much the same way that the String() function makes life easier with strings.

178,179. These two lines update the value of order_info.subtotal and order_info.weight based on the price, weight factor, and quantity of the item currently being added to cart_items.

187–209. These lines are fairly straightforward: Properties of the Session("order_info") object are assigned to the same properties of the local order_info object. Note that I'm not entirely sure that Session("order_info.zip") is a property of Session("order_info") in the same way that order_info.zip is a property of the JavaScript object order_info. If you play around with the Session object a little, you'll see what I mean.

217–312. The functions that are defined in these lines create the tables that are used by the three scripts that display the contents of the shopping cart: cart_view_contents.asp, collect_shipping_information.asp, and collect_billing_information.asp.

221. This line begins the work of defining the function cart_contents_editable_row(). Depending on how many items are currently in the shopper's cart (stored in the cart_items object, describe above), this function generates one or more rows that make up the body of the table that is displayed on cart_view_contents.asp. The word editable is included in the function name because these row(s) created by the function allow the shopper to edit the number of items being purchased or to remove the item from the cart.

222,223. The first thing that the function does is to check to see whether the subtotal of the order is zero, which would mean that there are not any items in the cart. If this is the case, line 223 creates a row that contains the message that there are "No Items in cart."

224. If the subtotal is not equal to zero, there is presumably something in the cart, and lines 225–243 are executed.

226. This line begins a for() loop that runs from line 227 to line 243. This loops through the contents of the cart_items object. Because cart_items contains objects that store information about items the shopper has selected, this is an easy way to loop through each item in turn.

227–242. These lines create a row of an HTML table for each item that is found inside of cart_items.

229. The first cell gets the item's part number.

230. The next, its name.

231. The third displays the description of the item and its weight factor.

Tip

Although you may not want to display the weight factor in the finished cart, it's useful to have when you're getting it set up, because it helps you to make sure that the shipping amount is correct.


232–234. The number of items being purchased (default is one, editable by user) is displayed in the next cell inside of a text box that the user can then edit. The name of the text box is a concatenation of the string "uid_" and the part number of the item. The name of this field is important, because it determines how the field is used by the script that updates item quantities when the user edits this field.

235. Displays the price of the item, using the dollarise() function, defined later in this file, to express the price as a dollar amount.

236. Displays the product of the price times the quantity.

237. An empty cell. I'm not sure why it's there.

238–240. This cell creates a link that can be used to remove an item from the cart. This works by sending the user to the change_qty_script and passing two form variables: set_qty, which is set to zero (same effect as deleting the item), and uid, which is the part number of the item being removed from the cart.

250–252. The next function is cart_contents_header_row(), which returns the HTML code for the first row. There is no code within the function, which is a function, rather than a variable, for the sake of consistency.

257–259. cart_contents_subtotal_row() generates a subtotal row with the subtotal order_info.subtotal, which was calculated earlier in cart.js (line 178). Once again, the dollarise() function is used to make sure that the subtotal has the right number of decimal places.

264–284. The cart_contents_non_editable_row() function is simply a pared-down version of its cousin, cart_contents_editable_row(), described above. The text box and delete button, the editable aspects of the row, have been removed.

291–300. The cart_contents_shipping_row() function generates a single piece of information: the amount that the shopper will be billed for shipping, generated by multiplying the total weight factor of the order by the or der_info.ship_cost.

Because the first time this function is invoked, or der_in fo.ship_cost will not yet have been set, the function checks whether ship_type is null. If it is, the ship_type and ship_cost properties are set.

305–309. cart_contents_tax_row() generates a row that adds the tax to the order, if any. This is already calculated and stored in order_info.tax.

313–317. The last row function is cart_contents_grand_total_row(), which calculates the grand total for the order. This is calculated by adding the subtotal, tax, and shipping cost that are stored in the order_info object.

Script 5-13 view_session_contents.asp

1. <%@ Language=JavaScript %>
2. <!-- #include file="cart.js" -->
3.
						4. <%
5. session_contents()
6. %>

How the Script Works

Debugging the Cart

Because so much is going on in the ASP Session object, sometimes it's hard to figure out what the script is doing. view_ses sion_contents.asp helps to deal with this: It invokes the session_contents() function defined in cart.js to display all the information that is stored in the Session object.

I'm often surprised by how the code I write is doing something that is not quite what I thought it was doing. Scripts like this one help me to figure out how to whip my code into line.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.19.31.73