function page_removeChildren( element ){
  while( element.childNodes.length > 0 ){
    element.removeChild( element.firstChild );
  }
}

function pageIndex_changeQty( eventFF ){
  var elemTarget;
  var elemINPUT;
  var itemSku, regexp, newQty;

  // Here we change the item's qty after a change event on one of the
  // text input fields.

  if( window.event ){
    elemTarget = window.event.srcElement;
  }
  else{
    elemTarget = eventFF.target;
  }

  regexp = /item_([a-z]+)/;
  itemSku = elemTarget.parentNode.parentNode.name.replace( regexp, "$1" );

  // Check for sanity of the new quantity
  regexp = /\D*0*(\d*).*/;
  newQty = elemTarget.value.replace( regexp, "$1" );

  // TODO: The regexp replace above will trash "0" as an invalid number,
  // and so you can't remove an item by entering "0" in the qty field.
  // We are trying
  // to do too many things in one replace().  First test for a valid number,
  // then remove leading zeros.  Don't try to do both at once.

  if( newQty.length <= 0 ){
    // The qty given is invalid, so revert back to the old qty by
    // refreshing the html
    pageIndex_refreshHtml();
    return 1;
  }

  // Change the quantity in the cart object
  cart_changeQty( itemSku, newQty );

  // Update the totals for the cart
  cart_updateTotals();

  // Save changes to the cart cookie
  cart_setOrderCookie();

  // Refresh the html
  pageIndex_refreshHtml();
  return 0;
}

function pageIndex_addItem( itemSku ){
  // Add the item to the cart, or increment the quantity.
  cart_addItem( itemSku );

  // Update the totals for the cart.
  cart_updateTotals();

  // Save the vital cart info to the cart cookie
  cart_setOrderCookie();

  // Refresh the html
  pageIndex_refreshHtml();
}

function pageIndex_removeItem( eventFF ){
  var elemTarget;
  var regexp;
  var itemSku;

  // This function was called by the onclick event on one of the
  // delete item buttons.
  // Use the 'event' object to determine which of those buttons it came from

  if( window.event ){
    elemTarget = window.event.srcElement;
  }
  else{
    elemTarget = eventFF.target;
  }

  regexp = /item_([a-z]+)/;
  itemSku = elemTarget.parentNode.parentNode.name.replace( regexp, "$1" );

  cart_removeItem( itemSku );

  // Update the totals for the cart.
  cart_updateTotals();

  // Save the vital cart info to the cart cookie
  cart_setOrderCookie();

  // Refresh the html
  pageIndex_refreshHtml();
}

function pageIndex_clearCart( ){
  var i;

  /* This doesn't work... can you see why?
   for( i in cart.item ){
   cart_removeItem( cart.item[i].sku );
   }
   */

  i = 0;
  while( cart.item.length > 0 ){
    cart_removeItem( cart.item[0].sku );
  }

  // Update the totals for the cart.
  cart_updateTotals();

  // Save the vital cart info to the cart cookie
  cart_setOrderCookie();

  // Refresh the html
  pageIndex_refreshHtml();
}

function pageIndex_aasp( ){
  var elemTarget;

  // Check if they checked or cleared the AASP box,
  // and set the cart.coupon appropriately.

  elemTarget = document.getElementById( "idInputAasp" );

  if( elemTarget.checked ){
    cart.coupon = "aasp";
  }
  else{
    cart.coupon = "";
  }

  cart_updateTotals();

  cart_setOrderCookie();

  pageIndex_refreshHtml();
}

function pageIndex_coupon( ){
  // Check if they entered a coupon code
  // and set the cart.coupon appropriately.

  elemTarget = document.getElementById( "idInputCoupon" );

  if( elemTarget.value == "WB-2010" ){
      cart.coupon = "WB-2010";
  }
  else if( elemTarget.value  == "TB2010" ){
      cart.coupon = "TB2010";
  }
  else if( elemTarget.value == "WHMIS-2010" ){
      cart.coupon = "WHMIS-2010";
  }
  else{
      cart.coupon = "";
  }

  cart_updateTotals();
  
  cart_setOrderCookie();

  pageIndex_refreshHtml();
}

