﻿// Copyright 2007 by Developer Utility
// Please see http://www.developerutility.com/Terms.htm for terms of use.

/*
=========================================
Grid Marshall Event Quick list definition
=========================================
- SaveDataEntry            : save every cell change on any grid display;
- AddRowInGrid             : add a new data row inside any grid.
- DeleteRowInGrid          : delete any data row(s) inside any grid.
- OnGridCellEdition        : monitor every cell being edit on any grid display (lock db record);
- OnGridCellEditionCancel  : monitor every cell cancellation being edit on any grid display (unlock db record);
- OnGridCellChange         : monitor every cell change on any grid display;
- OnGridRowChange          : monitor every row change on any grid display;
- OnGridRowSelect          : monitor every row selection on any grid display in the browser;
- FormatCellContent        : specify some special column display inside you xml head (attribute format);
- UnFormatCellContent      : specify some special column display inside you xml head (attribute format);
- ValidateInputOnKeyDown   : monitor every keyboard entry typing inside the grid cell input text element;
- ValidateInputAfterChange : monitor every cell change on any grid display in the browser.
*/

/////////////////////////////////////////////////////////////////////////////////
// THE FUNCTIONS BELOW ARE FOR SAVING/ADDING/DELETING ROW(S) YOU CAN CUSTOMIZE //
/////////////////////////////////////////////////////////////////////////////////
function SaveDataEntry(evt, currentGrid) {
  try {
    // This is where you save every cell change on any grid display in the browser 
    // as well as any other input, select or textarea element outside the grid.
    // This function is call every time the user click the floppy disk icon (or save button).
    // This function should be call every time the "OnGridRowChange" function is call.
    // The event (evt) is pass only as reference in case you want to tailor some special coding.
    // The grid object (currentGrid) is pass only as reference by the "OnGridRowChange" function
    // in case you want to tailor some special coding.
    //
    // You will have to monitor the user change (save on the server) when they filter the grid 
    // or request another page display (page 1 of 10, next page) by calling this function.
    //
    // If you display MDI - multiple document interface - (like Developer Utility propose, or like the Dojo
    // project for example), you will have to monitor the window close (or window change) 
    // event by calling this function.
    //
    // If you display MDI, you should append the window ID (oWindow.id) at the begining
    // of the xml node head property "id" before generating the grid object 
    // (ex: <Head id="WindowId.SQLServerName.CandidatesTable" ... >).
    // Doing this will allow the user to open multiple windows of the same database table grid and apply
    // different filter for comparing the data "side by side" the information.  If you don't, you will
    // create two (or more) grid objects with the same ID.  Hence, it will be impossible for you to 
    // to monitor inner join relation or simple data grid change.
    //
    // For server side data saving, you will have to implement your transport logic here since 
    // we don't know the method you use for it (json, xml, ajax, post, hidden frame, iFrame, etc).
    //
    // You should take advantage of some of the GridMarshall method (describe below).
    //
    // For exemple you might have 2 grid display and the second allow multiple row change
    // but the first one allow change per row only. The second grid display content
    // in relation of the first one. Hence, you will have to save on the server side 
    // the current grid change and the second grid change. This example show this relation.
    
    // For log purpose count the number of error (if any) on each database to report back to developers
    var serverError = 0;
    
    // Get the floppy disk icon (or button) like regular .exe application
    var disk = document.getElementById("saveDisk");
    
    // If user click on the save disk icon, no display message will be show to user (ex: do you want to save ...)
    var oEle = (browser.isIE) ? window.event.srcElement : evt.target;
    var isUserSaving = true;
    if(disk != oEle) {
      isUserSaving = false;
    }
    
    // Check change on all grid inside the browser
    var oGridChange;
    for (var oGrid in arrGridMarshall) {
      if(arrGridMarshall[oGrid] instanceof GridMarshall) {
        if(arrGridMarshall[oGrid].gridChange) {
          oGridChange = arrGridMarshall[oGrid];
          
          // Tip:
          // For inner join table or other relation you will have to map the parent/child relation
          // inside a object of your choice (hash table, etc) or hard code all relation.
          // If you use multiple document interface, you will have to monitor the window ID at
          // beginning of the oGridChange.id.
          //
          // For heavy database table exposure (like 1000 tables), you will probably break this coding 
          // example into a more readable and object driven style function
          
          switch(oGridChange.id) {
            case "SQLServerName.CandidatesTable":
              // Ask user to save change on the server (only if did not press the saving icon himself)
              var save = true;
              if(!isUserSaving) {
                if(!confirm("Do you want to SAVE the CANDIDATE record change?")){
                  // Set current grid back to it's original state
                  oGridChange.gridChange = false;
                  // Reset node change property on grid (set change property to 0 and originalvalue to value)
                  oGridChange.resetRowsChange();
                  save = false;
                }
              } 
              
              if(save) {
                // Tip:
                // Here we return the current row change of the grid via a xml format for easy xPathQuery.
                // You should take advantage of this method for getting all 
                // information you seak about the row (keys field, data type, etc).
                // You can then decide to send the entire xmlDoc on the server or transform it 
                // on the client side (in json or super-light xml transport) before pushing.
                
                var xmlRowChange = oGridChange.getRowsChangeXml(); // Only one row return saveoneachrowchange="1"
                
                // Make sure there is one (or more) row change (if user change 
                // a row grid content and then decide to delete the same row).
                if(!xmlRowChange.lastChild.hasChildNodes()) { return false; }
                
                // Please see implementation exemple BELOW for detail 
                // on how the transport should be build.
                //
                // ... send transport to server here ... build from xmlRowChange
                
                // Receiving transport from server for saving data (with new row, insert key field)
                // Here the server response contain key(s) field(s) for the new row
                //
                // NB1: You can set the document node <Keys> and his child <Key> to any name.
                //      You have to set the child(s)-child(s) node(s) - here <CandidateID> - 
                //      with the same name inside your xml head. You can set multiple rows
                //      with multiple keys with this method (the key distribution order is
                //      set by the data collection order (rows) and key order (columns).
                // NB2: You will have to set the current grid to an original state
                //      and confirm node change property on grid as describe below in
                //      the receiving transport to server for saving data (without new row key field)
                // NB3: See also data saving of case "SQLServerName.CandidatesAddressTable"
                //      for an example of multiple rows changes
                var serverResponse = "<Keys><Key><CandidateID>" + ++keyFieldCounter + "</CandidateID></Key></Keys>";
                
                var xmlDoc = CreateXmlDoc(serverResponse);
                oGridChange.setRowsKeys(xmlDoc);
                
                // Receiving transport from server for saving data (without new row key field)
                serverResponse = "OK"; // DEMO ONLY
                if(serverResponse == "OK") {
                  // Set current grid to an original state
                  oGridChange.gridChange = false;
                  // Confirm node change property on grid (set change property to 0 and originalvalue to value)
                  oGridChange.applyRowsChange();
                } else {
                    serverError++;
                  }
                
//                // THIS EXAMPLE IS ONLY PROPOSE AS A DEMO (BUILD SERVER TRANSPORT)
//                // IMPLEMENTATION STYLE MAY (AND PROBABLY WILL) VARY
//                
//                var xmlRowChange = oGridChange.getRowsChangeXml(); // Only one row return saveoneachrowchange="1"
//                
//                // Make sure there is one (or more) row change (if user change 
//                // a row grid content and then decide to delete the same row).
//                if(!xmlRowChange.lastChild.hasChildNodes()) { return false; }
//                
//                var serverNameAndTableName = oGridChange.id        // usually set to be your database table name
//                                                                   // (if you use multiple document interface, 
//                                                                   // you will probably have to stripe out the 
//                                                                   // window ID at beginning of the oGridChange.id)
//                var columnname;
//                var type;
//                var value;
//                var originalvalue;
//                var transport;
//                var serverError = 0;
//                
//                // Get all key field for the current row
//                var xPathQuery = ".//TD[@key = '1']";
//                var oNodeList = xmlRowChange.selectNodes(xPathQuery);
//                var oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//                
//                while(oNode) {
//                  columnname = oNode.getAttribute("columnname");
//                  type = oNode.getAttribute("type");
//                  value = oNode.getAttribute("value");
//                  
//                  // Here your transport formating ...
//                  // transport = ... sql key field ...
//                  
//                  oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//                }
//                
//                // Get all change field for the current row
//                xPathQuery = ".//TD[@change = '1']";
//                oNodeList = xmlRowChange.selectNodes(xPathQuery);
//                oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//                
//                while(oNode) {
//                  columnname = oNode.getAttribute("columnname");
//                  type = oNode.getAttribute("type");                   // nb: all checkbox type boolean 
//                                                                       // value are converted to "1" or "0"
//                  value = oNode.getAttribute("value");
//                  originalvalue = oNode.getAttribute("originalvalue"); // audit change on the server side
//                  
//                  // Here your transport formating ...
//                  // transport = ... sql value change ...
//                  
//                  oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//                }
              }
              break;
            
            case "SQLServerName.CandidatesAddressTable":
              // Ask user to save change (of inner join relation) on the server (only if did not press the saving icon himself)
              var save = true;
              if(!isUserSaving) {
                if(!confirm("Do you want to SAVE the ADDRESS change of the current candidate?")){
                  // Set current grid back to it's original state
                  oGridChange.gridChange = false;
                  // Reset node change property on grid (set change property to 0 and originalvalue to value)
                  oGridChange.resetRowsChange();
                  save = false;
                }
              } 
              
              if(save) {
                // Get the parent row select (for having the foreing key field)
                var parentID = "SQLServerName.CandidatesTable";
      					var xmlRowSelect = arrGridMarshall[parentID].getRowSelectXml();
                
			          // Get all parent key field (foreing key of sql table)
                /*
			          var xPathQuery = ".//TD[@key = '1']";
			          var oNodeList = xmlRowSelect.selectNodes(xPathQuery);
			          var oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
			          
			          while(oNode) {
			            columnname = oNode.getAttribute("columnname");
			            type = oNode.getAttribute("type");
			            value = oNode.getAttribute("value");
			            
			            // Here your transport formating ...
			            // transport = ... sql foreing key field ...
			            
				          oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
			          }
                */
                
                var xmlRowChange = oGridChange.getRowsChangeXml(); // Many rows return saveoneachrowchange="0"
                
                // Make sure there is one (or more) row change (if user change 
                // a row grid content and then decide to delete the same row).
                if(!xmlRowChange.lastChild.hasChildNodes()) { return false; }
                
                // If you want to do some code validation, for example making sure 
                // a valid postal code is enter for each row(s) change(s), this is
                // where you would implement your code line.
                
                // Please see implementation exemple ABOVE for detail 
                // on how the transport should be build
                // 
                // ... send transport to server here ...
                
                // Receiving transport from server for saving data (with new row,  insert key field)
                // Here the server response contain key(s) field(s) for the new row
                // For demo purpose, we fake the number of key value for new row received by the server
                // In real case scenario, you will also have to get the foreing key from the parent table
                var xPathQuery = ".//TD[@columnname='CandidateID' and @value='']" + "/parent::node()";
                var oNodeList = (browser.isIE) ? xmlRowChange.selectNodes(xPathQuery) : xmlRowChange.selectNodes(xPathQuery, 1);
                
                var nbOfKeyToGenerate = (browser.isIE) ? oNodeList.length : oNodeList.snapshotLength;
                var arrTemp = new Array();
                
                arrTemp.push("<Keys>");
                var i = 0;
                while(i < nbOfKeyToGenerate) {
                  arrTemp.push("<Key><CandidateID>" + ++keyFieldCounter + "</CandidateID></Key>");
                  i++;
                }
                arrTemp.push("</Keys>");
                
                var xmlDoc = CreateXmlDoc(arrTemp.join(""));
                oGridChange.setRowsKeys(xmlDoc);
                
                // Receiving transport from server for saving data (without new row key field)
                var serverResponse = "OK"; // DEMO ONLY
                if(serverResponse == "OK") {
                  // Set current grid to an original state
                  oGridChange.gridChange = false;
                  
                  // Confirm node change property on grid (set change property to 0 and originalvalue to value)
                  oGridChange.applyRowsChange();
                } else {
                    serverError++;
                  }
              }
              break;
            
            case "SQLServerName.CandidatesTable2":
              // Ask user to save change on the server (only if did not press the saving icon himself)
              var save = true;
              if(!isUserSaving) {
                // Ask user to save change on the server
                if(!confirm("Do you want to SAVE the CANDIDATE record change?")){
                  // Set current grid back to it's original state
                  oGridChange.gridChange = false;
                  // Reset node change property on grid (set change property to 0 and originalvalue to value)
                  oGridChange.resetRowsChange();
                  save = false;
                }
              } 
              
              if(save) {
                var xmlRowChange = oGridChange.getRowsChangeXml(); // Only one row return saveoneachrowchange="1"
                
                // Make sure there is one (or more) row change (if user change 
                // a row grid content and then decide to delete the same row).
                if(!xmlRowChange.lastChild.hasChildNodes()) { return false; }
                
                // Please see implementation exemple ABOVE for detail 
                // on how the transport should be build.
                //
                // ... send transport to server here ... build from xmlRowChange
                
                var serverResponse = "<Keys><Key><CandidateID>" + ++keyFieldCounter + "</CandidateID></Key></Keys>";
                
                var xmlDoc = CreateXmlDoc(serverResponse);
                oGridChange.setRowsKeys(xmlDoc);
                
                // Receiving transport from server for saving data (without new row key field)
                serverResponse = "OK"; // DEMO ONLY
                if(serverResponse == "OK") {
                  // Set current grid to an original state
                  oGridChange.gridChange = false;
                  // Confirm node change property on grid (set change property to 0 and originalvalue to value)
                  oGridChange.applyRowsChange();
                } else {
                    serverError++;
                  }
              }
              break;
            
            //case "...":
            //  ...
            //  break;
          }
          
        } // fin arrGridMarshall[oGrid].gridChange
      }   // fin arrGridMarshall[oGrid] instanceof GridMarshall
    }     // fin for (var oGrid in arrGridMarshall)
    
    // This is where you verity other input, select element outside the grid
    //if("any other element you monitor have change" == true) {
      // Here is you custom code for saving input element in your page
      // Send data to server ...
    //}
    
    // Tip:
    // For different implementation style, for example if you decide to save
    // in one transport to the server all change on all wanted grid and 
    // element inside the browser, this is where you would build your transport.
    
    // If server indicate the save are done without error
    var bReturn = false;
    if(serverError < 1) {
      bReturn = true;
      
      // All change have been save, set the disk icon back to disable
      disk.disabled = true;
    } else {
        // Call another server for saving data ...
        // Report error log ...
      }
    
    // You always have to return a boolean value for the "OnGridRowChange" function to work properly
    return bReturn;
    
  } catch(e) {
      StatusBarWrite("Error (fnc : " + "SaveDataEntry" + "). " + e.message, 3000);
    }
}

