//
//  Class:
//      VdfForm
//
//  Core of the DataFlex ajax framework. It contains generic functionality for
//  forms using ajax.
//
//  Since:
//      24-10-2005
//  Changed:
//      --
//  Version:
//      0.9
//  Creator:
//      Data Access Europe (Harm Wibier)
//

//
//  Used to fetch the form that an html object is in. It bubbles up through
//  the html dom and until an form element is found an returns the VdfForm that
//  is attached to the form.
//
//  Params:
//      oObj    An object in the form
//  Returns:
//      The VdfForm or null if not found
//
function findForm(oObj){
    var oForm = browser.dom.searchParent(oObj, "form");

    if(oForm == null)
        return null;

    return oForm.oVdfForm;
}

//  Keeps all vdfcontrols (forms, grids, lookups, ...)
var aVdfControls = new Array();

//
//  Used to fetch controls to call functions (like find() on initialisation)
//
//  Params:
//      sName   Name of the searched object
//  Returns:
//      Vdf control with the given name (null & alert if not found)
//
function getVdfControl(sName){
    if(aVdfControls[sName] != null){
        return aVdfControls[sName];
    }else{
        alert("Control " + sName + " not found!");
        return null;
    }
}

browser.events.addGenericListener("load", window, initVdfControls);
//
//  Starts the initialisation of the vdf controls (starts the scan)
//
function initVdfControls(){
    scanVdfControls(document.body, null);
    
    if(typeof(initForm) == "function"){
        initForm();
    }
}

//
//  Makes recursive scan through the document searching for vdfControlType 
//  tags to initialize vdf controls.
//
//  Params:
//      oElement    Element to scan
//      oVdfParent  Parent vdf element
//
function scanVdfControls(oElement, oVdfParent){
    var oVdfControl = null, bInit = false, iChild;
    //  Init control if needed
    switch(browser.dom.getVdfAttribute(oElement, "sControlType")){
        case "form":
            oVdfControl = new VdfForm(oElement);
            oVdfParent = oVdfControl;
            bInit = true;
            break;
        case "grid":
            if(oVdfParent != null){            
                oVdfControl = new VdfGrid(oElement, oVdfParent);
            }else{
                alert("Grid control must have form as parent!");
            }
            break;
        case "group":
            if(oVdfParent != null){            
                oVdfControl = new VdfGroup(oElement, oVdfParent);
            }else{
                alert("Grid control must have form as parent!");
            }
            break;
        case "lookup":
            if(oVdfParent != null){
                oVdfControl = new VdfLookup(oElement, oVdfParent);
            }else{
                alert("Lookup control must have form as parent!");
            }
            break;
        case "lookupdialog":
            if(oVdfParent != null){
                oVdfControl = new VdfLookupDialog(oElement, oVdfParent);
                bInit = true;
            }else{
                alert("Lookupdialog control must have form as parent!");
            }
            break;
    }
    
    //  Scan child elements
    for(iChild = 0; iChild < oElement.childNodes.length; iChild++){
        if(oElement.childNodes[iChild].nodeType != 3 && oElement.childNodes[iChild].nodeType != 8){
            scanVdfControls(oElement.childNodes[iChild], oVdfParent);
        }
    }
    
    //  Add to controls array
    if(oVdfControl != null){
        if(typeof(oVdfControl.sName) != "undefined"){
            aVdfControls[oVdfControl.sName] = oVdfControl;
        }
    }
    
    //  Init if needed
    if(bInit){
        oVdfControl.init();
    }
}



//
//  Constructor of the VdfForm class. Initializes properties.
//
//  Params:
//      oForm   The html form element
//
function VdfForm(oForm){
    // Main references
    this.oForm                  = oForm;
    oForm.oVdfForm              = this;

    //  Version
    this.sVersion               = "1.0 Beta 1";
    
    //  Settings
    this.sWebObject             = browser.dom.getVdfAttribute(this.oForm, "sWebObject", null);
    this.sMainTable             = browser.dom.getVdfAttribute(this.oForm, "sMainTable", null);
    this.sServerTable           = browser.dom.getVdfAttribute(this.oForm, "sServerTable", null);
    this.sWebServiceUrl         = browser.dom.getVdfAttribute(this.oForm, "sWebServiceUrl", "WebService.wso");
    this.sName                  = browser.dom.getVdfAttribute(this.oForm, "sControlName", "Form1");

    this.bValidateServer        = browser.dom.getVdfAttribute(this.oForm, "bValidateServer", true);
    this.bValidateClient        = browser.dom.getVdfAttribute(this.oForm, "bValidateClient", true);
    this.bValidateAdjust        = browser.dom.getVdfAttribute(this.oForm, "bValidateAdjust", true);
    this.bValidatePrevent       = browser.dom.getVdfAttribute(this.oForm, "bValidatePrevent", true);
        
    this.bAttachVdfKeyActions   = browser.dom.getVdfAttribute(this.oForm, "bAttachVdfKeyActions", true)
    this.bAutoClearDeoState     = browser.dom.getVdfAttribute(this.oForm, "bAutoClearDeoState", true)
    this.bAutoFill              = browser.dom.getVdfAttribute(this.oForm, "bAutoFill", true)
    
    //  Child objects of the form (DbGrid/DbLookup/..)
    this.aVdfChilds             = new Array();
    this.oVdfInfo               = new VdfInfo(this);
    this.oVdfErrorHandler       = new VdfErrorHandler(this);
    this.oVdfActiveObject       = this;
    this.oVdfValidator          = new VdfValidator(this);
    
    //  Data structures
    this.aStatusFields          = new Array();  //  Set of all statusfields managed by this form (rowid fields)
    this.aFields                = new Array();  //  Set of all fields managed by this form
	this.aTables                = new Array();  //  2 dimension array with al fields ordered by table 
                                            //  (this.aTables["TableName"]["FieldName"])
    this.aUserFields            = new Array();
    this.aLookupDialogs         = new Array();  //  Set of all lookupdialogs attached indexed by the field name
    
    this.oPreviousFocus         = null;
    this.oLastFocus             = null; //  The last focused field
    

    
    if(this.sWebObject == null){
        this.oVdfErrorHandler.showGlobalError("sWebObject / vdfWebObject bust be set!");
    }
    if(this.sMainTable == null){
        this.oVdfErrorHandler.showGlobalError("sMainTable / vdfMainTable bust be set!");
    }
    if(this.sServerTable == null){
        this.sServerTable = this.sMainTable;
    }
    
    this.sServerTable = this.sServerTable.toLowerCase();
    this.sMainTable = this.sMainTable.toLowerCase();
}

