reptile7's JavaScript blog
Saturday, August 10, 2013
 
Cracking the Frameset Cookie
Blog Entry #298

We return now to our deconstruction of the cookie functionality of the "So, You Want A Shopping Cart, Huh?" shopping cart. At the end of the previous post, we respectively stored the values of the first nine inputs of the order.html Customer Information / Details table as cookie values in the shopcartindex.html document.cookie string. In today's post we'll go through the get-cookies part of the cookie functionality, which sees to it that those values are ready to load into the Customer Information / Details table if our visitor returns to the cart.

The value-extraction process begins with a get_that_cookie( ) function, which is called when the shopcartindex.html frameset has finished loading.

<frameset cols="25%,*" onload="parent.get_that_cookie( );">

The get_that_cookie( ) function first declares an i counter for a for loop (vide infra). Subsequently, get_that_cookie( ) calls and passes the gifttails string to a getCookieArray( ) function.

function get_that_cookie( ) {
    var i;
    getCookieArray("gifttails"); ... }


The getCookieArray( ) function initially initializes an i counter for a while loop to 0. Next, getCookieArray( ) iteratively sends gifttails+i to a getCookie( ) function.

function getCookieArray(name) {
    var i = 0;
    while (getCookie(name + i) != null) { ... } }


The getCookie( ) function gets the value of each gifttails+i-named cookie in the shopcartindex.html document.cookie string and then returns the value to the getCookieArray( ) function.

function getCookie(name) {
    var dcookie = document.cookie;
    var cname = name + "=";
    var clen = dcookie.length;
    var cbegin = 0;
    while (cbegin < clen) {
        var vbegin = cbegin + cname.length;
        if (dcookie.substring(cbegin, vbegin) == cname) {
            var vend = dcookie.indexOf (";", vbegin);
            if (vend == -1) vend = clen;
            return unescape(dcookie.substring(vbegin, vend)); }
        cbegin = dcookie.indexOf(" ", cbegin) + 1;
        if (cbegin == 0) break; }
    return null; }


If this code looks familiar, it should: its different variabilization notwithstanding, the getCookie( ) function is identical to the GetCookie( ) + getCookieVal( ) functionality appearing in HTML Goodies' "How Can I Set A Cookie Based On A User's Selection On A Form?" tutorial, which we dissected in Blog Entry #250. BTW, the unescape( ) function is deprecated: use the decodeURIComponent( ) function instead.

Let's get back to that while loop in the getCookieArray( ) function:

while (getCookie(name + i) != null) {
    shiparray[i + 1] = getCookie(name + i);
    i++;
    shiparray.length = i; }


For the document.cookie string we assembled in the previous post, the i counter increases to 9 before the while action ends. The getCookie( ) function is called 10 times; in the tenth getCookie( ) run:
(a) the gifttails9 cname string is not found in document.cookie;
(b) the cbegin = dcookie.indexOf(" ", cbegin) + 1; statement resets the cbegin index to 0;
(c) a break statement terminates the getCookie( ) while loop; and
(d) null is returned to the getCookieArray( ) while condition;
getCookie(name + i) != null now returns false and therefore the loop stops at the beginning of (what would be) its tenth iteration.

In The cookie jar section of Blog Entry #287, we created a 10-member shiparray Object object.

shiparray[1] = null;
shiparray[2] = null;
...
shiparray[9] = null;
shiparray.length = 10;


The i <= 8 getCookieArray( ) while loop iterations assign the getCookie(name + i) values to the non-length members of the shiparray object:

shiparray[1] = "Joe"; // getCookie("gifttails0");
shiparray[2] = "Blow"; // getCookie("gifttails1");
shiparray[3] = "username@gmail.com"; // getCookie("gifttails2");
shiparray[4] = "1234 Main Street"; // getCookie("gifttails3");
shiparray[5] = ""; // getCookie("gifttails4");
shiparray[6] = "New Orleans"; // getCookie("gifttails5");
shiparray[7] = "LA"; // getCookie("gifttails6");
shiparray[8] = "USA"; // getCookie("gifttails7");
shiparray[9] = "70118"; // getCookie("gifttails8");


Joe Blow left the Address #2 field blank and the value of the gifttails4 cookie is an empty string. Although the empty string and null separately convert to false in a boolean context, the "" != null comparison returns true and therefore the getCookie("gifttails4") return does not stop the getCookieArray( ) while loop.

The getCookieArray( ) function lastly 're-lengths' the shiparray object via a shiparray.length = i; statement; per the i counter, shiparray.length maxes out at 9 in the while loop's ninth iteration.

After the getCookieArray( ) function has finished executing, the get_that_cookie( ) function iteratively copies the shiparray[1]-shiparray[9] data to the ship_details object.

