Skip to main content

End-to-end Ajax application development, Part 3: Integrate, test, and debug the application

Separate your application tiers to produce a clean and elegant Web app


Return to article



Listing 1. Contents of BankTeller.js file
/*
============================================================
Project: End-to-End-Ajax application development

Purpose: This is an example scenario to be used in
 an IBM developerWorks article.

Last modified: May/16/2007.

This JavaScript file provides the client-side logic for the
bank teller application. This application is a
single-page based Ajax application. The logic in here depends
on code from two other files (xhr.js and json.js). The code
in this file primarily addresses the following Ajax-related
concepts.

 a) Single-page browser application (DOM manipulation etc.)
 b) Browser DOM manipulation
 c) Using XHR in a context of an application
 d) Role of JSON as a data interchange format
============================================================
*/
/*
Define the variables used in this JavaScript file.
*/
// This is the command key that is sent to the REST service.
// This field will carry the value of a specific 
// bank teller action (deposit, debit, portfolio value) 
var BANK_TELLER_COMMAND_KEY = "Bank_Teller_Command";
// This is the post data key sent to the REST service.
var BANK_TELLER_POST_DATA_KEY = "Post_Data";
// The following are the 4 different bank teller commands.
var GET_ALL_ACCOUNTS_INFO = "Get_All_Accounts_Info";
var DEPOSIT_TO_ACCOUNT = "Deposit_To_Account";
var DEBIT_FROM_ACCOUNT = "Debit_From_Account";
var   GET_STOCK_PORTFOLIO_VALUE = "Get_Stock_Portfolio_Value";
// This is the DIV element for the main page.
var DIV_MAIN_PAGE = "mainPage";
// This is the DIV element that holds the application menu.
var DIV_TELLER_OPTIONS = "tellerOptions";
// This is the DIV element that holds the deposit HTML form.
var DIV_DEPOSIT_ACTION = "depositAction";
// This is the DIV element that holds the debit HTML form.
var DIV_DEBIT_ACTION = "debitAction";
// This is the DIV element that holds the portfolio HTML form.
var DIV_PORTFOLIO_ACTION = "portfolioAction";
// This is the DIV element that holds the teller action result.
var DIV_TELLER_ACTION_RESULT = "tellerActionResult";
// This is the DIV element that holds the footer section of the pages.
var DIV_PAGE_FOOTER = "pageFooter";
// This is name of the table where the page footer is laid out.
var FOOTER_TABLE = "footerTable";
// This is the HTML id of the field that displays the current user.
var CURRENT_USER_DISPLAY = "currentUserDisplay";
// Define the CSS values for visible and hidden controls.
var CSS_STYLE_VISIBLE = "visible";
var CSS_STYLE_HIDDEN = "hidden";
// Some of the element ids are defined below.
var TELLER_OPTION = "tellerOptionsLink";
var SPAN_TELLER_OPTION = "tellerOptionsLink";
var SPAN_TELLER_OPTION_SELECTION = "tellerOptionsLinkSelection";
var FOOTER_OPTION_STRNG = "FOOTER_OPTION";
// A number to identify which footer option is highlighted.
var FOOTER_OPTION_NUMBER1 = 1;
// More element ids below.
var FOOTER_OPTION1 = "goToMainMenuLink";
var GOTO_MAIN_MENU_LINK_SELECTION = "goToMainMenuLinkSelection";
var INTERNAL_SERVER_ERROR_MSG = "Internal error in server. Please try again.";
var DUMMY_MSG = "Dummy_Msg";
var HTTP_PART_IN_URL = "http://";
var BANK_ACTIONS_REST_WEBSERVICE_URL_RESOURCE = 
   "/BankTeller/BankActions_REST_Service.php";
// Integer identifiers for three options.
var TELLER_OPTION1 = 1;
var TELLER_OPTION2 = 2;
var TELLER_OPTION3 = 3;
// Element ids for the three drop-down lists.
var TELLER_OPTION1_DROP_DOWN_LIST_ID = "depositAccountOwner";
var TELLER_OPTION2_DROP_DOWN_LIST_ID = "debitAccountOwner";
var TELLER_OPTION3_DROP_DOWN_LIST_ID = "portfolioAccountOwner";
// More element ids.
var DEPOSIT_AMOUNT_HTML_ID = "depositAmount";
var DEBIT_AMOUNT_HTML_ID = "debitAmount";
var TELLER_ACTION_RESULT_AREA_HTML_ID = "tellerActionResultArea";
var SUCCESS = 0;

// Some variables used in the application.
var currentTellerOptionBeingPerformed = 0;
var teller_request = null;
var accountOwnerDataAvailable = false;
var allAccountsInfo = null;
var tellerActionResultInfo = null;
// Variables to hold all the div elements.
var mainPageDivElement = null;
var tellerOptionsDivElement = null;
var depositActionDivElement = null;
var debitActionDivElement = null;
var portfolioActionDivElement = null;
var tellerActionResultDivElement = null;
var pageFooterDivElement = null;
var currentlyShownElement = null;
var currentUserName = "John Doe";

// Different HTTP readystate values. 
// Taken from w3.org XMLHttpRequest working group document
// http://www.w3.org/TR/XMLHttpRequest/
// Uninitialized i.e. this is the initial state.
var HTTP_READYSTATE_UNINITIALIZED = 0;
// Open method has been called.
var HTTP_READYSTATE_OPEN = 1;
// The User Agent successfully completed the request, but no data has been received yet.
var HTTP_READYSTATE_SENT = 2;
// All HTTP headers have been received. But HTTP body is yet to be received.
var HTTP_READYSTATE_RECEIVING = 3;
// The data transfer has been completed.
var HTTP_READYSTATE_LOADED = 4;
// The only HTTP status code that we care about here in this program.
var HTTP_STATUS_OK = 200;

