/**
 * File:    eskadb.js
 * Purpose: Contains scripts required by the online database manager
 *******************************************************************************
 * History:
 *  24/04/2010  SP, Created
 */
// The number of items to show on view and in a page in the list view
var ITEMS_ON_VIEW = 10, ITEMS_PER_PAGE = 10;

// Notes related data...
var currentCustomerID = -1, lastCustomerID = -1, aryNotes = null;
var customerDoc = null, notesElement = null;
var notesDialog = null, addNoteDlg = null;
var strNotesRoot = "notes";
var strLastRequest = "";

var intCMD = 0, lngTotalRecs = 0, lngNextRecord = 0;
var strServerAddr = "http://www.eska.co.uk/"; // Live
//var strServerAddr = "http://localhost/";        // Development            
//var strServerAddr = "http://192.168.0.3/";
var lngRecords = 0, strMsgs = "";
var blnSearchMode = false;
var aryMoreOptions = null;

// For report generation
var lngReportElapsed, lngReportTimeout, lngReportTmrID = 0;
var winReport, cbReport;

/**
 * Method:
 *  getSelectedText
 *  
 * Parameters:
 *  selection, the selecton box
 *  index, the index of the item to get
 *  
 * Returns:
 *  The text at the specified index or null if invalid
 */           
function getSelectedText( selection, index ) {
  try{
    if ( selection && selection.options ) {
      if ( index >= 0 && index < selection.options.length ) {
        if ( selection.options[index].text ) {
          return selection.options[index].text;
        } 
      }
    }
  } catch( e ) {
    reportError( "getSelectedText", e );
  }
  return null;      
};
/**
 * Method:
 *  getElementFromEvent
 *  
 * Parameters:
 *  ev, the event object
 *  
 * Returns:
 *  The element associated with the event
 */
function getElementFromEvent( ev ) {
  var el = null;
  
  if ( !ev && window.attachEvent ) {
    ev = window;
  }
  if ( ev ) {
    if ( ev.srcElement ) {  
      el = ev.srcElement;    
    } else if ( ev.target ) {
      el = ev.target;
    }
  }
  return el;
};       
/**
 * Method:
 *  bindEvent
 *  
 * Parameters:
 *  el, the element to bind the event to
 *  strEvent, the event without the 'on' prefix
 *  cb, the callback
 *  
 * Returns:
 *  none
 */           
function bindEvent( el, strEvent, cb ) {
  if ( el.addEventListener ) {  
    el.addEventListener( strEvent, cb, false );
  } else if ( el.attachEvent ) { 
    el.attachEvent( "on" + strEvent, cb );
  }
};

/**
 * Method:
 *  checkReportTimeout
 *  
 * Parameters:
 *  win, the report window
 *  cbPopulate, callback to populate report 
 *  
 * Returns:
 *  none
 */        
function checkReportTimeout( win, cbPopulate ) {
  try{
    if ( lngReportTmrID == 0 ) {
      winReport = win;
      cbReport = cbPopulate;  
      lngReportElapsed  = 0;
      lngReportTimeout  = 10;
    } else {
      clearTimeout( lngReportTmrID );
    }
    if ( !(winReport && winReport.document && cbReport) ) {
      return;
    }
// Make sure the timer ID is cleared    
    lngReportTmrID = 0;
// Attempt to get the content element    
    var content = getElementByID( "content", winReport.document ); 
    
    if ( content ) {
// Ok, we can stop checking and call the call-back to populate
      cbReport( content );
      return;  
    }    
    if ( lngReportElapsed++ < lngReportTimeout ) {
      lngReportTmrID = setTimeout( "checkReportTimeout()", 500 );
    }
  } catch( e ) {
    reportError( "checkReportTimeout", e );
  }      
}
/**
 * Method:
 *  closeReport
 *  
 * Parameters:
 *  none
 *  
 * Returns:
 *  none
 */        
function closeReport() {
  try{
    if ( winReport && winReport.close ) {
      winReport.close();
      winReport = null;
      lngReportTmrID = 0;
    }  
  } catch( e ) {
    reportError( "closeReport", e );
  }      
}
/**
 * Method:
 *  buildURI
 *  
 * Parameters:
 *  strParams, the parameter list so far
 *  strKey, the text key to use for this parameter
 *  strParam, the data to assign to the parameter
 *  
 * Returns:
 *  The build up URI parameter string
 */          
function buildURI( strParams, strKey, strParam ) {
  try{
    if ( strParam == undefined || strParam == null || strParam.length == 0 ) {
      return strParams;
    }   
    if ( strParams.length > 0 ) {
      strParams += "&";
    }
    strParams += strKey + "=" + encodeURI( strParam );
  } catch( e ) {
    reportError( "buildURI", e );
  }    
  return strParams;
};
/**
 * Method:      pageData
 * Parameters:  intTabIdx, the index of the tab the page is associated with
 *              strRoot, the name of the root node to expect in the server
 *              response
 *              pHandler, the handler to call when a response is recieved
 *              intSearchCmd, the command use to issue search request   
 *              blnAllowNew, true or false 
 *              blnAllowSearch, true or false
 *              blnAllowEdit, true or false 
 *              blnAllowSave, true or false
 *              blnAllowNav, true of false
 *              blnAllowPrint, true or false
 *              blnAllowDelete, true or false  
 *              cbSelect, custom select callback                 
 *              cbCancel, Custom cancel callback    
  * Returns:     padeData object               
 */    
function pageData( intTabIdx,     strRoot, 
                   pHandler,      intSearchCmd,
                   blnAllowNew,   blnAllowSearch, 
                   blnAllowEdit,  blnAllowSave,
                   blnAllowNav,   blnAllowPrint,
                   blnAllowDelete,  
                   cbSelect,      cbCancel ) {  
                    
  try{
    if ( !parent ) {
      throw( "This document can only be a child of the eskadb.php" );
    }
    if ( !top.aryDocs ) {
      throw( "\nTab: " + intTabIdx +
             "\nRoot: " + strRoot  + 
             "\nInvalid parent document!" );
    }        
// Set-up members
    this.intTabIdx      = intTabIdx;  
    this.strRoot        = strRoot;
    this.document       = document;
    this.strTitle       = document.title;
    this.searchCmd      = intSearchCmd;
    this.blnAllowNew    = blnAllowNew;
    this.blnAllowSearch = blnAllowSearch; 
    this.blnAllowEdit   = blnAllowEdit;
    this.blnAllowSave   = blnAllowSave;
    this.blnAllowNav    = blnAllowNav;
    this.blnAllowPrint  = blnAllowPrint;
    this.blnAllowDelete = blnAllowDelete;
    this.btnNew         = null;
    this.btnSearch      = null;
    this.btnEdit        = null;
    this.btnCopy        = null;
    this.btnCancel      = null;
    this.btnSave        = null;
    this.btnPrint       = null;
    this.btnDelete      = null;
    this.btnUpdateWS    = null;    
    this.btnFirst       = null; 
    this.btnPrev        = null;
    this.btnNext        = null;
    this.btnLast        = null;
    this.arySavedState  = null;          
    this.strLastRequest = "";
    this.cbNew          = null;
    this.cbEdit         = null;
    this.cbSave         = null;
        
    if ( cbSelect ) {
      this.cbSelect     = cbSelect;
    } else {
      this.cbSelect     = null;
    }
    if ( cbCancel ) {
      this.cbCancel     = cbCancel;
    } else {
      this.cbCancel     = null;
    }
// Install AJAX client and handler
    this.objAJAX = new AJAX_CLIENT( "docInfo.objAJAX" ); 
    this.objAJAX.AddHandler( strRoot, pHandler );
// Add this object to the parents collection of documents
    parent.intSelectedTab = top.aryDocs.length;
    parent.intLastSelectedTab = -1; 
    top.aryDocs[top.aryDocs.length] = this;    
// Check for the required elements
    var strMsg = "";
        
    if ( this.blnAllowNew == true ) {    
      this.btnNew = getElementByID( "btnNew", this.document );
      
      if ( !this.btnNew ) {
        strMsg += "'btnNew' was expected in document and not found!\n";
      } else {
        this.btnNew.disabled = false;
      }
    }
    if ( this.blnAllowSearch == true ) {
      this.btnSearch = getElementByID( "btnSearch", this.document );
      
      if ( !this.btnSearch ) {
        strMsg += "'btnSearch' was expected in document and not found!\n";
      }
    }
    if ( this.blnAllowEdit == true ) {
      this.btnEdit = getElementByID( "btnEdit", this.document );

      if ( !this.btnEdit ) {
        strMsg += "'btnEdit' was expected in document and not found!\n";
      }
      this.btnCancel = getElementByID( "btnCancel", this.document ); 

      if ( !this.btnCancel ) {
        strMsg += "'btnCancel' was expected in document and not found!\n";
      }
      this.btnCopy = getElementByID( "btnCopy", this.document );

      if ( !this.btnCopy ) {
        strMsg += "'btnCopy' was expected in document and not found!\n";
      }
    }
    if ( this.blnAllowSave == true ) {
      this.btnSave = getElementByID( "btnSave", this.document );

      if ( !this.btnSave ) {
        strMsg += "'btnSave' was expected in document and not found!\n";
      }
    }
    if ( this.blnAllowPrint == true ) {
      this.btnPrint = getElementByID( "btnPrint", this.document );

      if ( !this.btnPrint ) {
        strMsg += "'btnPrint' was expected in document and not found!\n";
      }
      this.btnPrint.disabled = false;
    }
    if ( this.blnAllowDelete == true ) {
      this.btnDelete = getElementByID( "btnDelete", this.document );

      if ( !this.btnDelete ) {
        strMsg += "'btnDelete' was expected in document and not found!\n";
      }
    }
    if ( strMsg.length ) {
      throw( "Missing elements from document!\n" + strMsg );
    }
// Set-default button states
    if ( this.btnEdit ) {
      this.btnEdit.disabled   = true;
      this.btnCancel.disabled = true;
      this.btnCopy.disabled   = true;
      
      if ( this.btnDelete ) {
        this.btnDelete.disabled = true;
      }
    }
    if ( this.btnSave ) {
      this.btnSave.disabled = true;
    }
// Enable the update web-site button
    this.btnUpdateWS = getElementByID( "btnUpdateSite", this.document );
    
    if ( this.btnUpdateWS ) {    
      this.btnUpdateWS.disabled = false;
    }
// Initialise navigation buttons    
    this.setupNavigationButtons();        
  } catch( e ) {
    reportError( "pageData", e );
  }
}
/**
 * Method:      registerNewCallback
 * Parameters:  cb, the routine to register   
 * Returns:     none
 */