for (i = 0; i < shiparray.length + 1; i++) {
    if (shiparray[i])
        ship_details[i] = shiparray[i];
    else
        ship_details[i] = ""; }


The ship_details object was not initially equipped with 0-9 members, i.e., these properties are being added to the ship_details object on the fly. The for loop runs for 10 iterations: for the i = 0 and i = 5 iterations the else clause is operative and for the other iterations the if clause is operative.

The get_that_cookie( ) function finally copies the ship_details[1]-ship_details[9] data to the ship_details object properties corresponding to the first nine fields of the Customer Information / Details table

parent.ship_details.f_namea = ship_details[1];
parent.ship_details.l_namea = ship_details[2];
parent.ship_details.email = ship_details[3];
parent.ship_details.ad_onea = ship_details[4];
parent.ship_details.ad_twoa = ship_details[5];
parent.ship_details.citya = ship_details[6];
parent.ship_details.statea = ship_details[7];
parent.ship_details.countrya = ship_details[8];
parent.ship_details.zipa = ship_details[9];


so that the data will be loaded into the table by the refresh_ship_details( ) function if the visitor goes to the order.html page.

First-time visitors

The get-cookies functionality is operative for a first-time shopcartindex.html visitor; in brief, here's what happens:

• Loading the shopcartindex.html frameset calls the get_that_cookie( ) function, which calls the getCookieArray( ) function and passes it gifttails.

• The getCookieArray( ) while loop calls the getCookie( ) function and passes it gifttails0. Assuming that the document.cookie string does not contain a gifttails0 cookie, getCookie( ) returns null to the getCookieArray( ) while loop, shutting it down.

• The get_that_cookie( ) for loop runs for 11 iterations (shiparray.length = 10); in each iteration the else ship_details = ""; clause is operative (shiparray[i] is either null or undefined) and empty strings are assigned to ship_details[0]-ship_details[10]. After the loop, ship_details[1]-ship_details[9] is copied to
ship_details.f_namea-ship_details.zipa; the latter is written to the Customer Information / Details table if the visitor goes to the order.html page.

Other code possibilities

In the Functional compaction subsection of Blog Entry #293, we collected the input element descendants of the Customer Information / Details and Shipping Address table elements via:

var userInputs = document.getElementById("userTable").getElementsByTagName("input");
var shippingInputs = document.getElementById("shippingTable").getElementsByTagName("input");


We can use the userInputs and shippingInputs collections to
(1) dramatically simplify the shopping cart's cookie functionality and
(2) bring the omitted Customer Information / Details and Shipping Address text inputs into the cookie process.

Setting the cookies, take two

There are 19 text boxes in the Customer Information / Details and Shipping Address tables. The code below sets cookies for the values of all 19 boxes in one go:

/* Declare textInputValues globally, put the loop in a function: */
var textInputValues = new Array(19);
for (i = 0; i < textInputValues.length; i++) {
    if (i < 11) textInputValues[i] = userInputs[i].value;
    else textInputValues[i] = shippingInputs[i - 10].value;
    document.cookie = "gifttails" + i + "=" + encodeURIComponent(textInputValues[i]) + "; expires=" + parent.expdate.toUTCString( ) + "; path=/"; }


The for loop can be tied to the order.html unload event or perhaps to the submission of the order form:

document.order.onsubmit = function ( ) { ... }

value retrieval

The loop below returns the cookie values to the Customer Information / Details and Shipping Address tables:

for (i = 0; i < textInputValues.length; i++) {
    var nameIndex = document.cookie.indexOf("gifttails" + i);
    if (nameIndex != -1) {
        var valueIndex = document.cookie.indexOf("=", nameIndex) + 1;
        var endstr = document.cookie.indexOf(";", valueIndex);
        if (endstr == -1) endstr = document.cookie.length;
        if (i < 11) userInputs[i].value = decodeURIComponent(document.cookie.substring(valueIndex, endstr));
        else shippingInputs[i - 10].value = decodeURIComponent(document.cookie.substring(valueIndex, endstr)); }
    else {
        if (i < 11) userInputs[i].value = "";
        else shippingInputs[i - 10].value = ""; } }


This code can be tied to either the shopcartindex.html load event or the order.html load event. The original getCookie( ) procedure for locating the first characters of the cookies' names and values is more complicated than it needs to be, and has been replaced by a simpler approach taken from the getCookieVal( ) function in the Getting the cookie subsection of Blog Entry #251.

That's it - there's no need to use six separate functions or to involve the ship_details and shiparray objects in any way.

Excluding four other not-called shopcartindex.html functions and the shopcartindex.html remove_nil_items( ) function, we have at this point covered the "So, You Want A Shopping Cart, Huh?" shopping cart in its entirety. We will complete our shopping cart odyssey by mopping up some sections of the running order part of the cart over the next 2-3 posts.

Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)