function AddRowInGrid(evt, currentGridID) {
  try {
    // This is where you add a new data row inside any grid.
    // This function is call every time the "add row icon" is trigger.
    // The event (evt) is pass only as reference in case you want to tailor some special coding.
    // Tip:
    // Since you can have multiple grid object in the browser, you will always have to pass
    // the grid object ID inside the button (or icon) calling this function.
    // An easy way of doing this is to have a generic xml template of the your "toolbar"
    // and swap "on the fly" the property before rendering on the user screen.
    
    // Get the current grid object
		var currentGrid = arrGridMarshall[currentGridID];
    
    // Add the new row (always append at the bottom of the current table grid, always empty)
    // If the user do not add any data modification inside it, no change will be record and
    // the grid object will not consider it (user made a mistake by clicking the button).
    
    // There is two differents techniques to add a new row:
    // ====================================================
    // (1) The first one - à postiori - is to generate a row with empty key field. Once the user 
    // save the data to the server, the server will return all key(s) field(s) inside it's response 
    // and you will have to insert it(them) inside the cell of the new row in the table DOM. 
    // Please see function SaveDataEntry for detail (search for the keyword "insert key field").
    switch(currentGrid.id) {
      case "SQLServerName.CandidatesTable":        // Demo 1
        currentGrid.addRow(evt);
        break;
      
      case "SQLServerName.CandidatesAddressTable": // Demo 1
        
        // Make sure only one parent row is selected
        var parentID = "SQLServerName.CandidatesTable";
        var rowsSelect = arrGridMarshall[parentID].getRowsSelect();
        //alert(rowsSelect[0].xml());
        
        var xmlRowsSelect = arrGridMarshall[parentID].getRowsSelectXml();
        //alert(xmlRowsSelect.xml);
        
        if(rowsSelect.length == 1) { currentGrid.addRow(evt); }
        break;
      
      case "SQLServerName.CandidatesTable2":       // Demo 2
        currentGrid.addRow(evt);
        break;
      
      //case "...":
      //  ...
      //  break;
    }
    
//    // (2) The second one - à priori - is to generate all key field before inserting the new row 
//    // inside the table DOM. Something like:
//    // 
//    // THIS EXAMPLE IS ONLY PROPOSE AS A DEMO
//    // IMPLEMENTATION STYLE MAY (AND PROBABLY WILL) VARY.
//    // PLEASE NOTE ASYNCHRONOUS AJAX SHOULD BE PROHIBITED 
//    // SINCE YOU ARE WORKING WITH REAL DATA EDITION.
//    
//    var serverNameAndTableName = currentGrid.id        // usually set to be your database table name
//                                                       // (if you use multiple document interface, 
//                                                       // you will probably have to stripe out the 
//                                                       // window ID at beginning of the oGridChange.id)
//    
//    switch(currentGrid.id) {
//      case "SQLServerName.CandidatesTable":
//        // Get all key field of the current grid
//        var xmlGrid = currentGrid.xmlDoc;
//        var oNodeHead = xmlGrid.selectSingleNode(xPathQueryHead);
//        var columnname;
//        
//        // Get all key field
//        var xPathQuery = ".//*[@key = '1']";
//        var oNodeList = oNodeHead.selectNodes(xPathQuery);
//        var oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//        
//        while(oNode) {
//          columnname = oNode.tagName;
//          //var type = oNode.getAttribute("type");
//          
//          // Here your transport formating ...
//          // transport = ... sql key field column name ...
//          
//          oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//        }
//        
//        // Send/Receive transport to server for getting new key field
//        // ... send transport to server here ...
//        var serverResponse = "<Keys><Key><CandidateID>" + ++keyFieldCounter + "</CandidateID></Key></Keys>";
//        
//        // Generate a temporary table grid and clone it's last row
//        var xmlDoc = CreateXmlDoc(MakeXmlDocument(""));
//        var oClone = oNodeHead.cloneNode(true);
//        xmlDoc.lastChild.appendChild(oClone);
//        
//        var oNodeBody = xmlDoc.createElement("Body");
//        xmlDoc.lastChild.appendChild(oNodeBody);
//        
//        var oNewNodeRow = xmlDoc.createElement("NewNodeRow");
//        oNodeBody.appendChild(oNewNodeRow);
//        
//        // Insert key field node inside the new node row
//        var xmlSvrRsp = CreateXmlDoc(serverResponse); // If you use Ajax, you can use the .responseXML
//        xPathQuery = "./*/*/*";
//        oNodeList = xmlSvrRsp.selectNodes(xPathQuery);
//        oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//        
//        while(oNode) {
//          oClone = oNode.cloneNode(true);
//          oNewNodeRow.appendChild(oClone);
//          
//          oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//        }
//        
//        // Build a temporary grid table (i.e. it will not be include inside the arrGridMarshall
//        //                               object with the second argument set to "true")
//        var oTableTemp = BuildGrid(xmlDoc, true);
//        var oTBody = oTableTemp.tBodies[0];
//        
//        // Get temp table last row (always the new row)
//        var newTR = oTBody.rows[oTBody.rows.length - 1];
//        
//        // Append new row already generate with key key(s) field(s)
//        currentGrid.addRow(evt, newTR);
//        break;
//      
//      //case "...":
//      //  ...
//      //  break;
//    }
    
  } catch(e) {
      StatusBarWrite("Error (fnc : " + "AddRowInGrid" + "). " + e.message, 3000);
    }
}

