reptile7's JavaScript blog
Thursday, September 19, 2013
 
Be in My Network
Blog Entry #303

(We interrupt our regularly scheduled programming to bring you this special announcement...)

"The first step in getting a job is to let everyone know that you are looking for a job."

I've been writing and running reptile7's JavaScript blog for the past 8½ years. This blog regiments my life at least as much as a part-time job would, but I earn no money from it.

I'm employed, but not gainfully employed. I have a position, but not a paying position.

I will need to find gainful employment of some sort in the very near future, so in this post let me take the opportunity
(1) to announce to the world at large that I need a job, and
(2) to cordially invite you, the reader, to be in my "network", as I don't have a network, and the 'general consensus' is that having a network is really important for getting a job.

I have no family or friends here in New Orleans; my local social network comprises my landlord and the guy at the Touro coffee counter (that would be you, Gene) at which I get an iced coffee several times a week.

As for my professional network, I don't really have one of those either in that I am a "career-changer". Prior to becoming a Web coding blogger, I was an instructor in the Chemistry Department at Tulane University. I suppose I could go back to Tulane and (attempt to) discuss my situation with my former colleagues, but I strongly suspect that they would say to me, "You're not working in chemistry anymore, why are you wasting our time?"

Actually, there is a connection between what I'm doing now and my Tulane instructor gig. In preparing a set of lecture notes, in crafting a handout or problem set, in writing an exam, I was doing what I call 'data consolidation', and data consolidation is very much the essence of my blog - perhaps I could say that data consolidation is my "transferable skill", to use the argot of the job-hunting world. A related aside: In his book What Color Is Your Parachute?, Richard Nelson Bolles divides the world of work into
(a) jobs for which you work with your hands,
(b) jobs for which you interact with other people, and
(c) jobs for which you work with information.
Rather obviously, I am meant for the last category.

So, be in my network, won't you? If anyone out there has any tips, tricks, recommendations, advice, strategies, etc. as to how I might parlay what I do - a mixture of technical writing, coding, and archival work - into gainful employment - even better, if anyone out there would like to offer me a job that would leverage my craftsmanship in some way - then I would like to hear from you. I am making this appeal for the simple, fundamental reason that my blog says astronomically more about me than a résumé or my LinkedIn profile ever could.

When the late, great Frank Zappa hired musicians for his band, he didn't ask for their résumés or give them a questionnaire of some kind: he actually sat down and listened to them play (sometimes he even played with them). And that's what I would want an employer to do with me. Anyone in the world is welcome to go through my blog with the finest-toothed of combs and then decide if they want me to be part of their team or not.

There, I've thrown my Hail Mary pass. Wish me luck.

"What about the blog?" As circumstances permit, I may write some posts on some of the scripts that were part of Joe Burns' late-1990s Java Goodies JavaScript Repository project, beginning with the Make Me A Password script (I'll also try to occasionally post at my 'non-technical' blog), but I can't make any promises.

Wednesday, September 11, 2013
 
Thanks for Doing Business with Us
Blog Entry #302

Our long and winding discourse on the "So, You Want A Shopping Cart, Huh?" tutorial's e-commerce shopping cart concludes with today's post. Our final topic will be the shopping cart's thanku.htm page; a demo will follow.

Post-submission

The "Shopping Cart" tutorial's shopcart.zip package includes a thanku.htm file that, via an

<input type="hidden" name="redirect" value="thanku.htm">

hidden control in the order.html source, is served to the user by the formmail.pl PERL script after the order form has been submitted. The one-at-a-time shopping cart pages offered by the tutorial's The Pages section include neither the thanku.htm document nor the formmail.pl script.

The thanku.htm page displays
(a) a thank-you message and
(b) a table recapping the user's order if the user has ordered anything.
If the user enters a comment or question into the comments textarea box at the order.html page and submits the order form without ordering anything, then s/he'll just see the thank-you message.

Order duly noted

The thank-you message is coded via the HTML below:

<center><table border="0" cellpadding="0" width="100%">
<tr><td colspan="2">
<p align="center"><font size="5"><b>: </b></font><font color="#0000ff" size="5"><b>Thank You</b></font></p>
</td></tr>
<tr><td align="center" colspan="2">
<b><i>We should like to thank you for your order or enquiry and please be assured it will be dealt with promptly.</i></b>
</td></tr></table></center>


Ugh, more dysfunctional table-based layout; as I'm sure Willie Nelson would agree, Mammas, don't let your babies grow up to use HTML editors. Here's how we should code the message:

#thanksPara1 { text-align: center; font-size: 24px; font-weight: bold; color: blue; }
#thanksPara2 { text-align: center; font-weight: bold; font-style: italic; }
...
<p id="thanksPara1">Thank You</p>
<p id="thanksPara2">We should like to thank you for your order or enquiry and please be assured it will be dealt with promptly.</p>
<!-- No, there is no need to precede the Thank You with a colon. -->


Order on the page, revisited

A user goes to the pagetwo.html page and adds, in order, one p2i3 ($4.44) item, one p2i2 ($3.33) item, and one p2i1 ($2.22) item to the shopping cart. The user proceeds to the order.html page, fills out the Customer Information / Details and Shipping Address tables, and submits the order form. At the thanku.htm page, the thanksPara2 paragraph is followed by the following table:

A thanku.htm order table and its captions

The aforedetailed center element's nextSibling is a p element containing a script that writes out the table, the pre-table caption, and the post-table caption:

<p align="center"><br>
<script type="text/javascript"><!-- hide

if (self == parent) { document.write("<font color='red'><b><a href='shopcartindex.html'>Start Page</a></b></font>"); }
else {
    if (parent.all_order_totals( ) > 0) {
        var index = 0; // No use is made of index; throw its statements out.
        document.write("Items Ordered");
        for (i = 1; i < parent.item_num; i++) {
            if (parent.itemlist[i].quan > 0) {
                index = index + 1;
                if (i == 1) {
                    document.write("<table border='1'><td>Item<br>Code</td><td>Item<br>Price</td><td>Item Description</td><td>Quantity Ordered</td><tr>"); }
                document.write("<td>" + parent.itemlist[i].code + "</td><td>" + parent.itemlist[i].price + "</td><td>"
                + parent.itemlist[i].desc + "</td><td align='center'>" + parent.itemlist[i].quan + "</td><tr>"); } }
        document.write("</table>");
        document.write("Your Total order Value was : $" + format(parent.all_order_totals( ), 2)); ... } }


You may recall that when we printed this stuff out on the order.html page, we didn't use a table, specifically, we loaded the order data into text boxes and separated the rows with <br>s: perhaps that was necessary in order to submit the data along with the rest of the order form, but a real <table> with real rows and cells is definitely a better way to go here.

The script has a design flaw in that the table element's start-tag and the table's first row are not written to the page if parent.itemlist[1].quan is 0, as would be true if the user had subtracted the p2i3 item from the shopping cart prior to submitting the order form; in this case, the table output for our example would be:

Items Orderedp2i23.33 Page_2_Item_2 1p2i12.22 Page_2_Item_1 1Your Total order Value was : $5.55

This problem can be solved by placing the document.write("<table border='1'>..."); command outside and before the for loop.

The first row's cells should be marked up as <th>s* as they are all header cells. The line breaks in the Item Code and Item Price headers give the left side of the table a scrunched feel; the table looks much more balanced upon subtracting those line breaks and then inserting a corresponding line break in the Quantity Ordered header, i.e., <th>Quantity<br>Ordered</th>.

*The th element typically bolds its content; you can unbold the headers with a th { font-weight: normal; } style rule.

The outer p element and its align="center" attribute are there to horizontally center the table and its captions. However, the p element has an (%inline;)* content model and consequently should not contain a table element**; moreover, the align attribute of the p element is now deprecated. To horizontally center this content today, we should
(1) throw out the p container and give the table a margin-left:auto;margin-right:auto; styling (at least on the Mac platform, this method of centering is supported by neither IE 4.x nor Netscape 4.x), and then
(2) mark up the Items Ordered text as a <caption>, which will automatically center it vis-à-vis the left and right edges of the table, and lastly
(3) put the post-table caption in a separate text-align:center;-ed p element.