//
//  Initializes the form "engine" by requesting info about the WBO and scanning
//  for fields, initializing children, attaching events and doing the autofill.
//
VdfForm.prototype.init = function(){
    //  Load DDInfo
    if(!this.oVdfInfo.isInfoLoaded()){
        this.oVdfInfo.loadInfo(this.sWebObject, this.sWebServiceUrl);
    }

    //  Initialize fields
    this.initFields();

    //  Initialize childs
    for(iChild = 0; iChild < this.aVdfChilds.length; iChild++){
        this.aVdfChilds[iChild].init();
    }
    
    //  Initialize keylisteners & validation
    this.initKeylisteners();
    
    //  Auto fill using rowid's already in the hidden status fields
    if(this.bAutoFill){
        this.fill();
    }
    
    this.returnToFirstField();
}

//
//  Builds the field indexes (arrays) aStatusField & aTables, generates missing 
//  status fields and attaches events & styles.
//
VdfForm.prototype.initFields = function(){
    var sField, oField, sTable;
    
    //  Scan for fields
    this.scanForFields(this.oForm);
    
    //  Loop through all tables available in wo and generate statusfield if needed
    for(sTable in this.oVdfInfo.aTables){
        if(this.aStatusFields[sTable + "__rowid"] == null){
            oInput = document.createElement("input");
            oInput.type = "hidden";
            oInput.name = sTable + "__rowid";
            this.oForm.appendChild(oInput);
            this.aStatusFields[sTable + "__rowid"] = new VdfField(oInput, this);
        }
        
        this.aTables[sTable] = new VdfTable(this, sTable);
    }
    
    for(sTable in this.aTables){
        this.aTables[sTable].init();
    }
    
    //  Initialize status fields
    for(sField in this.aStatusFields){
        if(this.aStatusFields[sField].getType() != "hidden"){
            this.aStatusFields[sField].addGenericListener("focus", this.onFieldFocus);
        }
    }
    
    
    //  Initialize fields
    for(sField in this.aFields){
        oField = this.aFields[sField]
        
		//	Add to <TABLE><FIELD> structure
		if(this.aTables[oField.getTableName()] == null){
            if(this.oVdfInfo.tableExists(oField.getTableName())){
                this.aTables[oField.getTableName()] = new VdfTable(this, oField.getTableName());
            }else{
                this.oVdfErrorHandler.showGlobalError("Unknown table: " + oField.getTableName() + " (form: " + this.sName + " field: " + oField.getName() + ")");
                return;
            }
		}
        if(this.oVdfInfo.fieldExists(oField.getTableName(), oField.getFieldName())){
            this.aTables[oField.getTableName()].aFields[oField.getFieldName()] = oField;
        }else{
            this.oVdfErrorHandler.showGlobalError("Unknown field: " + oField.getFieldName() + " (form: " + this.sName + " field: " + oField.getName() + ")");
            return;
        }
        
        //  Set datatype class for alignment (if no css class is set)
        if(oField.getCSSClass() == null || oField.getCSSClass() == ""){
            oField.setCSSClass(this.getVdfFieldAttribute(oField, "sDataType") + "_data");
        }
        
        //  Attach generic form events
        oField.addGenericListener("focus", this.onFieldFocus);
        
        //  Set server table
        oField.setServerTable(this.sServerTable, false);
    }
}

//
//  Initializes the validation by letting the validator initializing all the 
//  fields.
//
VdfForm.prototype.initKeylisteners = function(){
    var sField, oField;

    //  Init field key listeners
    for(sField in this.aFields){
        oField = this.aFields[sField];
        
        oField.addKeyListener(this.onFieldKeyPress);
        
        this.oVdfValidator.initField(oField, oField.getServerTable());
    }
    
    //  Init status field key listeners
    for(sField in this.aStatusFields){
        if(this.aStatusFields[sField].getType() != "hidden"){
            this.aStatusFields[sField].addKeyListener(this.onFieldKeyPress);
            this.aStatusFields[sField].addGenericListener("change", this.onStatusFieldChange);
        }
    }
}
    


//
//  Loops recursive through the form child elements and adds them to the array 
//  if they are vdf form elements.
//
//  Param:
//      oCurrentElement Element to scan
//
VdfForm.prototype.scanForFields = function(oCurrentElement){
    if(oCurrentElement.nodeType != 3 && oCurrentElement.nodeType != 8){
        if((oCurrentElement.tagName == "INPUT" && oCurrentElement.type != "button") || oCurrentElement.tagName == "SELECT" || oCurrentElement.tagName == "TEXTAREA"){
            if(oCurrentElement.name.indexOf("__") != -1){

                //  Add the field (element) to the fields array(s)
                if(oCurrentElement.name.toLowerCase().substr(oCurrentElement.name.length - 7) == "__rowid"){
                    this.aStatusFields[oCurrentElement.name.toLowerCase()] = new VdfField(oCurrentElement, this);
                }else{
                    if(this.aFields[oCurrentElement.name.toLowerCase()] == null){
                        this.aFields[oCurrentElement.name.toLowerCase()] = new VdfField(oCurrentElement, this);
                    }else{
                        this.aFields[oCurrentElement.name.toLowerCase()].addElement(oCurrentElement);
                    }
                }
            }else{
                //  Add the field (element) to the userfields array
                if(this.aUserFields[oCurrentElement.name.toLowerCase()] == null){
                    this.aUserFields[oCurrentElement.name.toLowerCase()] = new VdfField(oCurrentElement, this);
                }else{
                    this.aUserFields[oCurrentElement.name.toLowerCase()].addElement(oCurrentElement);
                }
            }
        }
        
        for(var iChild = 0; iChild < oCurrentElement.childNodes.length; iChild++){
            this.scanForFields(oCurrentElement.childNodes[iChild]);
        }
    }
}