function DeleteRowInGrid(evt, currentGridID) {
  try {
    // This is where you delete any data row(s) inside any grid.
    // This function is call every time the "delete row(s) icon" is trigger.
    // The event (evt) is pass only as reference in case you want to tailor some special coding.
    // Tip:
    // Since you can have multiple grid object in the browser, you will always have to pass
    // the grid object ID inside the button (or icon) calling this function.
    // An easy way of doing this is to have a generic xml template of the your "toolbar"
    // and swap "on the fly" the property before rendering on the user screen.
    
    // Get the current grid object
		var currentGrid = arrGridMarshall[currentGridID];
		
    // Make sure one (or more) row is select
    var rowsSelect = currentGrid.getRowsSelect();
    if(rowsSelect.length < 1) { return false; }
    
    switch(currentGrid.id) {
      case "SQLServerName.CandidatesTable": // Demo 1
        // Ask user to delete the row(s) selected on the server
        if(confirm("Do you want to DELETE the current CANDIDATE selected row(s)?")){
          // Tip:
          // Here we return the current rows select of the grid via a xml format for easy xPathQuery.
          // You should take advantage of this method for getting all 
          // information you seak about the row (keys field, data type, etc).
          // You can then decide to send the entire xmlDoc on the server or transform it 
          // on the client side (in json or super-light xml transport) before pushing.
          var xmlRowsSelect = currentGrid.getRowsSelectXml();
          
          // Tip: 
          // If you delete parent table content (like here), you will have to check  
          // dependency table(s) on the server side (DB SQL or business logic)
          
          // Please see implementation exemple BELOW for detail 
          // on how the transport should be build.
          //
          // ... send transport to server here ... build from xmlRowChange
          
          // Receiving transport from server for deleting data
          var serverResponse = "OK"; // DEMO ONLY
          if(serverResponse == "OK") {
            // Delete row(s) on grid
            currentGrid.deleteRows();
            
            // Call the grid row select event to clear all child data table (inner join)
            OnGridRowSelect(evt, currentGrid);
          }
          
//          // THIS EXAMPLE IS ONLY PROPOSE AS A DEMO (BUILD SERVER TRANSPORT)
//          // IMPLEMENTATION STYLE MAY (AND PROBABLY WILL) VARY
//          
//          var xmlRowsSelect = currentGrid.getRowsSelectXml();
//          var serverNameAndTableName = currentGrid.id        // usually set to be your database table name
//                                                             // (if you use multiple document interface, 
//                                                             // you will probably have to stripe out the 
//                                                             // window ID at beginning of the oGridChange.id)
//          var columnname;
//          var type;
//          var value;
//          var transport;
//          
//          // Get all key field for the selected row(s) 
//          var xPathQuery = ".//TD[@key = '1']"; // this xpath if for one row
//          var oNodeList = xmlRowsSelect.selectNodes(xPathQuery);
//          var oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//          
//          while(oNode) {
//            columnname = oNode.getAttribute("columnname");
//            type = oNode.getAttribute("type");
//            value = oNode.getAttribute("value");
//            
//            // Here your transport formating ...
//            // transport = ... sql key field ...
//            
//            oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//          }
        }
        break;
      
      case "SQLServerName.CandidatesAddressTable": // Demo 1
        // Ask user to delete the row(s) selected on the server
        if(confirm("Do you want to DELETE the current ADDRESS selected row(s)?")){
          // Get the parent row select (for having the foreing key field)
          var parentID = "SQLServerName.CandidatesTable";
					var xmlRowSelect = arrGridMarshall[parentID].getRowSelectXml();
          
          // Get all parent key field (foreing key of sql table)
          /*
          var xPathQuery = ".//TD[@key = '1']";
          var oNodeList = xmlRowSelect.selectNodes(xPathQuery);
          var oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
          
          while(oNode) {
            columnname = oNode.getAttribute("columnname");
            type = oNode.getAttribute("type");
            value = oNode.getAttribute("value");
            
            // Here your transport formating ...
            // transport = ... sql foreing key field ...
            
	          oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
          }
          */
          
          var xmlRowsSelect = currentGrid.getRowsSelectXml();
          
          // Please see implementation exemple ABOVE for detail 
          // on how the transport should be build
          //
          // ... send transport to server here ...
          
          // Receiving transport from server for deleting data
          var serverResponse = "OK"; // DEMO ONLY
          if(serverResponse == "OK") {
            // Delete row(s) on grid
            currentGrid.deleteRows();
          }
        }
        break;
      
      case "SQLServerName.CandidatesTable2": // Demo 2
        // Ask user to delete the row(s) selected on the server
        if(confirm("Do you want to DELETE the current CANDIDATE selected row(s)?")){
          var xmlRowsSelect = currentGrid.getRowsSelectXml();
          
          // Please see implementation exemple ABOVE for detail 
          // on how the transport should be build.
          //
          // ... send transport to server here ... build from xmlRowChange
          
          // Receiving transport from server for deleting data
          var serverResponse = "OK"; // DEMO ONLY
          if(serverResponse == "OK") {
            // Delete row(s) on grid
            currentGrid.deleteRows();
            
            // Call the grid row select event to clear all child data table (inner join)
            OnGridRowSelect(evt, currentGrid);
          }
        }
        break;
      
      //case "...":
      //  ...
      //  break;
    }
    
  } catch(e) {
      StatusBarWrite("Error (fnc : " + "DeleteRowInGrid" + "). " + e.message, 3000);
    }
}