/*
============================================================
Function: initOnPageLoad

Last Modified: May/15/2007

This function is called during page load time i.e.
when the bank teller URL is first visited. It is the
best time for us to do various intializations.
 
In this function, all the div elements are obtained and
stored away in the application variables. Then all the 
DIV elements except the ones that are needed for the
initial page are hidden. After that, the div elements
in other screens are removed from the main or the 
super div element. In the end, an Ajax asynchronous
call is made to the server to fetch the account details for
all the account holders.
============================================================
*/
function initOnPageLoad() {
   // Get all the Div elements in our program and store them in local variables.
   mainPageDivElement = document.getElementById(DIV_MAIN_PAGE);
   tellerOptionsDivElement = document.getElementById(DIV_TELLER_OPTIONS);
   depositActionDivElement = document.getElementById(DIV_DEPOSIT_ACTION);
   debitActionDivElement = document.getElementById(DIV_DEBIT_ACTION);
   portfolioActionDivElement = document.getElementById(DIV_PORTFOLIO_ACTION);
   tellerActionResultDivElement = document.getElementById(DIV_TELLER_ACTION_RESULT);
   pageFooterDivElement = document.getElementById(DIV_PAGE_FOOTER);

   // Set all the DivElements as Hidden except the MainPage, teller options and
   // Footer Div elements.
   depositActionDivElement.style.visibility = CSS_STYLE_HIDDEN;
   debitActionDivElement.style.visibility = CSS_STYLE_HIDDEN;
   portfolioActionDivElement.style.visibility = CSS_STYLE_HIDDEN;
   tellerActionResultDivElement.style.visibility = CSS_STYLE_HIDDEN;

   // Remove all the Div elements except the MainPage, teller options and the 
   // Footer Div Elements. Since we have a local copy of them in our  variables,
   // whenever we need them later, we will add them as needed. 
   // (This will avoid the vertical scroll bar appearing 
   // on the browser screen due to too many hidden but dormant div elements).   
   mainPageDivElement.removeChild(depositActionDivElement);
   mainPageDivElement.removeChild(debitActionDivElement);
   mainPageDivElement.removeChild(portfolioActionDivElement);
   mainPageDivElement.removeChild(tellerActionResultDivElement);

   // In the teller option screen, hide the footer options.
   // We don't need the page footer options in the menu screen. 
   showFooterOptions(false);
   // Record the current DIV element being shown to the user.
   // i.e. Teller menu options.
   currentlyShownElement = tellerOptionsDivElement;
   // Start collecting the account information for all the
   // account holders.
   getAllAccountsInformation_Async();
} // End of function initOnPageLoad.

/*
============================================================
Function: showFooterOptions

Last Modified: May/15/2007

This function takes a boolean parameter and accordingly shows 
or hides the footer options.
============================================================
*/
function showFooterOptions (showOptions) {
   // Get the element id for the table that holds the
   // footer options.
   var footerTable = document.getElementById(FOOTER_TABLE);

   // Check if the caller wants to show the footer options.
   if (showOptions == true) {
      // Set the footer table style to visible.
      footerTable.style.visibility = CSS_STYLE_VISIBLE;
      // Get the current user field id. (Bank teller's name)
      var currentUserDisplayElement = document.getElementById(CURRENT_USER_DISPLAY);
      // Create a text node with the current user name.
      // Replace the first child of the current user display element, which itself
      // is a SPAN element.
      var textElement = document.createTextNode(currentUserName);
      var kids = currentUserDisplayElement.childNodes;
      currentUserDisplayElement.replaceChild(textElement, kids[0]);
   } else {
      // No need to show the footer. Hide it now.
      footerTable.style.visibility = CSS_STYLE_HIDDEN;
   }
} // End of function hideFooterOptions

/*
============================================================
Function: changeOptionsLinkStyleForSelection

Last Modified: May/15/2007

This function simulates the menuitem highlighting for the
teller options menu. All the menu items are SPAN elements with
menu text inside of them. When the user moves his mouse over 
this element, this event handler will be called.
It simply applies a different style to that SPAN element.
That makes a visual cue for the user by highlighting the 
text in a different color.

This function gets the teller option number where the 
mouse is at now. Refer to the HTML file from where this
parameter is passed.
============================================================
*/
function changeOptionsLinkStyleForSelection(currentTellerOption) {
   // Get the current SPAN element that is being moused over.
   // Caller gives us the SPAN element number (deposit, debit or portfolio).
   // From that number, get the HTML id and get the element object.
   var tellerOptionElement = 
      document.getElementById(TELLER_OPTION + currentTellerOption);
   // Simply assign the CSS rule for that classname.
   // That is all you have to do to simulate the menu highlighting.   
   tellerOptionElement.className = SPAN_TELLER_OPTION_SELECTION;      
} // End of function ChangeOptionsLinkStyleForSelection

/*
============================================================
Function: changeOptionsLinkStyleToDefault

Last Modified: May/15/2007

This function simulates the menuitem unhighlighting for the
teller options menu. All the menu items are SPAN elements with
menu text inside of them. When the user moves his mouse out of 
this element, this event handler will be called.
It simply applies a different style to that SPAN element.
That makes a visual cue for the user by removing the 
highlighted text into a normal text with no background color.

This function gets the teller option number where the 
mouse is at now. Refer to the HTML file from where this
parameter is passed.
============================================================
*/
function changeOptionsLinkStyleToDefault(currentTellerOption) {
   // Get the current SPAN element from where the mouse is moving out.
   // Caller gives us the SPAN element number (deposit, debit or portfolio).
   // From that number, get the HTML id and get the element object.   
   var tellerOptionElement = document.getElementById(TELLER_OPTION + 
      currentTellerOption);
   tellerOptionElement.className = SPAN_TELLER_OPTION;
} // End of function changeOptionsLinkStyleToDefault