function pageIndex_reloadPage( ){
  var regexp;

  // Here we call the functions necessary to reload all variable data
  // and then refresh the html.

  // Depending on if there is a query string or not, we do things in
  // a different order.  With a query string, we just parse the
  // query, update what we need to update, and then imediately
  // return from this function with an empty query string.

  // The query string returned by Moneris upon clicking their "cancel" button:
  // ?order_id=mhp23898338816&cancelTXN=Cancel+Transaction

  // In IE7 and safari and opera and chrome, setting the query
  // string to the empty string doesn't remove the "?".
  // ie. window.location.search will not return the empty string
  // after you set it to be empty.  So, we'll just set
  // window.location directly, and forget about setting
  // location.search.  Still, the regexp test below will correctly
  // test "?" (and "? " and " ?") as an empty query string.

  regexp = /^\s*\??\s*$/;
  if( !regexp.test(window.location.search) ){
    // Yes, we have something in our query string. Parse it...

    // Read in the cart's cookie and update the cart object with it
    cart_parseOrderCookie( document.cookie );

    // Read the query string and do what it says
    cart_parseQueryString( window.location.search );
    // if there was another object that could possibly use a query string,
    // it's method would go right here.

    // Update the totals for the cart
    cart_updateTotals();

    // Save new data to cookie, which might not have changed at all
    cart_setOrderCookie();

    // Now that we've got what we wanted out of the query string,
    // we can reset the query string so that a query is not
    // triggered multiple times without reason.
    window.location = "/cart/";
    // You have to return from the function before it reloads --
    // it doesn't just stop executing your function just because
    // you set window.location.
    return;
  }

  // Our query string is empty, so we can go and do what we need to
  // do upon reload of the page.

  // Read in the cart's cookie and update the cart object with it
  cart_parseOrderCookie( document.cookie );

  // Update the totals for the cart
  cart_updateTotals();

  // Save new data to cookie, which might not have changed at all
  cart_setOrderCookie();

  // Refresh the html
  pageIndex_refreshHtml();
}

function pageIndex_refreshHtml( ){
  var i, j;
  var elemTABLE;
  var elemTBODY, elemTHEAD, elemTR, elemTD, elemDIV, elemINPUT;
  var elemCHECKBOX;

  var desc = new Array();

  // refreshing is done in two steps: first the header of the table is
  // modified if necessay (the discount colum may be removed).  After that,
  // the body of the table with the costs is created.
  elemTABLE = document.getElementById( "idCartTable" );

  // create the table header with or without the discount column
  elemTHEAD = document.createElement( "thead" );
  elemTR = document.createElement( "tr" );

  // The emtpy cell for the delete item icon
  elemTD = document.createElement( "th" );
  elemTD.innerHTML = "";
  elemTD.className = "butt";
  elemTR.appendChild( elemTD );

  elemTD = document.createElement( "th" );
  elemTD.innerHTML = "description";
  elemTD.className = "description";
  elemTR.appendChild( elemTD );

  elemTD = document.createElement( "th" );
  elemTD.innerHTML = "qty";
  elemTR.appendChild( elemTD );

  elemTD = document.createElement( "th" );
  elemTD.innerHTML = "unit cost";
  elemTR.appendChild( elemTD );

  if( cart.coupon.length > 0 ){
    // the discount column
    elemTD = document.createElement( "th" );
    elemTD.innerHTML = "discount";
    elemTR.appendChild( elemTD );
  }

  elemTD = document.createElement( "th" );
  elemTD.innerHTML = "extended cost";
  elemTR.appendChild( elemTD );

  elemTHEAD.appendChild( elemTR );


  // Before we recreate the table body, we have to find the description of
  // each item in the cart.
  for( i in cart.item ){
    for( j in catalog ){
      if( catalog[j].sku == cart.item[i].sku ){
	// we found the item in the catalog, now get the description
	desc[i] = catalog[j].title;
	break; // don't have to keep looking in the catalog,
	// we found what we needed.
      }
    }
  }

  // What we're going to do here is clear the body of the cart
  // table, and then populate it from scratch with the updated content.
  // Before we do that, we're going to create all the tr's and td's
  // with the new content.

  elemTBODY = document.createElement( "tbody" );

  // Populate the table starting with the items and then the totals,
  // if they're available.
  for( i in cart.item){
    // Working bottom up here, starting with the td and its content
    elemTR = document.createElement( "tr" );

    // The delete item icon
    elemTD = document.createElement( "td" );
    elemDIV = document.createElement( "div" );
    elemDIV.className = "butt";
    elemDIV.onclick = pageIndex_removeItem;
    elemTD.appendChild( elemDIV );
    elemTR.appendChild( elemTD );

    // The description
    elemTD = document.createElement( "td" );
    elemTD.innerHTML = desc[i];
    elemTD.className = "description";
    elemTR.appendChild( elemTD );

    // The quantity
    elemTD = document.createElement( "td" );
    elemINPUT = document.createElement( "input" );
    elemINPUT.type = "text";
    elemINPUT.value = cart.item[i].qty;
    elemINPUT.onchange = pageIndex_changeQty;
    elemINPUT.className = "qtyField";
    elemTD.appendChild( elemINPUT );
    elemTR.appendChild( elemTD );

    // The unit cost
    elemTD = document.createElement( "td" );
    elemTD.innerHTML = cart.item[i].unitCost.toFixed(2);
    elemTR.appendChild( elemTD );

    // check if a discount column should be applied
    if( cart.coupon.length > 0 ){
      elemTD = document.createElement( "td" );
      elemTD.innerHTML = (cart.item[i].unitDisc * cart.item[i].qty).toFixed(2);
      elemTR.appendChild( elemTD );
    }

    // The extended cost
    elemTD = document.createElement( "td" );
    elemTD.innerHTML = cart.item[i].extCost.toFixed(2);
    elemTR.appendChild( elemTD );

    // The name attribute on the row element
    elemTR.name = "item_" + cart.item[i].sku;

    // Append the row to the new (not yet part of DOM) table body
    elemTBODY.appendChild( elemTR );
  }

  // Clean out the existing children of the table (because we have a brand new table here)
  removeAllChildren( elemTABLE );

  elemTABLE.appendChild( elemTHEAD );
  elemTABLE.appendChild( elemTBODY );

  // Either set or clear the checkbox for AASP membership
  elemCHECKBOX = document.getElementById( "idInputAasp" );
  if( cart.coupon == "aasp" ){
    elemCHECKBOX.checked = 1;
  }
  else{
    elemCHECKBOX.checked = 0;
  }
  
  elemINPUT = document.getElementById( "idInputCoupon" );
  if( cart.coupon == "WB-2010" ){
      elemINPUT.value = "*******";
  }
  else if( cart.coupon == "TB2010" ){
      elemINPUT.value = "******";
  }
  else if( cart.coupon == "WHMIS-2010" ){
      elemINPUT.value = "**********";
  }
  else{
      elemINPUT.value = "";
  }

}