/////////////////////////////////////////////////////////////////////////////////
// THE FUNCTIONS BELOW ARE SPECIAL EVENT CALL YOU CAN CUSTOMIZE FOR DATA ENTRY //
/////////////////////////////////////////////////////////////////////////////////
function OnGridCellEdition(evt, currentGrid) {
  try {
    // This is where you monitor every cell being edit on any grid display in the browser.
    // This function is call every time the content of a grid cell is edit 
    // (when the user type his first character inside the cell).
    // This function is call BEFORE the "ValidateInputAfterChange" function.
    // The event (evt) is pass only as reference in case you want to tailor some special coding.
    //
    // You can take advantage of this function to lock or advise the server that the 
    // current record is being edit. You can then decide to "lock" the record for other user
    // until the change are commit to the database. Hence, if two or more user try to modify the 
    // same record, the second one will receive a warning message.
    //
    // Detail:
    // - If you return "false", the cell edition request will be cancel;
    // - If you return "false", the grid object consider ANOTHER user is currently editing the record;
    // - If you return "true", the cell edition request will happend;
    // - If you return "true", the grid object consider NO user is currently editing the record;
    
//    // THIS EXAMPLE IS ONLY PROPOSE AS A DEMO
//    // IMPLEMENTATION STYLE MAY (AND PROBABLY WILL) VARY.
//    // PLEASE NOTE ASYNCHRONOUS AJAX SHOULD BE PROHIBITED 
//    // SINCE YOU ARE WORKING WITH REAL DATA EDITION.
//    
//    // Get the selected row
//		var xmlRowSelect = currentGrid.getRowSelectXml();
//    var serverNameAndTableName = currentGrid.id        // usually set to be your database table name
//                                                       // (if you use multiple document interface, 
//                                                       // you will probably have to stripe out the 
//                                                       // window ID at beginning of the oGridChange.id)
//    var columnname;
//    var type;
//    var value;
//    var transport;
//    
//    // Get all key field for the current row
//    var xPathQuery = ".//TD[@key = '1']";
//    var oNodeList = xmlRowSelect.selectNodes(xPathQuery);
//    var oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//    
//    while(oNode) {
//      columnname = oNode.getAttribute("columnname");
//      type = oNode.getAttribute("type");
//      value = oNode.getAttribute("value");
//      
//      // Here your transport formating ...
//      // transport = ... sql key field ...
//      
//      oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//    }
//    
//    // Send transport to server to lock data
//    var bReturn = true;
//    var serverResponse = SendData(serverAddress, transport);
//    if(serverResponse == "the record is currently being edit by another user") {
//      bReturn = false;
//    }
    
    // You always have to return a boolean value
    var bReturn = true;
    return bReturn;
    
  } catch(e) {
      StatusBarWrite("Error (fnc : " + "OnGridCellEdition" + "). " + e.message, 3000);
    }
}