/*
============================================================
Function: processTellerOperation

Last Modified: May/15/2007

This function processes the teller operation for the given
teller option. This is an event handler for the 
SPAN elements that include the teller options text
a.k.a menu text. Depending on which option the bank
teller selects (deposit, debit or portfolio value),
it brings up the screen corresponding to that operation.
============================================================
*/
function processTellerOperation(currentTellerOption) {
   // Let us change the style of this menuitem text in the list to 
   // default text and background colors.
   changeOptionsLinkStyleToDefault(currentTellerOption);

   // Do we have the account owners data from the server?
   // This would have happened in the background with the
   // asynchronous request, when the application was 
   // first visited. If we received the data from the server,
   // we would have set this variable.
   if (accountOwnerDataAvailable == false) {
      // We haven't received anything from the server.
      // Return now.
      alert("No account owner data available. Can't perform teller functions. Sorry!");
      return;
   }

   // Depending on the bank teller's selection, bring the correct screen.
   switch(currentTellerOption) {
      // Deposit to an account.
      case TELLER_OPTION1:
         // Set the option to 1 (deposit) 
         currentTellerOptionBeingPerformed = TELLER_OPTION1;
         // Hide whatever is shown now and show the deposit screen.
         hideShowAndInsertAsTopElement(currentlyShownElement, 
            depositActionDivElement,
            null, null, null, null, true);
         // Fill the drop-down list on this deposit screen with
         // all the account holder names.
         populateDropDownListWithAccountOwnersData(TELLER_OPTION1_DROP_DOWN_LIST_ID);
         // Clear out the deposit amount input field.
         document.getElementById(DEPOSIT_AMOUNT_HTML_ID).value = "";
         break;
      
      // Debit from an account
      case TELLER_OPTION2:
         // Set the option to 2 (debit)
         currentTellerOptionBeingPerformed = TELLER_OPTION2;
         // Hide whatever is shown now and show the debit screen.
         hideShowAndInsertAsTopElement(currentlyShownElement, 
            debitActionDivElement,
            null, null, null, null, true);
         // Fill the drop-down list on this debit screen with
         // all the account holder names.
         populateDropDownListWithAccountOwnersData(TELLER_OPTION2_DROP_DOWN_LIST_ID);
         // Clear out the debit amount input field.
         document.getElementById(DEBIT_AMOUNT_HTML_ID).value = "";
         break;
      
      // Get current portfolio value      
      case TELLER_OPTION3:
         // Set the option to 3 (Get Portfolio Value)
         currentTellerOptionBeingPerformed = TELLER_OPTION3;
         // Hide whatever is shown now and show the portfolio value screen.
         hideShowAndInsertAsTopElement(currentlyShownElement, 
            portfolioActionDivElement,
            null, null, null, null, true);
         // Fill the drop-down list on this portfolio value screen with
         // all the account holder names.         
         populateDropDownListWithAccountOwnersData(TELLER_OPTION3_DROP_DOWN_LIST_ID);
         break;   
         
      default:
         break;
   } // End of switch(currentTellerOption)
} // End of function processTellerOperation

/*
============================================================
Function: hideShowAndInsertAsTopElement

Last Modified: May/16/2007

This function hides an element and shows another element by
inserting the newly shown element as the top element in the
browser DOM tree hierarchy. This function is central to
achieving the goal of making a true single page based 
Ajax application. This function takes several parameters
as input as shown below:
1) Element that needs to be hidden
2) Element that needs to be shown
3) Any specific UI control to receive focus in the newly
   shown (DIV) element.
4) Any text that need to be customized on a UI control.
5) If there is an edit control, where the cursor should be set?
6) End position of the cursor region (Read comments below)
7) Do we need to display page footer menu?
============================================================
*/
function hideShowAndInsertAsTopElement(elementToHide, elementToShow, 
   childElementIdToFocus, childElementIdText, childElementTextCursorStartPos,
   childElementTextCursorEndPos, displayFooterOptions) {
   // Hide and show as per the caller's wish.
   elementToHide.style.visibility = CSS_STYLE_HIDDEN;
   elementToShow.style.visibility = CSS_STYLE_VISIBLE;
   // Store the element id that is being currently shown to the user.
   currentlyShownElement = elementToShow;      
   // Now insert the newly shown element as the first child of the 
   // root div element of this document.
   mainPageDivElement.insertBefore(elementToShow, elementToHide);
   // Also insert the footer element right-after the element that was just shown.
   // i.e. insert the footer in between the elementToShow and elementToHide.
   mainPageDivElement.insertBefore(pageFooterDivElement, elementToHide);
   // Remove the elementToHide temporarily until there is a need to show it later.
   // (This will avoid the vertical scroll bar appearing on the browser screen due 
   // to too many hidden but dormant div elements).
   mainPageDivElement.removeChild(elementToHide);
   // If the caller opted for footer options, fulfill that wish now.
   showFooterOptions(displayFooterOptions);
   
   // Any special request to set focus on a particular child element?
   if (childElementIdToFocus != null) {
      // If we were asked to do that, set the child element id focus, 
      // text, cursor position etc.
      setElementFocusAndTextAndCursorPosition(childElementIdToFocus, 
         childElementIdText, childElementTextCursorStartPos, 
         childElementTextCursorEndPos);   
   } // End of if (childElementIdToFocus != null)      
} // End of function hideShowAndInsertAsTopElement

