With the application loaded, one of the first things the user will want to do is view the inventory. The user can navigate from category to category with the “Previous Category” and “Next Category” links or from product to product with the “Previous Product” and “Next Product” links. Here’s how it works. Recall lines 235-248 in inventory.js:
categorySet = new Array( new category("Appliances", "Kitchen machines to make life easier"), new category("Buildings", "Architectural structures you can't " + "resist"), new category("Clothing", "Fashionably questionable apparel for " + "the 21st century"), new category("Electronics", "Nifty gizmos that drain your wallet"), new category("Food", "The best product to order over the Net"), new category("Hardware", "All kinds of general purpose " + "construction tools"), new category("Music", "The hottest new instruments from places " + "you've never heard of") );
categorySet
has seven
category objects. The first one is referenced as
categorySet[0]
, the next
categorySet[1]
, and so forth. No matter what
product a user is viewing, Shopping Bag knows the number (in this
case, 0-6) of the category to which it belongs. If the user decides
to move to the previous category, Shopping Bag subtracts 1 from the
current category number, and shows the first product in that
category. If Shopping Bag is in category
and the user wants to move to the previous category, Shopping Bag
starts back at the top category number (in this case, 6).
If the user wants to view the next category, Shopping Bag simply adds 1 to the current category number. If the user is already in the last category, Shopping Bag starts at again.
The same holds true for the products. Each category has a certain number of products. Shopping Bag knows the number to reference the current product in view, so choosing “Previous Product” or “Next Product” will cause the next or previous product to be displayed simply by subtracting or adding one, depending on what the user wants to do.
When the user reaches the last product in a category (going forwards), Shopping Bag realizes this and begins with product of the next category. When the user reaches the first product in a category (going backwards), Shopping Bag realizes this as well, and begins with the last product of the previous category.
If I’ve confused you with that last explanation, the next diagram should help. Figure 8.10 shows how Shopping Bag takes the user through the categories. It works the same way with navigating through the products. When you reach the last product in one category, you move to the first product in the following category.
All this functionality comes from the file manager.html. Example 8.4 shows the code.
Example 8-4. manager.html
1 <HTML> 2 <HEAD> 3 <TITLE>Shopping Bag Manager</TITLE> 4 <STYLE TYPE="text/css"> 5 <!-- 6 TD {font-weight: bold; margin-left: 20; margin-right: 20; padding: 10} 7 //--> 8 </STYLE> 9 </HEAD> 10 <BODY onLoad="freshStart(); makeProducts();" 11 LINK=BLUE ALINK=BLUE VLINK=BLUE> 12 <SCRIPT LANGUAGE="JavaScript1.2" SRC="inventory.js"></SCRIPT> 13 <SCRIPT LANGUAGE="JavaScript1.2"> 14 <!-- 15 var gimmeControl = false; 16 var browseControl = false; 17 var curCLoc = -1; 18 var curPLoc = -1; 19 var infoStr = ''; 20 var shoppingBag; 21 function Bag() { 22 this.taxRate = .06; 23 this.taxTotal = 0; 24 this.shipRate = .02; 25 this.shipTotal = 0; 26 this.subTotal = 0; 27 this.bagTotal = 0; 28 this.things = new Array(); 29 } 30 31 shoppingBag = new Bag(); 32 33 function showStore() { 34 gimmeControl = false; 35 var header = '<HTML><TITLE>Category</TITLE><BODY BGCOLOR=FFFFFF>'; 36 var intro = '<H2>Shopping Bag Product Categories</H2><B>'; 37 var footer = '</DL></BLOCKQUOTE></BODY></HTML>'; 38 var storeStr = '<BLOCKQUOTE><DL>'; 39 for (var i = 0; i < categorySet.length; i++) { 40 storeStr += '<DT><A HREF="javascript: parent.frames[1].reCall(' + 41 i + ', 0);">' + categorySet[i].name + '</A><DD>' + 42 categorySet[i].description + '<BR><BR>'; 43 } 44 infoStr = header + intro + storeStr + footer; 45 parent.frames[0].location.replace('javascript: 46 parent.frames[1].infoStr'), 47 } 48 49 function portal() { 50 gimmeControl = false; 51 parent.frames[0].location.href = "search/index.html"; 52 } 53 function display(cOffset, pOffset) { 54 if(!browseControl) { 55 alert("Start shopping by selecting a product category from " + 56 "Show All Categories or searching products from Product Search."); 57 return; 58 } 59 gimmeControl = true; 60 if (curPLoc + pOffset < 0 || curPLoc + pOffset == 61 categorySet[curCLoc].prodLine.length) { 62 if (curPLoc + pOffset < 0) { 63 if (curCLoc - 1 < 0) { curCLoc = categorySet.length - 1; } 64 else { curCLoc--; } 65 curPLoc = categorySet[curCLoc].prodLine.length - 1; 66 } 67 else if (curPLoc + pOffset == categorySet[curCLoc].prodLine.length) { 68 if (curCLoc + 1 == categorySet.length) { curCLoc = 0; } 69 else { curCLoc++; } 70 curPLoc = 0; 71 } 72 } 73 else { 74 if (curCLoc + cOffset < 0 || curCLoc + cOffset == 75 categorySet.length) { 76 curCLoc = (curCLoc + cOffset < 0 ? categorySet.length - 1 : 0); 77 } 78 else { curCLoc += cOffset; } 79 if (cOffset == -1 || cOffset == 1) { curPLoc = 0; } 80 else if (pOffset == 0) { 81 curPLoc = (curPLoc >= categorySet[curCLoc].prodLine.length ? 0 : 82 curPLoc) 83 } 84 else { curPLoc = curPLoc + pOffset; } 85 } 86 infoStr = '<HTML><HEAD><TITLE>Product Name</TITLE></HEAD>' + 87 '<BODY><TABLE CELLPADDING=3><TR><TD VALIGN=TOP COLSPAN=2>' + 88 '<FONT FACE=Tahoma><H2>Shopping Bag: <I>' + 89 categorySet[curCLoc].name + '</I></H2><TR>' + 90 '<TD VALIGN=TOP><IMG SRC="' + 91 categorySet[curCLoc].prodLine[curPLoc].icon.src + 92 '"></TD><TD VALIGN=TOP><FONT FACE=Tahoma>' + 93 '<B>Name: </B>' + categorySet[curCLoc].prodLine[curPLoc].name + 94 '<BR><B>Description: </B>' + 95 categorySet[curCLoc].prodLine[curPLoc].description + '<BR>' + 96 '<B>Price: </B> $' + 97 numberFormat(categorySet[curCLoc].prodLine[curPLoc].price) + '/' + 98 categorySet[curCLoc].prodLine[curPLoc].unit + '<BR>' + 99 '<B>PLU: </B>' + categorySet[curCLoc].prodLine[curPLoc].plu + 100 '</TD></TR></TABLE></BODY></HTML>'; 101 parent.frames[0].location.href = 'javascript: 102 parent.frames[1].infoStr'; 103 } 104 105 function reCall(cReset, pReset) { 106 browseControl = true; 107 curCLoc = cReset; 108 curPLoc = pReset; 109 display(0, 0); 110 } 111 112 function gimmeOne() { 113 if (!gimmeControl) { 114 alert("Nothing on this screen to give you."); 115 return; 116 } 117 for (var i = 0; i < shoppingBag.things.length; i++) { 118 if (categorySet[curCLoc].prodLine[curPLoc].plu == 119 shoppingBag.things[i].plu) { 120 alert("That's already in your bag. You can change the quantity " + 121 "by choosing View/Change Bag."); 122 return; 123 } 124 } 125 shoppingBag.things[shoppingBag.things.length] = 126 categorySet[curCLoc].prodLine[curPLoc]; 127 shoppingBag.things[shoppingBag.things.length - 1].itemQty = 1; 128 shoppingBag.things[shoppingBag.things.length - 1].category = 129 categorySet[curCLoc].name; 130 alert("OK. You put the " + 131 shoppingBag.things[shoppingBag.things.length - 1].name + 132 " in your bag."); 133 } 134 135 function showBag() { 136 if (shoppingBag.things.length == 0) { 137 alert("Your bag is currently empty. Put some stuff in."); 138 return; 139 } 140 gimmeControl = false; 141 var header = '<HTML><HEAD><TITLE>Your Shopping Bag</TITLE>' + 142 '</HEAD><BODY BGCOLOR=FFFFFF ' + 143 'onLoad="parent.frames[1].runningTab(document.forms[0]);">'; 144 var intro = '<H2>Your Shopping Bag!!!</H2>' + 145 '<FORM onReset="' + 146 'setTimeout('parent.frames[1].runningTab(document.forms[0])', ' + 147 '25);">'; 148 var tableTop = '<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=5>' + 149 '<TR><TH><B>Index' + 150 '<TH><B>Product<TH><B>Category' + 151 '<TH><B>PLU<TH><B>Unit Price' + 152 '<TH><B>Quantity<TH><B>Product Total' + 153 '<TH><B>Remove' + 154 '</TR>'; 155 var itemStr = ''; 156 for (var i = 0; i < shoppingBag.things.length; i++) { 157 itemStr += '<TR>' + 158 '<TD ALIGN=CENTER>' + (i + 1) + '</TD>' + 159 '<TD>' + shoppingBag.things[i].name + '</TD>' + 160 '<TD>' + shoppingBag.things[i].category + '</TD>' + 161 '<TD>' + shoppingBag.things[i].plu + '</TD>' + 162 '<TD ALIGN=RIGHT>$' + 163 parent.frames[1].numberFormat(shoppingBag.things[i].price) + 164 '</TD>' + 165 '<TD ALIGN=CENTER>' + 166 parent.frames[1].genSelect(shoppingBag.things[i].price, 167 shoppingBag.things[i].itemQty, i) + '</TD>' + 168 '<TD ALIGN=CENTER><INPUT TYPE=TEXT SIZE=10 VALUE="' + 169 parent.frames[1].numberFormat(shoppingBag.things[i].price * 170 shoppingBag.things[i].itemQty) + 171 '" onFocus="this.blur();"></TD>' + 172 '<TD ALIGN=CENTER><INPUT TYPE=CHECKBOX></TD>' + 173 '</TR>'; 174 } 175 var tableBottom = '<TR>' + 176 '<TD ALIGN=RIGHT COLSPAN=6>SubTotal:</TD>' + 177 '<TD ALIGN=CENTER><INPUT TYPE=TEXT SIZE=10 NAME="subtotal" ' + 178 onFocus="this.blur();"></TD></TR>' + 179 '<TR><TD ALIGN=RIGHT COLSPAN=6> + 6% Tax:</TD>' + 180 '<TD ALIGN=CENTER><INPUT TYPE=TEXT SIZE=10 NAME="tax" ' + 181 'onFocus="this.blur();"></TD></TR><TR><TD ALIGN=RIGHT COLSPAN=6>' + 182 '2% Shipping:</TD><TD ALIGN=CENTER><INPUT TYPE=TEXT ' + 183 'SIZE=10 NAME="ship" onFocus="this.blur();"></TD></TR>' + 184 '<TR>' + 185 '<TD ALIGN=RIGHT COLSPAN=3><INPUT TYPE=BUTTON VALUE="Check Out" ' + 186 'onClick="parent.frames[1].checkOut(this.form);"></TD>' + 187 '<TD ALIGN=RIGHT><INPUT TYPE=RESET VALUE="Reset Qtys"></TD>' + 188 '<TD ALIGN=RIGHT><INPUT TYPE=BUTTON VALUE="Change Bag" ' + 189 'onClick="parent.frames[1].changeBag(this.form, true);"></TD>' + 190 '<TD ALIGN=RIGHT>Total:</TD><TD ALIGN=CENTER>' + 191 '<INPUT TYPE=TEXT NAME="total" SIZE=10 onFocus="this.blur();">' + 192 '</TD></TR>'; 193 194 var footer = '</TABLE></FORM></BODY></HTML>'; 195 infoStr = header + intro + tableTop + itemStr + tableBottom + footer; 196 parent.frames[0].location.replace('javascript: 197 parent.frames[1].infoStr'), 198 } 199 200 function genSelect(priceAgr, qty, idx) { 201 var selStr = '<SELECT onChange="this.form.elements[' + (idx * 3 + 1) + 202 '].value = this.options[this.selectedIndex].value; 203 parent.frames[1].runningTab(this.form);">'; 204 for (var i = 1; i <= 10; i++) { 205 selStr += '<OPTION VALUE="' + numberFormat(i * priceAgr) + '"' + 206 (i == qty ? ' SELECTED' : '') + '>' + i; 207 } 208 selStr += '</SELECT>'; 209 return selStr; 210 } 211 212 function runningTab(formObj) { 213 var subTotal = 0; 214 for (var i = 0; i < shoppingBag.things.length; i++) { 215 subTotal += parseFloat(formObj.elements[(i * 3) + 1].value); 216 } 217 formObj.subtotal.value = numberFormat(subTotal); 218 formObj.tax.value = numberFormat(subTotal * shoppingBag.taxRate); 219 formObj.ship.value = numberFormat(subTotal * shoppingBag.shipRate); 220 formObj.total.value = numberFormat(subTotal + 221 round(subTotal * shoppingBag.taxRate) + 222 round(subTotal * shoppingBag.shipRate)); 223 shoppingBag.subTotal = formObj.subtotal.value; 224 shoppingBag.taxTotal = formObj.tax.value; 225 shoppingBag.shipTotal = formObj.ship.value; 226 shoppingBag.bagTotal = formObj.total.value; 227 } 228 229 function numberFormat(amount) { 230 var rawNumStr = round(amount) + ''; 231 rawNumStr = (rawNumStr.charAt(0) == '.' ? '0' + rawNumStr : rawNumStr); 232 if (rawNumStr.charAt(rawNumStr.length - 3) == '.') { 233 return rawNumStr 234 } 235 else if (rawNumStr.charAt(rawNumStr.length - 2) == '.') { 236 return rawNumStr + '0'; 237 } 238 else { return rawNumStr + '.00'; } 239 } 240 function round(number,decPlace) { 241 decPlace = (!decPlace ? 2 : decPlace); 242 return Math.round(number * Math.pow(10,decPlace)) / 243 Math.pow(10,decPlace); 244 } 245 246 function changeBag(formObj, showAgain) { 247 var tempBagArray = new Array(); 248 for (var i = 0; i < shoppingBag.things.length; i++) { 249 if (!formObj.elements[(i * 3) + 2].checked) { 250 tempBagArray[tempBagArray.length] = shoppingBag.things[i]; 251 tempBagArray[tempBagArray.length - 1].itemQty = 252 formObj.elements[i * 3].selectedIndex + 1; 253 } 254 } 255 shoppingBag.things = tempBagArray; 256 if(shoppingBag.things.length == 0) { 257 alert("You've emptied your bag. Put some stuff in."); 258 parent.frames[1].showStore(); 259 } 260 else { showBag(); } 261 } 262 263 function checkOut(formObj) { 264 gimmeControl = false; 265 if(!confirm("Do you have every product in the right quantity " + 266 "you need? Remember that you have to choose Change Bag to " + 267 "remove products or change quantities. If so, choose OK to check " + 268 "out.")) { 269 return; 270 } 271 if(shoppingBag.things.length == 0) { 272 showStore(); 273 return; 274 } 275 var header = '<HTML><TITLE>Shopping Bag Check Out</TITLE>' + 276 '<BODY BGCOLOR=FFFFFF>'; 277 278 var intro = '<H2>Shopping Bag Check Out</H2><FORM METHOD=POST ' + 279 'ACTION="http://your.webserver.com/cgi-bin/bag.cgi" ' + 280 'onSubmit="return parent.frames[1].cheapCheck(this);">'; 281 282 var shipInfo = '<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=5>' + 283 '<TR><TD><B>Shipping Information</TD></TR>'+ 284 '<TR><TD>First Name</TD><TD><INPUT TYPE=TEXT NAME="fname"></TD>' + 285 '</TR><TR><TD>Last Name</TD><TD>' + 286 '<INPUT TYPE=TEXT NAME="lname"></TD></TR><TR><TD>Company Name</TD>' + 287 '<TD><INPUT TYPE=TEXT NAME="cname"></TD></TR><TR>' + 288 '<TD>Street Address1</TD><TD><INPUT TYPE=TEXT NAME="saddress1">' + 289 '</TD></TR><TR><TD>Street Address2</TD>' + 290 '<TD><INPUT TYPE=TEXT NAME="saddress2"></TD></TR><TR>' + 291 '<TD>City</TD><TD><INPUT TYPE=TEXT NAME="city"></TD></TR>' + 292 '<TR><TD>State/Province</TD>' + 293 '<TD><INPUT TYPE=TEXT NAME="stpro"></TD></TR><TR>' + 294 '<TD>Country</TD><TD><INPUT TYPE=TEXT NAME="country"></TD></TR>' + 295 '<TR><TD>Zip/Mail Code</TD><TD><INPUT TYPE=TEXT NAME="zip"></TD>' + 296 '</TR><TR><TD><BR><BR></TD></TR></TABLE>'; 297 298 var payInfo = '<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=5>' + 299 '<TR><TD><B>Payment Information</TD></TR>'+ 300 '<TR><TD>Credit Card Type: </TD>' + 301 '<TD>Visa <INPUT TYPE=RADIO NAME="ctype" VALUE="visa" CHECKED> ' + 302 ' ' + 303 'Amex <INPUT TYPE=RADIO NAME="ctype" VALUE="amex"> ' + 304 ' ' + 305 'Discover <INPUT TYPE=RADIO NAME="ctype" VALUE="disc"> ' + 306 ' </TD></TR>' + 307 '<TR><TD>Credit Card Number</TD>' + 308 '<TD><INPUT TYPE=TEXT NAME="cnumb"></TD></TR><TR>' + 309 '<TD>Expiration Date</TD><TD><INPUT TYPE=TEXT NAME="edate"></TD>' + 310 '</TR><TR><TD><INPUT TYPE=SUBMIT VALUE="Send Order"></TD>' + 311 '<TD><INPUT TYPE=RESET VALUE="Clear Info"></TD></TR>' + 312 '</TABLE>'; 313 314 var itemInfo = ''; 315 for (var i = 0; i < shoppingBag.things.length; i++) { 316 itemInfo += '<INPUT TYPE=HIDDEN NAME="prod' + i + 317 '" VALUE="' + shoppingBag.things[i].plu + '-' + 318 shoppingBag.things[i].itemQty + '">'; 319 } 320 var totalInfo = '<INPUT TYPE=HIDDEN NAME="subtotal" VALUE="' + 321 shoppingBag.subTotal + '">' + 322 '<INPUT TYPE=HIDDEN NAME="taxtotal" VALUE="' + 323 shoppingBag.taxTotal + '">' + 324 '<INPUT TYPE=HIDDEN NAME="shiptotal" VALUE="' + 325 shoppingBag.shipTotal + '">' + 326 '<INPUT TYPE=HIDDEN NAME="bagtotal" VALUE="' + 327 shoppingBag.bagTotal + '">'; 328 329 var footer = '</FORM></BODY></HTML>'; 330 331 infoStr = header + intro + shipInfo + payInfo + itemInfo + 332 totalInfo + footer; 333 parent.frames[0].location.replace('javascript: 334 parent.frames[1].infoStr'), 335 } 336 337 function cheapCheck(formObj) { 338 for (var i = 0; i < formObj.length; i++) { 339 if (formObj[i].type == "text" && formObj.elements[i].value == "") { 340 alert ("You must complete all fields."); 341 return false; 342 } 343 } 344 if(!confirm("If all your information is correct, " + 345 "choose OK to send your order, or choose Cancel to make changes.")) { 346 return false; 347 } 348 alert("Thank you. We'll be living off your hard-earned money soon."); 349 shoppingBag = new Bag(); 350 showStore(); 351 return true; 352 } 353 354 function help() { 355 gimmeControl = false; 356 parent.frames[0].location.href = "intro.html"; 357 } 358 359 function freshStart() { 360 if(parent.frames[0].location.href != "intro.html") { help(); } 361 } 362 363 //--> 364 </SCRIPT> 365 <TABLE ALIGN=CENTER BORDER=0> 366 <TR> 367 <TD> 368 <A HREF="javascript: gimmeOne();">Gimme One<A> 369 </TD> 370 <TD> 371 <A HREF="javascript: showBag();">View/Change Bag<A> 372 </TD> 373 <TD> 374 <A HREF="javascript: showStore();">Show All Categories<A> 375 </TD> 376 <TD> 377 <A HREF="javascript: portal();">Product Search<A> 378 </TD> 379 <TD> 380 <A HREF="javascript: help();">Help<A> 381 </TD> 382 </TR> 383 </TABLE> 384 <TABLE ALIGN=CENTER BORDER=0> 385 <TR> 386 <TD> 387 <A HREF="javascript: display(-1,0);">Previous Category<A> 388 </TD> 389 <TD> 390 <A HREF="javascript: display(0,-1);">Previous Product<A> 391 </TD> 392 <TD> 393 <A HREF="javascript: display(0,1);">Next Product<A> 394 </TD> 395 <TD> 396 <A HREF="javascript: display(1,0);">Next Category<A> 397 </TD> 398 </TR> 399 </TABLE> 400 </BODY> 401 </HTML>
Just a quick note. Did you see that all the JavaScript is embedded
after the BODY
tag? Since
there is a lot of image preloading and object creation at the
beginning, Netscape Navigator will display that dull gray background
in the window (or the frame in this case) until all that work is
done. Only then will it parse the rest of the contents. As it stands,
the browser will parse the BODY
tag, and hence,
the BGCOLOR
attribute, before
going about all the work.
Following is the code that makes the product display happen. Lines
15-18 set up four variables, and lines 53-103 define function
display()
. Variable
gimmeControl
indicates to
Shopping Bag whether there is something on the screen (a product)
that can be added to the shopping bag. Variable
browseControl
enforces the
rule that the user must start browsing by clicking on “Show All
Categories” or “Product Search.” (See Rule 1.)
You’ll see both of these variables throughout the application,
but display()
deals with them first, so
let’s introduce them:
var gimmeControl = false; var browseControl = false; var curCLoc = -1; var curPLoc = -1;
Variables curCLoc
and
curPLoc
hold the respective
index numbers of the category and product in view. These are the
numbers I mentioned earlier in the section. Though both are set
arbitrarily to -1, they change the moment the user chooses a category
or a product. More on these two in a moment. Now let’s see how
it all happens. Here are lines 53-103:
function display(cOffset, pOffset) { if(!browseControl) { alert("Start shopping by selecting a product category from Show " + "All Categories or searching products from Product Search."); return; } gimmeControl = true; if (curPLoc + pOffset < 0 || curPLoc + pOffset == categorySet[curCLoc].prodLine.length) { if (curPLoc + pOffset < 0) { if (curCLoc - 1 < 0) { curCLoc = categorySet.length - 1; } else { curCLoc--; } curPLoc = categorySet[curCLoc].prodLine.length - 1; } else if (curPLoc + pOffset == categorySet[curCLoc].prodLine.length) { if (curCLoc + 1 == categorySet.length) { curCLoc = 0; } else { curCLoc++; } curPLoc = 0; } } else { if (curCLoc + cOffset < 0 || curCLoc + cOffset == categorySet.length) { curCLoc = (curCLoc + cOffset < 0 ? categorySet.length - 1 : 0); } else { curCLoc += cOffset; } if (cOffset == -1 || cOffset == 1) { curPLoc = 0; } else if (pOffset == 0) { curPLoc = (curPLoc >= categorySet[curCLoc].prodLine.length ? 0 : curPLoc) } else { curPLoc = curPLoc + pOffset; } } infoStr = '<HTML><HEAD><TITLE>Product Name</TITLE></HEAD>' + '<BODY><TABLE CELLPADDING=3><TR><TD VALIGN=TOP COLSPAN=2>' + '<FONT FACE=Tahoma><H2>Shopping Bag: <I>' + categorySet[curCLoc].name + '</I></H2><TR>' + '<TD VALIGN=TOP><IMG SRC="' + categorySet[curCLoc].prodLine[curPLoc].icon.src + '"></TD><TD VALIGN=TOP><FONT FACE=Tahoma>' + '<B>Name: </B>' + categorySet[curCLoc].prodLine[curPLoc].name + '<BR><B>Description: </B>' + categorySet[curCLoc].prodLine[curPLoc].description + '<BR>' + '<B>Price: </B> $' + numberFormat(categorySet[curCLoc].prodLine[curPLoc].price) + '/' + categorySet[curCLoc].prodLine[curPLoc].unit + '<BR>' + '<B>PLU: </B>' + categorySet[curCLoc].prodLine[curPLoc].plu + '</TD></TR></TABLE></BODY></HTML>'; parent.frames[0].location.href = 'javascript:parent.frames[1].infoStr'; }
Determine whether it is allowed to display a product.
Determine which category/product the user wants to view.
Display that product.
Job 1 is pretty simple. If browseControl
is
true
, the answer is yes.
browseControl
is originally
set to false
. Once the user chooses a product from
“Product Search” or chooses a category from “Show
All Categories,” browseControl is set to
true. Now display()
can carry out jobs 2 and 3.
Since a product will be displayed,
gimmeControl
is then set to
true.
Notice that display()
expects two arguments,
cOffset
and pOffset
. One
holds a value to determine how far to move from the current category
number. The other does the same for the product number.
cOffset and pOffset can be
positive, negative, or zero. To make things simpler, let’s
assume that shopper Daisy Deep Pockets has already satisfied Rule 1
and can now use the “Next”
and “Previous” links to navigate through the
inventory. Look at the code for each of these links in lines 386-397:
<TD> <A HREF="javascript: display(-1,0);">Previous Category<A> </TD> <TD> <A HREF="javascript: display(0,-1);">Previous Product<A> </TD> <TD> <A HREF="javascript: display(0,1);">Next Product<A> </TD> <TD> <A HREF="javascript: display(1,0);">Next Category<A> </TD>
Each of these links calls display()
and passes in
a pair of integers. Table 8.1 explains what each
function call represents. Remember that curCLoc
is the category number, and curPLoc is the
product number.
Table 8-1. Determining the Value of curCLoc and curCPloc
Link |
Arguments Passed |
Explanation |
---|---|---|
Previous Category |
-1, 0 |
Add -1 to curCLoc; add to curPLoc. |
Previous Product |
0, -1 |
Add to curCLoc; add -1 to curPLoc. |
Next Product |
0, 1 |
Add to curCLoc; add 1 to curPLoc. |
Next Category |
1, 0 |
Add 1 to curCLoc; add to curPLoc. |
This makes sense. If you want to go back one category, subtract 1 from the category number. If you want to view the next product, add 1 to the category number. There are three exceptions, however, that require additional logic:
There is no category or product with the number -1. If either the category number or product number is 0, and the user chooses “Previous Category” or “Previous Product,” Shopping Bag is headed straight for an error.
There is no category with the number
categorySet[categorySet.length]
. Since there are
only categorySet.length
categories, the category
number can never be higher than categorySet.length -1
. If the category number is categorySet.length -1
, and the user chooses “Next Product” or
“Next Category,” we get a JavaScript error. The same
holds true for the products.
Navigating from category to category always displays the first product in the category no matter what the product number of the product the user is currently viewing.
Lines 60-85 provide the desired functionality and accommodate these three exceptions. This is a fairly extensive use of nested if-else statements, so you might want to review it for a while.
if (curPLoc + pOffset < 0 || curPLoc + pOffset == categorySet[curCLoc].prodLine.length) { if (curPLoc + pOffset < 0) { if (curCLoc - 1 < 0) { curCLoc = categorySet.length - 1; } else { curCLoc--; } curPLoc = categorySet[curCLoc].prodLine.length - 1; } else if (curPLoc + pOffset == categorySet[curCLoc].prodLine.length) { if (curCLoc + 1 == categorySet.length) { curCLoc = 0; } else { curCLoc++; } curPLoc = 0; } } else { if (curCLoc + cOffset < 0 || curCLoc + cOffset == categorySet.length) { curCLoc = (curCLoc + cOffset < 0 ? categorySet.length - 1 : 0); } else { curCLoc += cOffset; } if (cOffset == -1 || cOffset == 1) { curPLoc = 0; } else if (pOffset == 0) { curPLoc = (curPLoc >= categorySet[curCLoc].prodLine.length ? 0 : curPLoc) } else { curPLoc = curPLoc + pOffset; } }
The following pseudo-code rendition should clear up how this block works. The line numbers of the actual code follow each line of our translation:
IF the product number will be too small or too big THEN (73) IF the product number will be too small THEN (74) IF the category number will be too small THEN the category number equals the number of categories minus 1 (75) ELSE The category number equals itself minus 1 (76) The product number equals the number of products in the category number minus 1 (77) ELSE IF the product number will be too big THEN (79) IF the category number will be too big THEN the category number equals 0 (80) ELSE the category number equals itself plus 1 (81) The product number equals 0 (82) ELSE (85) IF the category number will be too small OR too big THEN (86) IF the category number is too small THEN category number equals the number of categories minus 1 (87) ELSE the category number equals 0 (88) ELSE the category number equals itself plus the category offset (89) IF the category offset equals -1 OR 1 THEN the product number equals 0 (90) ELSE IF the product offset equals 0 THEN (91) IF the product number is greater than or equal to the number of products in the category number THEN the product number equals 0 (92) ELSE the product number equals itself plus the product offset (94)
The outermost if block handles the variables if the product number falls under either of the first two exceptions. The outermost else block handles the variables if the category number falls under either of the first two exceptions. To accommodate the third exception, line 80 sets the product number equal to 0 if the category offset moves up or down by 1.
Knowing the category and product number, Shopping Bag can now build
the HTML to display the correct product. Nearly all of the remaining
code in display()
is dedicated to getting that
product on the screen. Look at lines 86-102:
infoStr = '<HTML><HEAD><TITLE>Product Name</TITLE></HEAD>' + '<BODY><TABLE CELLPADDING=3><TR><TD VALIGN=TOP COLSPAN=2>' + '<FONT FACE=Tahoma><H2>Shopping Bag: <I>' + categorySet[curCLoc].name + '</I></H2><TR><TD VALIGN=TOP><IMG SRC="' + categorySet[curCLoc].prodLine[curPLoc].icon.src + '"></TD><TD VALIGN=TOP><FONT FACE=Tahoma><B>Name: </B>' + categorySet[curCLoc].prodLine[curPLoc].name + '<BR>' + '<B>Description: </B>' + categorySet[curCLoc].prodLine[curPLoc].description + '<BR>' + '<B>Price: </B> $' + numberFormat(categorySet[curCLoc].prodLine[curPLoc].price) + '/' + categorySet[curCLoc].prodLine[curPLoc].unit + '<BR>' + '<B>PLU: </B>' + categorySet[curCLoc].prodLine[curPLoc].plu + '</TD></TR></TABLE></BODY></HTML>'; parent.frames[0].location.href = 'javascript: parent.frames[1].infoStr';
As you can see, everything is based on one large concatenation of
HTML to the initially empty string value of variable
infoStr. Notice that the values of
curPLoc and curCLoc are
vital in referencing all the correct product information.
categorySet[curCLoc]
refers to the correct
category, while
categorySet[curCLoc].prodLine[curPLoc]
refers to
the correct product. Once the values of curCLoc
and curPLoc have been determined, you
can display the product information any way you like.
After infoStr has all the HTML it needs to
display the product, the href
property of the
top frame is set to its value by way of a
javascript:
protocol.
Remember that because of the scope of this protocol, you must provide
an absolute reference to it (i.e.,
parent.frames[1].infoStr
instead of just
infoStr). See the JavaScript technique in Chapter 2
, for the details.
18.189.182.96