function OnGridCellEditionCancel(evt, currentGrid) {
  try {
    // This is where you cancel every cell being edit on any grid display in the browser.
    // This function is call every time the content of a grid cell in edition is being cancel
    // (when the user have press the "ESC" keyboard key).
    // The event (evt) is pass only as reference in case you want to tailor some special coding.
    //
    // You can take advantage of this function to unlock or advise the server that the 
    // current record edition is cancel. You can then decide to "unlock" the record for other user.
    
//    // THIS EXAMPLE IS ONLY PROPOSE AS A DEMO
//    // IMPLEMENTATION STYLE MAY (AND PROBABLY WILL) VARY.
//    // PLEASE NOTE ASYNCHRONOUS AJAX SHOULD BE PROHIBITED 
//    // SINCE YOU ARE WORKING WITH REAL DATA EDITION.
//    
//    // Get the selected row
//		var xmlRowSelect = currentGrid.getRowSelectXml();
//    var serverNameAndTableName = currentGrid.id        // usually set to be your database table name
//                                                       // (if you use multiple document interface, 
//                                                       // you will probably have to stripe out the 
//                                                       // window ID at beginning of the oGridChange.id)
//    var columnname;
//    var type;
//    var value;
//    var transport;
//    
//    // Get all key field for the current row
//    var xPathQuery = ".//TD[@key = '1']";
//    var oNodeList = xmlRowSelect.selectNodes(xPathQuery);
//    var oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//    
//    while(oNode) {
//      columnname = oNode.getAttribute("columnname");
//      type = oNode.getAttribute("type");
//      value = oNode.getAttribute("value");
//      
//      // Here your transport formating ...
//      // transport = ... sql key field ...
//      
//      oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
//    }
//    
//    // Send transport to server to unlock data
//    var serverResponse = SendData(serverAddress, transport);
        
  } catch(e) {
      StatusBarWrite("Error (fnc : " + "OnGridCellEditionCancel" + "). " + e.message, 3000);
    }
}