/*
============================================================
Function: setElementFocusAndTextAndCursorPosition

Last Modified: May/16/2007

This function sets focus on a particular element. Optionally,
it also sets text for an input field. In addition, it can
also set the cursor position of the input field.
This function is useful, when a particular input field
needs to be initialized with default values so that the
user can either accept or modify only a portion of 
the default text.
============================================================
*/
function setElementFocusAndTextAndCursorPosition(elementIdToFocus, 
   elementIdText, elementTextCursorStartPos, elementTextCursorEndPos) {
   // Do it only if the element id is valid.
   if (elementIdToFocus != null) {
      // If we were asked to do that, set the element id focus, text, 
      // cursor position etc.
      if (elementIdText != null) {      
         // Change the text.
         document.getElementById(elementIdToFocus).value=elementIdText;
      }
      
      // Set the cursor to specific position inside the input field.
      if ((elementTextCursorStartPos != null) && 
         (elementTextCursorEndPos != null)) {
         // IE specific cursor positioning inside a text field.
         if (document.selection) {
            var range = document.getElementById(elementIdToFocus).createTextRange();
            range.collapse(true);
            range.moveStart("character", elementTextCursorStartPos);
            range.moveEnd("character", elementTextCursorEndPos);
            range.select();
         } else { // Mozilla and Firefox specific cursor position inside a text field.
            document.getElementById(elementIdToFocus).selectionStart = 
               elementTextCursorStartPos;
            document.getElementById(elementIdToFocus).selectionEnd = 
               elementTextCursorEndPos;       
         } // End of else - if (document.selection)
      } // End of if ((elementTextCursorStartPos != null)
       
      // Set the focus to the required element.
      document.getElementById(elementIdToFocus).focus();            
   } // End of if (elementIdToFocus != null)
} // End of function setElementFocusAndTextAndCursorPosition.

/*
============================================================
Function: changeFooterLinkStyleForSelection

Last Modified: May/16/2007

This function is an event handler for the SPAN elements
in the page footer section. When the user hovers the mouse
over the span elements in the footer section, then this
function will simply apply a CSS class rule so that the
background color of the span element will change. That will
give a visual cue for the user to indicate which footer
option is clickable now.
============================================================
*/
function changeFooterLinkStyleForSelection(currentFooterOption) {
   var footerOptionElement =    
      document.getElementById(eval('(' + FOOTER_OPTION_STRNG + 
      currentFooterOption + ')'));
   footerOptionElement.className = GOTO_MAIN_MENU_LINK_SELECTION;   
} // End of function changeFooterLinkStyleForSelection.

/*
============================================================
Function: changeFooterLinkStyleToDefault

Last Modified: May/16/2007

This function is an event handler for the SPAN elements
in the page footer section. When the user moves the mouse
out of the span elements in the footer section, then this
function will simply apply a CSS class rule so that the
background color of the span element will be plain white. 
That will give a visual cue for the user to indicate 
that particular footer option is not clickable now.
============================================================
*/
function changeFooterLinkStyleToDefault(currentFooterOption) {
   var footerOptionElement =    
      document.getElementById(eval('(' + FOOTER_OPTION_STRNG + 
      currentFooterOption + ')'));
   footerOptionElement.className = FOOTER_OPTION1;
} // End of function changeFooterLinkStyleToDefault.

/*
============================================================
Function: processFooterOperation

Last Modified: May/16/2007

This function is an event handler for the SPAN elements
in the page footer section. When the user clicks any of 
the span elements in the footer section, then this
function will process that user selection. 

This function gets parameter that tells which particular
footer option was clicked by the user (This parameter is
passed from the HTML file's footer table.)
============================================================
*/
function processFooterOperation(currentFooterOption) {
   // Let us change the style of this link in the list to default text 
   // and background colors.
   changeFooterLinkStyleToDefault(currentFooterOption);
   
   // The bank teller application has only one link in the
   // page footer section (Go to Main Menu link).
   // It is possible to add more if needed and process them here.
   switch(currentFooterOption) {
      case FOOTER_OPTION_NUMBER1:
         // Hide the currently shown UI control and show the teller options UI control.
         // User clicked "Go to Main Menu" link in the footer.
       // In this case, we have to show the teller main menu.
       hideShowAndInsertAsTopElement(currentlyShownElement, tellerOptionsDivElement,
            null, null, null, null, false);
       break;
   
      default:
         break;
   } // End of switch(currentFooterOption)   
} // End of function processFooterOperation.

/*
============================================================
Function: getHostnameFromThisURL

Last Modified: May/16/2007

This function parses the host name from a given URL string.
It is a utility function mostly used by the XHR send
functions in this application.
============================================================
*/
function getHostnameFromThisURL(url) {
   if (url == null || url.length <= 0) {
      return("");
   }
   
   // Parse the hostname portion from the URL.
   //The URL could be in one of the following formats
   // http://www.xyz.com/ccc/ddd/eee.php
   // www.xyz.com/ccc/ddd/eee.php
   // In either case, our goal in this method is to parse www.xyz.com
   // Let us begin now.
   // See if the URL contains http:// substring
   var index = url.indexOf("/", 0);
   
   // If there is no / character in the URL, it is really odd and it 
   // can occur only when the URL is just www.xyz.com
   // In the context of this application, that condition should never occur.
   // But still, check for that condition.
   if (index == -1) {
      // In this case, they specified only the hostname portion in the URL without
      // specifying other parts such as http:// or URL resource /ccc/ddd/eee.php.
      // i.e. www.xyz.com
      // The hostname in this case is the URL as it is specified.
      return(url);
   } else if (index == (url.length - 1)) {
      // We are now at the last character of the url string.
      // This is the same as the above-mentioned case with an addition of  
      // a / character at the end of the hostname portion.
      // i.e. www.xyz.com/
      // Return the URL portion as it is then (except the trailing /)
      url = url.substr(0, index);
      return(url);
   } else {
      // Check to see if there is another / following this one as in http://
      if (url.charAt(index+1) == '/') {
         // That means, the URL contains the http:// part.
         // Proceed further and look for the next /.
         newIndex = url.indexOf("/", index+2);
         
         if (newIndex == -1) {
            // This is the case as in 
            // i.e no resource portion in the URL.
            // i.e. http://www.xyz.com
            // Just return the hostname portion of the URL.
            url = url.substr(index+2);
            return(url);            
         } else {
            // This is the case in http://www.xyz.com/ccc/ddd/eee.php
            // index = 5
            // newIndex = 18
            // Length of the hostname part = 11
            // i.e. (newIndex - index - 2 )
            // Return the host portion of the URL now.
            url = url.substr(index+2, (newIndex-index-2));
            return(url);
         } // End of else matching with if (newIndex == -1)
      } else {
         // This is the case as in www.xyz.com/ccc/ddd/eee.php
         // i.e. There is no http:// part in the URL.
         // In this case, just return the substring before the first occurrence of /
         url = url.substr(0, index);
         return(url);
      } // End of else.
   } // End of else matching with if (index = -1). 
} // End of function getHostnameFromThisURL.