pageData.prototype.registerNewCallback = function( cb ) {
  try{
    this.cbNew = cb;
  } catch( e ) {
    reportError( "registerNewCallback", e );
  }
}
/**
 * Method:      registerEditCallback
 * Parameters:  cb, the routine to register   
 * Returns:     none
 */
pageData.prototype.registerEditCallback = function( cb ) {
  try{
    this.cbEdit = cb;
  } catch( e ) {
    reportError( "registerEditCallback", e );
  }
}
/**
 * Method:      registerSaveCallback
 * Parameters:  cb, the routine to register   
 * Returns:     none
 */
pageData.prototype.registerSaveCallback = function( cb ) {
  try{
    this.cbSave = cb;
  } catch( e ) {
    reportError( "registerSaveCallback", e );
  }
}
/**
 * Method:      setupNavigationButtons
 * Parameters:  none  
 * Returns:     none
 */
pageData.prototype.setupNavigationButtons = function() {       
  try{
    this.dbNav = getElementByID( "dbNav" );  
    
    if ( this.blnAllowNav != true ) {
      if ( this.dbNav ) {
        this.dbNav.style.display = "none";
      }
      return;    
    }
    if ( this.dbNav ) {
      this.dbNav.style.display = "block";
    }
// Get the navigation buttons
    this.btnFirst = getElementByID( "btnFirst" ); 
    this.btnPrev  = getElementByID( "btnPrev" );
    this.btnNext  = getElementByID( "btnNext" );
    this.btnLast  = getElementByID( "btnLast" );    
    this.btnFirst.disabled = false;
    this.btnPrev.disabled = false;
    this.btnNext.disabled = false;
    this.btnLast.disabled = false;
/*    
alert( "lngNextRecord: " + lngNextRecord + "\n" +
       "lngTotalRecs: " + lngTotalRecs );
*/           
    if ( lngNextRecord <= 1 && this.btnPrev ) {
      this.btnFirst.disabled = true;
      this.btnPrev.disabled = true;
    }
    if ( lngNextRecord >= lngTotalRecs && this.btnNext ) {
      this.btnNext.disabled = true;
      this.btnLast.disabled = true;  
    }
    if ( this.btnSearch ) {    
      if ( lngTotalRecs == 0 ) {
        this.btnSearch.disabled = true;
      } else {
        this.btnSearch.disabled = false;
      }
    }
  } catch( e ) {
    reportError( "setupNavigationButtons", e );
  }         
}
/**
 * Method:      contactServer
 * Parameters:  strParams, the parameters to send to the server
 *              blnShowBusy, true or false to show busy form 
 * Returns:     The parameters string sent to the server
 */   
pageData.prototype.contactServer = function( strParams, blnShowBusy ) {
  try{
    var intCmd = strParams.indexOf("cmd="); 
    
    if ( intCmd >= 0 ) {
      intCmd = parseInt( strParams.substring( intCmd + 4 ), 10 );      
    }
    if ( !intCmd ) {
      throw( "No command specified!" );
    }
    if ( (intCmd & EDB_IMPORT) != EDB_IMPORT ) {      
      if ( (intCmd & EDB_CUSTOMERS) == EDB_CUSTOMERS ) {
        var members = getElementByID( "members" );

        if ( members != null ) {
          strParams += "&sa=" + members.selectedIndex; 
        }
      }
      var selectOrderBy    = getElementByID( "orderBy" );
      var selectOrderOrder = getElementByID( "orderOrder" );
      
      if ( selectOrderBy != null && selectOrderBy.length > 0 ) {
        var i = selectOrderBy.selectedIndex;                                     
        strParams += "&ob=" + selectOrderBy.options[i].value;
      }
      if ( selectOrderOrder != null && selectOrderOrder.length > 0 ) {
        var i = selectOrderOrder.selectedIndex;                                     
        strParams += "&oo=" + selectOrderOrder.options[i].value;
      }
      strParams += "&r=" + Math.max( lngNextRecord, 1 );
    }    
    if ( (intCmd & EDB_GET) == EDB_GET || (intCmd & EDB_FIND) == EDB_FIND ) {
      var aryParams = strParams.split( "&" );
      this.strLastRequest = "";
      
      for( var i=0; i<aryParams.length; i++ ) {
        var aryParam = aryParams[i].split( "=" );
        
        if ( aryParam[0] == "sa" ||
             aryParam[0] == "ob" ||
             aryParam[0] == "oo" ||  
             aryParam[0] == "io" ) {
          continue;
        }
        if ( this.strLastRequest.length > 0 ) {
          this.strLastRequest += "&";
        }                             
        this.strLastRequest += aryParam[0] + "=" + encodeURI( aryParam[1] );
      }
    }        
// Disable all form controls
    this.disableEditableFields( true );    
// Hide any existing results      
    showSearchResults( false );
        
    if ( blnShowBusy ) {                   
      showBusy();
    }
//alert( strParams );
    this.objAJAX.POST( strServerAddr + 'db/lib/server.php', strParams );    
    return strParams;
  } catch( e ) {
    reportError( "contactServer", e );
  }
  return "";         
}
/**
 * Method:      loadRecord
 * Parameters:  strRoot, the name of the root XML node
 *              intCmd, the command to send
 *              intDelta, +1 for next record, -1 for previous record or 0 for
 *                        current record
 *              lngID, optional record ID
 *              strExtras, optional any extra parameters to send  
 * Returns:     none
 */
 pageData.prototype.loadRecord = function( intCmd, intDelta, 
                                           lngID, strExtras ) {       
  try{
    if ( this.btnSave && this.btnSave.disabled == false ) {
      if ( confirm( "Are you sure?\n" +
                    "Any unsaved changes will be lost!" ) != true ) {
        return;
      }
    }    
    if ( intDelta == -999 ) {
      lngNextRecord = 1; 
    } else if ( intDelta == 9999999 ) {
      lngNextRecord = lngTotalRecs;
    } else {
      lngNextRecord += intDelta;
    }
    docInfo.blankForm();    
    var strParams = 'root=' + this.strRoot +
                    '&cmd=' + intCmd;

    if ( lngID ) {
      strParams += "&id=" + lngID;
    }
    if ( strExtras && strExtras.length > 0 ) {
      strParams += "&" + strExtras;      
    }
//alert( strParams );    
    this.contactServer( strParams, true );
  } catch( e ) {
    reportError( 'loadRecord', e );
  }                
}
/**
 * Method:      getAllFormElements
 * Parameters:  blnElements, true to build array of element's, 
 *                           false to build array of id's
 *              blnSaveState, save current state of elements 
 * Returns:     none
 * Purpose:     Builds an array of all input and selected elements in the 
 *              document.
 */    
pageData.prototype.getAllFormElements = function( blnElements, blnSaveState ) {
  try{   
    var aryFinal = new Array();
// Find all input, select and textarea elements
    var aryTagTypes = new Array( "input", "select", "textarea" );

    for( var t=0; t<aryTagTypes.length; t++ ) {    
      var aryElements = this.document.getElementsByTagName( aryTagTypes[t] );
    
      if ( !aryElements || !aryElements.length ) {
        continue;
      }
      for( var i=0; i<aryElements.length; i++ ) {
        var el = aryElements[i];
      
        if ( !el.id                || 
           ( el.type == "button" && 
              el.value != "..." && el.value != "Add..." ) || 
             el.id == "members"    || 
             el.id == "orderBy"    || 
             el.id == "orderOrder" ||
             el.id == "sessions"   ||
             el.id == "howMuch" ) {
// Skip elements without an id      
          continue;
        }
        if ( blnElements == false ) {
          aryFinal[aryFinal.length] = el.id;
        } else {
          aryFinal[aryFinal.length] = el;
        }
      }
    }
    if ( blnElements == true && blnSaveState == true ) {
      var arySavedState = new Array();
      for( var i=0; i<aryFinal.length; i++ ) {
        var objTmp = new Object();
        objTmp.id       = aryFinal[i].id;
        objTmp.nodeName = aryFinal[i].nodeName;
        objTmp.type     = aryFinal[i].type;
        
        if ( objTmp.nodeName == "INPUT" ) {
          if ( objTmp.type == "button" ) {
// Don't bother saving the state of buttons          
            continue;
          } else if ( objTmp.type == "checkbox" ) {
            objTmp.value = (aryFinal[i].checked) ? 1 : 0;
          } else {
            objTmp.value = aryFinal[i].value;
          }
        } else if ( objTmp.nodeName == "SELECT" ) {
          objTmp.value = aryFinal[i].selectedIndex;
        } else if ( objTmp.nodeName == "TEXTAREA" ) {
          objTmp.value = aryFinal[i].value;
        }
        arySavedState[arySavedState.length] = objTmp;  
      }
      this.arySavedState = arySavedState;
    }
    return aryFinal; 
  } catch( e ) {
    reportError( "getAllFormElements", e );
  }
  return null;         
}
/**
 * Method:      blankForm
 * Parameters:  none
 * Returns:     none
 */