function OnGridCellChange(evt, currentGrid) {
  try {
    // This is where you monitor every cell change on any grid display in the browser.
    // This function is call every time the content of a grid cell change (after the user have press "ENTER").
    // This function is call AFTER the "ValidateInputAfterChange" function have return "true".
    // The event (evt) is pass only as reference in case you want to tailor some special coding.
    var bChange = false;
    
    // Get the floppy disk icon (or button) like regular .exe application
    var disk = document.getElementById("saveDisk");
    
    // Get the current grid cell edited
    var oEle = currentGrid.cellSelected;
    
    if(oEle.getAttribute("type") == "select" || oEle.getAttribute("type") == "checkbox") {
      // Cell type select or checkbox (current value is inside a TD property)
      if(oEle.getAttribute("value") != oEle.getAttribute("originalvalue")) { bChange = true; }
    } else {
        // All other cell type (current value is inside the TD .innerHTML)
        var value = (browser.isIE) ? oEle.innerText : oEle.textContent;
        if(value != oEle.getAttribute("originalvalue")) { bChange = true; }
      }
    
    if(bChange) {
      // Tip:
      // The cell content is different than the original value so we set the change property to true
      // (you should take advantage of this value for future xPathQuery)
      // (yes xPathQuery even Ie, please refer to our toolbox's function ConvertIeDomToDHTML for detail)
      oEle.setAttribute("change", "1");
      bChange = true;
      
      // Here you indicate that the current grid have some change in it
      // in case you display multiple grid in the browser
      currentGrid.gridChange = true;
      
      // Turn on the icon (or button) to indicate some change have been done
      disk.disabled = false;
      
    } else {
        // The cell content is identical than the original value so we set it's change property to false
        oEle.setAttribute("change", "0");
        
        // This is where you can control if there is any other changes made
        // by the user, if not, you can turn off the disk icon
        var isSomethingChange = false;
        
        // This is where you verify if any other cell in the current grid row have change
        var rowsChange = currentGrid.getRowsChange();
        if(rowsChange.length > 0) {
          // The current grid  is NOT back to it's original state
          isSomethingChange = true;
        } else {
            // The current grid is back to it's original state
            currentGrid.gridChange = false;
          }
        
        // Check change in other grid inside the browser
        for (var oGrid in arrGridMarshall) {
          if(arrGridMarshall[oGrid] instanceof GridMarshall) {
            if(currentGrid.id != arrGridMarshall[oGrid].id) {
              if(arrGridMarshall[oGrid].gridChange) {
                // Another grid have been change so you cannot cancel the disk icon
                isSomethingChange = true;
              }
            }
          }
        }
        
        // This is where you verity other input, select element outside the grid
        //if("any other element you monitor have change" == true) {
        //  Here is you custom code for monitoring input element in your page
        //  isSomethingChange = true;
        //}
        
        // Nothing change, set the disk icon back to disable
        if(!isSomethingChange) { disk.disabled = true; }
      }

  } catch(e) {
      StatusBarWrite("Error (fnc : " + "OnGridCellChange" + "). " + e.message, 3000);
    }
}

function OnGridRowChange(evt, currentGrid) {
  try {
    // This is where you monitor every row change on any grid display in the browser.
    // This function is call every time the row of a grid change (by mouse selection or arrow-up or down)
    // and you have set the node head property "saveoneachrowchange" of your xml to "1".
    // This function is call BEFORE the "OnGridRowSelect" function.
    //
    // Detail:
    // - If you return "false", the row change request will be cancel;
    // - If you return "false", the "OnGridRowSelect" function WILL NOT be call (i.e. row select remain as is);
    // - If you return "true", the row change request will happend;
    // - If you return "true", the "OnGridRowSelect" function WILL be call (i.e. new row select).
    
    // You always have to return a boolean value
    var bReturn = SaveDataEntry(evt, currentGrid);
    return bReturn;
    
  } catch(e) {
      StatusBarWrite("Error (fnc : " + "OnGridRowChange" + "). " + e.message, 3000);
    }
}