/*
============================================================
Function: getAllAccountsInformation_Async

Last Modified: May/16/2007

This function sends a request to the mid-tier PHP service
to get all the account information stored in the server.
It does it in an Ajax style by setting up an asynchronous
callback to receive the server response. Mid-tier service
is accessed using REST (POST) and by using JSON as a 
data interchange format. Read the comments below for
more details. 

Note: Just for convenience, this application is designed
to get all the account information in one swoop. This 
may not be the case in real bank applications. Since,
the goal of this example is to demonstrate Ajax, it was 
designed to do it this way.
============================================================
*/
function getAllAccountsInformation_Async() {
   // Get the hostname from the document URL.
   var hostName = getHostnameFromThisURL(document.URL.toString());      
   var url;   

   // For getting all accounts information the browser client
   // need not send any input data to the mid-tier service.
   // Hence, set up a JSON object with dummy message.
   var requestJson = {
      "msg": DUMMY_MSG
   };
   
   // Use Douglas Crockford's JSON string converter to
   // convert the JSON object into JSON formatted text.
   var requestJsonString = requestJson.toJSONString();
   requestJson = "";
   
   // We have to make a REST call.
   // Make post data with different key-value pairs.
   // On the PHP server side, these individual key-value
   // pairs will be available as part of the superglobal
   // $_REQUEST associative array.
   var postData = BANK_TELLER_COMMAND_KEY + "=" + 
      escape(GET_ALL_ACCOUNTS_INFO) +
      "&" + BANK_TELLER_POST_DATA_KEY + "=" + 
      escape(requestJsonString);
   // Reset the JSON string since we have no further need for it.
   requestJsonString = "";
   // Form the fully qualified URL now.
   url = HTTP_PART_IN_URL + hostName + BANK_ACTIONS_REST_WEBSERVICE_URL_RESOURCE;
   // Create a new XHR object.
   teller_request = createRequest();
   // Set a callback function to receive the server response asynchronously.
   var callbackFunction = http_response_async_callback_for_get_all_acounts_info;
   // Send this XHR request now.
   sendHttpRequest(teller_request, callbackFunction, url, postData);   
} // End of function getAllAccountsInformation_Async

/*
============================================================
Function: http_response_async_callback_for_get_all_acounts_info

Last Modified: May/16/2007

This function is an Ajax callback event handler to receive 
server response for getting the account information for all
the account holders. This will get called at various
state changes during the HTTP transaction. The logic to
obtain the response data will be put to work only when
the HTTP status is OK i.e. 200 OK.
============================================================
*/
function http_response_async_callback_for_get_all_acounts_info() {
   // Do something only when the HTTP readystate is LOADED.
   if (teller_request.readyState == HTTP_READYSTATE_LOADED) {
      // You have nothing to do until it is 200 OK.
      if (teller_request.status == HTTP_STATUS_OK) {
         // We will get the response data in JSON format as plain text.
         var textResult = teller_request.responseText;   
         // Convert the JSON formatted text into a JSON object.
         // Thanks to Douglas Crockford for this safe JSON parser.
         // This is much safer than using an eval() statement.
         var obj = textResult.parseJSON();
         
         // Check if we have a successful response back from the server.
         if (obj.ResultCode != SUCCESS) {
            alert(obj.ResultMsg);
            return;            
         }

         // Beauty of JSON at work here.
         // No extra work required to consume the JSON data.
         // It is readily accessible as a JavaScript object.
         allAccountsInfo = obj.AccountInfo;
         
         // If the server sent us some account data,
         // set the flag to indicate that.
         // Depending this flag, bank teller option menu will be enabled.
         if (allAccountsInfo.length > 0) {
            accountOwnerDataAvailable = true;
         }                           
      } // End of if (teller_request.status == HTTP_STATUS_OK)
   } // End of if (teller_request.readyState == HTTP_READYSTATE_LOADED)   
} // End of function http_response_async_callback_for_get_all_acounts_info

/*
============================================================
Function: populateDropDownListWithAccountOwnersData

Last Modified: May/16/2007

This function populates a given drop down list with the 
account holders' names in alphabetical order. It is called
by the teller menu option processing function, when the
bank teller clicks on a bank action menu (deposit, debit or
portfolio value). This function receives the drop-down list
HTML id string as a parameter.
============================================================
*/
function populateDropDownListWithAccountOwnersData(dropdownListHtmlId) {
   // Get the drop-down list object.
   var dropDownListElement = document.getElementById(dropdownListHtmlId);
   
   // Loop through all the account information received from the server.
   // In each iteration insert the account holder name in drop-down list.
   for (var i=0; i<allAccountsInfo.length; i++) {
      // Create a new option element for the HTML SELECT control.
      var optionElement = document.createElement('option');
      // Assign the text and value of the listbox entry.
      optionElement.text = allAccountsInfo[i].AccountHolderName;
      optionElement.value = allAccountsInfo[i].AccountHolderName;
      // This statement works in IE6 but not in Firefox-1.5.0.4
      //nameListElement.add(optionElement, i);
      // This statement works both in IE and Firefox.
      dropDownListElement.options[i] = optionElement;
   } // End of for (var i=0; i<employeeNamesArray.length; i++)
   
   // Sort the list entries alphabetically.
   sortSelect(dropDownListElement);
   // Make the first entry in the list as selected.
   dropDownListElement.selectedIndex = 0;   
   dropDownListElement.focus();         
} // End of function populateDropDownListWithAccountOwnersData