//
//  Method used for filling the form using the rowid fields. If no rowid fields
//  are available it won't send the VdfForm requestset. It also asks child 
//  elements for fill requestsets.
//
//  Params:
//      bSuppressError  If true no errors will be shown while handling the 
//                      request.
//  
VdfForm.prototype.fill = function(bSuppressError){
    var iChild, sXml = "", sField, bSend = false, sFieldValues = "";

    //  Check if fill request should be sent
    for(sField in this.aStatusFields){
        if(this.aStatusFields[sField].getValue() != ""){
            bSend = true;
            break;
        }
    }
    
    
    
    //  Create requestset
    if(bSend){
    
        //  Throw user event
        this.onBeforeFindByRowId(this, "");
        
        //  Fetch status values (make sure changedstate is true for filled fields)
        for(sField in this.aStatusFields){
            sFieldValues += VdfGlobals.soap.getFieldXml(sField, this.aStatusFields[sField].getValue(), (this.aStatusFields[sField].getValue() != ""));
        }
        //  Fetch fields
        sFieldValues += this.aTables[this.sMainTable].getDataXml(true, false);
        
        //  Create requestset
        sXml += this.getRequestSet("findByRowId", 1, this.sMainTable, "1", dfRowID, (bSuppressError ? "_NoWarning" : ""), sFieldValues);
    }
    
    
    for(iChild = 0; iChild < this.aVdfChilds.length; iChild++){
        if(typeof(this.aVdfChilds[iChild].getFillRequestSets) == "function"){
            sXml += this.aVdfChilds[iChild].getFillRequestSets(this.sMainTable);
        }
    }
        
    if(bSend || sXml.length > 0){
        sXml = "<aDataSets>\n" + sXml + "</aDataSets>\n";
        this.sendRequest(sXml, false);
    }

}


//
//  Performs an find by the main index of the field (if the field has an main 
//  index!!). It loops through the child objects to give them a change to add
//  their requestsets.
//
//  Params:
//      sFindMode       The mode to use for the find
//      oField          VdfField object of field to perform find on
//      bSuppressError  If true the not found error will not be shown
//  Returns:
//      True if the find request is send
//      
VdfForm.prototype.doFindByField = function(sFindMode, oField, bSuppressError){
    var sXml, sMainTable, sIndex, bResult = false;
   
    // Clear errors
	this.oVdfErrorHandler.clearErrors();
    
    //  Get last selected field / mainfile
    if(oField != null){
        sMainTable = oField.getTableName();
    
        //  Use first input element index else use data dictionary value or first
        iIndex = this.getVdfFieldAttribute(oField, "iIndex");

        if(iIndex != null && iIndex != "0"){
            //  Call user event
            this.onBeforeFind(this, sMainTable, iIndex, sFindMode);
        
            //  Append requestsets to the find
            var sXml = "<aDataSets>\n";
            sXml += this.getRequestSet("find", 1, sMainTable, iIndex, sFindMode, (bSuppressError ? "_NoWarning" : ""));

            //  Loop through childs for their additional requestsets
            for(iChild = 0; iChild < this.aVdfChilds.length; iChild++){
                if(typeof(this.aVdfChilds[iChild].getFindRequestSets) == "function"){
                    sXml += this.aVdfChilds[iChild].getFindRequestSets(sMainTable);
                }
            }

            sXml += "</aDataSets>\n";

            this.sendRequest(sXml, false);
            
            bResult = true;
        }else{
            if(!bSuppressError){
                this.oVdfErrorHandler.showGlobalError("Field not indexed!");
            }
        }
    }
    
    return bResult;
}

//
//  Sends a findByRowId request for the given table after replacing the rowid 
//  fields value with the given value. Childs are asked for their additional
//  requestsets.
//
//  Params:
//      sTable          Name of the table to do the find on
//      sRowId          RowId used for the find
//      bSuppressError  If true the not found error will not be shown
//
VdfForm.prototype.doFindByRowId = function(sTable, sRowId, bSuppressError){
    var sField, bHandled = false, sXml, oField;
    
    //  Find the statusfield
    oField = this.getStatusField(sTable + "__rowid");

    //  Clear errors
    this.oVdfErrorHandler.clearErrors();
    
    if(oField != null){
        //  Make sure changedstate is true
        oField.setValue("", true);
        oField.setValue(sRowId);
        
        //  Initiate user event
        this.onBeforeFindByRowId(this, sTable);
        
        //  Append requestsets to the find
        sXml = "<aDataSets>\n";
        sXml += this.getRequestSet("findByRowId", 1, sTable, 1, "", (bSuppressError ? "_NoWarning" : ""));

        //  Loop through childs for their additional requestsets
        for(iChild = 0; iChild < this.aVdfChilds.length; iChild++){
            if(typeof(this.aVdfChilds[iChild].getFindRequestSets) == "function"){
                sXml += this.aVdfChilds[iChild].getFindRequestSets(sTable);
            }
        }

        sXml += "</aDataSets>\n";

        this.sendRequest(sXml, false);
    }
}

//
//  Performes a find operation due to the given findmode on the index last 
//  focussed field. The childs are asked for additional requestsets.
//
//  Params:
//      sFindMode       Findmode used for the find
//      sMainTable      Forced maintable for the action (optional)
//      bSuppressError  If true the not found error will not be shown (optional)
//
VdfForm.prototype.doFind = function(sFindMode, sMainTable, bSuppressError){
    var sXml, iIndex;
    if(typeof(sMainTable) == "undefined"){
        var sMainTable = null;
    }
    
    // Clear errors
	this.oVdfErrorHandler.clearErrors();
    
    //  Get last selected field / mainfile
    if(this.oLastFocus != null || sMainTable != null){
        if(sMainTable == null){
            sMainTable = this.oLastFocus.getTableName();
        }
    
        //  Use first input element index else use data dictionary value or first
        iIndex = this.getVdfFieldAttribute(this.oLastFocus, "iIndex");

        if(iIndex != null && iIndex != "0"){
            
            //  Initiate user event
            this.onBeforeFind(this, sMainTable, iIndex, sFindMode);
        
            //  Append requestsets to the find
            sXml = "<aDataSets>\n";
            sXml += this.getRequestSet("find", 1, sMainTable, iIndex, sFindMode, (bSuppressError ? "_NoWarning" : ""));

            //  Loop through childs for their additional requestsets
            for(iChild = 0; iChild < this.aVdfChilds.length; iChild++){
                if(typeof(this.aVdfChilds[iChild].getFindRequestSets) == "function"){
                    sXml += this.aVdfChilds[iChild].getFindRequestSets(sMainTable);
                }
            }

            sXml += "</aDataSets>\n";

            this.sendRequest(sXml, false);
        }else{
            this.oVdfErrorHandler.showGlobalError("Field not indexed!");
        }
    }
    
}