function OnGridRowSelect(evt, currentGrid) {
  try {
    // This is where you monitor every row selection on any grid display in the browser.
    // This function is call every time the row of a grid is select (by mouse selection or arrow-up or down).
    // This function is call AFTER the "OnGridRowChange" function.
    //
    // For inner join table or other relation you will have to map the parent/child relation
    // inside a object of your choice (hash table, etc) or hard code all relation.
    //
    // For external data island link, this is where you will call the server side to get 
    // all information relative the record being select. You will have to make sure there is only
    // one row select (var rowsSelect = currentGrid.getRowsSelect();
    //                 if(rowsSelect.length == 1) { your code here; })
    //
    // If you use MDI - multiple document interface, you will have to monitor the window ID at
    // beginning of the oGridChange.id.
    //
    // If you display MDI, you should append the window ID (oWindow.id) at 
    // the begining of the xml node head property "id" before generating the grid object 
    // (ex: <Head id="WindowId.SQLServerName.CandidatesTable" ... >).
    // Doing this will allow the user to open multiple windows of the same database table grid and 
    // apply different filter for comparing the data "side by side".  If you don't, you will
    // create two (or more) grid objects with the same ID.  Hence, it will be impossible for  
    // you to monitor inner join relation or simple data grid change.
    
    switch(currentGrid.id) {
      case "SQLServerName.CandidatesTable":
        // This part is normally on the server side in PHP, ASP(.net), JSP, Java, Perl, JScript, etc.
        // For demo purpose we are building a xPathQuery and calling a local (with no security) xml file.
        //
        // For real case scenario, you will have to build a SQL query on the server side with the key 
        // field (just like this xPath example) of the selected row via Ajax transport or else.
        
        // Get the xml head template and relace current body with empty node
        var childID = "SQLServerName.CandidatesAddressTable";
        var xmlGrid = arrGridMarshall[childID].xmlDoc;
        
        var oNodeBody = xmlGrid.selectSingleNode(xPathQueryBody);
        var oNewNodeBody = xmlGrid.createElement("Body");
        
        var oParent = oNodeBody.parentNode;
        oParent.replaceChild(oNewNodeBody, oNodeBody);
        
        // Get the selected row (to get the candidate ID)
				var xmlRowsSelect = currentGrid.getRowsSelectXml();
        
        // If only one row is select, address table will display all address of the candidate
        if(xmlRowsSelect.lastChild.childNodes.length == 1) {
          // Get all parent key field (foreing key of sql table)
          var xPathQuery = ".//TD[@key = '1']";
          var xPathQueryAddress = "Body/Candidates/";
          var columnname;
          var value;
          
          var oNodeList = xmlRowsSelect.selectNodes(xPathQuery);
          var oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
          
          while(oNode) {
            columnname = oNode.getAttribute("columnname");
            value = oNode.getAttribute("value");
            xPathQueryAddress += columnname + "[. = \"" + value + "\"]";
            
            oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
          }
          
          // Get all address of the selected candidate in the body table
          var xmlBody = GetXmlDoc("Demo1DataCandidatesAddressTableBody.xml");
          xPathQueryAddress = xPathQueryAddress + "/parent::node()";
          var oClone;
          
          oNodeList = xmlBody.selectNodes(xPathQueryAddress);
          oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
          
          while(oNode) {
            oClone = oNode.cloneNode(true);
            oNewNodeBody.appendChild(oClone);
            
            oNode = (browser.isIE) ? oNodeList.nextNode : oNodeList.iterateNext();
          }
        }
        
        // Build the address grid object (per candidate base)
        var oTable = BuildGrid(xmlGrid);
        var gridShow = document.getElementById("gridShow2");
        gridShow.innerHTML = "";
        (browser.isIE) ? gridShow.innerHTML = oTable.outerHTML : gridShow.appendChild(oTable);
        
        break;
      
      //case "...":
      //  ...
      //  break;
    }
    
  } catch(e) {
      StatusBarWrite("Error (fnc : " + "OnGridRowSelect" + "). " + e.message, 3000);
    }
}

function FormatCellContent(value, format) {
  try {
    // If you have specify some special column display inside you xml head (attribute format).
    // This function will be call at the creation of every grid.
    // This function will also be call AFTER the "ValidateInputAfterChange" function.
    // After you have validated and accepted the user input, this will reformat your value for grid display.
    //
    // In our example, it is use in for the node Region inside the xml head.
    
    var valueFormated = value;
    switch(format) {
      case "dollars":
        valueFormated = FormatNumber(value, 2, true, true, true, 2, "$ ");
        break;
      //case "...":
      //  ...
      //  break;
    }
    return valueFormated;
    
  } catch(e) {
      StatusBarWrite("Error (fnc : " + "FormatCellContent" + "). " + e.message, 3000);
      return value;
    }
}