pageData.prototype.blankForm = function() {
  try{
// Make sure the search mode is reset  
    blnSearchMode = false;    
// This collection will hold all the input elements    
    var aryELs = this.getAllFormElements( true );
     
    if ( aryELs ) {
      for( var i=0; i<aryELs.length; i++ ) {
        var el = aryELs[i];
        
        if ( !el || !el.name || el.name.length == 0 
/*              || el.name.indexOf( "." ) == -1 */ ) {
// Skip elements that do not have a name or do not feature in the database or
// Special case, the trainingDate              
          continue;
        }
// Clear the option
        if ( el.nodeName == "INPUT" ) {
          if ( el.type == "text" ||  el.type == "hidden" ) {
            el.value = "";
          } else if ( el.type == "checkbox" || el.type == "radio" ) {
            el.checked = false;          
          }
        } else if ( el.nodeName == "SELECT" ) {
          el.selectedIndex = 0;

          if ( el.onchange ) {
            el.onchange( el );
          }
        } else if ( el.nodeName == "TEXTAREA" ) {
          el.value = "";
        }
      }
    }
    removeAllChildren( getElementByID( "gradeResultsTbody" ) );
    removeAllChildren( getElementByID( "attendanceTbody" ) );
    removeAllChildren( getElementByID( "trainingResultsTbody" ) );
    removeAllChildren( getElementByID( "previousPaymentsTbody" ) );
    removeAllChildren( getElementByID( "membersTbody" ) );    
  } catch( e ) {
    reportError( "blankForm", e );
  }         
}
/**
 * Method:      disableEditableFields
 * Parameters:  blnDisable, true or false
 *              strID, the text string of the ID that determines if editing is
 *              permitted   
 * Returns:     none 
 */    
pageData.prototype.disableEditableFields = function( blnDisable, strID ) {
  try{
// Get the actual form elements    
    var aryELs = this.getAllFormElements( true );

    if ( !aryELs ) {
      throw( "Cannot extract form elements" );
    }
    for( var i=0; i<aryELs.length; i++ ) {
      aryELs[i].disabled = blnDisable;
    }
    if ( blnDisable == true ) {
      if ( this.btnEdit ) {
        var blnEditable = true;
         
        if ( strID ) {       
          var editable = getElementByID( strID );
          blnEditable = ( editable ) ? editable.value.length > 0 : false;
        }        
        if ( blnEditable ) {        
// Enable the edit button
          this.btnEdit.disabled = false;
          this.btnCopy.disabled = false;
          
          if ( this.btnDelete ) {
            this.btnDelete.disabled = false;
          }          
        }
      }
      if ( this.btnSave ) {
// Disable the save button
        this.btnSave.disabled = true;
      }
      if ( this.btnCancel ) {
// Disable the cancel button      
        this.btnCancel.disabled = true;
      }
    } else {
      if ( this.btnEdit ) {        
// Disable the edit button
        this.btnEdit.disabled = true;
      }
      if ( this.btnCopy ) {
// Disable the copy button      
        this.btnCopy.disabled = true;
      }
      if ( this.btnDelete ) {
        this.btnDelete.disabled = true;
      }                
/*      
      if ( this.btnCancel ) {
// Enabled the cancel button      
        this.btnCancel.disabled = false;
      }
*/      
      if ( this.btnSave ) {
// Disable the save button
        this.btnSave.disabled = false;
      }    
    }
  } catch( e ) {
    reportError( "disableEditableFields", e );
  }             
}
/**
 * Method:      addToNavBar
 * Parameters:  el, the element to add to the nav bar
 * Returns:     none 
 */    
pageData.prototype.addToNavBar = function( el ) {
  try{
    if ( this.dbNav ) {
      this.dbNav.appendChild( el );
    }
  } catch( e ) {
    reportError( "addToNavBar", e );
  }             
}
// Class members
pageData.prototype.intTabIdx;
pageData.prototype.strRoot;
pageData.prototype.strTitle;
pageData.prototype.objAJAX;
pageData.prototype.document;
pageData.prototype.searchCmd;
pageData.prototype.blnAllowNew;
pageData.prototype.blnAllowSearch, 
pageData.prototype.blnAllowEdit;
pageData.prototype.blnAllowSave;
pageData.prototype.blnAllowNav;
pageData.prototype.blnAllowPrint;
pageData.prototype.blnAllowDelete;
pageData.prototype.dbNav;  
pageData.prototype.btnNew;
pageData.prototype.btnSearch;
pageData.prototype.btnEdit;
pageData.prototype.btnCopy;
pageData.prototype.btnCancel;
pageData.prototype.btnSave;
pageData.prototype.btnPrint;
pageData.prototype.btnDelete;
pageData.prototype.btnUpdateWS;    
pageData.prototype.btnFirst; 
pageData.prototype.btnPrev;
pageData.prototype.btnNext;
pageData.prototype.btnLast;    
pageData.prototype.strLastRequest;
pageData.prototype.arySavedState;
pageData.prototype.cbNew;
pageData.prototype.cbEdit;
pageData.prototype.cbSave;
pageData.prototype.cbCancel;

/**
 * Method:      addOptionToSelect
 * Parameters:  el, the SELECT element to add the option to
 *              text, the text to associate with the option 
 *              value, the value to associate with the option, (optional)  
 * Returns:     none
 */   
function addOptionToSelect( el, text, value ) {
  try{
    if ( el ) {
// Make sure options does not already contain this item
      for( var i=0; i<el.length; i++ ) {
        var tmp = el.options[i];
       
        if ( tmp.text == text ) {
          return;
        }        
      }
      el.options[el.length] = new Option(text, value);
    }
  } catch( e ) {
    reportError( "addOptionToSelect", e );
  }         
}        
/**
 * Method:      removeAllChildren
 * Parameters:  el, the element to strip of child nodes
 * Returns:     none
 */   
function removeAllChildren( el ) {
  if ( el && el.firstChild ) {
    while( el.firstChild ) {
      el.removeChild( el.firstChild );
    }
  }
}
/**
 * Method:      xmlToDOM
 * Parameters:  objAJAX, AJAX object passed from handler
 *              strContainerName, the name of the encapsulating nodes
 *              strID, the name of the container to hold the results if
 *                we are decoding multiple records ONLY.
 *              blnCheckboxes, true to include check boxes on each row
 *              strCallback, optional callback to register when checkbox clicked
 *              cbHighlight, cbLeave, cbSelect, optional row callbacks 
 * Returns:     none
 */    
function xmlToDOM( objAJAX, strContainerName, strID, 
                   blnCheckboxes, strCallback,
                   cbHighlight, cbLeave, cbSelect ) {
  try{
    var el, HTML = "";

    if ( strID ) {
      el = getElementByID( strID );
      
      if ( !el ) {
        throw( "Cannot find element '" + strID + "' in document!" );
      }
      removeAllChildren( el );
    }
    var containerNode = objAJAX.FindXMLnode( strContainerName );

    if ( !containerNode || !containerNode.length ) {
// Cannot do anything without customer node, this occurs if there are no
// records in the database      
      return;
    }
    var trNode = null, tdNode = null;
    
    for ( var c=0; c<containerNode.length; c++ ) {            
      if ( strID ) {
// Create row element
        var strRow = "r" + (c + 1) + strID;
        trNode = document.createElement( "TR" );
        
        if ( !trNode ) {
          throw( "Cannot create TR element" );
        }
// Install optional callbacks        
        if ( cbHighlight ) {
          bindEvent( trNode, "mouseover", cbHighlight );
        }
        if ( cbLeave ) {
          bindEvent( trNode, "mouseout", cbLeave );
        }
        if ( cbSelect ) {
          bindEvent( trNode, "click", cbSelect );
        }
        el.appendChild( trNode );
        
        if ( blnCheckboxes == true ) {
          tdNode = document.createElement( "TD" );
          
          if ( !tdNode ) {
            throw( "Cannot create TD element" );
          }
          trNode.appendChild( tdNode );
          tdNode.style.textAlign = "center";
          var strHTML = "<input type=\"checkbox\" " +
                               "name=\"" + strRow + "\" " +
                               "id=\"" + strRow + "\" ";                    
           
          if ( strCallback ) {
            strHTML += "onclick=\"" + strCallback + "(this);\" ";
          }
          strHTML += " />";
          tdNode.innerHTML = strHTML;
        }                                       
      }
// Extract each child node
      for ( var i=0; i<containerNode[c].childNodes.length; i++ ) {
        var childNode = containerNode[c].childNodes[i];
        var tmpNode = objAJAX.FindXMLnode( childNode.nodeName, containerNode[c] );        
        tmpNode = objAJAX.GetXMLvalue( tmpNode );
              
        if ( trNode ) {
          tdNode = document.createElement( "TD" );
        
          if ( !tdNode ) {
            throw( "Cannot create TD element" );
          }
          tdNode.style.textAlign = "center";
          
          if ( tmpNode ) {          
            tdNode.innerHTML = tmpNode;
          }
          trNode.appendChild( tdNode );
          continue;  
        }        
        if ( !tmpNode ) {
          continue;
        }
// Find the corresponding element in the DOM
        el = getElementByID( childNode.nodeName );

        if ( el ) {
          if ( el.nodeName == "INPUT" ) {
            if ( el.type == "text" || el.type == "hidden" ) {
              el.value = tmpNode;
            } else if ( el.type == "checkbox" ) {
              el.checked = (tmpNode > 0) ? true : false;          
            } else if ( el.type == "radio" && el.value == tmpNode ) {
              el.checked = true;
            }
          } else if ( el.nodeName == "SELECT" ) {
            el.selectedIndex = 0;
          
            for( var j=0; j<el.options.length; j++ ) {
              if ( el.options[j].value == tmpNode ||
                   el.options[j].text == tmpNode ) {
                el.selectedIndex = j;                   
                break;
              }
            }
          } else if ( el.nodeName == "TEXTAREA" ) {
            el.value = tmpNode;
          }
        }          
      }
    }
  } catch( e ) {
    reportError( "xmlToDOM", e );
  }         
}
/**
 * Method:      xmlSearchResultsToDOM
 * Parameters:  objAJAX, AJAX object passed from handler
 *              objHandler, the handler object  
 *              strContainerName, the name of the encapsulating nodes
 *              aryTranslation, an array of translater keys and values, the
 *                array is keyed using the XML node name and the value is the
 *                translation to use
 * Returns:     none
 */    