//
//  Sends a save request using the servertable of the last selected field as 
//  maintable.
//
//  Params:
//      sForcedMainTable            Forced maintable for the action (optional)
//      bForcedAutoClearDeoState    Forced autoclear deo state value (optional) 
//
VdfForm.prototype.doSave = function(sForcedMainTable, bForcedAutoClearDeoState){
    var sXml, iChild, bResult = false, sMainTable;
    
    if(typeof(sForcedMainTable) != "undefined"){
        sMainTable = sForcedMainTable;
    }
    
    // Clear errors
	this.oVdfErrorHandler.clearErrors();
    
    //  Get last selected field / mainfile
    if(this.oLastFocus != null || sMainTable != null){
        if(sMainTable == null){
            sMainTable = this.oLastFocus.getServerTable();
        }

    	if(this.aTables[sMainTable].validate(false)){
        
            //  Initiate user event
            this.onBeforeSave(this, sMainTable);
        
            sXml = "<aDataSets>\n";
        	sXml += this.getRequestSet("save", 1, sMainTable, "", "");
            
            //  Add clear requestset if AutoClearDeoState is true
            if((bForcedAutoClearDeoState || (typeof(bForcedAutoClearDeoState) == "undefined" && this.bAutoClearDeoState)) && (typeof(sForcedMainTable) == "undefined")){
                sXml += this.getRequestSet("clear", 1, sMainTable, "", "");
            }
            
            for(iChild = 0; iChild < this.aVdfChilds.length; iChild++){
                if(typeof(this.aVdfChilds[iChild].getSaveRequestSets) == "function"){
                    sXml += this.aVdfChilds[iChild].getSaveRequestSets(sMainTable);
                }
            }
                
        	sXml += "</aDataSets>\n";

            this.bSaveResult = false;
        	this.sendRequest(sXml, false);
            
            bResult = this.bSaveResult;
    	}else{
            this.returnToField();
        }
    }
    
    return bResult;
}

//
//  Sends a clear request using the servertable of the last selected field as 
//  maintable.
//
//  Params:
//      sMainTable  Forced maintable for the action (optional)
//
VdfForm.prototype.doClear = function(sMainTable){
    var sXml, bHandled = false;
    if(typeof(sMainTable) == "undefined"){
        var sMainTable = null;
    }
    
   // Clear errors
	this.oVdfErrorHandler.clearErrors();
    
    //  Get last selected field / mainfile
    if(this.oLastFocus != null || sMainTable != null){
        if(sMainTable == null){
            sMainTable = this.oLastFocus.getServerTable();
        }
        for(iChild = 0; iChild < this.aVdfChilds.length && !bHandled; iChild++){
            if(typeof(this.aVdfChilds[iChild].doClear) == "function"){
                bHandled = this.aVdfChilds[iChild].doClear(sMainTable);
            }
        }
        
        if(!bHandled){
        
            //  Initiate user event
            this.onBeforeClear(this, sMainTable);
        
            sXml = "<aDataSets>\n";
        	sXml += this.getRequestSet("clear", 1, sMainTable, "", "");

        	for(iChild = 0; iChild < this.aVdfChilds.length; iChild++){
                if(typeof(this.aVdfChilds[iChild].getClearRequestSets) == "function"){
                    sXml += this.aVdfChilds[iChild].getClearRequestSets(sMainTable);
                }
            }
        	
            sXml += "</aDataSets>\n";

            this.sendRequest(sXml, false);
        }
    }
}

//
//  Sends a delete request using the servertable of the last selected field as 
//  maintable.
//
//  Params:
//      sMainTable  Forced maintable for the action (optional)
//
VdfForm.prototype.doDelete = function(sMainTable){
    var sXml;
    if(typeof(sMainTable) == "undefined"){
        var sMainTable = null;
    }

    // Clear errors
	this.oVdfErrorHandler.clearErrors();
    
    //  Get last selected field / mainfile
    if(this.oLastFocus != null || sMainTable != null){
        if(sMainTable == null){
            sMainTable = this.oLastFocus.getServerTable();
        }
        
        //  Initiate user event
        this.onBeforeDelete(this, sMainTable);
    
        sXml = "<aDataSets>\n";
        sXml += this.getRequestSet("delete", 1, sMainTable, "", "");
    	
    	for(iChild = 0; iChild < this.aVdfChilds.length; iChild++){
            if(typeof(this.aVdfChilds[iChild].getDeleteRequestSets) == "function"){
                sXml += this.aVdfChilds[iChild].getDeleteRequestSets(sMainTable);
            }
        }
	
        sXml += "</aDataSets>\n";

        this.sendRequest(sXml, false);
    }
}

//
//  Is called when a responseset for this form of a find request is received. It
//  generates datasets from the responsrows and sets the values using the 
//  recursive setValues method of the VdfTable.
//
//  Param:
//      oXmlData    The xml response data
//      sName       The name of the Request / Response set
//  Returns:
//      True if succesfully handled (false if errors occured)
//
VdfForm.prototype.handleFind = function(oXmlData, sName){
    var bResult = true, aRows, iRow, sMainTable = null;

    sMainTable = browser.xml.findNodeContent(oXmlData, "sMainTableName")
    
    if(browser.xml.findNodeContent(oXmlData, "bFound") == "true"){
        aRows = browser.xml.find(oXmlData, "TAjaxResponseRow");
        for(iRow = 0; iRow < aRows.length; iRow++){
            oDataSet = new VdfDataSet(this, aRows[iRow]);
            this.aTables[sMainTable].setValues(oDataSet, true, true, true, true);
        }
        
        this.returnToField(sMainTable);
    }else{
        if(sName.indexOf("_NoWarning") == -1){
            this.oVdfErrorHandler.showGlobalError("Not found!");
        }
        
        bResult = false;
        
        this.returnToField();
    }

    //  Initiate user event
    this.onAfterFind(this, sMainTable, bResult);
    
    return bResult;
}