function UnFormatCellContent(value, format) {
  try {
    // If you have specify some special column display inside you xml head (attribute format).
    // This function will be call before any grid cell edition.
    // It allow the user to type an unformat entry field you have decide to apply some custom display
    // (like a monetary column display with the $ sign).
    //
    // In our example, it is use in for the node Region inside the xml head for displaying monetary type field.
    
    var valueUnFormated = value;
    switch(format) {
      case "dollars":
        valueUnFormated = UnformatNumber(value);
        break;
      //case "...":
      //  ...
      //  break;
    }
    return valueUnFormated;
    
  } catch(e) {
      StatusBarWrite("Error (fnc : " + "UnFormatCellContent" + "). " + e.message, 3000);
      return value;
    }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// THE FUNCTIONS BELOW ARE SPECIAL EVENT CALL YOU CAN CUSTOMIZE FOR DATA ENTRY VALIDATION BY FIELD TYPE //
//////////////////////////////////////////////////////////////////////////////////////////////////////////
function ValidateInputOnKeyDown(evt, oInput) {
  try {
    // This is where you monitor every keyboard entry typing inside the grid cell input text element.
    // This function is call every time the user type a character (before the user have press "ENTER").
    // The event (evt) is pass only as reference in case you want to tailor some special coding.
    //
    // To see the full list of keyboard numeric value (currently set to US setting)
    // please refer to our toolbox's function GetKeyEnter for detail.
    //
    // You can decide to skip this function (always return true) and validate all you data entry 
    // inside the ValidateInputAfterChange function (after user have press the "ENTER").
    // This type of validation is demonstrate with the "date" field type in our demo.
    
    if(!oInput) { oInput = (browser.isIE) ? window.event.srcElement : evt.target; }
    //(browser.isIE) ? alert(oInput.outerHTML) : alert(oInput.xml());
    
    var k = (browser.isIE) ? window.event.keyCode : evt.keyCode; // evt.which;
    //StatusBarWrite("k = " + k, 3000);
    
    var fieldType = oInput.getAttribute("fieldType");
    var bChange = false;
    
    if (k == 13 || k == 8 || k == 46 || k == 9 || k == 16 || k == 17 || k == 18 || k == 27 || k == 36 || k == 35 || k == 37 || k == 38 || k == 39 || k == 40) {
    //   Enter  || BkSpace|| Delete  ||   Tab  ||  Shift  || Control ||   Alt   ||   Esc   ||  Home   ||   End   || Arr Left||  Arr Up ||Arr Right|| Arr Down
        bChange = true;
        // Escape the validation routine (special keyboard entry, usually backspace or delete)
        return bChange;
    
    } else if (fieldType == "float") { 
        bChange = ((k > 47 && k < 58) || k == 190);
        if(oInput.value.length == 0) {
      	  bChange = ((k > 47 && k < 58) || k == 189 || k == 109);
        }
        if(bChange && oInput.value.length > 1) {
      	  if(!Number(oInput.value + GetKeyEnter(k))) { bChange = 0; }
        }
      
    } else if (fieldType == "int") { 
        bChange = (k > 47 && k < 58);
        if(oInput.value.length == 0) {
      	  bChange = ((k > 47 && k < 58) || k == 189 || k == 109);
        }
      
    } else if (fieldType == "stringstrict") { 
  		  bChange = (k > 64 && k < 91 || k == 32);
    	
    } else { 
        bChange = 1; // STRING
    }
    
    // Cancel or allow keyboard event
    if(bChange) {
      return true;
    } else {
        // Tell the user why his input have been refuse
        var msg = "";
        switch(fieldType) {
          case "float":
            msg = fieldType + " value [ex: 22.44]";
            break;
          case "int":
            msg = fieldType + " value [ex: 189]";
            break;
          case "stringstrict":
            msg = fieldType + " value [A-Z, a-z, space]";
            break;
        }
        StatusBarWrite("You can only enter " + msg + " in this field.", 2000);
        
	      if(browser.isIE) { window.event.returnValue = 0; }
	      if(browser.isGecko) { evt.preventDefault(); }
      }
    
  } catch(e) {
      StatusBarWrite("Error (fnc : " + "ValidateInputOnKeyDown" + "). " + e.message, 3000);
    }
}

function ValidateInputAfterChange(evt, oInput, currentGrid) {
  try {
    // This is where you monitor every cell change on any grid display in the browser.
    // This function is call every time the content of a grid cell change (after the user have press "ENTER").
    // This function is call BEFORE the "OnGridCellChange" function.
    // The event (evt) is pass only as reference in case you want to tailor some special coding.
    // 
    // Detail:
    // - If you return "false", the content of the cell will stay in edition mode;
    // - If you return "false", the "OnGridCellChange" function WILL NOT be call (i.e. change refuse, bad data entry);
    // - If you return "true", the content on the cell will be change with the new user entry;
    // - If you return "true", the "OnGridCellChange" function WILL be call (i.e. change accept, valid data entry).
    //
    // Why use this function and why break the change validation into two different function:
    // - it is easier to break for reading the code;
    // - it is easier to validate entry after completion than while typing
    //   (like the ValidateInputOnKeyDown function);
    // - you can round up number, add or substract on the fly (accounting);
    // - you can take advantage of regular expression for complex type (like postal code per country);
    // - here you can validate complex data type you have create (big Int, small Int, Time, etc);
    // - you can easily implement a switch [case:] by grid, by column name, by type, etc
    //   to accept/refuse user change.
    
    var bChange = false;
    var fieldType = oInput.getAttribute("fieldType");
    var value = oInput.value;
    
    if(fieldType == "date") {
      var day =   "(?:0[1-9]|[12][0-9]|3[01])";
      var month = "(?:0[1-9]|1[0-2])";
      var year =  "(?:19|20\\d{2})";
      
      //var reg = new RegExp(day + "/" + month + "/" + year, "i");
      var reg = new RegExp(year + "-" + month + "-" + day, "i");
      bChange = reg.test(value);
      
    } else {
    		// Here for Ie xPathQuery in the DOM, you should stripe out all quote (") value of the user input.
    		// If you want to allow the user to type quote value inside the grid, 
    		// you will have to modify the ConvertIeDomToDHTML function inside our toolbox's .
    		// Ie don't replace the '"' value by '&quot;' like Gecko browser.
    		// For consistency across user input, we replace the value in all browser type.
    		
    		var reg = new RegExp("\x22", "gi");
        var replacer = "\x27\x27"; // ''
    		
        var arrMatch = value.match(reg);
        if(arrMatch != null) {
          var j = 0;
          while(j < arrMatch.length) {
            value = value.replace(arrMatch[j], replacer);
            j++;
          }
        }
		    
		    oInput.value = value;
        bChange = true;
      }
    
    // You always have to return a boolean value
    return bChange; 
    
//    // THIS EXAMPLE IS ONLY PROPOSE AS A DEMO
//    // IMPLEMENTATION STYLE MAY (AND PROBABLY WILL) VARY
//    
//    var bChange = false;
//    // oInput                                        // current input text being edit
//    // currentGrid                                   // current grid being edit instanceof GridMarshall
//    var oTD = currentGrid.cellSelected;              // the current cell being edit
//                                                     // to see all properties of the current cell being edit: 
//                                                     // var demo = (browser.isIE) ? oTD.outerHTML : oTD.xml();
//                                                     // alert(demo);
//    var value = oInput.value;                        // what the user have type
//    var serverNameAndTableName = currentGrid.id      // usually set to be your database table name
                                                       // (if you use multiple document interface, 
                                                       // you will probably have to stripe out the 
                                                       // window ID at beginning of the oGridChange.id)
//    var columnname = oTD.getAttribute("columnname"); // usually set to be your database column name
//    
//    switch(serverNameAndTableName) {
//      case "CandidatesTable":
//        if(columnname == "your name here") {
//          // Do some validation
//          // ...
//          bChange = true;
//        }
//        break;
//      case "EmployesTable":
//        // ...
//        break;
//    }
//    
//    return bChange;

  } catch(e) {
      StatusBarWrite("Error (fnc : " + "ValidateInputAfterChange" + "). " + e.message, 3000);
    }
}