function xmlSearchResultsToDOM( objAJAX, objHandler, 
                                strContainerName, aryTranslation ) {
  try{
    var containerNode = objAJAX.FindXMLnode( strContainerName );
     
    if ( !containerNode || !containerNode.length ) {
      alert( "No matches found" );
      return;
    }
    var HTML =  "<table border=\"0\">" +
                  "<thead>" +
                    "<tr>";      
    for( var j=0; j<containerNode[0].childNodes.length; j++ ) {
      var childNode = containerNode[0].childNodes[j];
      var strHdr = "";
      if ( aryTranslation ) {
        if ( aryTranslation[childNode.nodeName] ) {
          strHdr = aryTranslation[childNode.nodeName];
        } else {
          continue;
        }
      } else {
        strHdr = childNode.nodeName;
      }
      HTML += "<th>&nbsp;" + strHdr + "&nbsp;</th>";
    }      
    HTML +=   "</tr>" +
            "</thead>" +
            "<tbody>";
    for ( var i=0; i<containerNode.length; i++ ) {
      var childNodes = containerNode[i].childNodes;
      HTML += "<tr onmouseover=\"highlight(this);\" " +
                  "onmouseout=\"normal(this);\" " + 
                  "onclick=\"selectNode(this, '" + objHandler.strNode + "');\" >";
       
      for( var j=0; j<childNodes.length; j++ ) {
        var childNode = childNodes[j];

        if ( aryTranslation && !aryTranslation[childNode.nodeName] ) {
//alert( "Missing translation: " + childNode.nodeName );        
          continue;
        }        
        var tmpNode = objAJAX.FindXMLnode( childNode.nodeName, containerNode[i] );
        tmpNode = objAJAX.GetXMLvalue( tmpNode );
        var strData;

        if ( tmpNode ) {
          strData = tmpNode;
        } else {
          strData = "";
        }
        HTML += "<td id=\"" + childNode.nodeName + "\" " +
                    "style=\"text-align: center;\">";
                     
        var el = getElementByID( childNode.nodeName, docInfo );
        
        if ( el ) {
          var strData;
        
          if ( el.nodeName == "INPUT" && el.type == "checkbox" ) {
            if ( strData == "1" ) {
              strData = "checked";
            } else {
              strData = "";
            }
            strData = "<input type=\"checkbox\" " + strData + " disabled />";
          }
        }
        HTML +=   strData + 
                "</td>";
      }
      HTML += "</tr>";
    }
    HTML +=   "</tbody>" +
            "</table>";
    showSearchResults( true, HTML );  
  } catch( e ) {
    reportError( "xmlSearchResultsToDOM", e );
  }         
}
/**
 * Method:      getRecordRefs
 * Parameters:  objAJAX the AJAX object passed to the Handler
 *              docInfo, the document info object 
 *              strID, the text string of the ID that determines if editing is
 *              permitted   
 * Returns:     none
 */    
function getRecordRefs( objAJAX, docInfo, strID ) {
  try{    
    lngNextRecord = recordNo = 0;
// Look for the record nodes in the response         
    var totalRecs = objAJAX.FindXMLnode( "totalRecs" );
    totalRecs = objAJAX.GetXMLvalue( totalRecs );
    
    if ( totalRecs ) {
      lngTotalRecs = parseInt( totalRecs, 10 );
      totalRecs = getElementByID( "totalRecs" );
      
      if ( totalRecs ) {
        totalRecs.innerHTML = lngTotalRecs; 
      }
    }
    var recordNo = objAJAX.FindXMLnode( "recNo" );
    recordNo = objAJAX.GetXMLvalue( recordNo );
  
    if ( recordNo ) {
      lngNextRecord = parseInt( recordNo, 10 );
    }  
    recordNo = getElementByID( "recordNo" );
    
    if ( recordNo ) {
      recordNo.innerHTML = lngNextRecord; 
    }
    var insertedID = objAJAX.FindXMLnode( "insertedID" );
    insertedID = objAJAX.GetXMLvalue( insertedID );
    
    if ( insertedID ) {
      var id_el = getElementByID( strID );
      
      if ( id_el ) {
        id_el.value = insertedID;
      }  
    }
// Disable all form controls
    docInfo.disableEditableFields( true, strID );
// Setup the navigation buttons    
    docInfo.setupNavigationButtons();
  } catch( e ) {
    reportError( "getRecordRefs", e );
  }         
}    
/**
 * Method:      getElementByID
 * Parameters:  strID, the id of the element to find
 *              objLevel, optional, could be parent or top 
 * Returns:     The element from the DOM corresponding to the ID
 */    
function getElementByID( strID, objLevel )
{
  try{
    var doc = null;
    
    if ( objLevel == undefined || objLevel == null ) {
      doc = document;
    } else {
      if ( objLevel.document ) {
        doc = objLevel.document;
      } else {
        doc = objLevel;
      }      
    }
    if ( doc == null ) {
      throw( "Cannot obtain document reference!" );
    }
    if ( doc.getElementById ) {
      return doc.getElementById( strID );
    } else if ( doc.all ) {
      return doc.all[ strID ];     
    }
  } catch( e ) {
    reportError( "getElementByID", e );
  }    
  return null;
}
/**
 * Method:      hideBusy
 * Parameters:  none
 * Returns:     none
 */   
function hideBusy() {
  try{
    var divBusy;
    
    if ( parent ) {
      divBusy = getElementByID( "busy", parent.document );
    } else {
      divBusy = getElementByID( "busy" );
    }
    if ( divBusy ) {
      divBusy.style.display = "none";
    }
  } catch( e ) {
    reportError( "hideBusy", e );
  }    
}
/**
 * Method:      showBusy
 * Parameters:  none
 * Returns:     none
 */   
function showBusy() {
  try{
    var divBusy;
    if ( parent ) {
      divBusy = getElementByID( "busy", parent.document );
    } else {
      divBusy = getElementByID( "busy" );
    }
    if ( divBusy ) {
      divBusy.style.display = "block";
    }    
  } catch( e ) {
    reportError( "showBusy", e );
  }    
}
/**
 * Method:      leadingZero
 * Parameters:  intNumber
 * Returns:     string with leading 0 if required
 */   
function leadingZero( intNumber ) {
  try{
    if ( intNumber < 10 ) {
      return "0" + intNumber;
    }
    return intNumber;
  } catch( e ) {
    reportError( "leadingZero", e );
  }             
}
/**
 * Method:      updateElapsedTime
 * Parameters:  lngMS, the elapsed time in milliseconds
 * Returns:     none
 */   
function updateElapsedTime( lngMS ) {
  try{
    var intHours = 0, intMins = 0, intSecs = 0;
    
    if ( lngMS >= 3600 ) {
      intHours = Math.round( (lngMS / 3600), 0 );
      lngMS -= intHours * 3600;
    }
    if ( lngMS >= 60 ) {
      intMins = Math.round( (lngMS / 60), 0 );
      lngMS -= intMins * 60;
    }
    if ( lngMS > 0 ) {
      intSecs = Math.round( lngMS, 0 );
    }
    var divElapsedTime;
    if ( parent ) {
      divElapsedTime = getElementByID( "elapsedTime", parent.document );
    } else {
      divElapsedTime = getElementByID( "elapsedTime" );
    }
    if ( divElapsedTime ) {
      divElapsedTime.innerHTML = leadingZero(intHours) + ":" + 
                                 leadingZero(intMins) + ":" + 
                                 leadingZero(intSecs);
    }                                     
  } catch( e ) {
    reportError( "updateElapsedTime", e );
  }    
}

/**
 * Method:      updateStatus
 * Parameters:  strMsg, the message to add to the display
 * Returns:     none
 */  
