reptile7's JavaScript blog
Tuesday, July 16, 2013
The Credit to My Credit Card
Blog Entry #295

We return now to the "So, You Want A Shopping Cart, Huh?" order.html page and the submission of its order form. Unlike Joe's order form, the order form at Gordon's original order.htm page provides a credit card facility, namely, the page's Choose Order Method table offers an On-line / Credit Card: option and is followed by a Credit Card Info table with fields for inputting a credit card number and expiration date.

Gordon's Choose Order Method and Credit Card Info tables

As you would expect, the order.htm page's form submission code is quite a bit different than that for the order.html page, and I thought it would be interesting to look at the former in today's post.

The orderSubmit( ) function

As at the order.html page, clicking the button at the bottom of the order.htm page calls an order.htm orderSubmit( ) function

function orderSubmit(type) {
    if (type == "order") { ... }
    else {
        if (!(checkGiftLength( ))) {
            document.order.form_action.value = type;
            document.order.submit( );
            return true; }
        else return false; } }

and passes it an order string argument. The if (type == "order") { ... } gate is accompanied by an else clause that, if type and order were not equal, would call a checkGiftLength( ) function whose (convertible-to-)false return would cause the form to submit. However, there is no checkGiftLength( ) function in the order.htm source.

Here's what we've got if type and order are equal (and they are):

if (type == "order") {
    if ( ! ((document.order.order_type[0].checked && checkCardNumber( )) || checkRequired( ) || checkExpireDate( ))) {
        document.order.form_action.value = type;
        document.order.submit( );
        return true; }
    else return false; }

Three subconditions must evaluate to false in order to submit the order form:
(1) (document.order.order_type[0].checked && checkCardNumber( ))
(2) checkRequired( )
(3) checkExpireDate( )

I've got your number