/*
============================================================
Function: compareText

Last Modified: May/16/2007

This function takes two listbox option elements as input 
arguments and compares them alphabetically. It returns -1, 
if text1 is smaller than text2. It returns 0, if both strings 
are the same. It returns 1, if text1 is bigger than
text2.
============================================================
*/
function compareText (option1, option2) {
   var text1=option1.text.toLowerCase();
   var text2=option2.text.toLowerCase();
   return text1 < text2 ? -1 : text1 > text2 ? 1 : 0;
} // End of function compareText

/*
============================================================
Function: sortSelect

Last Modified: May/16/2007

This function sorts the select (listbox) control.
============================================================
*/
function sortSelect (select) {
   // Create a new array with a size of the available listbox entries.  
   var options = new Array (select.options.length);
   
   // Take a local copy of all the listbox elements and store it
   // in the array created above.
   for (var i = 0; i < options.length; i++) {
      options[i] = 
         new Option (
           select.options[i].text,
           select.options[i].value,
           select.options[i].defaultSelected,
           select.options[i].selected
         );
   } // End of for (var i = 0; i < options.length; i++)
   
   // Call the recursive array sort function now.
   options.sort(compareText);
   // Reset the select list box control.
   select.options.length = 0;

   // Populate the listbox with the newly sorted option elements.
   for (var i = 0; i < options.length; i++) {
      select.options[i] = options[i];
   }
} // End of function sortSelect

/*
============================================================
Function: depositAmountAction_Async

Last Modified: May/16/2007

This function sends a request to the mid-tier PHP service
to perform the deposit bank teller action.
It does it in an Ajax style by setting up an asynchronous
callback to receive the server response. Mid-tier service
is accessed using REST (POST) and by using JSON as a 
data interchange format. Read the comments below for
more details. 
============================================================
*/
function depositAmountAction_Async() {
   // Get the dollar amount to be deposited.
   var amountElement = document.getElementById(DEPOSIT_AMOUNT_HTML_ID);
   // Convert the string value into float value.
   var floatAmount = parseFloat(amountElement.value);
   
   // Validate the amount if it is a non-zero number value.
   if ((floatAmount == 0) || (isNaN(floatAmount) == true)) {
      alert("Please enter a valid amount.");
      document.getElementById(DEPOSIT_AMOUNT_HTML_ID).focus();
      return;
   } 

   // Reset the amount input field.
   amountElement.value = "";
   // Read the selected account holder name in the listbox.
   var dropDownListElement = document.getElementById(TELLER_OPTION1_DROP_DOWN_LIST_ID);
   var selectedOption = dropDownListElement.options[dropDownListElement.selectedIndex];
   var accountHolderName = selectedOption.value;
   
   // Get the host name from the document URL.
   var hostName = getHostnameFromThisURL(document.URL.toString());      
   var url;   

   // Set up the REST service input in JSON format.
   // This input will have the account holder name and the
   // amount to be deposited.
   var requestJson = {
      "accountHolderName": accountHolderName,
      "amount": floatAmount
   };
   
   // Use Douglas Crockford's JSON string converter to
   // convert the JSON object into JSON formatted text.   
   var requestJsonString = requestJson.toJSONString();
   requestJson = "";
   
   // We have to make a REST call.
   // Make post data with different key-value pairs.
   // On the PHP server side, these individual key-value
   // pairs will be available as part of the superglobal
   // $_REQUEST associative array.
   var postData = BANK_TELLER_COMMAND_KEY + "=" + 
      escape(DEPOSIT_TO_ACCOUNT) + "&" + BANK_TELLER_POST_DATA_KEY + "=" + 
      escape(requestJsonString);
   // Reset the JSON string since we have no further need for it.   
   requestJsonString = "";
   // Form the fully qualified URL now.
   url = HTTP_PART_IN_URL + hostName + BANK_ACTIONS_REST_WEBSERVICE_URL_RESOURCE;
   // Create a new XHR object.
   teller_request = createRequest();
   // Set a callback function to receive the server response asynchronously.
   var callbackFunction = http_response_async_callback_for_deposit_to_account;
   // Send this XHR request now.
   sendHttpRequest(teller_request, callbackFunction, url, postData);      
} // End of function depositAmountAction_Async.

/*
============================================================
Function: http_response_async_callback_for_deposit_to_account

Last Modified: May/16/2007

This function is an Ajax callback event handler to receive 
server response for the "deposit to an account" bank
teller action. This function will get called at various
state changes during the HTTP transaction. The logic to
obtain the response data will be put to work only when
the HTTP status is OK i.e. 200 OK.
============================================================
*/
function http_response_async_callback_for_deposit_to_account() {
   // Do something only when the HTTP readystate is LOADED.   
   if (teller_request.readyState == HTTP_READYSTATE_LOADED) {
      // You have nothing to do until it is 200 OK.
      if (teller_request.status == HTTP_STATUS_OK) {
         // We will get the response data in JSON format as plain text.
         var textResult = teller_request.responseText;   
         // Convert the JSON formatted text into a JSON object.
         // Thanks to Douglas Crockford for this safe JSON parser.
         // This is much safer than using an eval() statement.
         var obj = textResult.parseJSON();
         
         // Check if we have a successful response back from the server.
         if (obj.ResultCode != SUCCESS) {
            alert(obj.ResultMsg);
            return;            
         }
         
         // Beauty of JSON at work here.
         // No extra work required to consume the JSON data.
         // It is readily accessible as a JavaScript object.         
         tellerActionResultInfo = obj.AccountInfo;
         // Hide the deposit action screen and display the 
         // teller action result screen.
         hideShowAndInsertAsTopElement(currentlyShownElement, 
            tellerActionResultDivElement,
            null, null, null, null, true);
         // Display the deposit action results.
         updateTellerActionResultTextArea();
      } // End of if (teller_request.status == HTTP_STATUS_OK)
   } // End of if (teller_request.readyState == HTTP_READYSTATE_LOADED)      
} // End of function http_response_async_callback_for_deposit_to_account.