**Technically, Gordon's code is not invalid: script element content is classified as CDATA and the script element is itself an %inline; element. We might say that the table-in-a-paragraph structure violates the spirit but not the letter of the DTD.

The user's order total in the post-table caption is processed by the same format( ) function we encountered in our discussion of the order.html page; a local copy of the format( ) function is held by a script element placed between the thanku.htm head and body (in violation of the content model of the html element). The format( ) functionality can again be replaced by a corresponding numberObject.toFixed( ) command, i.e.:

#totalPara { text-align: center; }
...
document.write("<p id='totalPara'>Your order total is : $" + parent.all_order_totals( ).toFixed(2) + "</p>");


Would you like some cookies?

Interestingly, the if (parent.all_order_totals( ) > 0) { ... } clause concludes with a

document.write("<p>If you would like to maintain your order address details (Not your credit card Details), for future visits to this site please do so <a href='javascript:go_with_cookie( );'>here.</a>");

command that writes out a

If you would like to maintain your order address details (Not your credit card Details), for future visits to this site please do so here.

paragraph ending with a link that when clicked calls the go_with_cookie( ) function, a local copy of which sits in the aforementioned </head> ... <body> script element, and thereby sets the set-cookies part of the cart's cookie machinery in motion. How 'bout that: Gordon actually gives the user the option of setting or not setting cookies for the Customer Information / Details data on the order.html page - what a guy, huh?

Mind you, the user may want to be 'remembered' even if nothing was ordered; for this reason the paragraph should be placed outside and after the script element.

As noted in the Other code possibilities section of Blog Entry #298, it is not necessary to call the go_with_cookie( ) function and then the setCookieArray( ) function and then the setCookie( ) function to set cookies for the customer/shipping parts of the order form; a single function will do:

function setCookies( ) {
    for (i = 0; i < parent.textInputValues.length; i++)
        document.cookie = "gifttails" + i + "=" + encodeURIComponent(parent.textInputValues[i]) + "; expires=" + parent.expdate.toUTCString( ) + "; path=/"; }

/* Although the textInputValues array is stocked with field values at the order.html page, its constructor statement should be placed in the [shopcart]index.html source. */

When self and parent are equal

The thanku.htm page is meant to replace the order.html page within the frameset as opposed to the frameset page itself. If the thanku.htm page is accessed outside of the frameset:
(1) The thanku.htm body begins with the THIS IS A FRAME ELEMENT : GO TO Start Page TO LOAD MAIN PAGE message discussed in the welcome.html section of Blog Entry #288.
(2) The Order on the page script's if (self == parent) { ... } clause prints out a separate Start Page link that points to the frameset page.
Do we need both (1) and (2)? Nah - I'd ditch the latter.

Demo (Revised March 2017)

The div below holds a demo for the shopping cart whose structure corresponds to but differs somewhat from that described by the Frame retool section of Blog Entry #299, specifically, I've recast the orderIframe iframe as a display:inline-block; orderDiv div whose content is changed via a document.getElementById("orderDiv").innerHTML = document.getElementById(newDiv).innerHTML; operation.

All of the demo's JavaScript is in the source of the current page; the demo's order functionality is based on the JavaScript changes detailed in Blog Entry #300.

The Order Page One, Order Page Two, and Order Page Three item pages sport buttons that when clicked display small clip art images in a pictureDiv placeholder per the Show me the goods section of Blog Entry #301.

If you go to the Order Form page and fill out the Customer Information / Details and Shipping Address tables and then click the button, your data won't be sent to me but you will be taken to a Thank You page that leverages the code presented in this entry; of course, you'll have to add items to your shopping cart in order to see the Items Ordered table.

Clicking the here link at the end of the Thank You page will set cookies based on your Order Form inputs on your hard disk.

Like Joe's demo, my demo does not feature a Credit Card Info table on the Order Form page; however, I trust you are up to the task of merging my credit card validation demo with the rest of the Order Form source.

This page (well, the file name of its corresponding document) is titled welcome.html.