//
//  Is called when a responseset for this form of a findByRowId request is 
//  received. It generates datasets and updates the displayed values. Then it
//  tries to select the first field of the table.
//
//  Param:
//      oXmlData    The xml response data
//      sName       The name of the Request / Response set
//  Returns:
//      True if succesfully handled (false if errors occured)
//
VdfForm.prototype.handleFindByRowId = function(oXmlData, sName){
    var bResult = true, aRows, iRow, sMainTable, sField;

    sMainTable = browser.xml.findNodeContent(oXmlData, "sMainTableName")
    
    if(browser.xml.findNodeContent(oXmlData, "bFound") == "true"){
        //  Set new field values
        aRows = browser.xml.find(oXmlData, "TAjaxResponseRow");
        for(iRow = 0; iRow < aRows.length; iRow++){
            oDataSet = new VdfDataSet(this, aRows[iRow]);
            this.aTables[sMainTable].setValues(oDataSet, true, true, true, true);
        }
        
        //  Set focus to the first field of this table
        this.returnToField(sMainTable);
        
    }else{
        //  Give error if needed
        if(sName.indexOf("_NoWarning") == -1){
            this.oVdfErrorHandler.showGlobalError("Not found!");
        }
        
        bResult = false;

        this.returnToField();
    }
    
    //  Initiate user event
    this.onAfterFindByRowId(this, sMainTable, bResult);
    
    return bResult;
    
}

//
//  Is called when a responseset for this form of a save request is received. It
//  generates a dataset from the responsrows and sets the values using the 
//  recursive setValues method of the VdfTable. The global bSaveResult property 
//  is set to true so the save method can return true if the save was 
//  succesfull.
//
//  Params:
//
//  Param:
//      oXmlData    The xml response data
//      sName       The name of the Request / Response set
//  Returns:
//      True if succesfully handled (false if errors occured)
//
VdfForm.prototype.handleSave = function(oXmlData, sName){
    var oXmlRow, bResult = true, sMainTable;

    sMainTable = browser.xml.findNodeContent(oXmlData, "sMainTableName");
    
    if(browser.xml.findFirst(oXmlData, "iRows").firstChild.nodeValue > 0){
        oXmlRow = browser.xml.findFirst(oXmlData, "TAjaxResponseRow");
        
        oDataSet = new VdfDataSet(this, oXmlData);
        this.aTables[sMainTable].setValues(oDataSet, true, true, true, true);

        this.bSaveResult = true;
        
        this.returnToField(sMainTable);
        
        //  Initiate user event
        this.onAfterFind(this, sMainTable);
    }else{
        bResult = false;
     
        this.returnToField();
    }
    
    //  Initiate user event
    this.onAfterSave(this, sMainTable);
    
    return bResult;
}

//
//  Is called when a responseset for this form of a clear request is received. 
//  It generates a dataset from the responsrow and sets the values using the 
//  recursive setValues method of the VdfTable. 
//
//  Param:
//      oXmlData    The xml response data
//      sName       The name of the Request / Response set
//  Returns:
//      True if succesfully handled (false if errors occured)
//
VdfForm.prototype.handleClear = function(oXmlData, sName){
    var oXmlRow, bResult = true, oDataSet, sMainTable;
    
    sMainTable = browser.xml.findNodeContent(oXmlData, "sMainTableName")

    if(browser.xml.findFirst(oXmlData, "iRows").firstChild.nodeValue > 0){
        oXmlRow = browser.xml.findFirst(oXmlData, "TAjaxResponseRow");

        oDataSet = new VdfDataSet(this, oXmlData);
        this.aTables[sMainTable].setValues(oDataSet, true, true, true, true);
        
        this.returnToFirstField();
    }else{
        bResult = false;
        this.returnToField();
    }
    
    //  Initiate user event
    this.onAfterClear(this, sMainTable);
    
    return bResult;
}

//
//  Is called when a responseset for this form of a clear request is received. 
//  It generates a dataset from the responsrow and sets the values using the 
//  recursive setValues method of the VdfTable. It calls the returnToFirstField
//  method if the delete was succesfull.
//
//  Param:
//      oXmlData    The xml response data
//      sName       The name of the Request / Response set
//  Returns:
//      True if succesfully handled (false if errors occured)
//
VdfForm.prototype.handleDelete = function(oXmlData, sName){
    var oXmlRow, oDataSet, bResult = true, sMainTable;
    
    sMainTable = browser.xml.findNodeContent(oXmlData, "sMainTableName");

    if(browser.xml.findFirst(oXmlData, "iRows").firstChild.nodeValue > 0){
        oXmlRow = browser.xml.findFirst(oXmlData, "TAjaxResponseRow");

        oDataSet = new VdfDataSet(this, oXmlData);
        this.aTables[sMainTable].setValues(oDataSet, true, true, true, true);

        this.returnToFirstField();
    }else{
        bResult = false;
        this.returnToField();
    }
    
    //  Initiate user event
    this.onAfterDelete(this, sMainTable);
    
    return bResult;
}

//
//  Called when lookup should be opened. Loops through aLookupDialogs to find 
//  lookup for the table of the currently selected field.
//
//  Returns:
//      True if lookup is shown
//
VdfForm.prototype.doLookup = function(){
    var bResult = false;
    
    if(this.oLastFocus != null){
        if(this.aLookupDialogs[this.oLastFocus.getTableName()] != null){
            bResult = this.aLookupDialogs[this.oLastFocus.getTableName()].display(this.oLastFocus);
        }
    }
    
    return bResult;
}