function updateStatus( strMsg ) {
  try{
    var divStatusMsg;
        
    if ( parent ) {
      divStatusMsg = getElementByID( "statusMsg", parent.document );
    } else {
      divStatusMsg = getElementByID( "statusMsg" );
    }
    if ( divStatusMsg ) {
// Split the contents of the list into a buffer    
      var strBuffer = divStatusMsg.innerHTML;
      var aryLines = strBuffer.split( "." );
// Insert the new messaage at the front      
      divStatusMsg.innerHTML = strMsg + ".<br />";
// Append the first 15 lines from the buffer       
      for( var i=0; i<aryLines.length; i++ ) { 
        if ( aryLines[i].length == 0 || i >= 15 ) {
          break;
        }        
        divStatusMsg.innerHTML += aryLines[i] + ".";  
      }
    }    
  } catch( e ) {
    reportError( "updateStatus", e );
  }    
}
/**
 * Method:      reportError
 * Parameters:  strFunction, the name of the function the error occurred in
 *              objErr, the error or exception object 
 * Returns:     none
 */    
function reportError( strFunction, objErr ) {
  if ( !objErr ) {
    alert( "reportError: Cannot report error as " + objErr + " is invalid" );
    return;
  }
  var strMsg = "ERROR ";
  
  if ( strFunction != undefined ) {
    strMsg += "in " + strFunction;
  }
  strMsg += ": ";
  
  if ( objErr.message ) {
    strMsg += objErr.message;
  } else {
    strMsg += objErr;
  }  
  alert( strMsg );
}
/**
 * Method:      repeatLastRequest
 * Parameters:  none
 * Returns:     none
 */   
function repeatLastRequest() {
  try{
    if ( docInfo.strLastRequest.length == 0 ) {
      return;
    }
    docInfo.contactServer( docInfo.strLastRequest, true );
  } catch( e ) {
    reportError( "repeatLastRequest", e );
  }         
}
/**
 * Method:      newRecord
 * Parameters:  none
 * Returns:     none
 * Purpose:     Prepares the database form for a new record
 */    
function newRecord() {
  try{
    docInfo.blankForm();
    docInfo.disableEditableFields( false );

    if ( docInfo.cbNew ) {
// Call registered callback    
      docInfo.cbNew();      
    }        
  } catch( e ) {
    reportError( "newRecord", e );
  }         
};
/**
 * Method:      searchDialog
 * Parameters:  none
 * Returns:     none
 */       
function searchDialog() {
  try{  
    if ( blnSearchMode == false ) {
// Blank the form    
      docInfo.blankForm();
// Hide any existing results      
      showSearchResults( false );
/*                         
// Display instructions    
      alert( "The form has been cleared.\n" +
             "Please enter any search criteria you require.\n" +
             "You may use the wildcard '*':\n\n" +
             "\t\te.g.\n\t\t\tHitch*\n\n" +
             "Entered in Surname would find all members with\n" +
             "a surname that begins with 'Hitch'.\n\n" +
             "When you wish to begin the search click on the\n" +
             "search button again." );
*/             
      blnSearchMode = true;
      docInfo.disableEditableFields( false );
    } else {
// Build search criteria
      var aryELs = docInfo.getAllFormElements( true );      

      if ( aryELs ) {
        var strParams = "cmd=" + docInfo.searchCmd;      
        strParams += "&root=" + docInfo.strRoot; 
        
        for( var i=0; i<aryELs.length; i++ ) {
          var el = aryELs[i];
          var strRef;        
          
          if ( el.name ) {
            strRef = el.name;
          } else if ( el.id ) {
            strRef = el.id;
          } else {
            continue;
          }
          if ( el.nodeName == "INPUT" ) {
            if ( el.type == "text" && el.value.length > 0 ) {
              strParams += "&" + strRef + "=" + encodeURI( el.value );
            } else if ( el.type == "checkbox" && el.checked == true ) {
              strParams += "&" + strRef + "=1";          
            }
          } else if ( el.nodeName == "SELECT" && 
                      el.name.length > 0 &&
                      el.name.indexOf( "." ) != -1 ) {                    
            if ( el.selectedIndex == 0 ) {
// Do nothing for the first entry of a list, it should be empty            
              continue;
            }
            strParams += "&" + strRef + "=" + encodeURI( el.options[el.selectedIndex].value ); 
          }
        }
// Add all search criteria
        strParams = docInfo.contactServer( strParams, true );
//alert( strParams );
      }    
    }           
  } catch( e ) {
    reportError( "searchDialog", e );
  }         
};     
/**
 * Method:      selectFromList
 * Parameters:  selEl, the select list element
 * Returns:     none
 * Purpose:     shows an element by the same name as the selected item
 */    
function selectFromList( selEl ) {
  try{
    if ( !aryMoreOptions ) {
      throw( "No dimension options array!" );
    }
    if ( parent.intSelectedTab < 0 ) {
      throw( "No selected tab!" );
    }
    var intSelectedItem = 0;
    for( var i=0; i<selEl.length; i++ ) {
      var strID = selEl.options[i].value;
      
      if ( !strID || strID.length == 0 ) {
        continue;
      }
      var el = getElementByID( strID );
        
      if ( !el ) {
        throw( "Cannot find '" + strID + "' in document!" );
      } else if ( el.id == selEl.value ) {
        el.style.display = "block";
        intSelectedItem = i;
      } else {
        el.style.display = "none";
      }    
    }
// Adjust the size of the parent frame
    if ( intSelectedItem >= 0 ) {
      var el = getElementByID( "panel" + parent.intSelectedTab, parent.document );
      if ( !el ) {
        throw( "Cannot find 'panel" + parent.intSelectedTab + "' in document!" );
      }
      el.height = aryMoreOptions[intSelectedItem];
    }
  } catch( e ) {
    reportError( "selectFromList", e );
  }         
};
/**
 * Method:      populateOrderByList
 * Parameters:  none
 * Returns:     none
 * Purpose:     Populates the orderby list 
 */    
function populateOrderByList() {
  try{
    var selectOrderBy    = getElementByID( "orderBy" );
    var selectOrderOrder = getElementByID( "orderOrder" );

    if ( !selectOrderBy || !selectOrderOrder ) {
      return;
    }
    removeAllChildren( selectOrderBy );
    removeAllChildren( selectOrderOrder );
    var aryELs = docInfo.getAllFormElements( true );
    
    if ( aryELs ) {
      for( var i=0; i<aryELs.length; i++ ) {
        if ( aryELs[i].type == "button" ||
             aryELs[i].name.indexOf(".") == -1 ) {
          continue;
        }
        addOptionToSelect( selectOrderBy, aryELs[i].id, aryELs[i].name );
      }
      addOptionToSelect( selectOrderOrder, "Descending", "DESC" );
      addOptionToSelect( selectOrderOrder, "Ascending", "ASC" );
    }
  } catch( e ) {
    reportError( "populateOrderByList", e );
  }         
};
/**
 * Method:      tabChange
 * Parameters:  el, the tab element
 * Returns:     none
 */    
function tabChange( el ) {
  try{
    if ( !el ) {
      el = getElementByID( "tab0" );
      
      if ( !el ) {
        throw( "Cannot resolve 'el'" );
      }
    }
    var objTab, objPanel;
    for( var t=0; t<99; t++ ) {
      objTab = getElementByID( "tab" + t );
      objPanel = getElementByID( "panel" + t );
      
      if ( !objTab || !objPanel ) {
        break;
      }
      if ( objTab.id == el.id ) {
        parent.intSelectedTab = t;
        continue;
      }
      objTab.className = "tabNormal";
      objPanel.style.display = "none";      
    }
    if ( parent.intSelectedTab == -1 ) {
      return;
    }
    if ( parent.intLastSelectedTab == parent.intSelectedTab ) {
      return;
    }                    
    parent.intLastSelectedTab = parent.intSelectedTab;
    objTab = getElementByID( "tab" + parent.intSelectedTab );
    objPanel = getElementByID( "panel" + parent.intSelectedTab );
    objTab.className = "tabSelected";
// Show the panel    
    objPanel.style.display = "block";
// Hide the search results panel
    showSearchResults( false );
  } catch( e ) {
    reportError( "tabChange", e );
  }         
};
/**
 * Method:      funEnter
 * Parameters:  objEvent, the event object
 * Returns:     none
 */
function funEnter( objEvent ) {
  try{
    var keycode;
    if ( window.event ) {
      keycode = window.event.keyCode;
    } else if ( objEvent ) {
      keycode = objEvent.which;
    } else {
      return true;
    }
    if ( keycode != 13 ) {
      return true;      
    }
  // Attempt to submit the form contents
    saveRecord();
    return false;
  } catch( e ) {
    reportError( "funEnter", e );
  }
};
/*    
function funEnter( objEvent ) {
  try{
    var keycode;
    if ( window.event ) {
      keycode = window.event.keyCode;
    } else if ( objEvent ) {
      keycode = objEvent.which;
    } else {
      return true;
    }
    if ( keycode != 13 ) {
      return true;      
    }
    var el;
    if ( objEvent.srcElement ) {
      el = objEvent.srcElement; 
    } else {
      el = objEvent.target;
    }
    if ( el && el.id && aryCustomerTranslation ) {
// Find next input node
      var strCurrId = String(el.id);
      var blnFound = false;
      var strID;
      for( strID in aryCustomerTranslation ) {
        if ( strID == strCurrId ) {
          blnFound = true;
        } else if ( blnFound == true ) {
          break;
        }
      }
      if ( blnFound == true ) {
        var nextEl = getElementByID( strID );
        
        if ( !nextEl ) {
          for( strID in aryCustomerTranslation ) {
            nextEl = getElementByID( strID );
            break;
          }
          if ( !nextEl ) {
            return false;
          }                  
        }
        nextEl.focus();
      }
    }
    return false;
  } catch( e ) {
    reportError( "funEnter", e );
  }
};
*/
/**
 * Method:      commonSetup
 * Parameters:  intTabIdx, the index of the tab the page is associated with
 *              strRoot, the name of the root node to expect in the server
 *              response
 *              pHandler, the handler to call when a response is recieved
 *              intSearchCmd, the command use to issue search request
 *              blnAllowNew, true or false 
 *              blnAllowSearch, true or false
 *              blnAllowEdit, true or false 
 *              blnAllowSave, true or false
 *              blnAllowNav, true of false
 *              blnAllowPrint, true or false
 *              blnAllowDelete, true or false  
 *              cbSelect, Custom select callback
 *              cbCancel, Custom cancel callback    
 * Returns:     none
 * Purpose:     Generic setup routine
 */    