The common name of the radio buttons in the order.htm Choose Order Method table is order_type. Strangely, the On-line / Credit Card: radio button has a checked attribute set to an empty string (the other two radio buttons don't have a checked attribute):

<font size="2">On-line / Credit Card: <input type="radio" checked="" name="order_type" value="credit"></font>

The checked="" setting is syntactically incorrect and I'm not quite sure what Gordon meant for it to do - on my computer, it checks the radio button with Communicator 4.61 but has no effect (the radio button remains unchecked) with IE 4.5 - if we want the radio button to be checked, then the correct syntax would be:

<input type="radio" checked name="order_type" value="credit"> <!-- for HTML -->
<input type="radio" checked="checked" name="order_type" value="credit"> <!-- for XHTML -->

In any case, if the order_type[0] (On-line / Credit Card: ) radio button is checked, then the orderSubmit( ) function calls the order.htm checkCardNumber( ) function, which validates the value of the Credit Card # field

<td align="right" bgcolor="#ccffff"><b>Credit Card #:</b></td>
<td bgcolor="#ccffff"><input type="text" size="16" maxlength="16" name="card_no" onblur="checkCardNumber( );"><font size="2"><br>
(no spaces allowed)</font></td>

in the Credit Card Info table. The checkCardNumber( ) function first runs through the Credit Card # characters and checks if they're all digits.

function checkCardNumber( ) {
    var i, c, outString = "", retVal = false;
    for (i = 0; i < document.order.card_no.value.length; i++) {
        c = document.order.card_no.value.charAt(i);
        if (c >= "0" && c <= "9") outString += c;
        else retVal = true; }
    document.order.card_no.value = outString;
    /* Credit Card Sanity Check ... */
    return retVal; }

For comparison operations involving string literals, Mozilla states, Strings are compared based on standard lexicographical ordering, using Unicode values. The Unicode code positions for the 0 to 9 digit characters respectively run from U+0030 to U+0039; any and all non-digit characters will fall outside this range and cause the (c >= "0" && c <= "9") if condition to return false.

If the Credit Card # string contains one or more non-digit characters, then true is assigned to a retVal variable, which at the end of the checkCardNumber( ) function is returned to the orderSubmit( ) function. Back at the orderSubmit( ) function, the (document.order.order_type[0].checked && checkCardNumber( )) subcondition returns true and therefore the overall ( ! ((document.order.order_type[0].checked && checkCardNumber( )) || checkRequired( ) || checkExpireDate( ))) if condition returns false, sending control to the else return false; clause. No explanation is given to the user for why form submission is not successful.

If all of the Credit Card # characters are digits, then those characters are used to build an outString string, which is subsequently assigned to the value of the Credit Card # field. Actually, if the Credit Card # string contained a mix of digit and non-digit characters, then checkCardNumber( ) would build and assign to document.order.card_no.value an outString string comprising the original string's digit characters, i.e., the non-digit characters would be effectively discarded by the c >= "0" && c <= "9" operation, e.g., a 432a543b654c765d entry would be converted to 432543654765. If the Credit Card # string contained only non-digit characters, then outString would remain an empty string, which would again be assigned to document.order.card_no.value, effectively clearing the original entry.

In my book, there is no reason to rebuild the Credit Card # value; if we encounter a non-digit character, then we should immediately pop up a relevant alert( ) message, return true;, and exit the checkCardNumber( ) function. In moving forward, let us assume that the user enters an all-digit value into the Credit Card # field.

You're not my type

As shown in the screen shot at the beginning of the post, the Credit Card Info table contains a Card Type radio button menu of four acceptable credit card options: Visa, MasterCard (M/C), Discover, and American Express (AMEX). After assigning outString to document.order.card_no.value, the checkCardNumber( ) function conducts a Credit Card Sanity Check that tests for card type-number mismatches via checking the user's Card Type choice against the first document.order.card_no.value character.

// Credit Card Sanity Check
if (document.order.card_type[0].checked && document.order.card_no.value.charAt(0) != "4") {
    window.alert("Please enter a valid VISA number");
    retVal = true; }
else if (document.order.card_type[1].checked && document.order.card_no.value.charAt(0) != "5") {
    window.alert("Please enter a valid MasterCard number");
    retVal = true; }
else if (document.order.card_type[2].checked && document.order.card_no.value.charAt(0) != "6") {
    window.alert("Please enter a valid Discover number");
    retVal = true; }
else if (document.order.card_type[3].checked && document.order.card_no.value.charAt(0) != "3") {
    window.alert("Please enter a valid AMEX number");
    retVal = true; }

The common name of the Card Type radio buttons is card_type; the first-in-source-order Visa: option has a checked="" attribute and may or may not be initially checked, depending on the browser (vide supra).

Visa card numbers always begin with a 4; if the Visa: radio button is checked and the first Credit Card # digit is not a 4, then the if clause displays a Please enter a valid VISA number alert( ) message and assigns true to retVal. The else if clauses analogously handle MasterCard, Discover, and American Express card numbers, which always begin with a 5, 6, and 3, respectively. However, Wikipedia's "Payment card number" entry notes that the first digit of a payment card number identifies a card issuer category and not the card issuer itself: an American Express card number may always begin with a 3, but a card number beginning with a 3 is not necessarily for an American Express card.

12 and over, please

The Credit Card Sanity Check concludes with an else if clause that checks the length of the Credit Card # string.

else if (document.order.card_no.value.length < 12) {     window.alert("The card number you entered is not valid");     retVal = true; }

If the Credit Card # string contains fewer than 12 digits, then a The card number you entered is not valid alert( ) message pops up - better than no message at all, but a Your card number must contain 12-16 digits* message would be more appropriate - and true is assigned to retVal.

*As specified in the I've got your number section above, the Credit Card # field has a maxlength="16" attribute that prevents the user from entering more than 16 characters into the field.

No freshness date

If the user's Card Type and Credit Card # inputs get through the Credit Card Sanity Check (if all five if conditions return false), then retVal = false is returned to the orderSubmit( ) function, causing the (document.order.order_type[0].checked && checkCardNumber( )) subcondition to return false. The orderSubmit( ) function moves on to the checkRequired( ) subcondition/function, which we discussed in the previous post; if the user's Customer Information / Details and Shipping Address table inputs are in order, then the checkRequired( ) function returns false to the orderSubmit( ) function - two down, one to go. The orderSubmit( ) function finally moves to the checkExpireDate( ) subcondition/function. The order.htm checkExpireDate( ) function

function checkExpireDate( ) {
    if (document.order.order_type[0].checked) {
        if (!document.order.expire_date.value.length) { // If the length is 0
            window.alert("Please enter a valid credit card expiration date");
            return true; } }
    return false; }

returns false - and thereby green-lights the submission of the order form - if the Expiration Date field in the Credit Card Info table

<td align="right" bgcolor="#ccffff"><b>Expiration Date:</b></td>
<td bgcolor="#ccffff"><input type="text" size="16" maxlength="20" name="expire_date" onblur="checkExpireDate( );"><br>
<img src="" alt="Cards" width="100" height="30"></td>

has not been left blank. (C'mon, Gordon, we can do better than that.) The checkExpireDate( ) function also returns false if the user has checked the Cheque: or Phone Call: radio button in the Choose Order Method table.

We'll upgrade the checkCardNumber( ) and checkExpireDate( ) functions in the following entry.

Comments: Post a Comment

<< Home

Powered by Blogger

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