It contains no shopping cart functions. It simply acts as your welcoming page.

The source of the parent div contains the majority of the shopping cart commands.


Not previously mentioned:
From 1999 to 2005 Gordon offered for download a second-generation version of the shopping cart: we're not going to discuss it but you can view it and interact with it here.

We'll talk about what we're doing next in the following entry.

Monday, September 02, 2013
 
shopcartindex.html Flotsam and Jetsam
Blog Entry #301

In today's post, we'll go through the orphaned parts of the shopcartindex.html document that coordinates the "So, You Want A Shopping Cart, Huh?" shopping cart. Over and above the set-cookies part of the cart's cookie machinery, there are four other never-called functions in the shopcartindex.html source:
(1) check_window( )
(2-3) display_pic( ) and updatenav_nav( )
(4) delCookie( )
In the name of completeness, let's look at these functions, shall we?

Automatic redirect, part 2

The check_window( ) function is the last (25th) function in the shopcartindex.html script element.

function check_window( ) {
    if (self == mainbody) {
        document.write("<head><meta http-equiv='refresh' content='15; url=index.html'></head>");
        document.write("<body><center><h1>Warning!</h1><br>");
        document.write("<font color='#ff0000'><b>This is inside a multiple frame window.<br>");
        document.write("The scripts may not work correctly!!!!</font><br>");
        document.write("We are jumping you directly to our <a href='index.htm' target='_top'>Main page</a> in 15 seconds.</center>");
        self.location = "index.html";
        parent.document.close( ); } }

/* In the original check_window( ) function, the five document.write( ) commands are written as a single command, which I have broken up for the sake of clarity. */

Evidently, the check_window( ) function is a forerunner of the JavaScript snippet that prints out the THIS IS A FRAME ... warning at the top of the various frame src pages if those pages are accessed outside of the shopcartindex.html frameset and that we discussed in the welcome.html section of Blog Entry #288. If the browser gets through the if (self == mainbody) { ... } gate (and it won't, as mainbody is an undefined variable), then check_window( ) writes out
(1) a head element containing a meta refresh that, if supported by the browser, sends the user to the index.html page after a 15-second delay, and then
(2) a body element that displays
(a) a somewhat-incoherent frame warning plus
(b) a redirection notification that includes a link to the index.htm (sic) page.
Strangely, the document.write( ) action is followed by a self.location = "index.html"; statement that immediately takes the user to index.html anyway.

Upon cleaning up the check_window( ) function - upon changing the if condition to self == parent and throwing out the post-document.write( ) commands - I find that the meta refresh operation works with Mozilla's browsers (Firefox, Camino, Netscape 9) and only with Mozilla's browsers. If you like the 15-second meta refresh idea, a much better approach is to just go with the self.location assignment and delay it via a window.setTimeout( ) command:

window.setTimeout("self.location = 'shopcartindex.html';", 15000);

Show me the goods

In the "Shopping Cart" tutorial's Altering The Pages section, Joe notes, You can go into these template pages and add text and images to your heart's delight. The display_pic( ) function provides a mechanism for displaying an image of an item in the left-hand navigate frame.

var st_astr = "<html><body bgcolor='white'>";
var en_astr = "</body></html>";


function display_pic(graphic, price) {
    parent.frames[0].document.close( );
    parent.frames[0].document.write(st_astr);
    parent.frames[0].document.write("<img src='images/" + graphic + "'><br>" + price);
    parent.frames[0].document.write("<p><a href='javascript:parent.updatenav_nav( );'>Navigation Table</a>");
    parent.frames[0].document.write(en_astr);
    parent.frames[0].document.close( ); }


I use the word "mechanism" above because the display_pic( ) function doesn't just display an image but completely overwrites the navigate.html document with a new document whose bodyObject.firstChild is the image we want to display. Situated below the graphic image for the item is the item's price and also a Navigation Table link, which when clicked calls an updatenav_nav( ) function that returns navigate.html to the navigate frame.

function updatenav_nav( ) {
    parent.frames[0].document.close( );
    parent.frames[0].location = "navigate.html";
    parent.frames[0].document.close( ); }