function pageAddr_readForm( ){
  var i, j;
  var elemFORM, elemINPUT, elemSELECT;
  var elemTEXT = new Array();

  // Here we read the form values into the cart object.  We don't do
  // any sanity checks here

  // First the Billing address

  // Read in the values of each form element into the cart object
  elemFORM = document.getElementById( "idAddrBill" );

  // This will get all the text inputs including the hidden
  // "country" input and the checkbox
  elemINPUT = elemFORM.getElementsByTagName( "input" );

  // For each input element of type text, retreive its value:
  j = 0;
  for( i in elemINPUT ){
    if( elemINPUT[i].getAttribute ){
      if( elemINPUT[i].getAttribute("type") == "text" ){
	elemTEXT[j++] = elemINPUT[i].value;
      }
    }
  }

  // Transfer the values to the cart object
  i = 0;
  cart.addrBill.fName    = elemTEXT[i++];
  cart.addrBill.lName    = elemTEXT[i++];
  cart.addrBill.cName    = elemTEXT[i++];
  cart.addrBill.street1  = elemTEXT[i++];
  cart.addrBill.city     = elemTEXT[i++];
  cart.addrBill.postCode = elemTEXT[i++];
  cart.addrBill.phone    = elemTEXT[i++];
  cart.addrBill.fax      = elemTEXT[i++];
  cart.addrBill.email    = elemTEXT[i];

  // Get the province from the form's select element
  elemSELECT = elemFORM.getElementsByTagName( "select" );
  cart.addrBill.province =  elemSELECT[0].value;

  // Get the value of the subscibe checkbox
  cart.addrBill.subsNews = 0;
  for( i in elemINPUT ){
    if( elemINPUT[i].getAttribute ){
      if( elemINPUT[i].getAttribute("type") == "checkbox" ){
	if( elemINPUT[i].checked ){
	  cart.addrBill.subsNews = 1;
	}
      }
    }
  }

  // The country is fixed at ca:
  cart.addrBill.country = "ca";

  // Then the shipping address

  // Read in the values of each form element into the cart object
  elemFORM = document.getElementById( "idAddrShip" );

  // This will get all the text inputs including the hidden "country" input
  elemINPUT = elemFORM.getElementsByTagName( "input" );

  // For each input element of type text, retreive its value:
  j = 0;
  for( i in elemINPUT ){
    if( elemINPUT[i].getAttribute ){
      if( elemINPUT[i].getAttribute("type") == "text" ){
	elemTEXT[j++] = elemINPUT[i].value;
      }
    }
  }

  // Transfer the values to the cart object
  i = 0;
  cart.addrShip.fName    = elemTEXT[i++];
  cart.addrShip.lName    = elemTEXT[i++];
  cart.addrShip.cName    = elemTEXT[i++];
  cart.addrShip.street1  = elemTEXT[i++];
  cart.addrShip.city     = elemTEXT[i++];
  cart.addrShip.postCode = elemTEXT[i++];
  cart.addrShip.phone    = elemTEXT[i++];
  cart.addrShip.fax      = elemTEXT[i];
  // There are more elements in this elemTEXT array, still left over
  // from the billing address

  // Get the province from the form's select element
  elemSELECT = elemFORM.getElementsByTagName( "select" );
  cart.addrShip.province =  elemSELECT[0].value;

  // Get the text in the special shipping instructions
  elemINPUT = elemFORM.getElementsByTagName( "textarea" );
  cart.addrShip.note = elemINPUT[0].value;

  // The country is fixed at ca:
  cart.addrShip.country = "ca";
}