/*
============================================================
Function: updateTellerActionResultTextArea

Last Modified: May/16/2007

This function displays the teller action result by
updating the results screen text area.
============================================================
*/
function updateTellerActionResultTextArea() {
   var resultInfo = "";
   
   // Check which teller action was last performed.
   switch(currentTellerOptionBeingPerformed) {
      case TELLER_OPTION1:
         // It is the results for deposit action.
         resultInfo = "Teller Operation: Deposit to an account";
         resultInfo += "\n\nAccount Holder Name: " + 
            tellerActionResultInfo[0].AccountHolderName;
         resultInfo += "\nAccount Number: " + tellerActionResultInfo[0].AccountNumber;
         resultInfo += "\nPrevious Checking Balance: " + 
            tellerActionResultInfo[0].PreviousCheckingBalance;
         resultInfo += "\n\nAmount deposited: " + 
            tellerActionResultInfo[1].TransactionAmount;
         resultInfo += "\nNew Checking Balance: " + 
            tellerActionResultInfo[1].NewCheckingBalance;
         break;

      case TELLER_OPTION2:
         // It is the results for debit action.
         resultInfo = "Teller Operation: Debit from an account";
         resultInfo += "\n\nAccount Holder Name: " + 
            tellerActionResultInfo[0].AccountHolderName;
         resultInfo += "\nAccount Number: " + 
            tellerActionResultInfo[0].AccountNumber;
         resultInfo += "\nPrevious Checking Balance: " + 
            tellerActionResultInfo[0].PreviousCheckingBalance;
         resultInfo += "\n\nAmount debited: " + 
            tellerActionResultInfo[1].TransactionAmount;
         resultInfo += "\nNew Checking Balance: " + 
            tellerActionResultInfo[1].NewCheckingBalance;
         break;      

      case TELLER_OPTION3:
         // It is the results for stock portfolio action.
         resultInfo = "Teller Operation: Get Stock Portfolio Value";
         resultInfo += "\n\nAccount Holder Name: " + 
            tellerActionResultInfo[0].AccountHolderName;
         resultInfo += "\nAccount Number: " + 
            tellerActionResultInfo[0].AccountNumber;
         resultInfo += "\nStock Name: " + tellerActionResultInfo[0].StockName;
         resultInfo += "\nStock Quantity: " + tellerActionResultInfo[0].StockQuantity;
         resultInfo += "\nPrevious Portfolio Value: " + 
            tellerActionResultInfo[0].PreviousPortfolioValue;
         resultInfo += "\n\nCurrent Stock Price: " + 
            tellerActionResultInfo[1].CurrentStockPrice;
         resultInfo += "\nCurrent Portfolio Value: " + 
            tellerActionResultInfo[1].NewPortfolioValue;
       break;       
         
      default:
         resultInfo = "No result data available from the teller operation.";
   }
      
   // Now replace the text area value to show the results to the user.
   var textAreaElement = document.getElementById(TELLER_ACTION_RESULT_AREA_HTML_ID);
   textAreaElement.value = resultInfo;   
} // End of function updateTellerActionResultTextArea.

/*
============================================================
Function: debitAmountAction_Async

Last Modified: May/16/2007

This function sends a request to the mid-tier PHP service
to perform the debit bank teller action.
It does it in an Ajax style by setting up an asynchronous
callback to receive the server response. Mid-tier service
is accessed using REST (POST) and by using JSON as a 
data interchange format. Read the comments below for
more details. 
============================================================
*/
function debitAmountAction_Async() {
   // Get the dollar amount to be deposited.
   var amountElement = document.getElementById(DEBIT_AMOUNT_HTML_ID);
   // Convert the string value into float value.
   var floatAmount = parseFloat(amountElement.value);
   
   // Validate the amount if it is a non-zero number value.
   if ((floatAmount == 0) || (isNaN(floatAmount) == true)) {
      alert("Please enter a valid amount.");
      document.getElementById(DEBIT_AMOUNT_HTML_ID).focus();
      return;
   } 

   // Reset the amount input field.
   amountElement.value = "";
   // Read the selected account holder name in the listbox.
   var dropDownListElement = document.getElementById(TELLER_OPTION2_DROP_DOWN_LIST_ID);   
   var selectedOption = dropDownListElement.options[dropDownListElement.selectedIndex];
   var accountHolderName = selectedOption.value;
   
   // Get the host name from the document URL.
   var hostName = getHostnameFromThisURL(document.URL.toString());      
   var url;   

   // Set up the REST service input in JSON format.
   // This input will have the account holder name and the
   // amount to be debited from the account.   
   var requestJson = {
      "accountHolderName": accountHolderName,
      "amount": floatAmount
   };
   
   // Use Douglas Crockford's JSON string converter to
   // convert the JSON object into JSON formatted text.
   var requestJsonString = requestJson.toJSONString();
   requestJson = "";
   
   // We have to make a REST call.
   // Make post data with different key-value pairs.
   // On the PHP server side, these individual key-value
   // pairs will be available as part of the superglobal
   // $_REQUEST associative array.
   var postData = BANK_TELLER_COMMAND_KEY + "=" + escape(DEBIT_FROM_ACCOUNT) + 
     "&" + BANK_TELLER_POST_DATA_KEY + "=" + escape(requestJsonString);
   // Reset the JSON string since we have no further need for it.   
   requestJsonString = "";
   // Form the fully qualified URL now.
   url = HTTP_PART_IN_URL + hostName + BANK_ACTIONS_REST_WEBSERVICE_URL_RESOURCE;
   // Create a new XHR object.
   teller_request = createRequest();
   // Set a callback function to receive the server response asynchronously.
   var callbackFunction = http_response_async_callback_for_debit_from_account;
   // Send this XHR request now.
   sendHttpRequest(teller_request, callbackFunction, url, postData);
} // End of function debitAmountAction_Async