//
//  Generates a requestset using the given properties and the 
//  VdfGlobals.soap.getRequestSet method. If no field values are given it adds
//  all statusfields and the fields of the maintable and its childs.
//
//  Params:
//      sRequestType    Type of request (find/save/delete)
//      iMaxRows        Maximum number of records
//      sMainTable       Name of the mainfile
//      sIndex          Index
//      sFindMode       Findmode constant nr
//      sNameExtend     Extension to add to the name
//      sFieldValues    (Optional field values)
//      bNoClear        True if requestset depends on previous(default = false)
//  Returns:
//      String with xml containing the requestset
//
VdfForm.prototype.getRequestSet = function(sRequestType, iMaxRows, sMainTable, sIndex, sFindMode, sNameExtend, sFieldValues, bNoClear){
    var sRequestName = (this.sName + (sNameExtend ? sNameExtend : ""));
    
    if(typeof(sFieldValues) == "undefined" || sFieldValues == null){
        var sFieldValues = this.getCurrentStatusXml();
        sFieldValues += this.aTables[sMainTable].getDataXml();
    }
    
    if(typeof(bNoClear) == "undefined"){
        var bNoClear = false;
    }

    return VdfGlobals.soap.getRequestSet(sRequestName, sRequestType, sMainTable, sFieldValues, sIndex, sFindMode, iMaxRows, this.getRequestSetUserData(sRequestType, sRequestName), false, "", bNoClear);
}


//
//  Generates xml for the status fields using the VdfGlobals.soap.getVdfFieldXml
//  method.
//
//  Returns:
//      TRequestCol xml for the status fields
//
VdfForm.prototype.getCurrentStatusXml = function(){
    var sField, sXml, iChild;
    sXml = "";

    for(sField in this.aStatusFields){
        if(this.aStatusFields[sField] != null){
            sXml += VdfGlobals.soap.getVdfFieldXml(this.aStatusFields[sField]);
        }
    }

    return sXml;
}

//
//  Params:
//      sName   Full name of the statusfield
//  Returns:
//      Statusfield VdfField object (null if not found)
//
VdfForm.prototype.getStatusField = function(sName){
    return this.aStatusFields[sName];
}

//
//  Params:
//      sName   Full name of the statusfield
//  Returns:
//      Field VdfField object (null if not found)
//
VdfForm.prototype.getField = function(sName){
    return this.aFields[sName];
}

//
//  Params:
//      sName   Full name of the user field
//  Returns:
//      Userfield VdfField object (null if not found)
//
VdfForm.prototype.getUserField = function(sName){
    return this.aUserFields[sName];
}

//
//  It returns the value of the field attribute. The attribute of the html dom 
//  element if set, else the vdfinfo value and if this is false it uses the 
//  foreign field value.
//
//  Params:
//      oField      VdfField object with field info
//      sAttribute  Name of the attribute
//  Returns:
//      Value of attribute (null if not found)
//
VdfForm.prototype.getVdfFieldAttribute = function(oField, sAttribute){
    var sResult, sForeign, sServerTable;
    
    sServerTable = oField.getServerTable();
    if(sServerTable == null){
        sServerTable = this.sServerTable;
    }
   
    //  Get field attribute
    sResult = oField.getVdfAttribute(sAttribute);
    
    //  Else get from info request
    if(sResult == null){
        sResult = this.oVdfInfo.getColumnProperty(oField.getName(), sAttribute);
        
        //  If result is false return foreign field value (if it has one)
        if(!sResult || sResult == "false"){
            sForeign = "bAutoFind, bAutoFind_GE, bDisplayOnly, bFindReq, bNoEnter, bNoPut, bSkipFound";

            if(sForeign.indexOf(sAttribute) != -1){
                if(this.aTables[sServerTable].isTableParent(oField.getTableName())){
                    sAttribute = "b" + "Foreign_" + sAttribute.substr(1);
                }
                
                sResult = this.oVdfInfo.getColumnProperty(oField.getName(), sAttribute);
            }
        }
    }
    
    //  Convert string to boolean
    if(sResult == "true"){
        return true;
    }else if(sResult == "false"){
        return false;
    }else{
        return sResult;
    }
}



//
//  Sends an request using the XmlRequest object of the given type containing
//  the given datasets. It adds the sessionkey, target webobject (see 
//  sWebObject property) and the request user data.
//
//  Params:
//      saDataSets  String with xml datasets
//      bASynchrome True if the request should be send ASynchrome
//
VdfForm.prototype.sendRequest = function(saDataSets, bASynchrome){
    var sXml;

    //  Complete request
    sXml = "<tRequestData>\n";
    sXml += "<sSessionKey>" + browser.cookie.get("vdfSessionKey") + "</sSessionKey>\n";
    sXml += "<sWebObject>" + this.sWebObject + "</sWebObject>\n";
    sXml += saDataSets;
    sXml += "<aUserData>\n"
    sXml += this.getRequestUserData();
    sXml += "</aUserData>\n"
    sXml += "</tRequestData>\n";

    //  Send request
    oRequest = new comm.XmlRequest(bASynchrome, "Request", sXml, this.handleRequest, this, this.sWebServiceUrl);
    oRequest.request();
}

//
//  Is called by the request object when the request is finished. (overloads
//  XmlRequest.onFinished method) It makes sure (using sRequestType and sName)
//  that the correct function & object (parent or one of the childs) handles
//  the request.
//
//  Param:
//      oRequest    The request object
//
VdfForm.prototype.handleRequest = function(oRequest){
    var oResult, bResult = true, oSource, aResponseSets, aErrors, sName, iSet; 
    var iError, iChild, aUserDataSets;

    if(oRequest.iError == 0){
        oResult = oRequest.getResponseXml();

        aErrors = browser.xml.find(oResult, "TAjaxError");
        if(aErrors.length > 0){
            for(iError = 0; iError < aErrors.length; iError++){
                this.oVdfErrorHandler.xmlVdfError(aErrors[iError]);
            }
        }else{   
            aResponseSets = browser.xml.find(oResult, "TAjaxResponseSet");

            //  Loop through responsesets
            for(iSet = 0; iSet < aResponseSets.length && bResult; iSet++){
                sName = browser.xml.findNodeContent(aResponseSets[iSet], "sName");
				sType = browser.xml.findNodeContent(aResponseSets[iSet], "sResponseType");
				
                //  Find source of requestset
                if(sName.indexOf(this.sName) != -1){
                    oSource = this;
                }else{
                    for(iChild = 0; iChild < this.aVdfChilds.length; iChild++){
                        if(sName.indexOf(this.aVdfChilds[iChild].sName) != -1){
                            oSource = this.aVdfChilds[iChild];

                        }
                    }
                }

                //  Call source to handle result
                if(sType == "find"){
                    bResult = oSource.handleFind(aResponseSets[iSet], sName);
                }else if(sType == "findByRowId"){
                    bResult = oSource.handleFindByRowId(aResponseSets[iSet], sName);
                }else if(sType == "save"){
                    bResult = oSource.handleSave(aResponseSets[iSet], sName);
                }else if(sType == "delete"){
                    bResult = oSource.handleDelete(aResponseSets[iSet], sName);
                }else if(sType == "clear"){
                    bResult = oSource.handleClear(aResponseSets[iSet], sName);
                }
                
                this.handleRequestSetUserData(browser.xml.findFirst(aResponseSets[iSet], "aUserData"), sType, sName);
            }
            
            aUserDataSets = browser.xml.find(oResult, "aUserData");
            this.handleRequestUserData(aUserDataSets[aUserDataSets.length - 1]);
        }
    }else{
        this.oVdfErrorHandler.showGlobalError(oRequest.sErrorMessage, oRequest.iError);
    }
}