function pageAddr_submitAddr( ){
  var wasError;

  // Read the form values into the cart object
  pageAddr_readForm();

  // Check for errors or ommisions -- errors on any value are
  // flagged with a leading ? character on the string in the cart
  // object
  wasError = cart_verifyAddrBill();
  wasError |= cart_verifyAddrShip();

  // encode cart contents suitable for a cookie, saving the errors
  // along with it this encodes the strings in-place, in the cart
  // object
  cart_encodeCookie();

  // Save the changes to the actual cookie, yes we are saving any
  // errors as well.
  cart_setAddrCookie();

  if( wasError ){
    alert( "There seems to be an\nerror or omission in the address." );
    pageAddr_refreshHtml();
    return;
  }
  else{
    window.location = "ship.html";
    return;
  }
}

function pageAddr_copyBillToShip( ){
  var wasError;

  // Read the form values into the cart object, yes all of them,
  // although we'll just ignore the shipping address part of it.
  pageAddr_readForm();

  // Flag any errors, but only in the billing address
  wasError = cart_verifyAddrBill();

  // encode it ready for stuffing into cookie
  cart_encodeCookie();

  // If there were errors, don't copy it
  if( wasError ){
    alert( "There seems to be an\nerror in the billing address." );
  }
  else{
    // Else, we're good to copy it over to the shipping address.
    // The cart contents are cookie encoded right now, so we can
    // copy it over as it is.
    cart_copyBillToShip();
  }

  // Save the changes to the actual cookie, after copying
  cart_setAddrCookie();

  // In either case, we refresh the html, to either show the errors
  // or to show the copied address.
  pageAddr_refreshHtml();
}

function pageAddr_reloadPage( ){
  // Here we call the functions necessary to reload all variable
  // data and then refresh the html.

  // Read in the cart's cookie and update the cart object with it
  // the cookie-encoded strings will be decoded just before
  // insertion into the html
  cart_parseAddrCookie( document.cookie );

  // Refresh the html (update the form data) taking into
  // consideration any error messages.
  pageAddr_refreshHtml();
}