/*
============================================================
Function: http_response_async_callback_for_debit_from_account

Last Modified: May/16/2007

This function is an Ajax callback event handler to receive 
server response for the "debit from an account" bank
teller action. This function will get called at various
state changes during the HTTP transaction. The logic to
obtain the response data will be put to work only when
the HTTP status is OK i.e. 200 OK.
============================================================
*/
function http_response_async_callback_for_debit_from_account() {
   // Do something only when the HTTP readystate is LOADED.
   if (teller_request.readyState == HTTP_READYSTATE_LOADED) {
      // You have nothing to do until it is 200 OK.
     if (teller_request.status == HTTP_STATUS_OK) {
         // We will get the response data in JSON format as plain text.
         var textResult = teller_request.responseText;   
         // Convert the JSON formatted text into a JSON object.
         // Thanks to Douglas Crockford for this safe JSON parser.
         // This is much safer than using an eval() statement.
         var obj = textResult.parseJSON();
         
         // Check if we have a successful response back from the server.
         if (obj.ResultCode != SUCCESS) {
            alert(obj.ResultMsg);
            return;            
         }
         
         // Beauty of JSON at work here.
         // No extra work required to consume the JSON data.
         // It is readily accessible as a JavaScript object.
         tellerActionResultInfo = obj.AccountInfo;
         // Hide the debit action screen and display the 
         // teller action result screen.
         hideShowAndInsertAsTopElement(currentlyShownElement, 
            tellerActionResultDivElement,
            null, null, null, null, true);
        // Display the debit action results. 
        updateTellerActionResultTextArea();
      } // End of if (teller_request.status == HTTP_STATUS_OK)
   } // End of if (teller_request.readyState == HTTP_READYSTATE_LOADED)   
} // End of function http_response_async_callback_for_debit_from_account.

/*
============================================================
Function: portfolioAction_Async

Last Modified: May/16/2007

This function sends a request to the mid-tier PHP service
to perform the Update portfolio value bank teller action.
It does it in an Ajax style by setting up an asynchronous
callback to receive the server response. Mid-tier service
is accessed using REST (POST) and by using JSON as a 
data interchange format. Read the comments below for
more details. 
============================================================
*/
function portfolioAction_Async() {
   // Get the selected account holder's name from the drop-down list.
   var dropDownListElement = document.getElementById(TELLER_OPTION3_DROP_DOWN_LIST_ID);   
   var selectedOption = dropDownListElement.options[dropDownListElement.selectedIndex];
   var accountHolderName = selectedOption.value;
   
   // Get the host name from the document URL.
   var hostName = getHostnameFromThisURL(document.URL.toString());      
   var url;   

   // Set up the REST service input in JSON format.
   // This input will have just the account holder name.
   var requestJson = {
      "accountHolderName": accountHolderName
   };
   
   // Use Douglas Crockford's JSON string converter to
   // convert the JSON object into JSON formatted text.
   var requestJsonString = requestJson.toJSONString();
   requestJson = "";
   
   // We have to make a REST call.
   // Make post data with different key-value pairs.
   // On the PHP server side, these individual key-value
   // pairs will be available as part of the superglobal
   // $_REQUEST associative array.
   var postData = BANK_TELLER_COMMAND_KEY + "=" + escape(GET_STOCK_PORTFOLIO_VALUE) +
      "&" + BANK_TELLER_POST_DATA_KEY + "=" + escape(requestJsonString);
   // Reset the JSON string since we have no further need for it.
   requestJsonString = "";
   // Form the fully qualified URL now.
   url = HTTP_PART_IN_URL + hostName + BANK_ACTIONS_REST_WEBSERVICE_URL_RESOURCE;
   // Create a new XHR object.
   teller_request = createRequest();
   // Set a callback function to receive the server response asynchronously.
   var callbackFunction = http_response_async_callback_for_get_stock_portfolio_value;
   // Send this XHR request now.
   sendHttpRequest(teller_request, callbackFunction, url, postData);   
} // End of function portfolioAction_Async.

/*
============================================================
Function: http_response_async_callback_for_get_stock_portfolio_value

Last Modified: May/16/2007

This function is an Ajax callback event handler to receive 
server response for the "update portfolio value" bank
teller action. This function will get called at various
state changes during the HTTP transaction. The logic to
obtain the response data will be put to work only when
the HTTP status is OK i.e. 200 OK.
============================================================
*/
function http_response_async_callback_for_get_stock_portfolio_value() {
   // Do something only when the HTTP readystate is LOADED.
   if (teller_request.readyState == HTTP_READYSTATE_LOADED) {
      // You have nothing to do until it is 200 OK.
     if (teller_request.status == HTTP_STATUS_OK) {
         // We will get the response data in JSON format as plain text.
         var textResult = teller_request.responseText;   
         // Convert the JSON formatted text into a JSON object.
         // Thanks to Douglas Crockford for this safe JSON parser.
         // This is much safer than using an eval() statement.
         var obj = textResult.parseJSON();
         
         // Check if we have a successful response back from the server.
         if (obj.ResultCode != SUCCESS) {
            alert(obj.ResultMsg);
            return;            
         }
         
         // Beauty of JSON at work here.
         // No extra work required to consume the JSON data.
         // It is readily accessible as a JavaScript object.
         tellerActionResultInfo = obj.AccountInfo;
         // Hide the debit action screen and display the 
         // teller action result screen.
         hideShowAndInsertAsTopElement(currentlyShownElement, 
            tellerActionResultDivElement,
            null, null, null, null, true);
         // Display the updated stock portfolio action results.
         updateTellerActionResultTextArea();
      } // End of if (teller_request.status == HTTP_STATUS_OK)
   } // End of if (teller_request.readyState == HTTP_READYSTATE_LOADED)   
} // End of function http_response_async_callback_for_get_stock_portfolio_value.
		


Return to article