function commonSetup( intTabIdx,      strRoot, 
                      pHandler,       intSearchCmd,
                      blnAllowNew,    blnAllowSearch, 
                      blnAllowEdit,   blnAllowSave,
                      blnAllowNav,    blnAllowPrint,
                      blnAllowDelete, 
                      cbSelect,     cbCancel ) {  
  try{
// Set-up the document class    
    docInfo = new pageData( intTabIdx,      strRoot, 
                            pHandler,       intSearchCmd,
                            blnAllowNew,    blnAllowSearch,
                            blnAllowEdit,   blnAllowSave,
                            blnAllowNav,    blnAllowPrint,
                            blnAllowDelete,  
                            cbSelect,       cbCancel );  
// Make sure the order by list is populated
    populateOrderByList();    
// Make sure all inputs have an onkey event setup to submit when enter is 
// pressed
    var aryELs = docInfo.getAllFormElements( true );
    
    if ( aryELs ) {
      if ( window.saveRecord != undefined ) {
        for( var i=0; i<aryELs.length; i++ ) {
          if ( !( aryELs[i].nodeName == "INPUT" ||
                  aryELs[i].nodeName == "SELECT" )  ) {
            continue;
          }
          bindEvent( aryELs[i], "keyup", funEnter );
        }                 
      }
    }
    return docInfo;            
  } catch( e ) {
    reportError( "commonSetup", e );
  }
  return null;         
};
/** 
 * Method:      findDocByRootNode
 * Parameters:  strRoot, the name of the server response root node
 * Returns:     null if not found or the document info object.
 */    
function findDocByRootNode( strRoot ) {
  try{
    for( var i=0; i<top.aryDocs.length; i++ ) {
      if ( top.aryDocs[i].strRoot == strRoot ) {
        return top.aryDocs[i];        
      }
    }
  } catch( e ) {
    reportError( "findDocByRootNode", e );
  }
  return null;             
};
/**
 * Method:      importData
 * Parameters:  blnInit, true to send initial import message
 * Returns:     none
 */   
function importData( blnInit ) {
  try{
// Get the buttons
    var btnImport = getElementByID( "btnImport" );
    
    if ( !btnImport ) {
      throw( "Cannot find 'btnImport' in document!" );
    }    
    var strParams = 'root=' + strAJAXrootNode;
    var objDate = new Date();
    var lngMS = objDate.getTime();
    var strMsg = "";
    
    if ( blnInit == true ) {
// Hide the busy window
      hideBusy();
// Have any import checkboxes been checked ?
      var importCheckBoxes = "";
      for( var i=0; i<999; i++ ) {
        var el = getElementByID( "imp" + i );
        
        if ( !el ) {
          break;
        }
        if ( el.checked ) {
          if ( importCheckBoxes.length > 0 ) {
            importCheckBoxes += ",";
          }
          importCheckBoxes += i; 
        }
      }
      if ( importCheckBoxes.length == 0 ) {
        alert( "No imports have been checked, cannot proceed!" );
        return;
      }
      strParams +=  '&cmd=' + EDB_INIT_IMPORT + 
                    '&implist=' + encodeURI( importCheckBoxes );
      strMsg = "Initialising import";                    
// Save reference time of when the import started              
      lngStartTime = Math.round( lngMS / 1000, 0 );
    } else {
      strParams += '&cmd=' + EDB_NEXT_IMPORT_ACTION;
      strMsg = "Next record";
// Show the elapsed time
      updateElapsedTime( (Math.round(lngMS / 1000, 0) - lngStartTime) );
    }
    strParams += "&t=" + lngMS;
// Example: root=ESKA&cmd=17&implist=0,1,2,3    
// Disable button preventing the action again
    btnImport.disabled = true;
// Issue command to server
//alert( strParams );
    objAJAX.POST( strServerAddr + 'db/lib/server.php', strParams );
    updateStatus( strMsg );
  } catch( e ) {
    reportError( "importData", e );
  }         
}
/** 
 * Method:      highlight
 * Parameters:  el, the current row the mouse is over
 * Returns:     none
 */   
function highlight( el ) {
  try{
    if ( !el || !el.childNodes || !el.childNodes.length ) {
      return;
    }
    for( var i=0; i<el.childNodes.length; i++ ) {
      var childNode = el.childNodes[i];
      childNode.className = "highlight"; 
    }
  } catch( e ) {
    reportError( "highlight", e );
  }             
};
/** 
 * Method:      normal
 * Parameters:  el, the row the mouse was over
 * Returns:     none
 */   
function normal( el ) {
  try{
    if ( !el || !el.childNodes || !el.childNodes.length ) {
      return;
    }
    for( var i=0; i<el.childNodes.length; i++ ) {
      var childNode = el.childNodes[i];
      childNode.className = "normal"; 
    }
  } catch( e ) {
    reportError( "normal", e );
  }             
};
/**
 * Method:      showSearchResults
 * Parameters:  blnShow, true or false
 *              strHTML, the search results
 * Returns:     none 
 */    
function showSearchResults( blnShow, strHTML ) {
  try{
    var tblSearchResults, divResultRows;

    if ( parent ) {
      tblSearchResults = getElementByID( "searchResults", parent.document );
      divResultRows = getElementByID( "searchResultRows", parent.document ); 
    } else {
      tblSearchResults = getElementByID( "searchResults" );
      divResultRows = getElementByID( "searchResultRows" );
    }
    if ( tblSearchResults ) {
      if ( blnShow == true ) {
        divResultRows.innerHTML = strHTML;    
        tblSearchResults.style.display = "block";        
      } else {
        divResultRows.innerHTML = "";
        tblSearchResults.style.display = "none";
      }
    }
  } catch( e ) {
    reportError( "showSearchResults", e );
  }             
};
/** 
 * Method:      selectNode
 * Parameters:  el, row selected
 *              strRoot, the server response root node name 
 * Returns:     none
 */   
function selectNode( el, strRoot ) {
  try{
    if ( !el || !el.childNodes || !el.childNodes.length ) {
      return;
    }
// Get the actual form elements
    var docInfo = findDocByRootNode( strRoot );
    
    if ( !docInfo ) {
      throw( "Cannot find document with root node: '" + strRoot + "'!" );
    }
    var aryELs = docInfo.getAllFormElements( true );

    if ( !aryELs ) {
      throw( "Cannot extract form elements" );
    }
// Blank the existing record    
    docInfo.blankForm();
    docInfo.disableEditableFields( true );
    
    if ( docInfo.cbSelect ) {
      docInfo.cbSelect( el );
      return;
    } 
    for( var i=0; i<el.childNodes.length; i++ ) {
      var childNode = el.childNodes[i];
// Locate the node
      for( var j=0; j<aryELs.length; j++ ) {
        if ( aryELs[j].id == childNode.id ) {
          var strHTML = childNode.innerHTML;
          
          if ( childNode.firstChild && 
               childNode.firstChild.nodeName == "INPUT" && 
               childNode.firstChild.type == "checkbox" &&
               childNode.firstChild.checked ) {
            strHTML = "1";
          }
          if ( aryELs[j].nodeName == "INPUT" && aryELs[j].type == "checkbox" ) {
            if ( parseInt( strHTML, 10 ) > 0 ) {
              aryELs[j].checked = true;
            }
          } else { 
            aryELs[j].value = strHTML;
          }
          break; 
        }
      }
    }
  } catch( e ) {
    reportError( "selectNode", e );
  }             
};
/**
 * Method:      editRecord
 * Parameters:  none
 * Returns:     none 
 */    
function editRecord() {
  try{
// Get the actual form elements    
    var aryELs = docInfo.getAllFormElements( true, true );

    if ( !aryELs ) {
      throw( "Cannot extract form elements" );
    }
    for( var i=0; i<aryELs.length; i++ ) {
      aryELs[i].disabled = false;
    }
    if ( docInfo.btnSave ) {
// Enable the save button    
      docInfo.btnSave.disabled = false;
    }
    if ( docInfo.btnEdit ) {
// Disable the edit button
      docInfo.btnEdit.disabled = true;
    }
    if ( docInfo.btnCopy ) {
// Disable the copy button
      docInfo.btnCopy.disabled = true;
    }
    if ( docInfo.btnCancel ) {
// Enable the cancel button
      docInfo.btnCancel.disabled = false;
    }
    if ( docInfo.btnDelete ) {
      docInfo.btnDelete.disabled = true;
    }                  
    if ( docInfo.cbEdit ) {
// Call registered callback    
      docInfo.cbEdit();      
    }
    return aryELs;  
  } catch( e ) {
    reportError( "editRecord", e );
  }
  return null;
};
/**
 * Method:      copyRecord
 * Parameters:  none
 * Returns:     none 
 */