function pageAddr_refreshHtml( ){
  var i, j;
  var elemFORM, elemINPUT, elemSELECT;
  var elemTEXT = new Array();

  // First the Billing address
  elemFORM = document.getElementById( "idAddrBill" );

  // This will get all the text inputs including the hidden
  // "country" input and the checkbox
  elemINPUT = elemFORM.getElementsByTagName( "input" );

  // Transfer the values to the cart object
  i = 0;
  elemTEXT[i++] = cart.addrBill.fName;
  elemTEXT[i++] = cart.addrBill.lName;
  elemTEXT[i++] = cart.addrBill.cName;
  elemTEXT[i++] = cart.addrBill.street1;
  elemTEXT[i++] = cart.addrBill.city;
  elemTEXT[i++] = cart.addrBill.postCode;
  elemTEXT[i++] = cart.addrBill.phone;
  elemTEXT[i++] = cart.addrBill.fax;
  elemTEXT[i++] = cart.addrBill.email;

  // For each input element of type text, set its value:
  j = 0;
  for( i=0; i < elemINPUT.length; i++ ){
    if( elemINPUT[i].getAttribute ){
      if( elemINPUT[i].getAttribute("type") == "text" ){
	if( elemTEXT[j].substr(0,1) == "?" ){
	  elemINPUT[i].className = "error";
	  elemINPUT[i].value = cookieDecode( elemTEXT[j++] );
	}
	else{
	  elemINPUT[i].className = "";
	  elemINPUT[i].value = cookieDecode( elemTEXT[j++] );
	}
      }
    }
  }

  // Put the province into the form's select element value
  elemSELECT = elemFORM.getElementsByTagName( "select" );
  elemSELECT[0].value = cart.addrBill.province;

  // Get the value of the subscibe checkbox
  for( i=0; i < elemINPUT.length; i++ ){
    if( elemINPUT[i].getAttribute ){
      if( elemINPUT[i].getAttribute("type") == "checkbox" ){
	elemINPUT[i].checked = cart.addrBill.subsNews;
      }
    }
  }

  // Then the shipping address
  elemFORM = document.getElementById( "idAddrShip" );

  // This will get all the text inputs including the hidden
  // "country" input and the checkbox
  elemINPUT = elemFORM.getElementsByTagName( "input" );

  // Transfer the values to the cart object
  i = 0;
  elemTEXT[i++] = cart.addrShip.fName;
  elemTEXT[i++] = cart.addrShip.lName;
  elemTEXT[i++] = cart.addrShip.cName;
  elemTEXT[i++] = cart.addrShip.street1;
  elemTEXT[i++] = cart.addrShip.city;
  elemTEXT[i++] = cart.addrShip.postCode;
  elemTEXT[i++] = cart.addrShip.phone;
  elemTEXT[i++] = cart.addrShip.fax;

  // For each input element of type text, set its value:
  j = 0;
  for( i=0; i < elemINPUT.length; i++ ){
    if( elemINPUT[i].getAttribute ){
      if( elemINPUT[i].getAttribute("type") == "text" ){
	if( elemTEXT[j].substr(0,1) == "?" ){
	  elemINPUT[i].className = "error";
	  elemINPUT[i].value = cookieDecode( elemTEXT[j++] );
	}
	else{
	  elemINPUT[i].className = "";
	  elemINPUT[i].value = cookieDecode( elemTEXT[j++] );
	}
      }
    }
  }

  // Put the province into the form's select element value
  elemSELECT = elemFORM.getElementsByTagName( "select" );
  elemSELECT[0].value = cart.addrShip.province;

  // Set the value of the special shipping instrucionts textarea
  elemINPUT = elemFORM.getElementsByTagName( "textarea" );
  elemINPUT[0].value = cart.addrShip.note;
}





function pageShip_reloadPage( ){
  // Here we call the functions necessary to reload the variable
  // data from the cookie and then refresh the html.

  // Read in the cart's cookie and update the cart object with it
  // the cookie-encoded strings will be decoded just before
  // insertion into the html
  cart_parseOrderCookie( document.cookie );
  // We need the address as well, to see if it's being shipped
  // within Calgary.
  cart_parseAddrCookie( document.cookie );

  // Refresh the html (update the form data) taking into
  // consideration any error messages.
  pageShip_refreshHtml();
}

function pageShip_submit( ){

  pageShip_readForm();

  if( cart.ship.meth <= 0 ){
    alert( "please select a shipping method" );
  }
  else{
    cart_setOrderCookie();
    window.location = "review.html";
  }
}

function pageShip_refreshHtml( ){
  var i,j;
  var elemINPUT;

  elemINPUT = document.getElementsByTagName( "input" );

  // First clear all checked radio buttons.
  for( i=0; i < elemINPUT.length; i++ ){
    if( elemINPUT[i].type && elemINPUT[i].type == "radio" ){
      elemINPUT[i].checked = 0;
      elemINPUT[i].removeAttribute( "disabled" );
    }
  }

  // Then grey-out the Makeda Courier checkbox, unless the shipping
  // address is in Calgary, AB.
  if( cart.addrShip.city != "Calgary" || cart.addrShip.province != "AB" ){
    // if they have Makeda courier chosen, reset their choice.
    if( cart.ship.meth == 1 ) cart.ship.meth = 0;

    // then gray out the first radio button
    for( i=0; i < elemINPUT.length; i++ ){
      if( elemINPUT[i].type && elemINPUT[i].type == "radio" ){
	elemINPUT[i].setAttribute( "disabled", "disabled" );
	break;
      }
    }
  }

  // Then set the appropriate radio button, or none at all.
  j = 1;
  for( i=0; i < elemINPUT.length; i++ ){
    if( elemINPUT[i].type && elemINPUT[i].type == "radio" ){
      if( cart.ship.meth == j++ ){
	elemINPUT[i].checked = 1;
      }
    }
  }
}