//
//  Is calles to handle keyactions using the given event information. It 
//  executes the action that belongs to the keyaction.
//
//  Params:
//      iKeyCode    Keycode of the pressed key
//      iCharCode   Code of the char that belongs to the key
//      bCrtl       True if control (ctrl) is down
//      bShift      True if shift key is down
//      bAlt        True if alt key is down
//  Returns:
//      True if an action is executed
//
VdfForm.prototype.keyAction = function(iKeyCode, iCharCode, bCrtl, bShift, bAlt){
    var bResult = false;

    if(this.bAttachVdfKeyActions){
        if(!bCrtl && !bShift && !bAlt){
            switch(iKeyCode){
                case 119:   // F8:  find next
                    this.doFind(dfGT);
                    bResult = true;
                    break;
                case 118:   // F7:  find previous
                    this.doFind(dfLT);
                    bResult = true;
                    break;
                case 120:   // F9:  find equal
                    this.doFind(dfGE);
                    bResult = true;
                    break;
                case 113:   // F2:  save
                    this.doSave();
                    bResult = true;
                    break;
                case 116:   // F5:  clear
                    this.doClear();
                    bResult = true;
                    break;
                case 115:   // F4:  lookup
                    bResult = this.doLookup();
                    break;
                case 13:
                    bResult = this.onEnter();
                    break;
            }
        }else if(bCrtl && !bShift && !bAlt){
            switch(iKeyCode){
                case 36:    // ctrl - home: find first
                    this.doFind(dfFirst);
                    bResult = true;
                    break;
                case 35:    // ctrl - end:  find last
                    this.doFind(dfLast);
                    bResult = true;
                    break;
            }
        }else if(!bCrtl && bShift && !bAlt){
            switch(iKeyCode){
                case 113:   // shift - F2:  delete
                    this.doDelete();
                    bResult = true;
                    break;
            }
        }
    }

    return bResult;
}



//
//  Returns the focus to the last selected field (if known) otherwise selects 
//  the first field.
//
//  Params:
//      sTable  (Optional) Restricts to a table
//
VdfForm.prototype.returnToField = function(sTable){
    var sField;
    
    if(typeof(sTable) != "undefined"){  //  Return focus to a table field
        if(this.oLastFocus != null && this.oLastFocus.getTableName() == sTable){
            this.oLastFocus.focusSelect();
        }else{
            for(sField in this.aFields){
                if(this.aFields[sField].getTableName() == sTable){
                    this.aFields[sField].focusSelect();
                    this.oLastFocus = this.aFields[sField];
                    break;
                }
            }
        }
    }else{  //  Return focus to the last or a table field
        if(this.oLastFocus != null){
            this.oLastFocus.focusSelect();
        }else{
            this.returnToFirstField();
        }
    }
}

//
//  Gives the focus to the first field or the field with the vdfFocus property
//
VdfForm.prototype.returnToFirstField = function(){
    var bFirst = true;
    if(this.oVdfActiveObject == this){ 
        for(sField in this.aFields){
            if(bFirst || this.aFields[sField].getAttribute("bFocus") == "true"){
                this.aFields[sField].focusSelect();
                
                if(this.oLastFocus != this.aFields[sField]){
                    if(this.oPreviousFocus != this.oLastFocus){
                        this.oPreviousFocus = this.oLastFocus;
                    }
                    this.oLastFocus = this.aFields[sField];
                }
                bFirst = false;
            }
        }
    }
}

//
//  Fetches user data by adding the fields in aUserFields and calling the user
//  defined onRequestUserData method.
//
//  Returns:
//      String with user data xml.
//
VdfForm.prototype.getRequestUserData = function(){
    var sXml = "", sUserField;
    
    for(sUserField in this.aUserFields){
        sXml += VdfGlobals.soap.getUserData(this.aUserFields[sUserField].getName(), this.aUserFields[sUserField].getValue());
    }
    
    sXml += this.onRequestUserData();
    return sXml; 
}             

//
//  Fetches user data for the requestset by calling the onRequestSetUserData 
//  method.
//  
//  Params:
//      sRequestType    Type of request (find/save/clear/delete...)
//      sRequestName    Name of request
//  Returns:
//      String with user data xml
//
VdfForm.prototype.getRequestSetUserData = function(sRequestType, sRequestName){
    return this.onRequestSetUserData(sRequestType, sRequestName);
}

//
//  Called when the request is finished and handled. Parses the user data and
//  updates the userfields where needed. Then it calls the user defined 
//  onRequestFinished method.
//
//  Params:
//      oUserDataXml    Xml node with user data
//
VdfForm.prototype.handleRequestUserData = function(oUserDataXml){
    var aUserData = this.parseUserData(oUserDataXml);
    
    for(sVar in aUserData){
        if(this.aUserFields[sVar] != null){
            this.aUserFields[sVar].setValue(aUserData[sVar]);
        }
    }
    
    this.onRequestFinished(aUserData);
}

//
//  Called when an requestset is finished and handled. Parses the user data and
//  calls the user defined onRequestSetFinished.
//
//  Params:
//      oUserDataXml    Xml node with user data
//      sRequestType    Type of request (find/save/clear/delete...)
//      sRequestName    Name of request
//
VdfForm.prototype.handleRequestSetUserData = function(oUserDataXml, sRequestType, sRequestName){
    var aUserData = this.parseUserData(oUserDataXml);
    
    this.onRequestSetFinished(aUserData, sRequestType, sRequestName);
}