function copyRecord() {
  try{
// Simply get rid of the ID, then we are ready to edit and save as a new record    
    var aryELs = editRecord();
    
    if ( aryELs && aryELs[0] ) {
      aryELs[0].value = "";
    }
    if ( docInfo.btnCancel ) {
// Disable the cancel button, we can't cancel a copy
      docInfo.btnCancel.disabled = true;
    }
  } catch( e ) {
    reportError( "copyRecord", e );
  }
}
/**
 * Method:      cancelEdit
 * Parameters:  none
 * Returns:     none 
 */
function cancelEdit() {
  try{
    if ( docInfo.cbCancel ) {
// A custom callback to handle the cancel has been installed call it!    
      docInfo.cbCancel();
      return;
    }   
// Restore the inputs using the saved states array
    if ( docInfo.arySavedState ) {
      for( var i=0; i<docInfo.arySavedState.length; i++ ) {
        var objSaved = docInfo.arySavedState[i];        
        var el = getElementByID( objSaved.id );
        
        if ( !el ) {
          continue;
        }
        if ( el.nodeName == "INPUT" ) {
          if ( el.type == "checkbox" ) {
            el.checked = (objSaved.value) ? true : false; 
          } else {
            el.value = objSaved.value;
          }
        } else if ( el.nodeName == "SELECT" ) {
          el.selectedIndex = objSaved.value;
        }
      }
    }
// Get the actual form elements    
    var aryELs = docInfo.getAllFormElements( true, false );

    if ( !aryELs ) {
      throw( "Cannot extract form elements" );
    }
    for( var i=0; i<aryELs.length; i++ ) {
      aryELs[i].disabled = true;
    }
// Disable the cancel and save buttons
    if ( docInfo.btnCancel ) { 
      docInfo.btnCancel.disabled = true;
    }
    if ( docInfo.btnSave ) { 
      docInfo.btnSave.disabled = true;
    }
// Enbable the edit button
    if ( docInfo.btnEdit ) {
      docInfo.btnEdit.disabled = false;
    }
    if ( docInfo.btnCopy ) {
      docInfo.btnCopy.disabled = false;
    }
    if ( docInfo.btnDelete ) {
      docInfo.btnDelete.disabled = false;
    }              
    if ( docInfo.cbCancel ) {
// Call registered callback    
      docInfo.cbCancel();      
    }        
  } catch( e ) {
    reportError( "cancelEdit", e );
  }
};
/**
 * Method:      writeRecord
 * Parameters:  intCmd, the command to issue
 *              strExtraParams, optional extra parameters to save  
 * Returns:     none
 */   
function writeRecord( intCmd, strExtraParams ) {
  try{
// Get the actual form elements    
    var aryELs = docInfo.getAllFormElements( true );

    if ( !aryELs ) {
      throw( "Cannot extract form elements" );
    }
// Disable all editable fields    
    docInfo.disableEditableFields( true );

    if ( docInfo.cbSave ) {
// Call registered callback    
      docInfo.cbSave();      
    }        
// Issue command to server        
    var strParams = 'root=' + docInfo.strRoot + 
                    '&cmd=' + intCmd;
    for( var i=0; i<aryELs.length; i++ ) {
      var strValue = "", el = aryELs[i];

      if ( el.name.indexOf( "." ) == -1 ) {
// Exclude any elements that do not have a period in the name      
        continue;
      }
      if ( el.nodeName == "INPUT" ) {
        if ( el.type == "text" || el.type == "hidden" ) {
          strValue = EncodeData( Trim( el.value ) );
        } else if ( el.type == "checkbox" || el.type == "radio" ) {
          strValue = ( el.checked ) ? 1 : 0;
        }
      } else if ( el.nodeName == "SELECT" ) {
        strValue = EncodeData( el.options[el.selectedIndex].value );
      } else if ( el.nodeName == "TEXTAREA" ) {
        strValue = EncodeData( Trim( el.value ) );
      }
      strParams += "&" + el.id + "=" + strValue;
    }
    if ( strExtraParams ) {
      strParams += "&" + strExtraParams; 
    }
//alert( strParams );
    docInfo.contactServer( strParams, true );         
  } catch( e ) {
    reportError( "writeRecord", e );
  }
};
/**
 * Method:      updateWebsite
 * Parameters:  none  
 * Returns:     none
 */   
function updateWebsite() {
  try{
    var strParams = 'root=' + docInfo.strRoot +
                    '&cmd=' + EDB_UPDATE_WEBSITE;                    
    strParams = docInfo.contactServer( strParams, false );
    docInfo.btnUpdateWS.disabled = true;
//alert( strParams );    
  } catch( e ) {
    reportError( "updateWebsite", e );
  }
};
/**
 * Method:      isDate
 * Parameters:  strDate, the date to validate in the format of: dd/mm/yyyy
 * Returns:     empty string if ok, or error messages
 */  
function isDate( strDate ) {
  var strMsgs = "";
  try{    
    if ( !strDate || strDate.length == 0 ) {
      return "\nNo date passed";
    }
    var aryDate = strDate.split( "/" );
    var curDate = new Date();
        
    if ( aryDate.length != 3 ) {
      return "\nInvalid date, syntax: dd/mm/yyyy";
    }
    var intDay   = parseInt(aryDate[0], 10);
    var intMonth = parseInt(aryDate[1], 10);
    var intYear  = parseInt(aryDate[2], 10);
    
    if ( isNaN(intDay) ) {
      strMsgs += "\nDay must be a number";
    } 
    if ( isNaN(intMonth) ) {
      strMsgs += "\nMonth must be a number";
    } 
    if ( isNaN(intYear) ) {
      strMsgs += "\nYear must be a number";
    }
    if ( intYear < 1900 ) {
      strMsgs += "\nInvalid year, syntax: yyyy";
    }
    strDate = intMonth + "/" + intDay + "/" + intYear;    
    var testDate = new Date( strDate );
    
    if ( testDate.getMonth() + 1 != intMonth ) {
      strMsgs = "\nInvalid date"; 
    }
    if ( testDate > curDate ) {
      strMsgs = "\nDate cannot be in the future";
    }
  } catch( e ) {
    reportError( "isDate", e );
  }
  return strMsgs;
};
/**
 * Method:
 *  requestNotes
 *  
 * Parameters:
 *  none
 */       
function requestNotes() {
  try{
    if ( !top.customerDoc ) {
      return;
    }
    var docInfo = top.customerDoc;
    var strParams = 'root=' + strNotesRoot +  
                    '&cmd=' + EDB_GET_NOTES + 
                    '&cid=' + top.currentCustomerID;                
    var strResult = docInfo.contactServer( strParams, true );
//throw( strResult );      
  } catch( e ) {
    reportError( "requestNotes", e );
  }
};
/**
 * Method:
 *  notesHandler
 *  
 * Parameters:
 *  objAJAX, the ajax object
 *  objHandler, the handler object
 */        
function notesHandler( objAJAX, objHandler ) {
  try{  
// Hide any busy indicator
    hideBusy();
// Is there any error message to display?      
    var errmsg = objAJAX.FindXMLnode( "errmsg" );
    errmsg = objAJAX.GetXMLvalue( errmsg );
    
    if ( errmsg != null ) {
      throw( errmsg );
    }
    var objCmd = objAJAX.FindXMLnode( "cmd" );
    objCmd = objAJAX.GetXMLvalue( objCmd );
    
    if ( !objCmd ) {
      throw( "Cannot determine server command!" );
    }
    switch ( parseInt( objCmd, 10 ) ) {
    case EDB_GET_NOTES:
      var nNode = objAJAX.FindXMLnode( "n" );
      var aryNotes = new Array();
      
      if ( nNode && nNode.length ) {
        for( var i=0; i<nNode.length; i++ ) {
          var note_id     = objAJAX.FindXMLnode( "note_id",     nNode[i] );
          var customer_id = objAJAX.FindXMLnode( "customer_id", nNode[i] );
          var note_date   = objAJAX.FindXMLnode( "note_date",   nNode[i] );
          var note_text   = objAJAX.FindXMLnode( "note_text",   nNode[i] );
          note_id     = objAJAX.GetXMLvalue( note_id );
          customer_id = objAJAX.GetXMLvalue( customer_id );
          note_date   = objAJAX.GetXMLvalue( note_date );
          note_text   = objAJAX.GetXMLvalue( note_text );
          
          if ( !(note_id &&
                 customer_id && 
                 note_date &&
                 note_text) ) {
            continue;
          }
          var objNote = new Object();
          objNote['note_id']     = note_id;
          objNote['customer_id'] = customer_id;
          objNote['note_date']   = note_date;
          objNote['note_text']   = decodeURIComponent( note_text );
          objNote['note_text']   = objNote['note_text'].replace( /::NL::/gi, "<br />" );
          objNote['note_text']   = objNote['note_text'].replace( /::PER::/gi, "%" );
          aryNotes[aryNotes.length] = objNote;          
        }
      }
      top.aryNotes = aryNotes;
// Add the header
      var HTML = 
        "<table border=\"0\" id=\"notesList\">" +
          "<thead>" +
            "<tr>" +
              "<th>Select</th>" +
              "<th>Date</th>" +
              "<th width=\"100%\">Note</th>" +
            "</tr>" +
          "</thead>" +
          "<tbody>";
// Add the content
      for( var i=0; i<top.aryNotes.length; i++ ) {
        var objTmp = top.aryNotes[i];
        HTML +=
            "<tr>" +
              "<td class='notesID'>" +
                "<input type='checkbox' name='n" + objTmp['note_id'] + "' " +
                                         "id='n" + objTmp['note_id'] + "' " + 
                                         "/>" +
              "</td>" +
              "<td class='notesDate'>" +
                objTmp['note_date'] +
              "</td>" +
              "<td>" +
                "<div class='notesText'>" +
                  objTmp['note_text'] +
                "</div>" +
              "</td>" +                                        
            "</tr>";
//alert( objTmp['note_text'] );            
      }          
// Add the footer          
      HTML +=         
          "</tbody>" +
        "</table>";
      top.notesDlg.setContent( HTML, 420, 240 );
      top.notesDlg.showDialog( true );
      break;
      
    case EDB_REMOVE_NOTES:
    case EDB_ADD_NOTE:
// Re-request the notes for this customer      
      if ( top.notesDlg ) {
        requestNotes();
      }
      break;      
    }
	} catch(e) {
    reportError( "notesHandler", e ); 
  }
};
/**
 * Method:
 *  notesDialogClosed
 *  
 * Parameters:
 *  blnState, passed onto function when dialog is closed, true if ok button 
 *            clicked, false if cancel clicked
 */                   