function pageShip_readForm( ){
  var i;
  var elemINPUT;

  // Actually, there's no html form element on this page, just four
  // input elements.

  elemINPUT = document.getElementsByTagName( "input" );

  // TODO: if you do a for( i in elemINPUT ) loop, it seems to loop
  // for many iterations, and will give you garbage after it
  // finishes with the first few actual elemINPUT items.  We should
  // fix this in our other for( x in y ) loops that loop through DOM
  // elements.
  for( i=0; i < elemINPUT.length; i++ ){
    if( elemINPUT[i].type && elemINPUT[i].type == "radio" ){
      if( elemINPUT[i].checked ){
	switch( elemINPUT[i].value ){
	case "Bruce":
	  cart.ship.meth = 1;
	  break;
	case "cpReg":
	  cart.ship.meth = 2;
	  break;
	case "cpExp":
	  cart.ship.meth = 3;
	  break;
	case "other":
	  cart.ship.meth = 4;
	  break;
	default:
	  cart.ship.meth = 0;
	}
      }
    }
  }
}


function pageReview_reloadPage( ){
  // Here we call the functions necessary to reload the variable
  // data from the cookie and then refresh the html.

  // Read in the cart's cookie and update the cart object with it
  // the cookie-encoded strings will be decoded just before
  // insertion into the html
  cart_parseOrderCookie( document.cookie );
  cart_parseAddrCookie( document.cookie );

  cart_updateTotals( );

  // Refresh the html (update the form data) taking into
  // consideration any error messages.
  pageReview_refreshHtml();
}

function pageReview_submit( ){
  var elemFORM;
  var elemBODY;

  // Create a form with all the hidden elements that moneris needs,
  // and submit it to our web server.  From there, perl will make an
  // email and also generate a new, "redirect" page with the same
  // hidden form.  The javascript on that redirect page will submit
  // the hidden form to Moneris.

  elemBODY = document.getElementsByTagName( "body" );

  elemFORM = document.createElement( "form" );
  elemFORM.method =  "post";
  elemFORM.action =  "/cgi/checkout.pl";

  // the hidden field names are kept Perl compatible so that we can
  // import_names all the parameters at once in Perl Note that these
  // values are all still cookie encoded.
  page_addHiddenField( "bill_fName",    cart.addrBill.fName, elemFORM );
  page_addHiddenField( "bill_lName",    cart.addrBill.lName, elemFORM );
  page_addHiddenField( "bill_cName",    cart.addrBill.cName, elemFORM );
  page_addHiddenField( "bill_street1",  cart.addrBill.street1, elemFORM );
  page_addHiddenField( "bill_city",     cart.addrBill.city, elemFORM );
  page_addHiddenField( "bill_province", cart.addrBill.province, elemFORM );
  page_addHiddenField( "bill_postCode", cart.addrBill.postCode, elemFORM );
  page_addHiddenField( "bill_country",  cart.addrBill.country, elemFORM );
  page_addHiddenField( "bill_phone",    cart.addrBill.phone, elemFORM );
  page_addHiddenField( "bill_fax",      cart.addrBill.fax, elemFORM );
  page_addHiddenField( "bill_email",    cart.addrBill.email, elemFORM );
  page_addHiddenField( "bill_subsNews", cart.addrBill.subsNews, elemFORM );

  page_addHiddenField( "ship_fName",    cart.addrShip.fName, elemFORM );
  page_addHiddenField( "ship_lName",    cart.addrShip.lName, elemFORM );
  page_addHiddenField( "ship_cName",    cart.addrShip.cName, elemFORM );
  page_addHiddenField( "ship_street1",  cart.addrShip.street1, elemFORM );
  page_addHiddenField( "ship_city",     cart.addrShip.city, elemFORM );
  page_addHiddenField( "ship_province", cart.addrShip.province, elemFORM );
  page_addHiddenField( "ship_postCode", cart.addrShip.postCode, elemFORM );
  page_addHiddenField( "ship_country",  cart.addrShip.country, elemFORM );
  page_addHiddenField( "ship_phone",    cart.addrShip.phone, elemFORM );
  page_addHiddenField( "ship_fax",      cart.addrShip.fax, elemFORM );
  page_addHiddenField( "ship_note",     cart.addrShip.note, elemFORM );
  page_addHiddenField( "ship_email",    cart.addrShip.email, elemFORM );

  page_addHiddenField( "cart_total",     cart.total.toFixed(2), elemFORM );
  page_addHiddenField( "cart_subTotal",  cart.subTotal.toFixed(2), elemFORM );

  // The coupon code:
  if( cart.coupon == "" ){
      page_addHiddenField( "cart_coupon", "none", elemFORM );
  }
  else{
      page_addHiddenField( "cart_coupon", cart.coupon, elemFORM );
  }
  
  // The ship method: send a sensical value, not just a number from 1 to 4:
  switch( cart.ship.meth ){
  case "1":
      page_addHiddenField( "cart_ship_meth", "Bruce", elemFORM );
      break;
  case "2":
    page_addHiddenField( "cart_ship_meth", "CP expedited (slow)", elemFORM );
    break;
  case "3":
    page_addHiddenField( "cart_ship_meth", "CP EpressPost (fast)", elemFORM );
    break;
  case "4":
    page_addHiddenField( "cart_ship_meth", "other courier", elemFORM );
    break;
  default:
    page_addHiddenField( "cart_ship_meth", "error: " + cart.ship.meth, elemFORM );
  }

  if( cart.ship.cost >= 0 ){
    page_addHiddenField( "cart_ship_cost", cart.ship.cost.toFixed(2), elemFORM );
  }

  for( i in cart.tax ){
    page_addHiddenField( "cart_" + cart.tax[i].name,
			 cart.tax[i].cost.toFixed(2), elemFORM );
  }

  // find the item's description
  var description = new Array();
  var k;
  for( i = 0; i < cart.item.length; i++ ){
    k = 0;
    while( k < catalog.length ){
      if( cart.item[i].sku == catalog[k].sku ){
	description[i] = catalog[k].shortTitle;
	break;
      }
      k++;
    }
  }

  for( var i = 0; i < cart.item.length; i++ ){
    page_addHiddenField( "cart_item_qty" + i,      cart.item[i].qty, elemFORM );
    page_addHiddenField( "cart_item_sku" + i,      cart.item[i].sku, elemFORM );
    page_addHiddenField( "cart_item_unitCost" + i, cart.item[i].discCost.toFixed(2), elemFORM );
    page_addHiddenField( "cart_item_extCost" + i,  cart.item[i].extCost.toFixed(2) , elemFORM );
    page_addHiddenField( "cart_item_desc" + i,     description[i], elemFORM );
  }

  elemBODY[0].appendChild( elemFORM );
  elemFORM.submit();
}