//
//  Parses xml node with user data into array with name value pairs.
//
//  Params:
//      oUserDataXml    Xml node with user data
//  Returns:
//      Array with user data pairs
//
VdfForm.prototype.parseUserData = function(oUserDataXml){
    var aPairs, iPair, aUserData = new Array();
    
    aPairs = browser.xml.find(oUserDataXml, "TAjaxUserData");
    for(iPair = 0; iPair < aPairs.length; iPair++){
        aUserData[browser.xml.findNodeContent(aPairs[iPair], "sName")] = browser.xml.findNodeContent(aPairs[iPair], "sValue");
    }
    
    return aUserData;
}


//
//  Catches the onfocus event of the standart input elements of the form
//
//  Params:
//      e   Event object on some browsers
//
VdfForm.prototype.onFieldFocus = function(e){
    var oSource, oForm, oField = null;

    if(!browser.events.canceled(e)){
        oSource = browser.events.getTarget(e);
        if(oSource == null) return false;

        if(oSource.tagName == "INPUT" || oSource.tagName == "SELECT" || oSource.tagName == "TEXTAREA"){
            oForm = findForm(oSource);
            if(oForm == null) return false; 
            oForm.oVdfActiveObject = oForm;
            
            //  Fetch field
            oField = oForm.aFields[oSource.name.toLowerCase()];
            
            //  If not found fetch from status fields
            if(oField == null){
                oField = oForm.aStatusFields[oSource.name.toLowerCase()];
            }
            
            if(oField != null){
                if(oForm.oPreviousFocus != oForm.oLastFocus){
                    oForm.oPreviousFocus = oForm.oLastFocus;
                }
                oForm.oLastFocus = oField;
            }
        }
    }
    
}

//
//  Catches the keypress event of the standart input elements of the form and
//  then calls the method according to the pressed key.
//
//  Params:
//      e   Event object on some browsers
//
VdfForm.prototype.onFieldKeyPress = function(e){
    var oSource, oForm;

    if(!browser.events.canceled(e)){
        oSource = browser.events.getTarget(e);
        if(oSource == null) return false;
        
        oForm = findForm(oSource);
        if(oForm == null) return false;
        
        if(oForm.keyAction(browser.events.getKeyCode(e), browser.events.getCharCode(e), e.ctrlKey, e.shiftKey, e.altKey)){
            browser.events.stop(e);
            return false;
        }
    }
    
    return true;
}

//
//  Catches the change event of status fields that aren't hidden and calls the 
//  findByRowId method.
//
//  Params:
//      e   Event object on some browsers
//
VdfForm.prototype.onStatusFieldChange = function(e){
    var oSource, oForm;

    if(!browser.events.canceled(e)){
        oSource = browser.events.getTarget(e);
        if(oSource == null) return false;
        
        oForm = findForm(oSource);
        if(oForm == null) return false;
        
        oField = oForm.aStatusFields[oSource.name.toLowerCase()];
        
        if(oField != null){
            oForm.doFindByRowId(oField.getTableName(), oField.getValue());
        }
    }
        
}




//  - - - - - - - USER EVENTS - - - - - - - -

//
//  Is called when enter is pressed or a child event (like doubleclick in
//  DbList). If true is returned the event will be killed and no other
//  handling is possible.
//
//  Returns:
//      True if an action is done
//
VdfForm.prototype.onEnter = function(){
    return false;
}

//
//  Is called before an request is send to give developer an change to add his 
//  own data. Should be overloaded and return userdat (use 
//  VdfGlobals.soap.getUserData to generate userdata xml) (should be overloaded)
//
//  Returns:
//      User data xml
//
VdfForm.prototype.onRequestUserData = function(){
    return "";
}

//
//  Is called begore anr equest is send for each requestset to give the 
//  developer an change to add his own requestset data. (use 
//  VdfGlobals.soap.getUserData to generate userdata xml) (should be overloaded)
//
//  Params:
//      sRequestType    Type of request (find/save/delete/clear..)
//      sRequestName    Name of request
//  Returns:
//      User data xml
//
VdfForm.prototype.onRequestSetUserData = function(sRequestType, sRequestName){
    return "";
}

//
//  Is called when the request is finished and the response is handled. Gives
//  the developer an chance to handle the returned user data. (should be 
//  overloaded)
//
//  Params:
//      aUserData   Array with user data aUserData[<name>] == <value>
//
VdfForm.prototype.onRequestFinished = function(aUserData){
    
}

//
//  Is called after each requestset that is handled giving the developer an 
//  change to handler the returned user data. (should be overloaded)
//
//  Params:
//      aUserData       Array with user data aUserData[<name>] == <value>
//      sRequestType    Type of request (find/save/delete/clear..)
//      sRequestName    Name of request
//
VdfForm.prototype.onRequestSetFinished = function(aUserData, sRequestType, sRequestName){
    
}

//
//  Are called before and after user actions
//
//  Overloading can be done in asp file like:
//
//  function initForm(){
//      getUserControl("form").onBeforeFind = function(oVdfForm, sTable, sIndex, sFindMode){
//          alert("A find well be performed on form: " + oVdfForm.sName + " on table: " + sTable);
//      }
//  }
//

VdfForm.prototype.onBeforeFindByRowId = function(oVdfForm, sTable){}

VdfForm.prototype.onAfterFindByRowId = function(oVdfForm, sTable, bFound){}

VdfForm.prototype.onBeforeFind = function(oVdfForm, sTable, sIndex, sFindMode){}

VdfForm.prototype.onAfterFind = function(oVdfForm, sTable, bFound){}

VdfForm.prototype.onBeforeClear = function(oVdfForm, sTable){}

VdfForm.prototype.onAfterClear = function(oVdfForm, sTable){}

VdfForm.prototype.onBeforeDelete = function(oVdfForm, sTable){}

VdfForm.prototype.onAfterDelete = function(oVdfForm, sTable){}

VdfForm.prototype.onBeforeSave = function(oVdfForm, sTable){}

VdfForm.prototype.onAfterSave = function(oVdfForm, sTable){}