function notesDialogClosed( blnState ) {
  try{
    top.notesDlg = null;
    top.aryNotes = null;
	} catch(e) {
    reportError( "notesDialogClosed", e ); 
  }
};
/**
 * Method:
 *  addNoteDialogClosed
 *  
 * Parameters:
 *  blnState, passed onto function when dialog is closed, true if ok button 
 *            clicked, false if cancel clicked
 */                   
function addNoteDialogClosed( blnState ) {
  try{
    top.addNoteDlg = null;
// Issue command to add note
    var elTmp = getElementByID( "noteDate" );
    
    if ( !elTmp ) {
      throw( "Cannot find note date in document!" );
    }
    var strDate = elTmp.value;
    var elTmp = getElementByID( "noteText" );  

    if ( !elTmp ) {
      throw( "Cannot find note text in document!" );
    }
    var docInfo = top.customerDoc;
    var strText = elTmp.value;
    var strParams = 'root=' + strNotesRoot +  
                    '&cmd=' + EDB_ADD_NOTE +
                    "&cid=" + top.lastCustomerID +  
                    '&note_date=' + encodeURI( strDate ) +
                    '&text=' + encodeURI( strText );                    
    var strResult = docInfo.contactServer( strParams, true );
//throw( strResult );          
	} catch(e) {
    reportError( "addNoteDialogClosed", e ); 
  }
};
/**
 * Method:
 *  addNote
 *  
 * Parameters:
 *  none
 */     
function addNote( ev ) {
  try{
    var el = null;
    
    if ( window.event ) {
      ev = window.event;          
    }
    if ( ev.srcElement ) {
      el = ev.srcElement; 
    } else {
      el = ev.target;
    }
    if ( !el || top.addNoteDlg ) {
      return;
    }
    top.addNoteDlg = new clsDIALOG( "top.addNoteDlg", el, "addNoteDlg", 
                                    true, false, addNoteDialogClosed );
    top.addNoteDlg.setTitle( el.value );
                                                                                   
    var strDate = new Date().format( "%DD/%MM/%YYYY" );    
    var HTML =
      "<div style=\"margin: 4px;\">" + 
        "<div>" + 
          "<strong>Date:</strong>&nbsp;" +
          "<input type=\"text\" " +
               "name=\"noteDate\" id=\"noteDate\" " +
               "value=\"" + strDate + "\" disabled />" +
        "</div>" +
        "<div>" +            
          "<strong>Note:</strong><br />" +
          "<textarea cols=\"60\" rows=\"5\" " +
                 "name=\"noteText\" id=\"noteText\">" +
          "</textarea>" +                   
        "</div>" +
      "</div>";
    top.addNoteDlg.setContent( HTML, 400 );                                    
    top.addNoteDlg.showDialog( true );                                         
	} catch(e) {
    reportError( "addNote", e ); 
  }
};
/**
 * Method:
 *  getAllNoteCheckboxes
 * 
 * Parameters:
 *  none
 *  
 * Returns:
 *  An array of the check box objects   
 */      
function getAllNoteCheckboxes() {
  var aryCheckboxes = new Array();
  
  try{
    if ( !top.aryNotes || top.aryNotes.length == 0 ) {
      return aryCheckboxes; 
    }
    for( var i=0; i<top.aryNotes.length; i++ ) {
      var objTmp = top.aryNotes[i]; 
      var node = getElementByID( "n" + objTmp['note_id'] );

      if ( !node ) {
        continue;
      }
      aryCheckboxes[aryCheckboxes.length] = node;        
    }    
	} catch(e) {
    reportError( "getAllNoteCheckboxes", e ); 
  }
  return aryCheckboxes;  
};
/**
 * Method:
 *  selectAllNotes
 * 
 * Parameters:
 *  none
 */      
function selectAllNotes( ev ) {
  try{
// Get all checkbox nodes in the notes dialog
    var aryCheckBoxes = getAllNoteCheckboxes();

    if ( !aryCheckBoxes ) {
      return;
    }
// Check all boxes  
    for( var i=0; i<aryCheckBoxes.length; i++ ) {
      aryCheckBoxes[i].checked = true;
    }  
	} catch(e) {
    reportError( "selectAllNotes", e ); 
  }
};
/**
 * Method:
 *  deselectAllNotes
 * 
 * Parameters:
 *  none
 */
function deselectAllNotes( ev ) {
  try{
// Get all checkbox nodes in the notes dialog
    var aryCheckBoxes = getAllNoteCheckboxes();

    if ( !aryCheckBoxes ) {
      return;
    }
// Clear all boxes
    for( var i=0; i<aryCheckBoxes.length; i++ ) {
      aryCheckBoxes[i].checked = false;
    }  
	} catch(e) {
    reportError( "deselectAllNotes", e ); 
  }
};
/**
 * Method:
 *  deleteSelectedNotes
 * 
 * Parameters:
 *  none
 */
function deleteSelectedNotes( ev ) {
  try{
// Get all checkbox nodes in the notes dialog
    var aryCheckBoxes = getAllNoteCheckboxes();
    var docInfo = top.customerDoc;
    
    if ( !aryCheckBoxes || !docInfo ) {
      return;
    }
// Issue command to remove checked items  
    var strParams = 'root=' + strNotesRoot +  
                    '&cmd=' + EDB_REMOVE_NOTES;
    var strSelected = "";                     
    for( var i=0; i<aryCheckBoxes.length; i++ ) {
      if ( aryCheckBoxes[i].checked == false ) {
        continue;
      }
      var checkbox = aryCheckBoxes[i]; 
      strSelected += "&n[]=" + checkbox.name.substr( 1 );
    }
    if ( strSelected.length > 0 ) {
      strParams += strSelected;      
      var strResult = docInfo.contactServer( strParams, true );
//throw( strResult );      
    }
	} catch(e) {
    reportError( "deleteSelectedNotes", e ); 
  }
};                                       
/**
 * Method:
 *  showNotes
 *  
 * Parameters:  
 *  el, the parent element that trigged the call to this function, if null
 *      el is ignored 
 *  
 * Returns: 
 *  none
 */         
function showNotes( el ) {
  try{
// Make sure we have everything we need...  
    if ( !(top && 
           top.lastCustomerID && 
           top.currentCustomerID &&
           top.customerDoc &&
           top.customerDoc.contactServer && 
           top.customerDoc.objAJAX ) ) {
      throw( "Invalid references: " +
             "top: " + top + "\n" + 
             "top.lastCustomerID: " + top.lastCustomerID + "\n" +
             "top.currentCustomerID: " + top.currentCustomerID + "\n" +
             "top.customerDoc: " + top.customerDoc + "\n" +
             "top.customerDoc.contactServer: " + top.customerDoc.contactServer + "\n" +
             "top.customerDoc.objAJAX: " + top.customerDoc.objAJAX );
    }
    if ( el ) {
      top.notesElement = el;
    }
    if ( top.lastCustomerID != top.currentCustomerID ) {
// Customer number has changed, request the details from the server          
      top.lastCustomerID = top.currentCustomerID;
      
      if ( top.notesDlg ) {
        top.notesDlg.removeDialog( false );
      }
// Make sure there is a handler installed to deal with note responses from the
// server
      top.customerDoc.objAJAX.AddHandler( strNotesRoot, notesHandler );
    }
    if ( top.notesDlg == null ) {
// Create dialog    
      top.notesDlg = new clsDIALOG( "top.notesDlg", el, "notesDlg", 
                                    true, true, notesDialogClosed );
      top.notesDlg.setTitle( "NOTES" );
      top.notesDlg.removeButton( "Cancel", false );
      top.notesDlg.addButton( "Add Note...", addNote );
      top.notesDlg.addButton( "Select All", selectAllNotes );
      top.notesDlg.addButton( "De-select All", deselectAllNotes );
      top.notesDlg.addButton( "Delete Selected", deleteSelectedNotes );                                       
    }    
    if ( top.aryNotes == null ) {
// We don't have any notes yet, request them from the server
      requestNotes();    
    }
  } catch( e ) {
    reportError( "showNotes", e );
  }         
};         