function page_addHiddenField( name, value, frm ){
  var input = document.createElement( "input" );

  input.type = "hidden";
  input.name = name;
  input.value = value;

  frm.appendChild( input );
}

function pageReview_refreshHtml( ){
  var i, j;
  var elemTABLE;
  var elemTBODY, elemTHEAD, elemTR, elemTD, elemDIV, elemINPUT, elemUL, elemLI;
  var colSpan;

  var desc = new Array();

  elemDIV = document.getElementById( "idAddrBill" );
  elemUL = document.createElement( "ul" );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrBill.fName );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrBill.lName );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrBill.cName );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrBill.street1 );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrBill.city );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrBill.province );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrBill.postCode );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = "phohe " + cookieDecode( cart.addrBill.phone );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = "fax " + cookieDecode( cart.addrBill.fax );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrBill.email );
  elemUL.appendChild( elemLI );

  elemDIV.appendChild( elemUL );


  elemDIV = document.getElementById( "idAddrShip" );
  elemUL = document.createElement( "ul" );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrShip.fName );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrShip.lName );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrShip.cName );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrShip.street1 );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrShip.city );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrShip.province );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = cookieDecode( cart.addrShip.postCode );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = "phone " + cookieDecode( cart.addrShip.phone );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = "fax " + cookieDecode( cart.addrShip.fax );
  elemUL.appendChild( elemLI );

  elemLI = document.createElement( "li" );
  elemLI.innerHTML = "note: " + cookieDecode( cart.addrShip.note );
  elemUL.appendChild( elemLI );

  elemDIV.appendChild( elemUL );


  // Before we recreate the dom, we have to find the description of
  // each item in the cart.

  for( i in cart.item ){
    for( j in catalog ){
      if( catalog[j].sku == cart.item[i].sku ){
	// we found the item in the catalog, now get the description
	desc[i] = catalog[j].title;
	break; // don't have to keep looking in the catalog,
	// we found what we needed.
      }
    }
  }

  // refreshing is done in two steps: first the header of the table is
  // modified if necessay (the discount colum may be removed).  After that,
  // the body of the table with the costs is created.
  elemTABLE = document.getElementById( "idCartTable" );

  // create the table header with or without the discount column
  elemTHEAD = document.createElement( "thead" );
  elemTR = document.createElement( "tr" );

  elemTD = document.createElement( "th" );
  elemTD.innerHTML = "description";
  elemTD.className = "description";
  elemTR.appendChild( elemTD );

  elemTD = document.createElement( "th" );
  elemTD.innerHTML = "qty";
  elemTR.appendChild( elemTD );

  elemTD = document.createElement( "th" );
  elemTD.innerHTML = "unit cost";
  elemTR.appendChild( elemTD );

  if( cart.coupon.length > 0 ){
    // the discount column
    elemTD = document.createElement( "th" );
    elemTD.innerHTML = "discount";
    elemTR.appendChild( elemTD );
  }

  elemTD = document.createElement( "th" );
  elemTD.innerHTML = "extended cost";
  elemTR.appendChild( elemTD );

  elemTHEAD.appendChild( elemTR );


  elemTBODY = document.createElement( "tbody" );

  // Populate the table starting with the items and then the totals,
  // if they're available.
  for( i in cart.item){
    // Working bottom up here, starting with the td and its content
    elemTR = document.createElement( "tr" );

    // The description
    elemTD = document.createElement( "td" );
    elemTD.innerHTML = desc[i];
    elemTD.className = "description";
    elemTR.appendChild( elemTD );

    // The quantity
    elemTD = document.createElement( "td" );
    elemTD.innerHTML = cart.item[i].qty;
    elemTR.appendChild( elemTD );

    // The unit cost
    elemTD = document.createElement( "td" );
    elemTD.innerHTML = cart.item[i].unitCost.toFixed(2);
    elemTR.appendChild( elemTD );

    // check if a discount column should be applied
    if( cart.coupon.length > 0 ){
      elemTD = document.createElement( "td" );
      elemTD.innerHTML = (cart.item[i].unitDisc * cart.item[i].qty).toFixed(2);
      elemTR.appendChild( elemTD );
    }

    // The extended cost
    elemTD = document.createElement( "td" );
    elemTD.innerHTML = cart.item[i].extCost.toFixed(2);
    elemTR.appendChild( elemTD );

    // The name attribute on the row element
    elemTR.name = "item_" + cart.item[i].sku;

    // Append the row to the new (not yet part of DOM) table body
    elemTBODY.appendChild( elemTR );
  }

  elemTR.className = "last"; // because we put a line under the last row

  colSpan = 3;
  if( cart.coupon.length > 0 ){
    colSpan = 4;
  }

  // The sub total
  elemTR = document.createElement( "tr" );
  elemTD = document.createElement( "td" );
  elemTD.setAttribute( "colspan", colSpan );
  elemTD.innerHTML = "SubTotal";
  elemTR.appendChild( elemTD );

  elemTD = document.createElement( "td" );
  elemTD.innerHTML = cart.subTotal.toFixed(2);
  elemTR.appendChild( elemTD );
  elemTBODY.appendChild( elemTR );

  // The shipping
  // if the shipping cost is set to -1, that means it is $0.00 and
  // you shouldn't display it.  Otherwise, display it even if it's $0.00
  if( cart.ship.cost >= 0 ){
    elemTR = document.createElement( "tr" );
    elemTD = document.createElement( "td" );
    elemTD.setAttribute( "colspan", colSpan );
    elemTD.innerHTML = "Shipping";
    elemTR.appendChild( elemTD );

    elemTD = document.createElement( "td" );
    elemTD.innerHTML = cart.ship.cost.toFixed(2);
    elemTR.appendChild( elemTD );
    elemTBODY.appendChild( elemTR );
  }

  // The taxes
  for( i in cart.tax ){
    elemTR = document.createElement( "tr" );
    elemTD = document.createElement( "td" );
    elemTD.setAttribute( "colspan", colSpan );
    elemTD.innerHTML = cart.tax[i].name;
    elemTR.appendChild( elemTD );

    elemTD = document.createElement( "td" );
    elemTD.innerHTML = cart.tax[i].cost.toFixed(2);
    elemTR.appendChild( elemTD );
    elemTBODY.appendChild( elemTR );
  }

  // The total
  elemTR = document.createElement( "tr" );
  elemTD = document.createElement( "td" );
  elemTD.setAttribute( "colspan", colSpan );
  elemTD.innerHTML = "Total";
  elemTR.appendChild( elemTD );

  elemTD = document.createElement( "td" );
  elemTD.innerHTML = cart.total.toFixed(2);
  elemTR.appendChild( elemTD );
  elemTBODY.appendChild( elemTR );


  // Clean out the existing children of the table
  //(because we have a brand new table here)
  removeAllChildren( elemTABLE );

  elemTABLE.appendChild( elemTHEAD );
  elemTABLE.appendChild( elemTBODY );
}

function removeAllChildren( elem ){
  while( elem.hasChildNodes() )
  {
    elem.removeChild(elem.firstChild);
  }
}