Presumably the display_pic( ) function would be called by buttons on the item pages, for example:

<p>This Item Costs $2.22</p>
<button type="button" onclick="parent.display_pic('p2i1_photo.jpeg', '$2.22');">Show This Item In The Left Frame</button>


We can easily adapt the display_pic( )/updatenav_nav( ) functionality to the navigation parent + iframe structure described in the Frame retool section of Blog Entry #299:

(1) In the parent document,
(a) wrap the navigation content in a linksDiv div and
(b) put an empty pictureDiv div right after the linksDiv div.

<div id="navigateDiv">
<div id="linksDiv">
<p>This is the navigation div, and here are links to the order pages:</p>
<p><a href="pageone.html" target="main">Order Page One</a></p>
...
</div>
<div id="pictureDiv"></div>
</div>


(2) Code the display_pic( ) function as:

function display_pic(graphic, price) {
    document.getElementById("linksDiv").style.display = "none";
    document.getElementById("pictureDiv").innerHTML =
    "<img src='" + graphic + "'><br>" + price + "<p><a href='javascript:updatenav_nav( );'>Navigation Table</a></p>"; }


(3) Code the updatenav_nav( ) function as:

function updatenav_nav( ) {
    document.getElementById("linksDiv").style.display = "block";
    document.getElementById("pictureDiv").innerHTML = ""; }


Alternatively, you might prefer to display thumbnail images of the items on the item pages themselves:

.pictureDiv { display: none; }
...

function display_pic(show, itemID) {
    document.getElementById(itemID).style.display = show ? "block" : "none"; }

...
<p>This Item Costs $2.22</p>
<div id="p2i1" class="pictureDiv"><img width=imageWidth height=imageHeight src=imageURL alt="The p2i1 item"></div>
<button type="button" onclick="display_pic(true, 'p2i1');">Show Item</button>
<button type="button" onclick="display_pic(false, 'p2i1');">Hide Item</button><br> ...


The cookies are all gone

The delCookie( ) function provides a template for deleting the gifttails cookies associated with the shopcartindex.html document.

function delCookie(name) {
    var expireNow = new Date( );
    document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT" + "; path=/"; }


• The Thu, 01-Jan-70 00:00:01 GMT expires value may look deprecated, although it does exactly match the expires date string format given in the "Netscape Cookies" appendix of the JavaScript 1.3 Client-Side Reference.

• No use is made of the expireNow Date object, and its constructor statement can be thrown out. But you could make use of expireNow if you wanted to, for example, you could push it one millisecond into the past
expireNow.setTime(expireNow.getTime( ) - 1);
and then use its toUTCString( ) return for the expires value.
... expires=" + expireNow.toUTCString( ) + ...

• Note that the value of the name cookie is set to an empty string. It is not necessary to retrieve the original value of the cookie; in the Saving Cookies section of the aforecited "Netscape Cookies" resource, Netscape states, Saving a cookie with the same path and name values as an existing cookie overwrites the existing cookie.

Now, we could supplement the delCookie( ) function with a delCookieArray( ) function that iteratively feeds gifttails+i strings to delCookie( ), but I'm not sure I see the point of doing so given that we can put the requisite loop action in delCookie( ) itself:

function delCookie( ) {
    if (document.cookie.indexOf("gifttails") != -1) {
        for (i = 0; i < textInputValues.length; i++)
            document.cookie = "gifttails" + i + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT" + "; path=/"; } }


We can call delCookie( ) via a button on the navigation parent page:

<button type="button" onclick="delCookie( );">Clear All Cart Cookies</button>

N.B. After clearing the cookies, the user may (depending on the user's interaction with the cart, depending on the browser) end up resetting the cookies if the cookie-setting process is triggered by unloading the order.html page - this is one of the reasons I prefer to set cookies when the order form is submitted.

In the course of putting together my own demo for the shopping cart, I ran through the source of the shopcart.zip package's thanku.htm 'thank you' page, and realized: "I really should write a post on this code." So in the following entry, I'll discuss the thanku.htm document, and after that roll out my shopping cart demo, and then we'll finally be ready to move on to something else.


Powered by Blogger

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